EEPROM读写学习笔记与I2C总线(二)

无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在运行程序才会用到,有些数据体量较大对于获取时效性并不太强,各种各样的数据也就有不同的存储载体,这次在EEPROM读写中,顺道把看到的关于存储的一些东西整理一下,有些话来自于网友,所以还是那句话,看到的人要带着自己的思考去看,记住尽信书不如无书,fighting!!!

一、基本概念

最熟悉的两个词语应该是RAM与ROM,RAM(Random Access Memory)的全名为随机存取记忆体,它相当于PC机上的移动存储,用来存储和保存数据的。它在任何时候都可以读写,RAM通常是作为操作系统或其他正在运行程序的临时存储介质,它的一切都是最好的,唯一缺点断电一切东西都没有了。一般情况下,现在移动设备也多了,我们叫它内存,更通常的叫运行内存。还有一个熟悉的词DDR2或DDR3,后面还会学习到的。

 ROM(Read Only Memory)的全名为唯读记忆体,它相当于PC机上的硬盘,用来存储和保存数据。ROM数据不能随意更新,但是在任何时候都可以读取。即使是断电,ROM也能够保留数据。但是资料一但写入后只能用特殊方法或根本无法更改,但这么久了ROM已经有了很大的发展,不再是最初的摸样了。rom最初不能编程,出厂什么内容就永远什么内容,不灵活。后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉。人类文明不断进步,终于出现了可多次擦除写入的EPROM,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。历史的车轮不断前进,伟大的EEPROM出现了,拯救了一大批程序员,终于可以随意的修改rom中的内容了,这一段话就说出了ROM的发展历程。

狭义的EEPROM:这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。我们也就发现了EEPROM的确可以实现随意读写,EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。可介绍的这两种都不存在大容量并且也十分昂贵,那我们平时见到的几十G的存储设备是什么?flash就应运而生了。flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。flash做的改进就是擦除时不再以字节为单位,而是以为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash。

接下来说一下flash的分类,flash分为nor flashnand flash。nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。NOR Flash的读取,用户可以直接运行装载在NOR FLASH里面的代码。NAND Flash没有采取内存RAM的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。

二、I2C总线

这个在我转载的一篇文章里面有很详细的描述,就不在提及了。有一个问题是无论UART还是I2C都是串行按位传输数据,区别在哪?还有SPI传输,下面分别总结一下三者的特点。

UART:两线,一根发送一根接收,可以全双工通信,数据异步传输,对双方的时序要求比较严格,在多机通信上面用的最多。按照标准波特率完成双向通讯,速度慢,之前提到采集一位数据就需要16个时钟周期,适合远距离传输,比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。UART需要固定的波特率,就是说两位数据的间隔要相等,

I2C:能用于替代标准的并行总线,能连接的各种集成电路和功能模块。I2C是多主控总线,所以任何一个设备都能像主控器一样工作,并控制总线。总线上每一个设备都有一个独一无二的地址,根据设备它们自己的能力,它们可以作为发射器或接收器工作。多路微控制器能在同一个I2C总线上共存,当然在任何时间点上只能有一个主控。一般用于同一板卡上芯片之间的通信,较少用于远距离通信。

SPI:SPI接口和UART相比,多了一条同步时钟线,对通信双方的时序要求不严格不同设备之间可以很容易结合,而且通信速度非常快。一般用在产品内部元件之间的高速数据通信上面,如大容量存储器flash等。高速同步串行口,3~4线接口,收发独立、可同步进行。

三、EEPROM通信举例

同样通过一个程序来学习里面内容。自加内容用红笔标出。

