?導讀:《藍橋杯單片機組》專欄文章是博主2018年參加藍橋杯的單片機組比賽所做的學習筆記,在當年的比賽中,博主是獲得了省賽一等獎,國賽二等獎的成績。成績雖談不上最好,但至少問心無愧。如今2021年回頭再看該系列文章,仍然感觸頗多。為了能更好地幫助到單片機初學者,今年特地抽出時間對當年的文章邏輯和結構進行重構,以達到初學者快速上手的目的。需要指出的是,由于本人水平有限,如有錯誤還請讀者指出,非常感謝。那么,接下來讓我們一起開始愉快的學習吧。
上一節可謂是一舉翻過了單片機初學的兩座大山,今天我們再來翻一座被稱為“時序”的大山。時序在單片機的學習和應用中也是非常重要的概念,憶當初理解時序也是費了九牛二虎之力。廢話不多說,今天我們先來拿EEPROM來學習一下II2C時序,代碼下載可以到我的Github上<傳送門>。
一、基礎理論
1.1、IIC介紹
開始器件之前,先來復習一波I2C。(只撿重點提....)
- 【1】、I2C通信主要靠兩根線SCL和SDA
- 【2】、高位在先低位在后。(對比UART的低位在先)
- 【3】、有ACK和NAK一說。
- 【4】、起始信號和終止信號時在SCL在高電平器件變化。數據信號時在SCL為低電平期間變化,SCL高電平器件讀取。
- 【5】、關于時序的掌握,感覺有一句話說的特別好:“整體上來說器件都是有一個最快速度的限制,而沒有最慢限制,所以當換用高速的單片機后通常都是靠在各步驟間插入軟件延時來滿足較慢的時序要求。”
1.2、EEPROM介紹
EEPROM系列名字有24X01/02/04/08/16,名字就顯而易見的告訴了我們它的大小,比如01表示容量1K bit,其余同理。
還有幾個EEPROM的小知識:
-
【1】、8-byte Page (1K, 2K), 16-byte Page (4K, 8K, 16K) Write Modes(1K和2K的每頁都是8字節,其余幾個每頁16字節)
-
【2】、1K/2K硬件地址位有三個,言外之意可以一個總線掛8個1K/2K的I2C器件。 而4K兩個地址位最多掛四個。8K一個地址位最多掛2個。16K只能一個總線掛一個!
-
【3】、寫字節(停止信號前ACK)
- 【3.1】、寫頁(停止信號前ACK)
- 【4】讀當前地址(停止信號前NAK)
- 【4.1】讀隨機地址(停止信號前NAK)
- 【4.2】讀連續地址(停止信號前NAK)
- 【5】、寫數據的時候需要注意,eeprom是先寫到緩沖區,然后再“搬運到”到掉電非易失區。所以這個過程需要一定的時間,AT24C02這個過程是不超過5ms!如果在這個時候去讓它應答是沒有響應的!
- 【6】、24C02是2kbits也就是256字節,對應地址時0x00~0xFF.
二、動手實驗
好了,基礎知識的提取差不多就是這些了,下面開始搞代碼。
2.1、EEPROM初探
試讀取一個存在的器件地址,然后串口打印響應的ACK。
關于代碼有幾點要說的,
Q1:器件地址如何確定呢?根據這張圖:
高四位是1010,接下來三位硬件地址000,然后最后一個是寫對應0 合起來,0xA0。
但是細心的你一定發現了,我程序中的代碼地址似乎并不是如上面所分析的那樣。
而是0x50,對應 0101 0000。
別急,看我II2C里面的I2CAddressing
,有一個addr << 1
左移一位后對應的不就是0xA0了嘛!這樣寫的好處是,可以方便操作讀和寫,寫就和上面一樣,讀的話(addr << 1) | 0x01
不就行了嘛!一箭雙雕,一舉兩得,何樂不為呢?
Q2:延時為什么選擇5us呢?
I 2 C 通信分為低速模式 100kbit/s、快速模式 400kbit/s 和高速模式3.4Mbit/s。因為所有的 I 2 C 器件都支持低速,但卻未必支持另外兩種速度,所以作為通用的I 2 C 程序這里選擇了 100k 這個速率來實現,也就是說實際程序產生的時序必須小于等于 100k的時序參數。由datasheet,很明顯也就是要求 SCL 的高低電平持續時間都不短于 5us,
效果當然蹭蹭的打印1了。。。
Q3:回應按理說應該打印0,為什么你的打印1?
個人認為,這樣才更符合我們的邏輯!嘻嘻。。。
2.2、單字節讀寫
讀出0x02地址對應的數據,然后+1后再寫回去!
這個有一點需要知道,24C02是2kbits也就是256字節,對應地址時0x00~0xFF。
這個程序中我們雖然有去響應ACK,但是并未處理。因為我們僅僅是寫一個字節,再次上電時間遠遠大于5ms,所以不必處理ACK。但是如果我們連續寫字節,就必須考慮這個應答位的問題了,下面也將介紹到!
梳理一下幾個要點:
- A、在本例中單片機是主機,24C02 是從機;
- B、無論是讀是寫,SCL 始終都是由主機控制的;
- C、寫的時候應答信號由從機給出,表示從機是否正確接收了數據;
- D、讀的時候應答信號則由主機給出,表示是否繼續讀下去。
2.3、多字節讀寫
多字節讀寫模式訪問EEPROM,然后依次+1、+2、+3......返回。
注意程序是一步步進化的,較之上面的摒棄了,I2CAddressing
、I2CWriteByte
、I2CReadByte
而加入了E2Read
和E2Write
..
注意比較新加入的兩者的區別,len的位置,讀是可以連續地址讀的,不需要再進行一遍 起始信號 -> 控制字節 -> ..... 讀的整個流程 起始信號 -> 控制字節(寫) -> 地址字節 -> 起始信號 -> 控制字節(讀) -> ...ACK - (len- 1)個字節...->NAK 第len個字節 -> 停止信號
寫就不一樣了,每寫一個字節就得再進行一遍 起始信號 -> 控制字節 -> ..... 寫的整個流程 起始信號 -> 控制字節(寫) -> 地址字節 -> 寫入數據 -> 停止信號 ->起始信號 -> 控制字節(寫) ....... -> 停止信號
還有一點注意的是,每次寫操作之前,我們都要進行查詢判斷當前 EEPROM 是否響應,正常響應后才可以寫數據。
2.4、連續讀和分頁寫
連續讀分頁寫,同上訪問eeprom,然后依次+1+2+3返回。
上面第三個我們也能感受到一次寫一個字節的慢,然后等待ACK后才能寫入下一個字節。效率太低!所以就誕生了分頁寫的模式。 這一次我們專門成立了一個eeprom的模塊。
24C02,一共是 256 個字節,8 個字節一頁,那么就一共有 32 頁。
分配好頁之后,如果我們在同一個頁內連續寫入幾個字節后,最后再發送停止位的時序。EEPROM 檢測到這個停止位后,就會一次性把這一頁的數據寫到非易失區域,就不需要像上節課那樣寫一個字節檢測一次了,并且頁寫入的時間也不會超過 5ms。
如果我們寫入的數據跨頁了,那么寫完了一頁之后,我們要發送一個停止位,然后等待并且檢測 EEPROM 的空閑模式,一直等到把上一頁數據完全寫到非易失區域后,再進行下一頁的寫入,這樣就可以在很大程度上提高數據的寫入效率。
手冊當然也明確說了,如果強行跨頁就會從該頁開頭覆蓋寫!
貼出E2PROM的代碼
void E2Read(u8 *buf, u8 addr, u8 len)
{
do{
I2CStart();
if(I2CWrite(0x50<<1))
{
break;
}
I2CStop();
}while(1);
I2CWrite(addr);
I2CStart();
I2CWrite((0x50<<1)|0x01);
while(len > 1)
{
*buf++ = I2CReadACK();
len--;
}
*buf = I2CReadNAK();
I2CStop();
}
void E2Write(u8 *buf, u8 addr, u8 len)
{
while(len > 0)
{
do{
I2CStart();
if(I2CWrite(0x50<<1))
{
break;
}
I2CStop();
}while(1);
I2CWrite(addr);
while(len > 0)
{
I2CWrite(*buf++);
len--;
addr++;
if((addr & 0x07) == 0)
{
break;
}
}
I2CStop();
}
}
小結:本篇文章主要介紹了單片機學習中的一個重頭戲:EEPROM存儲器件的II2C時序操作,并結合了常見的EEPROM操作方式:單字節讀寫、多字節讀寫、連續讀和分頁寫進行了詳細的介紹。在該部分學習中比較困難的就是II2C時序這個東西的理解,一個新的概念一次理解不到位也沒關系,多來幾次。相信在某一個瞬間,你也能有醍醐灌頂的感覺。
希望大家多多支持我的原創文章。如有錯誤,請大家及時指正,非常感謝。