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