通过状态机 i 来切换 IIC 的不同状态,譬 如接收到写命令,状态机i=0 转入 Start 状态,SDA 先变低,再 SCL 变低;状态机i=1 开 始 转 入 写 设 备 地 址 0x80; 之 后 状 态 机 转 到 7 开 始 发 送 8 位 的 数 据 , 其 中 状 态 机 i=7,8,9,10,11,12,13,14 是 IIC 发送8位的数据,然后状态机进入 i=15 等待 IIC 从设备的应答 信号。状态机 i=16 为判断是否有应答,如果有的话状态机转到 i=2 写 IIC 的地址,然后状态机 又是重复i=7,8,9,10,11,12,13,14 发送地址和 i=15 等待应答,i=16 判断应答。最后状态机 i=3 开始发送 IIC 写数据。发送完数据 i=4 发送 Stop 信号。

  1 module iic_com
  2 (
  3     input CLK,
  4      input RSTn,
  5
  6      input [1:0] Start_Sig,             //read or write command
  7      input [7:0] Addr_Sig,              //eeprom words address
  8      input [7:0] WrData,                //eeprom write data
  9      output [7:0] RdData,               //eeprom read data
 10      output Done_Sig,                   //eeprom read/write finish
 11
 12      output SCL,
 13      inout SDA
 14
 15 );
 16
 17 parameter F250K = 9‘d200;                //250Khz的时钟分频系数    //200分频系数不必要用到9位,可改为8
 18
 19 reg [4:0]i;           //用来只是状态机
 20 reg [4:0]Go;
 21 reg [8:0]C1;
 22 reg [7:0]rData;         //读信号
 23 reg rSCL;
 24 reg rSDA;
 25 reg isAck;
 26 reg isDone;          //结束信号
 27 reg isOut;
 28
 29 assign Done_Sig = isDone;
 30 assign RdData = rData;
 31 assign SCL = rSCL;
 32 assign SDA = isOut ? rSDA : 1‘bz;        //SDA数据输出选择 //SDA的数据输出受到SCL的控制,SCL为高时SDA保持不变,在接收应答位期间SDA也受控制
 33
 34 //****************************************//
 35 //*             I2C读写处理程序            *//
 36 //****************************************//
 37 always @ ( posedge CLK or negedge RSTn )
 38      if( !RSTn )  begin
 39             i <= 5‘d0;               //状态机初始为0
 40             Go <= 5‘d0;
 41             C1 <= 9‘d0;
 42             rData <= 8‘d0;
 43             rSCL <= 1‘b1;            //数据线和时钟线保持高电平初始值
 44             rSDA <= 1‘b1;
 45             isAck <= 1‘b1;           //信号为低电平时规定为有效应答位,高电平为非有效应答位。
 46             isDone <= 1‘b0;
 47             isOut <= 1‘b1;
 48      end
 49      else if( Start_Sig[0] )                     //I2C 数据写   //start_sig用来判断数据为读或写
 50          case( i )
 51
 52             0: //发送IIC开始信号
 53              begin
 54                     isOut <= 1;                         //SDA端口输出
 55
 56                     if( C1 == 0 ) rSCL <= 1‘b1;
 57                     else if( C1 == 200 ) rSCL <= 1‘b0;       //SCL由高变低   //分频系数为200,计数到200表明经过一个新的时钟周期时钟线变为低电平。
 58
 59                     if( C1 == 0 ) rSDA <= 1‘b1;
 60                     else if( C1 == 100 ) rSDA <= 1‘b0;        //SDA先由高变低   //计数到100时,rSDA变为低电平,符合信号开始发送条件。
 61
 62                     if( C1 == 250 -1) begin C1 <= 9‘d0; i <= i + 1‘b1; end
 63                     else C1 <= C1 + 1‘b1;  //i=0用来表示开始信号发送,根据SDA与SCL变化可得,此处C1到达249,表示又过了四分之一个新时钟周期,i+1,运行下一步
 64              end
 65
 66              1: // Write Device Addr
 67              begin rData <= {4‘b1010, 3‘b000, 1‘b0}; i <= 5‘d7; Go <= i + 1‘b1; end     //非阻塞赋值,Go为2    
 68
 69              2: // Wirte Word Addr
 70              begin rData <= Addr_Sig; i <= 5‘d7; Go <= i + 1‘b1; end      //addr_sig为word address,Go为3
 71
 72              3: // Write Data
 73              begin rData <= WrData; i <= 5‘d7; Go <= i + 1‘b1; end       //写入数据,Go为4,WrData数值赋给rData。
 74
 75              4: //发送IIC停止信号
 76              begin
 77                 isOut <= 1‘b1;
 78
 79                 if( C1 == 0 ) rSCL <= 1‘b0;
 80                 else if( C1 == 50 ) rSCL <= 1‘b1;     //SCL先由低变高
 81
 82                  if( C1 == 0 ) rSDA <= 1‘b0;
 83                  else if( C1 == 150 ) rSDA <= 1‘b1;     //SDA由低变高     //SCL处于高电位时,SDA由低到高变化,处于结束位
 84
 85                  if( C1 == 250 -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end
 86                  else C1 <= C1 + 1‘b1;
 87              end
 88
 89              5:
 90              begin isDone <= 1‘b1; i <= i + 1‘b1; end       //写I2C 结束
 91
 92              6:
 93              begin isDone <= 1‘b0; i <= 5‘d0; end
 94
 95              7,8,9,10,11,12,13,14:                         //发送Device Addr/Word Addr/Write Data
 96              begin
 97                  isOut <= 1‘b1;           //isout=1, SDA <= rSDA
 98                   rSDA <= rData[14-i];                      //高位先发送
 99
100                   if( C1 == 0 ) rSCL <= 1‘b0;
101                  else if( C1 == 50 ) rSCL <= 1‘b1;         //SCL高电平100个时钟周期,低电平100个时钟周期
102                   else if( C1 == 150 ) rSCL <= 1‘b0;
103
104                   if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end     //产生250Khz的IIC时钟   //i=14运行之后,状态机i=15
105                   else C1 <= C1 + 1‘b1;
106              end
107
108              15:                                          // waiting for acknowledge
109              begin
110                  isOut <= 1‘b0;                            //SDA端口改为输入
111                  if( C1 == 100 ) isAck <= SDA;             //读取IIC 从设备的应答信号
112
113                   if( C1 == 0 ) rSCL <= 1‘b0;
114                   else if( C1 == 50 ) rSCL <= 1‘b1;         //SCL高电平100个时钟周期,低电平100个时钟周期
115                   else if( C1 == 150 ) rSCL <= 1‘b0;
116
117                   if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end    //产生250Khz的IIC时钟
118                   else C1 <= C1 + 1‘b1;
119              end
120
121              16:
122              if( isAck != 0 ) i <= 5‘d0;    //判断是否接收到应答信号
123              else i <= Go;                  //状态机i=1时,计算出i=2
124
125               endcase
126
127       else if( Start_Sig[1] )                     //I2C 数据读
128             case( i )
129
130              0: // Start
131              begin
132                   isOut <= 1;                      //SDA端口输出
133
134                   if( C1 == 0 ) rSCL <= 1‘b1;
135                     else if( C1 == 200 ) rSCL <= 1‘b0;      //SCL由高变低
136
137                     if( C1 == 0 ) rSDA <= 1‘b1;
138                     else if( C1 == 100 ) rSDA <= 1‘b0;     //SDA先由高变低
139
140                     if( C1 == 250 -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end
141                      else C1 <= C1 + 1‘b1;
142              end
143
144              1: // Write Device Addr(设备地址)
145              begin rData <= {4‘b1010, 3‘b000, 1‘b0}; i <= 5‘d9; Go <= i + 1‘b1; end    //先进行一个伪写操作
146
147              2: // Wirte Word Addr(EEPROM的写地址)
148              begin rData <= Addr_Sig; i <= 5‘d9; Go <= i + 1‘b1; end
149
150              3: // Start again
151              begin
152                  isOut <= 1‘b1;          //开始进行读操作
153
154                  if( C1 == 0 ) rSCL <= 1‘b0;
155                   else if( C1 == 50 ) rSCL <= 1‘b1;
156                   else if( C1 == 250 ) rSCL <= 1‘b0;
157
158                  if( C1 == 0 ) rSDA <= 1‘b0;
159                   else if( C1 == 50 ) rSDA <= 1‘b1;
160                   else if( C1 == 150 ) rSDA <= 1‘b0;
161
162                   if( C1 == 300 -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end
163                   else C1 <= C1 + 1‘b1;
164              end
165
166              4: // Write Device Addr ( Read )
167              begin rData <= {4‘b1010, 3‘b000, 1‘b1}; i <= 5‘d9; Go <= i + 1‘b1; end
168
169              5: // Read Data
170              begin rData <= 8‘d0; i <= 5‘d19; Go <= i + 1‘b1; end
171
172              6: // Stop
173              begin
174                  isOut <= 1‘b1;
175                  if( C1 == 0 ) rSCL <= 1‘b0;
176                   else if( C1 == 50 ) rSCL <= 1‘b1;
177
178                   if( C1 == 0 ) rSDA <= 1‘b0;
179                   else if( C1 == 150 ) rSDA <= 1‘b1;
180
181                   if( C1 == 250 -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end
182                   else C1 <= C1 + 1‘b1;
183              end
184
185              7:                                                       //写I2C 结束
186              begin isDone <= 1‘b1; i <= i + 1‘b1; end
187
188              8:
189              begin isDone <= 1‘b0; i <= 5‘d0; end
190
191
192              9,10,11,12,13,14,15,16:                                  //发送Device Addr(write)/Word Addr/Device Addr(read)
193              begin
194                   isOut <= 1‘b1;
195                     rSDA <= rData[16-i];                                //高位先发送   //将rData数据赋值给数据线,伪写操作
196
197                    if( C1 == 0 ) rSCL <= 1‘b0;
198                     else if( C1 == 50 ) rSCL <= 1‘b1;                   //SCL高电平100个时钟周期,低电平100个时钟周期
199                     else if( C1 == 150 ) rSCL <= 1‘b0;
200
201                     if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end   //产生250Khz的IIC时钟
202                     else C1 <= C1 + 1‘b1;
203              end
204
205              17: // waiting for acknowledge
206              begin
207                   isOut <= 1‘b0;                                       //SDA端口改为输入
208
209                     if( C1 == 100 ) isAck <= SDA;                        //读取IIC 的应答信号
210
211                     if( C1 == 0 ) rSCL <= 1‘b0;
212                     else if( C1 == 50 ) rSCL <= 1‘b1;                 //SCL高电平100个时钟周期,低电平100个时钟周期
213                     else if( C1 == 150 ) rSCL <= 1‘b0;
214
215                     if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end     //产生250Khz的IIC时钟
216                     else C1 <= C1 + 1‘b1;
217              end
218
219              18:
220                   if( isAck != 0 ) i <= 5‘d0;
221                     else i <= Go;
222
223
224              19,20,21,22,23,24,25,26: // Read data
225              begin
226                  isOut <= 1‘b0;
227                  if( C1 == 100 ) rData[26-i] <= SDA;                              //高位先接收
228
229                   if( C1 == 0 ) rSCL <= 1‘b0;
230                   else if( C1 == 50 ) rSCL <= 1‘b1;                  //SCL高电平100个时钟周期,低电平100个时钟周期
231                   else if( C1 == 150 ) rSCL <= 1‘b0;
232
233                   if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= i + 1‘b1; end     //产生250Khz的IIC时钟
234                   else C1 <= C1 + 1‘b1;
235              end
236
237              27: // no acknowledge
238              begin
239                  isOut <= 1‘b1;
240
241                   if( C1 == 0 ) rSCL <= 1‘b0;
242                   else if( C1 == 50 ) rSCL <= 1‘b1;
243                   else if( C1 == 150 ) rSCL <= 1‘b0;
244
245                   if( C1 == F250K -1 ) begin C1 <= 9‘d0; i <= Go; end
246                   else C1 <= C1 + 1‘b1;
247             end
248
249             endcase
250
251
252
253
254 endmodule
时间: 2024-10-10 05:15:15

EEPROM读写学习笔记与I2C总线(二)的相关文章

Object C学习笔记26-文件管理(二)

上一篇简单的介绍了如何获取文件属性,删除,拷贝文件等,本文继续记录Object C中文件IO操作. 一. 获取文件的执行主目录 在Object C中提供了一个方法 NSHomeDirectory() 用于获得执行执行的主目录,使用如下代码测试: NSString *homePath=NSHomeDirectory(); NSLog(@"执行文件的主目录:%@",homePath); 通过以上代码可以正确的输出应用程序的执行目录,上一张也提到了文件的目录问题,这个和Windows系统的有

Halcon学习笔记之支持向量机(二)

例程:classify_halogen_bulbs.hdev 在Halcon中模式匹配最成熟最常用的方式该署支持向量机了,在本例程中展示了使用支持向量机对卤素灯的质量检测方法.通过这个案例,相信大家可以对支持向量机的使用有一个更加清晰的了解.在相当多的检测和识别的应用中,都可以使用相同的方法来解决分类问题. 图1. 卤素灯图像 大致原理: 一.准备阶段:描述样本 1. 准备好两组卤素灯图像样本,好坏的各若干张图像: 2. 对样本图像进行分割,获取卤素灯关键部位区域: 3. 选择合适的对图像的描述

马程序员学习笔记——红黑树解析二

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 四.树中删除元素 1.先找到需要删除的元素. 2. 2.1如果被删元素没有子元素,那么直接用NIL节点代替他: 2.2如果被删元素只有一个子元素,那么直接用这个子元素代替他: 2.3如果被删元素有两个子元素,那么就用左子元素中的最大元素或者右子元素的最小元素代替他. 比如说原来要删除的元素是N,N有两个分支,其中P是N左分支中的最大元素,那么就

capwap学习笔记&mdash;&mdash;初识capwap(二)

2.5.1 AC发现机制 WTP使用AC发现机制来得知哪些AC是可用的,决定最佳的AC来建立CAPWAP连接. WTP的发现过程是可选的.如果在WTP上静态配置了AC,那么WTP并不需要完成AC的发现过程. WTP首先发送一个 Discovery Request message给受限的广播地址,或者CAPWAP的多播地址(224.0.1.140),或者是预配置的AC的单播地址.在IPV6网络中,由于广播并不存在,因此使用"All ACs multicast address" (FF0X

Nani_xiao的图像处理学习笔记:透视变换(二):X,Y方向校正原理

接着上一篇进行,上一篇为: Nani_xiao的图像处理学习笔记:透视变换(一) 这里采用一点透视投影 X 方向校正 图2 是透视投影的灭点原理图.在不考虑其他畸变的情况下,边ab 和边cd 平行于X 轴, 而边ac 和边bd 则和X 轴成一定的夹角.根据a .b .c .d 点的图像坐标,可以求出透视投影的灭点e 的坐标(mx , my)(在图像坐标系下). 然后根据透视缩小效应, 对其进行反运算, 进行X 方向的校正.在X 方向的校正中, 可以选择图像高度(0- H - 1)任意一条水平线的

Dynamic CRM 2013学习笔记(四十二)流程5 - 实时/同步工作流(Workflow)用法图解

实时工作流跟插件一样,也是用事件执行管道来执行,能在pre,post或核心操作中执行.跟插件一样,不能在创建之前和删除之后执行.如果执行过程中有异常发生,会取消并回滚整个操作.实时工作流里所有的活动和子流程都是一个事务,不像异步工作流里,子流程是单独的一个事务.不能使用等待或并行等待条件步骤.如果执行成功,就看不到执行的log.实时工作流能被转到异步工作流,还能再转回实时工作流.下面详细介绍如何创建一个实时工作流.   一.创建实时工作流 1. 打开 Setting > Process, 点击N

支持向量机学习笔记--原理篇(二)

支持向量机学习笔记(二) 前言 在上一篇中,讲述了感知机是什么.接下来将叙述感知机的对偶形式,这在解决支持向量机问题中非常有用,因为直接求解存在困难时,我们往往会把它变换到其等价形式进行求解.这篇将继续上篇内容,把遗留的两个问题解释清楚. 感知机 感知机学习算法的对偶形式 现在考虑感知机学习算法的对偶形式.感知机学习算法的原始形式和对偶形式在支持向量机学习算法的原始形式和对偶形式相对应. 对偶形式的基本想法是,将w和b表示为实例xi和标记yi的线性组合的形式,通过求解其系数而求得w和b,不失一般

Dynamic CRM 2013学习笔记(三十二)自定义审批流3 - 节点及实体配置

上次介绍了<Dynamic CRM 2013学习笔记(十九)自定义审批流1 - 效果演示> 以及如何配置自定义审批流的按钮:<Dynamic CRM 2013学习笔记(二十一)自定义审批流2 - 配置按钮>,这次接着介绍如何配置审批流的节点及节点关系.     一. 模板头: 二.流程节点:        一共有三种节点:开始节点,中间节点,结束节点: 1.  开始节点: 2. 中间节点: .3.  结束节点:       三.流程节点关系:   四.实体配置 1.添加二个字段:

iOS学习笔记之回调(二)

写在前面 上一篇学习笔记中简单介绍了通过目标-动作对实现回调操作:创建两个对象timer和logger,将logger设置为timer的目标,timer定时调用logger的sayOuch函数.在这个例子中,timer的任务比较简单,只完成一项任务:在指定的时刻触发事件.在这种情况下,适合选择目标-动作来实现回调,但这种方式不适合要发送多个回调的情况. 辅助对象 辅助对象是另一种实现回调的方式.在应用开始等待前,要求当等待的特定事件发生时,向遵守相应协议的辅助对象发送消息.委托对象和数据源是常见