EEPROM (Electrically Erasable Programmable read only memory)是指帶電可擦可編程只讀存儲(chǔ)器。是一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片。EEPROM 可以在電腦上或?qū)S迷O(shè)備上擦除已有信息,重新編程。一般用在即插即用。AT24C02是一個(gè)2K位串行CMOS E2PROM, 內(nèi)部含有256個(gè)8位字節(jié),CATALYST公司的先進(jìn)CMOS技術(shù)實(shí)質(zhì)上減少了器件的功耗。AT24C02有一個(gè)16字節(jié)頁(yè)寫緩沖器。該器件通過(guò)IIC總線接口進(jìn)行操作,有一個(gè)專門的寫保護(hù)功能。
一模塊來(lái)源
模塊實(shí)物展示:
二規(guī)格參數(shù)
工作電壓:1.8V-5.5V
工作電流:最大3mA
通信接口:IIC
內(nèi)存:2048位
時(shí)鐘速度:5V時(shí)最大1000Khz,其余為400Khz
以上信息見(jiàn)廠家資料文件
三移植過(guò)程
我們的目標(biāo)是將例程移植至CW32F030C8T6開發(fā)板上【能夠播報(bào)語(yǔ)音的功能】。首先要獲取資料,查看數(shù)據(jù)手冊(cè)應(yīng)如何實(shí)現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。
3.1查看資料
上圖是AT24CXX的設(shè)備地址(第一行的為AT24C02,它的容量為2K),我們發(fā)現(xiàn)AT24CXX整個(gè)系列芯片的地址高四位都相同,都是1010,這四位是由生產(chǎn)商固化在芯片內(nèi)部,無(wú)法改變。
AT24C02地址的低三位(不包括讀寫位)對(duì)應(yīng)芯片的三個(gè)引腳,也就是說(shuō)這三位是可以人為設(shè)定的,23=8,所以一條I2C總線上可以掛載8個(gè)AT24C02。
AT24C02的地址為7位二進(jìn)制數(shù),下圖中最后一位是讀寫位(數(shù)據(jù)方向位),1 表示讀數(shù)據(jù),0 表示寫數(shù)據(jù)。
這樣,7位設(shè)備地址加1位讀寫位,構(gòu)成I2C的尋址數(shù)據(jù)。I2C 總線的尋址過(guò)程中,通常在起始條件后的第一個(gè)字節(jié)決定了主機(jī)選擇哪一個(gè)從機(jī),該字節(jié)的最后一位決定數(shù)據(jù)傳輸方向。
AT24C02讀寫:AT24C02的存儲(chǔ)空間為2K位(256字節(jié)),在對(duì)其進(jìn)行寫數(shù)據(jù)時(shí),最小寫入單位為字節(jié)(Byte),最大寫入單位為頁(yè)(Page),AT24C02頁(yè)大小為 16 Byte。
字節(jié)寫
在字節(jié)寫模式下,主器件發(fā)送起始信號(hào)和從器件地址信息(R/W 位置零)給從器件,在從器件送回應(yīng)答信號(hào)后,主器件發(fā)送 AT24WC01/02/04/08/16 的字節(jié)地址,主器件在收到從器件的應(yīng)答信號(hào)后,再發(fā)送數(shù)據(jù)到被尋址的存儲(chǔ)單元。AT24WC01/02/04/08/16 再次應(yīng)答,并在主器件產(chǎn)生停止信號(hào)后開始內(nèi)部數(shù)據(jù)的擦寫,在內(nèi)部擦寫過(guò)程中,AT24WC01/02/04/08/16 不再應(yīng)答主器件的任何請(qǐng)求。
頁(yè)寫
用頁(yè)寫,AT24WC01 可一次寫入 8 個(gè)字節(jié)數(shù)據(jù),AT24WC02/04/08/16 可以一次寫入 16 個(gè)字節(jié)的數(shù)據(jù),頁(yè)寫操作的啟動(dòng)和字節(jié)寫一樣,不同在于傳送了一字節(jié)數(shù)據(jù)后并不產(chǎn)生停止信號(hào),主器件被允許發(fā)送 P(AT24WC01 P=7;AT24WC02/04/08/16 P=15)個(gè)額外的字節(jié)。每發(fā)送一個(gè)字節(jié)數(shù)據(jù)后 AT24WC01/02/04/08/16 產(chǎn)生一個(gè)應(yīng)答位并將字節(jié)地址低位加 1,高位保持不變。
如果在發(fā)送停止信號(hào)之前主器件發(fā)送超過(guò)P+1個(gè)字節(jié),地址計(jì)數(shù)器將自動(dòng)翻轉(zhuǎn),先前寫入的數(shù)據(jù)被覆蓋。
接收到P+1字節(jié)數(shù)據(jù)和主器件發(fā)送的停止信號(hào)后,AT24CXXX啟動(dòng)內(nèi)部寫周期將數(shù)據(jù)寫到數(shù)據(jù)區(qū),所有接收的數(shù)據(jù)在一個(gè)寫周期內(nèi)寫入AT24WC01/02/04/08/16。
當(dāng)前地址讀
AT24WC01/02/04/08/16 的地址計(jì)數(shù)器內(nèi)容為最后操作字節(jié)的地址加 1。也就是說(shuō) 如果上次讀/寫的操作地址為 N,則立即讀的地址從地址 N+1 開始。如果 N=E(這里對(duì) 24WC01 E=127;對(duì) 24WC02 E=255;對(duì) 24WC04 E=511;對(duì) 24WC08 E=1023;對(duì) 24WC16 E=2047)則計(jì)數(shù)器將翻轉(zhuǎn)到 0 且繼續(xù)輸出數(shù)據(jù)。AT24WC01/02/04/08/16 接收到從器件地址信號(hào)后(R/W 位置 1),它首先發(fā)送一個(gè)應(yīng)答信號(hào),然后發(fā)送一個(gè) 8 位字節(jié)數(shù)據(jù)。主器件不需發(fā)送一個(gè)應(yīng)答信號(hào),但要產(chǎn)生一個(gè)停止信號(hào)。
選擇讀(隨機(jī)讀)
選擇性讀操作允許主器件對(duì)寄存器的任意字節(jié)進(jìn)行讀操作,主器件首先通過(guò)發(fā)送起始信號(hào)、從器件地址和它想讀取的字節(jié)數(shù)據(jù)的地址執(zhí)行一個(gè)偽寫操作。在 AT24WC01/02/04/08/16 應(yīng)答之后,主器件重新發(fā)送起始信號(hào)和從器件地址,此時(shí) R/W 位置 1,AT24WC01/02/04/08/16 響應(yīng)并發(fā)送應(yīng)答信號(hào),然后輸出所要求的一個(gè) 8 位字節(jié)數(shù)據(jù),主器件不發(fā)送應(yīng)答信號(hào)但產(chǎn)生一個(gè)停止信號(hào)。
連續(xù)讀
連續(xù)讀操作可通過(guò)立即讀或選擇性讀操作啟動(dòng)。在 AT24WC01/02/04/08/16 發(fā)送完一個(gè) 8 位字節(jié)數(shù)據(jù)后,主器件產(chǎn)生一個(gè)應(yīng)答信號(hào)來(lái)響應(yīng),告知 AT24WC01/02/04/08/16 主器件要求更多的數(shù)據(jù),對(duì)應(yīng)每個(gè)主機(jī)產(chǎn)生的應(yīng)答信號(hào) AT24WC01/02/04/08/16 將發(fā)送一個(gè) 8 位數(shù)據(jù)字節(jié)。當(dāng)主器件不發(fā)送應(yīng)答信號(hào)而發(fā)送停止位時(shí)結(jié)束此操作。
從 AT24WC01/02/04/08/16 輸出的數(shù)據(jù)按順序由 N 到 N+1 輸出。讀操作時(shí)地址計(jì)數(shù)器在 AT24WC01/02/04/08/16 整個(gè)地址內(nèi)增加,這樣整個(gè)寄存器區(qū)域在可在一個(gè)讀操作內(nèi)全部讀出。當(dāng)讀取的字節(jié)超過(guò) E(對(duì)于 24WC01 E=127;對(duì) 24WC02 E=255; 對(duì) 24WC04 E=511;對(duì) 24WC08 E=1023;對(duì) 24WC16 E=2047)計(jì)數(shù)器將翻轉(zhuǎn)到零并繼續(xù)輸出數(shù)據(jù)字節(jié)。
3.2引腳選擇
模塊接線圖
3.3移植至工程
移植步驟中的導(dǎo)入.c和.h文件與【CW32模塊使用】DHT11溫濕度傳感器相同,只是將.c和.h文件更改為bsp_at24c02.c與bsp_at24c02.h。這里不再過(guò)多講述,移植完成后面修改相關(guān)代碼。
在文件bsp_at24c02.c中,編寫如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-25 LCKFB-LP first version
*/
#include "bsp_at24c02.h"
#include "stdio.h"
// SLAVE ADDRESS+W為0xA0,SLAVE ADDRESS+R為0xA1
#define AT24C02_ADDRESS_READ 0xA0
#define AT24C02_ADDRESS_WRITE 0xA1
/******************************************************************
* 函 數(shù) 名 稱:AT24C02_GPIO_Init
* 函 數(shù) 說(shuō) 明:AT24C02的引腳初始化
* 函 數(shù) 形 參:無(wú)
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void AT24C02_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體
RCC_AT24C02_GPIO_ENABLE(); // 使能GPIO時(shí)鐘
GPIO_InitStruct.Pins = GPIO_SDA|GPIO_SCL; // GPIO引腳
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 開漏輸出
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 輸出速度高
GPIO_Init(PORT_AT24C02, &GPIO_InitStruct); // 初始化
}
/******************************************************************
* 函 數(shù) 名 稱:IIC_Start
* 函 數(shù) 說(shuō) 明:IIC起始時(shí)序
* 函 數(shù) 形 參:無(wú)
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_us(5);
SCL(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
delay_us(5);
}
/******************************************************************
* 函 數(shù) 名 稱:IIC_Stop
* 函 數(shù) 說(shuō) 明:IIC停止信號(hào)
* 函 數(shù) 形 參:無(wú)
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
/******************************************************************
* 函 數(shù) 名 稱:IIC_Send_Ack
* 函 數(shù) 說(shuō) 明:主機(jī)發(fā)送應(yīng)答或者非應(yīng)答信號(hào)
* 函 數(shù) 形 參:0發(fā)送應(yīng)答 1發(fā)送非應(yīng)答
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* 函 數(shù) 名 稱:I2C_WaitAck
* 函 數(shù) 說(shuō) 明:等待從機(jī)應(yīng)答
* 函 數(shù) 形 參:無(wú)
* 函 數(shù) 返 回:0有應(yīng)答 1超時(shí)無(wú)應(yīng)答
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
unsigned char I2C_WaitAck(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SCL(0);
SDA(1);
SDA_IN();
delay_us(5);
SCL(1);
delay_us(5);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* 函 數(shù) 名 稱:Send_Byte
* 函 數(shù) 說(shuō) 明:寫入一個(gè)字節(jié)
* 函 數(shù) 形 參:dat要寫人的數(shù)據(jù)
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void Send_Byte(uint8_t dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低時(shí)鐘開始數(shù)據(jù)傳輸
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
delay_us(1);
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
/******************************************************************
* 函 數(shù) 名 稱:Read_Byte
* 函 數(shù) 說(shuō) 明:IIC讀時(shí)序
* 函 數(shù) 形 參:無(wú)
* 函 數(shù) 返 回:讀到的數(shù)據(jù)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
unsigned char Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA設(shè)置為輸入
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( SDA_GET() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
/******************************************************************
* 函 數(shù) 名 稱:AT24C02_WriteByte
* 函 數(shù) 說(shuō) 明:AT24C02寫入一個(gè)字節(jié)
* 函 數(shù) 形 參:WordAddress 要寫入字節(jié)的地址 Data 要寫入的數(shù)據(jù)
* 函 數(shù) 返 回:無(wú)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
void AT24C02_WriteByte(unsigned char WordAddress,unsigned char Data)
{
IIC_Start();
Send_Byte(AT24C02_ADDRESS_READ);
I2C_WaitAck();
Send_Byte(WordAddress);
I2C_WaitAck();
Send_Byte(Data);
I2C_WaitAck();
IIC_Stop();
}
/******************************************************************
* 函 數(shù) 名 稱:AT24C02_ReadByte
* 函 數(shù) 說(shuō) 明:AT24C02讀取一個(gè)字節(jié)
* 函 數(shù) 形 參:WordAddress 要讀出字節(jié)的地址
* 函 數(shù) 返 回:讀出的數(shù)據(jù)
* 作 者:LC
* 備 注:無(wú)
******************************************************************/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
IIC_Start();
Send_Byte(AT24C02_ADDRESS_READ);
I2C_WaitAck();
Send_Byte(WordAddress);
I2C_WaitAck();
IIC_Start();
Send_Byte(AT24C02_ADDRESS_WRITE);
I2C_WaitAck();
Data=Read_Byte();
IIC_Send_Ack(1);
IIC_Stop();
return Data;
}
在文件bsp_at24c02.h中,編寫如下代碼。
/*
?*?Change?Logs:
* Date Author Notes
* 2024-06-25 LCKFB-LP first version
*/
#ifndef _BSP_AT24C02_H_
#define _BSP_AT24C02_H_
#include "board.h"
//端口移植
#define RCC_AT24C02_GPIO_ENABLE() __RCC_GPIOB_CLK_ENABLE()
#define PORT_AT24C02 CW_GPIOB
#define GPIO_SDA GPIO_PIN_8
#define GPIO_SCL GPIO_PIN_9
//設(shè)置SDA輸出模式
#define SDA_OUT() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pins = GPIO_SDA;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(PORT_AT24C02, &GPIO_InitStruct);
}
//設(shè)置SDA輸入模式
#define SDA_IN() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pins = GPIO_SDA;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(PORT_AT24C02, &GPIO_InitStruct);
}
//獲取SDA引腳的電平變化
#define SDA_GET() GPIO_ReadPin(PORT_AT24C02, GPIO_SDA)
//SDA與SCL輸出
#define SDA(x) GPIO_WritePin(PORT_AT24C02, GPIO_SDA, (x?GPIO_Pin_SET:GPIO_Pin_RESET) )
#define SCL(x) GPIO_WritePin(PORT_AT24C02, GPIO_SCL, (x?GPIO_Pin_SET:GPIO_Pin_RESET) )
void AT24C02_GPIO_Init(void);
void AT24C02_WriteByte(unsigned char WordAddress,unsigned char Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif
四移植驗(yàn)證
>>>
在自己工程中的main主函數(shù)中,編寫如下。
/*
* Change Logs:
* Date Author Notes
* 2024-06-25 LCKFB-LP first version
*/
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "bsp_at24c02.h"
int32_t main(void)
{
unsigned char dat1 = 0;
unsigned char dat2 = 0;
board_init();
uart1_init(115200U);
AT24C02_GPIO_Init();
printf("startrn");
//向0地址寫入數(shù)據(jù)48
AT24C02_WriteByte(0,48);
delay_ms(5);
//向8地址寫入數(shù)據(jù)48
AT24C02_WriteByte(8,66);
delay_ms(5);
//從0地址讀取數(shù)據(jù)到dat1
dat1 = AT24C02_ReadByte(0);
delay_ms(5);
//從8地址讀取數(shù)據(jù)到dat2
dat2 = AT24C02_ReadByte(8);
delay_ms(5);
delay_ms(50);
//輸出dat查看數(shù)據(jù)是否正確
printf("dat1 = %drn",dat1);
delay_ms(1);
printf("dat2 = %drn",dat2);
delay_ms(1);
while(1)
{
}
}
移植現(xiàn)象:
向0地址寫入數(shù)據(jù)48,再讀出查看是否是48。
向8地址寫入數(shù)據(jù)66,再讀出查看是否是66。
模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/1vMkhtubSlSjCLW960FccxQ?pwd=LCKF
提取碼:LCKF