【51单片机快速入门指南】4.1: I2C 与 AT24C02 (EEPROM) 的跨页连续读写
目錄
- 硬知識(shí)
- AT24Cxx 介紹
- 引腳排列
- 引腳說(shuō)明
- 存儲(chǔ)結(jié)構(gòu)
- 器件尋址
- 器件操作
- 待機(jī)模式
- 存儲(chǔ)復(fù)位
- 寫操作
- 字節(jié)寫
- 頁(yè)寫
- 應(yīng)答查詢
- 讀操作
- 當(dāng)前地址讀
- 隨機(jī)讀
- 順序讀
- 示例程序
- 24C02.c
- 24C02.h
- 測(cè)試程序
- main.c
- 實(shí)驗(yàn)現(xiàn)象
- 通訊波形
- 寫入部分
- 讀取部分
普中51-單核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
硬知識(shí)
???????摘自《普中 51 單片機(jī)開發(fā)攻略》、《24C02/24C04/24C08/24C16/24C32/24C64芯片手冊(cè)》
AT24Cxx 介紹
???????AT24C01/02/04/08/16…是一個(gè)1K/2K/4K/8K/16K 位串行 CMOS,內(nèi)部含有 128/256/512/1024/2048 個(gè) 8 位字節(jié),AT24C01 有一個(gè) 8 字節(jié)頁(yè)寫緩沖器, AT24C02/04/08/16 有一個(gè) 16 字節(jié)頁(yè)寫緩沖器。該器件通過(guò) I2C 總線接口進(jìn)行操作,它有一個(gè)專門的寫保護(hù)功能。開發(fā)板上使用的是 AT24C02(EEPROM)芯片,此芯片具有 I2C 通信接口,芯片內(nèi)保存的數(shù)據(jù)在掉電情況下都不丟失, 所以通常用于存放一些比較重要的數(shù)據(jù)等。
引腳排列
AT24Cxx 芯片管腳如下圖所示:
引腳說(shuō)明
存儲(chǔ)結(jié)構(gòu)
器件尋址
???????起始條件使能芯片讀寫操作后,EEPROM都要求有8位的器件地址信息。
???????器件地址信息由"1"、“0"序列組成,前4位如圖中所示,對(duì)于所有串行EEPROM都是一樣的。對(duì)于24C02/32/64,隨后3位A2,A1和A0為器件地址位,必須與硬件輸入引腳保持一致。
???????對(duì)于24C04,隨后2位A2和A1為器件地址位,另1位為頁(yè)地址位。A2和A1必須與硬件輸入引腳保持一致,而A0是空腳。
???????對(duì)于24C08,隨后1位A2為器件地址位,另2位為頁(yè)地址位。A2必須與硬件輸入引腳保持一致,而A1和A0是空腳。
???????對(duì)于24C16,無(wú)器件地址位,3位都為頁(yè)地址位,而A2,A1和A0是空腳。
???????器件地址信息的LSB為讀/寫操作選擇位,高為讀操作,低為寫操作。
???????若比較器件地址一致,EEPROM將輸出應(yīng)答"0”,如果不一致,則返回到待機(jī)狀態(tài)。
器件操作
待機(jī)模式
EEPROM具有低功耗待機(jī)的特點(diǎn),條件為:
存儲(chǔ)復(fù)位
當(dāng)協(xié)議中產(chǎn)生中斷、掉電或系統(tǒng)復(fù)位后,I2C總線可通過(guò)以下步驟復(fù)位:
寫操作
字節(jié)寫
???????寫操作要求在接收器件地址和ACK應(yīng)答后,接收8位的字地址。接收到這個(gè)地址后EEPROM應(yīng)答"0",然后是一個(gè)8位數(shù)據(jù)。在接收8位數(shù)據(jù)后,EEPROM應(yīng)答"0",接著必須由主器件發(fā)送停止條件來(lái)終止寫序列。
???????此時(shí)EEPROM進(jìn)入內(nèi)部寫周期tWRt_{WR}tWR?,數(shù)據(jù)寫入非易失性存儲(chǔ)器中,在此期間所有輸入都無(wú)效。
???????直到寫周期完成,EEPROM才會(huì)有應(yīng)答。
頁(yè)寫
???????24C02器件按8字節(jié)/頁(yè)執(zhí)行頁(yè)寫,24C04/08/16器件按16字節(jié)/頁(yè)執(zhí)行頁(yè)寫,24C32/64器件按32字節(jié)/頁(yè)執(zhí)行頁(yè)寫。
???????頁(yè)寫初始化與字節(jié)寫相同,只是主器件不會(huì)在第一個(gè)數(shù)據(jù)后發(fā)送停止條件,而是在EEPROM的ACK以后,接著發(fā)送7個(gè)(24C02)或15個(gè)(24C04/08/16)或31個(gè)(24C32/64)數(shù)據(jù)。
???????EEPROM收到每個(gè)數(shù)據(jù)后都應(yīng)答"0",最后仍需由主器件發(fā)送停止條件,終止寫序列接收到每個(gè)數(shù)據(jù)后,字地址的低3位(24C02)或4位(24C04/08/16)或5位(24C32/64)內(nèi)部自動(dòng)加1,高位地址位不變,維持在當(dāng)前頁(yè)內(nèi)。當(dāng)內(nèi)部產(chǎn)生的字地址達(dá)到該頁(yè)邊界地址時(shí),隨后的數(shù)據(jù)將寫入該頁(yè)的頁(yè)首。如果超過(guò)8個(gè)(24C02)或16個(gè)(24C04/08/16)或32個(gè)24C32/64)數(shù)據(jù)傳送給了EEPROM,字地址將回轉(zhuǎn)到該頁(yè)的首字節(jié),先前的字節(jié)將會(huì)被覆蓋。
應(yīng)答查詢
???????一旦內(nèi)部寫周期啟動(dòng),EEPROM輸入無(wú)效,此時(shí)即可啟動(dòng)應(yīng)答查詢:發(fā)送起始條件和器件地址(讀/寫位為期望的操作)。只有內(nèi)部寫周期完成,EEPROM才應(yīng)答"0",之后可繼續(xù)讀/寫操作。
讀操作
???????讀操作與寫操作初始化相同,只是器件地址中的讀/寫選擇位應(yīng)為"1",有三種不同的讀操作方式:當(dāng)前地址讀,隨機(jī)讀和順序讀。
當(dāng)前地址讀
???????內(nèi)部地址計(jì)數(shù)器保存著上次訪問(wèn)時(shí)最后一個(gè)地址加1的值。只要芯片有電,該地址就一直保存。當(dāng)讀到最后頁(yè)的最后字節(jié),地址會(huì)回轉(zhuǎn)到0;當(dāng)寫到某頁(yè)尾的最后一個(gè)字節(jié),地址會(huì)回轉(zhuǎn)到該頁(yè)的首字節(jié)。
???????接收器件地址(讀/寫選擇位為"1")、EEPROM應(yīng)答ACK后,當(dāng)前地址的數(shù)據(jù)就隨時(shí)鐘送出。
???????主器件無(wú)需應(yīng)答"0",但需發(fā)送停止條件。
隨機(jī)讀
???????隨機(jī)讀需先寫一個(gè)目標(biāo)字地址,一旦EEPROM接收器件地址和字地址并應(yīng)答了ACK,主器件就產(chǎn)生一個(gè)重復(fù)的起始條件。
???????然后,主器件發(fā)送器件地址(讀/寫選擇位為"1"),EEPROM應(yīng)答ACK,并隨時(shí)鐘送出數(shù)據(jù)。主器件無(wú)需應(yīng)答"0",但需發(fā)送停止條件。
順序讀
???????順序讀可以通過(guò)“當(dāng)前地址讀”或"隨機(jī)讀”啟動(dòng)。主器件接收到一個(gè)數(shù)據(jù)后,應(yīng)答ACK,只要EEPROM接收到ACK,將自動(dòng)增加字地址并繼續(xù)隨時(shí)鐘發(fā)送后面的數(shù)據(jù)。若達(dá)到存儲(chǔ)器地址末尾,地址自動(dòng)回轉(zhuǎn)到0,仍可繼續(xù)順序讀取數(shù)據(jù)。
???????主器件不應(yīng)答"0",而發(fā)送停止條件,即可結(jié)束順序讀操作。
示例程序
???????stdint.h見【51單片機(jī)快速入門指南】1:基礎(chǔ)知識(shí)和工程創(chuàng)建
???????軟件I2C程序見【51單片機(jī)快速入門指南】4: 軟件 I2C
???????由原理圖,此24C02的地址為1010 000,即 0x50
24C02.c
#include "24C02.h"/******************************************************************************* * 函 數(shù) 名 : at24c02_delay_1ms 移植時(shí)需修改 * 函數(shù)功能 : 延時(shí)1ms * 輸 入 : 無(wú) * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_delay_1ms() //@11.0592MHz {unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i); }void at24c02_delay_ms(int i) {while(i--)at24c02_delay_1ms(); }/******************************************************************************* * 函 數(shù) 名 : at24c02_write_one_byte * 函數(shù)功能 : 在AT24CXX指定地址寫入一個(gè)數(shù)據(jù) * 輸 入 : addr:寫入數(shù)據(jù)的目的地址 dat:要寫入的數(shù)據(jù) * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_write_one_byte(uint8_t addr,uint8_t dat) { i2c_mem_write(ADDR_24C02, addr, &dat, 1);at24c02_delay_ms(10); }/******************************************************************************* * 函 數(shù) 名 : at24c02_read_one_byte * 函數(shù)功能 : 在AT24CXX指定地址讀出一個(gè)數(shù)據(jù) * 輸 入 : addr:開始讀數(shù)的地址 * 輸 出 : 讀到的數(shù)據(jù) *******************************************************************************/ uint8_t at24c02_read_one_byte(uint8_t addr) { uint8_t temp = 0; i2c_mem_read(ADDR_24C02, addr, &temp, 1);return temp; //返回讀取的數(shù)據(jù) }/******************************************************************************* * 函 數(shù) 名 : at24c02_read_one_page * 函數(shù)功能 : 在AT24CXX指定地址讀出一頁(yè)數(shù)據(jù) * 輸 入 : addr:寫入數(shù)據(jù)的目的地址 pbuffer:要寫入的緩沖區(qū)首地址Len:數(shù)據(jù)長(zhǎng)度 * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_read_one_page(uint8_t addr, uint8_t *pbuffer) { i2c_mem_read(ADDR_24C02, addr, pbuffer, 8); }/******************************************************************************* * 函 數(shù) 名 : at24c02_write_one_page * 函數(shù)功能 : 在AT24CXX指定頁(yè)寫入一頁(yè)數(shù)據(jù) * 輸 入 : addr:寫入數(shù)據(jù)的目的地址 dat:要寫入的數(shù)據(jù) * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_write_one_page(uint8_t addr, uint8_t *dat) { i2c_mem_write(ADDR_24C02, addr, dat, 8);at24c02_delay_ms(10); }/******************************************************************************* * 函 數(shù) 名 : at24c02_read_bytes * 函數(shù)功能 : 在AT24CXX指定地址讀出一段數(shù)據(jù) * 輸 入 : addr:寫入數(shù)據(jù)的目的地址 pbuffer:要寫入的緩沖區(qū)首地址Len:數(shù)據(jù)長(zhǎng)度 * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_read_bytes(uint8_t addr, uint8_t* pbuffer, uint8_t Len) { uint8_t pdat_id_S = addr % 8;uint8_t i, pages;if(pdat_id_S){for(i = pdat_id_S; i < 8; ++i){*pbuffer++ = at24c02_read_one_byte(addr++);--Len;if(!Len)return;}}pages = Len / 8;for (i = 0; i < pages; ++i){at24c02_read_one_page(addr, pbuffer);addr += 8;pbuffer += 8;Len -= 8;}if(!Len)return;i2c_mem_read(ADDR_24C02, addr, pbuffer, Len);pbuffer += Len;*pbuffer = '\0'; }/******************************************************************************* * 函 數(shù) 名 : at24c02_write_bytes * 函數(shù)功能 : 在AT24CXX指定地址寫入一段數(shù)據(jù) * 輸 入 : addr:寫入數(shù)據(jù)的目的地址 pdat:要寫入的數(shù)據(jù)首地址Len:數(shù)據(jù)長(zhǎng)度 * 輸 出 : 無(wú) *******************************************************************************/ void at24c02_write_bytes(uint8_t addr, uint8_t* pdat, uint8_t Len) { uint8_t Temp[8];uint8_t pdat_id_S = addr % 8;uint8_t i, pages;if(pdat_id_S){for(i = 0; i < pdat_id_S; ++i)Temp[i] = at24c02_read_one_byte(addr - pdat_id_S + i);for (; i < 8; ++i){Temp[i] = *pdat;++pdat;--Len;if(!Len){at24c02_write_one_page(addr - pdat_id_S, Temp);return;}}at24c02_write_one_page(addr - pdat_id_S, Temp);addr = addr + 8 - pdat_id_S;}pages = Len / 8;for (i = 0; i < pages; ++i){at24c02_write_one_page(addr, pdat);addr += 8;pdat += 8;Len -= 8;}if(!Len)return;for (i = 0; i < Len; ++i){Temp[i] = *pdat;++pdat;}for(; i < 8; ++i){Temp[i] = at24c02_read_one_byte(addr + i);}at24c02_write_one_page(addr, Temp); }24C02.h
#ifndef _24C02_H_ #define _24C02_H_#include "stdint.h" #include "intrins.h" #include "Software_I2C.h"#define ADDR_24C02 0x50 //24C02的7位地址void at24c02_write_one_byte(uint8_t addr,uint8_t dat); uint8_t at24c02_read_one_byte(uint8_t addr); void at24c02_write_one_page(uint8_t addr,uint8_t *dat); void at24c02_read_one_page(uint8_t addr, uint8_t *pbuffer);void at24c02_write_bytes(uint8_t addr, uint8_t *pdat, uint8_t Len); void at24c02_read_bytes(uint8_t addr, uint8_t *pbuffer, uint8_t Len);#endif測(cè)試程序
串口程序見【51單片機(jī)快速入門指南】3.3:USART 串口通信
main.c
???????在地址0x06處連續(xù)寫入
???????“123456789098765432123456789012345678909876543212345678901234567890987654321234567890”
???????讀取后通過(guò)串口返回,波特率為57600,晶振頻率為11.0592MHz。
實(shí)驗(yàn)現(xiàn)象
如圖,成功讀取
通訊波形
寫入部分
未對(duì)齊部分先讀取之前的字節(jié),在對(duì)齊順序?qū)懭?#xff0c;并自動(dòng)換頁(yè)
末尾未對(duì)齊部分先讀取后面的字節(jié),再連同要寫入的數(shù)據(jù)順序?qū)懭?br />
讀取部分
未8位對(duì)齊部分為隨機(jī)單字節(jié)讀取
對(duì)齊后進(jìn)行順序讀取,并自動(dòng)換頁(yè)
末尾未對(duì)齊部分也進(jìn)行順序讀取
總結(jié)
以上是生活随笔為你收集整理的【51单片机快速入门指南】4.1: I2C 与 AT24C02 (EEPROM) 的跨页连续读写的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 文件项目SVN+TortoiseSVN+
- 下一篇: 捷径 - The certain sho