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