1. 程式人生 > >EEPROM讀寫學習筆記與I2C總線(二)

EEPROM讀寫學習筆記與I2C總線(二)

一點 後來 並不是 完數 cal 效應 計算 spi 速度慢

無論任何電子產品都會涉及到數據的產生與數據的保存,這個數據可能並不是用來長久保存,只是在運行程序才會用到,有些數據體量較大對於獲取時效性並不太強,各種各樣的數據也就有不同的存儲載體,這次在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 = 9d200;                //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 : 1bz;        //SDA數據輸出選擇 //SDA的數據輸出受到SCL的控制,SCL為高時SDA保持不變,在接收應答位期間SDA也受控制
 33 
 34 //****************************************// 
 35 //*             I2C讀寫處理程序            *// 
 36 //****************************************// 
 37 always @ ( posedge CLK or negedge RSTn )
 38      if( !RSTn )  begin
 39             i <= 5d0;               //狀態機初始為0
 40             Go <= 5d0;
 41             C1 <= 9d0;
 42             rData <= 8d0;
 43             rSCL <= 1b1;            //數據線和時鐘線保持高電平初始值
 44             rSDA <= 1b1;
 45             isAck <= 1b1;           //信號為低電平時規定為有效應答位,高電平為非有效應答位。
 46             isDone <= 1b0;
 47             isOut <= 1b1;
 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 <= 1b1;
 57                     else if( C1 == 200 ) rSCL <= 1b0;       //SCL由高變低   //分頻系數為200,計數到200表明經過一個新的時鐘周期時鐘線變為低電平。
 58                               
 59                     if( C1 == 0 ) rSDA <= 1b1; 
 60                     else if( C1 == 100 ) rSDA <= 1b0;        //SDA先由高變低   //計數到100時,rSDA變為低電平,符合信號開始發送條件。
 61                               
 62                     if( C1 == 250 -1) begin C1 <= 9d0; i <= i + 1b1; end
 63                     else C1 <= C1 + 1b1;  //i=0用來表示開始信號發送,根據SDA與SCL變化可得,此處C1到達249,表示又過了四分之一個新時鐘周期,i+1,運行下一步
 64              end
 65                       
 66              1: // Write Device Addr
 67              begin rData <= {4b1010, 3b000, 1b0}; i <= 5d7; Go <= i + 1b1; end     //非阻塞賦值,Go為2    
 68                  
 69              2: // Wirte Word Addr
 70              begin rData <= Addr_Sig; i <= 5d7; Go <= i + 1b1; end      //addr_sig為word address,Go為3
 71                     
 72              3: // Write Data
 73              begin rData <= WrData; i <= 5d7; Go <= i + 1b1; end       //寫入數據,Go為4,WrData數值賦給rData。
 74      
 75              4: //發送IIC停止信號
 76              begin
 77                 isOut <= 1b1;
 78                           
 79                 if( C1 == 0 ) rSCL <= 1b0;
 80                 else if( C1 == 50 ) rSCL <= 1b1;     //SCL先由低變高       
 81         
 82                  if( C1 == 0 ) rSDA <= 1b0;
 83                  else if( C1 == 150 ) rSDA <= 1b1;     //SDA由低變高     //SCL處於高電位時,SDA由低到高變化,處於結束位
 84                            
 85                  if( C1 == 250 -1 ) begin C1 <= 9d0; i <= i + 1b1; end
 86                  else C1 <= C1 + 1b1; 
 87              end
 88                      
 89              5:
 90              begin isDone <= 1b1; i <= i + 1b1; end       //寫I2C 結束
 91                      
 92              6: 
 93              begin isDone <= 1b0; i <= 5d0; end
 94                  
 95              7,8,9,10,11,12,13,14:                         //發送Device Addr/Word Addr/Write Data
 96              begin
 97                  isOut <= 1b1;           //isout=1, SDA <= rSDA
 98                   rSDA <= rData[14-i];                      //高位先發送
 99                       
100                   if( C1 == 0 ) rSCL <= 1b0;
101                  else if( C1 == 50 ) rSCL <= 1b1;         //SCL高電平100個時鐘周期,低電平100個時鐘周期
102                   else if( C1 == 150 ) rSCL <= 1b0; 
103                           
104                   if( C1 == F250K -1 ) begin C1 <= 9d0; i <= i + 1b1; end     //產生250Khz的IIC時鐘   //i=14運行之後,狀態機i=15
105                   else C1 <= C1 + 1b1;
106              end
107                      
108              15:                                          // waiting for acknowledge
109              begin
110                  isOut <= 1b0;                            //SDA端口改為輸入
111                  if( C1 == 100 ) isAck <= SDA;             //讀取IIC 從設備的應答信號
112                           
113                   if( C1 == 0 ) rSCL <= 1b0;
114                   else if( C1 == 50 ) rSCL <= 1b1;         //SCL高電平100個時鐘周期,低電平100個時鐘周期
115                   else if( C1 == 150 ) rSCL <= 1b0;
116                           
117                   if( C1 == F250K -1 ) begin C1 <= 9d0; i <= i + 1b1; end    //產生250Khz的IIC時鐘
118                   else C1 <= C1 + 1b1; 
119              end
120                      
121              16:
122              if( isAck != 0 ) i <= 5d0;    //判斷是否接收到應答信號
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 <= 1b1;
135                     else if( C1 == 200 ) rSCL <= 1b0;      //SCL由高變低
136                           
137                     if( C1 == 0 ) rSDA <= 1b1; 
138                     else if( C1 == 100 ) rSDA <= 1b0;     //SDA先由高變低 
139                           
140                     if( C1 == 250 -1 ) begin C1 <= 9d0; i <= i + 1b1; end
141                      else C1 <= C1 + 1b1;
142              end
143                       
144              1: // Write Device Addr(設備地址)
145              begin rData <= {4b1010, 3b000, 1b0}; i <= 5d9; Go <= i + 1b1; end    //先進行一個偽寫操作
146                      
147              2: // Wirte Word Addr(EEPROM的寫地址)
148              begin rData <= Addr_Sig; i <= 5d9; Go <= i + 1b1; end
149                     
150              3: // Start again
151              begin
152                  isOut <= 1b1;          //開始進行讀操作
153                           
154                  if( C1 == 0 ) rSCL <= 1b0;
155                   else if( C1 == 50 ) rSCL <= 1b1; 
156                   else if( C1 == 250 ) rSCL <= 1b0;
157                           
158                  if( C1 == 0 ) rSDA <= 1b0; 
159                   else if( C1 == 50 ) rSDA <= 1b1;
160                   else if( C1 == 150 ) rSDA <= 1b0;  
161                           
162                   if( C1 == 300 -1 ) begin C1 <= 9d0; i <= i + 1b1; end
163                   else C1 <= C1 + 1b1;
164              end
165                      
166              4: // Write Device Addr ( Read )
167              begin rData <= {4b1010, 3b000, 1b1}; i <= 5d9; Go <= i + 1b1; end
168                     
169              5: // Read Data
170              begin rData <= 8d0; i <= 5d19; Go <= i + 1b1; end
171                  
172              6: // Stop
173              begin
174                  isOut <= 1b1;
175                  if( C1 == 0 ) rSCL <= 1b0;
176                   else if( C1 == 50 ) rSCL <= 1b1; 
177         
178                   if( C1 == 0 ) rSDA <= 1b0;
179                   else if( C1 == 150 ) rSDA <= 1b1;
180                            
181                   if( C1 == 250 -1 ) begin C1 <= 9d0; i <= i + 1b1; end
182                   else C1 <= C1 + 1b1; 
183              end
184                      
185              7:                                                       //寫I2C 結束
186              begin isDone <= 1b1; i <= i + 1b1; end
187                      
188              8: 
189              begin isDone <= 1b0; i <= 5d0; end
190                  
191                     
192              9,10,11,12,13,14,15,16:                                  //發送Device Addr(write)/Word Addr/Device Addr(read)
193              begin
194                   isOut <= 1b1;                          
195                     rSDA <= rData[16-i];                                //高位先發送   //將rData數據賦值給數據線,偽寫操作
196                           
197                    if( C1 == 0 ) rSCL <= 1b0;
198                     else if( C1 == 50 ) rSCL <= 1b1;                   //SCL高電平100個時鐘周期,低電平100個時鐘周期
199                     else if( C1 == 150 ) rSCL <= 1b0; 
200                           
201                     if( C1 == F250K -1 ) begin C1 <= 9d0; i <= i + 1b1; end   //產生250Khz的IIC時鐘
202                     else C1 <= C1 + 1b1;
203              end
204                    
205              17: // waiting for acknowledge
206              begin
207                   isOut <= 1b0;                                       //SDA端口改為輸入
208                          
209                     if( C1 == 100 ) isAck <= SDA;                        //讀取IIC 的應答信號
210                           
211                     if( C1 == 0 ) rSCL <= 1b0;
212                     else if( C1 == 50 ) rSCL <= 1b1;                 //SCL高電平100個時鐘周期,低電平100個時鐘周期
213                     else if( C1 == 150 ) rSCL <= 1b0;
214                           
215                     if( C1 == F250K -1 ) begin C1 <= 9d0; i <= i + 1b1; end     //產生250Khz的IIC時鐘
216                     else C1 <= C1 + 1b1; 
217              end
218                      
219              18:
220                   if( isAck != 0 ) i <= 5d0;
221                     else i <= Go;
222                      
223                      
224              19,20,21,22,23,24,25,26: // Read data
225              begin
226                  isOut <= 1b0;
227                  if( C1 == 100 ) rData[26-i] <= SDA;                              //高位先接收
228                           
229                   if( C1 == 0 ) rSCL <= 1b0;
230                   else if( C1 == 50 ) rSCL <= 1b1;                  //SCL高電平100個時鐘周期,低電平100個時鐘周期
231                   else if( C1 == 150 ) rSCL <= 1b0; 
232                           
233                   if( C1 == F250K -1 ) begin C1 <= 9d0; i <= i + 1b1; end     //產生250Khz的IIC時鐘
234                   else C1 <= C1 + 1b1;
235              end      
236                      
237              27: // no acknowledge
238              begin
239                  isOut <= 1b1;
240                       
241                   if( C1 == 0 ) rSCL <= 1b0;
242                   else if( C1 == 50 ) rSCL <= 1b1;
243                   else if( C1 == 150 ) rSCL <= 1b0;
244                           
245                   if( C1 == F250K -1 ) begin C1 <= 9d0; i <= Go; end
246                   else C1 <= C1 + 1b1; 
247             end
248                 
249             endcase        
250         
251 
252     
253                 
254 endmodule

EEPROM讀寫學習筆記與I2C總線(二)