看手册:
AT24Cxx xx代表能写入xxK bit=(xx K)/8 byte
内部写周期很关键,代表每一次页写或字节写结束后时间要大于5ms(延时5ms确保完成写周期),否则时序会出错。
页写:型不同号每一页可能写入不同大小的字节数
AT24C02: 一页8字节,地址第一页可写地址0x00~0x07
AT24C04/08/16: 一页16字节,地址第一页可写地址0x00~0x0f
AT24C32/64: 一页32字节,地址第一页可写地址0x00~0x1f
基本的读写操作(字节读写,页写,随机读,序列读)芯片内部会自动递增地址,无需在代码里操作地址
芯片引脚
器件地址(7位):
AT24C02: 前四位固定1010,后3位根据芯片引脚接高低电平决定
AT24C04: 前四位固定1010,A2、A1位根据芯片引脚接高低电平决定,后一位必须接地。
AT24C08: 前四位固定1010,A2位根据芯片引脚接高低电平决定,后两位必须接地。
AT24C16: 前四位固定1010,后3位必须接地。
读写位:读1 、写0
AT24Cxx系列优点就是低功耗
基本读写操作
字节写:
I2C时序通信:发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发送8位数据->主机接收应答(从机发送应答)->发出停止条件->等待5ms(延时5ms)保证完成写周期;
页写:
I2C时序通信:发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发送8位数据->主机接收应答(从机发送应答)->…………(最多发送8个数据否则会覆盖先前写入的数据(字节))->发出停止条件->等待5ms(延时5ms)保证完成写周期;
内部写周期机制:发送写命令->……->发送停止信号,启动写周期->可以通过发送I2起始信号->(1)(发送一个字节->读取EEPROM应答判断芯片是否应答0 )->不是就重复操作(1)->是,就进行下一个操作
随机读一个数据:
发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发出起始条件->发送8位地址(器件地址7位+读写位1)->主机接收应答(从机发送应答)->可用指针来操作(存储)读取8位数据->主机发送不应答->发送停止信号
顺序读(连续读数据):
发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发出起始条件->发送8位地址(器件地址7位+读写位1)->主机接收应答(从机发送应答)->可用指针来操作(存储)读取8位数据->主机发送应答->……(直到主机不想接受了)->主机发送不应答->发送停止信号
原理图
AT24C02实现连续页写思路:
用一个变量来判断当前地址还可写入多少个字节,判断是否要跨页写,判断为需要跨页,先写完本页,再通过地址偏移(+前一页还可写入的字节数)得到下一页的地址,在进行一页写操作,如果写完一页,还没停止还要写,再通过地址偏移(+一页可写的字节数)得到下一页的地址,再进行重复操作。
总代码
AT24C02.c
#include "AT24C02.h" // Device header
#include "Delay.h"//AT24C02模块地址0x01010111 0x57 0x10101111 AF AE
//AT24C02最多读写50个字节 0x31
#define AT24C02_ADDRESS (0xA0)void AT24C02_W_SCL(uint8_t x)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction) x);Delay_us(10);
}void AT24C02_W_SDA(uint8_t x)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction) x);Delay_us(10);
}uint8_t AT24C02_R_SDA(void)
{uint8_t BitVal;BitVal = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitVal;
}void AT24C02_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB10和PB11引脚初始化为开漏输出/*设置默认电平*/GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); //设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}void AT24C02_Start(void)
{ AT24C02_W_SDA(1) ;AT24C02_W_SCL(1) ;AT24C02_W_SDA(0) ;AT24C02_W_SCL(0) ;
}void AT24C02_Stop(void)
{AT24C02_W_SDA(0) ;AT24C02_W_SCL(1) ;AT24C02_W_SDA(1) ;
}void AT24C02_SendByte(uint8_t Byte)
{uint8_t i;for( i=0;i<8;i++){ AT24C02_W_SDA(Byte &(0x80 >> i));//从最高位一位一位移出AT24C02_W_SCL(1);AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差}}uint8_t AT24C02_RecviceByte(void)
{ uint8_t i;uint8_t Byte = 0x00; AT24C02_W_SDA(1);//主机放开对数据线的控制for(i=0;i<8;i++){AT24C02_W_SCL(1);//时钟线高电平期间读取时钟线if(AT24C02_R_SDA()==1) {Byte |= (0x80>>i);} AT24C02_W_SCL(0);}return Byte;
}void AT24C02_SendAck(uint8_t x)
{//AT24C02_W_SCL(0);AT24C02_W_SDA(x);AT24C02_W_SCL(1);AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差
}uint8_t AT24C02_RecviceAck(void)
{uint8_t Ack=0;AT24C02_W_SDA(1);//主机放开对数据线的控制AT24C02_W_SCL(1);//时钟线高电平期间读取时钟线Ack = AT24C02_R_SDA();AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差return Ack;
}uint8_t AT24C02_WriteByte(uint8_t address,uint8_t data)
{AT24C02_Start();AT24C02_SendByte(AT24C02_ADDRESS);if(AT24C02_RecviceAck()) return 1;AT24C02_SendByte(address);if(AT24C02_RecviceAck()) return 2;AT24C02_SendByte(data);if(AT24C02_RecviceAck()) return 3;AT24C02_Stop();Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期return 0;
}uint8_t AT24C02_ReadByte(uint8_t address,uint8_t *data )
{AT24C02_Start();AT24C02_SendByte(AT24C02_ADDRESS);if(AT24C02_RecviceAck()) return 1;AT24C02_SendByte(address);if(AT24C02_RecviceAck()) return 2; AT24C02_Start(); AT24C02_SendByte(AT24C02_ADDRESS+1);if(AT24C02_RecviceAck()) return 3;*data = AT24C02_RecviceByte();AT24C02_SendAck(1);AT24C02_Stop();Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期return 0;
}uint8_t AT24C02_PageWrite(uint8_t address,uint8_t *str,uint8_t count)
{AT24C02_Start();AT24C02_SendByte(AT24C02_ADDRESS);if(AT24C02_RecviceAck()) return 1;AT24C02_SendByte(address);if(AT24C02_RecviceAck()) return 2; for (uint8_t i = 0; i < count; i++){AT24C02_SendByte(*str++);if (AT24C02_RecviceAck()) return 3;}// AT24C02_SendAck(1); //无需应答AT24C02_Stop();Delay_ms(5);return 0;
} uint8_t AT24C02_NPageWrite(uint8_t address,uint8_t *str,uint8_t count)
{uint8_t num = address%8 +count ; //判读是否跨页if(num>8){while(count){uint8_t temp = 8 - address%8 ;if(count>=temp){AT24C02_PageWrite(address,str,temp); address +=temp;str += temp;count -= temp; }else{AT24C02_PageWrite(address,str,count);address +=count;str += count;count -= count; }}} else{AT24C02_PageWrite(address,str,count);}return 0;
} u8 AT24C02_NReadData(uint8_t address,uint8_t *str,uint8_t count)
{AT24C02_Start();AT24C02_SendByte(AT24C02_ADDRESS);if(AT24C02_RecviceAck()) return 1;AT24C02_SendByte(address);if(AT24C02_RecviceAck()) return 2; AT24C02_Start(); AT24C02_SendByte(AT24C02_ADDRESS+1);if(AT24C02_RecviceAck()) return 3;for(uint8_t i = 0;i<count-1;i++){ *str = AT24C02_RecviceByte();AT24C02_SendAck(0);str++;count--;} *str = AT24C02_RecviceByte();AT24C02_SendAck(1);AT24C02_Stop();// Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期return 0;
}
AT24C02.h
#ifndef _AT24C02_H_
#define _AT24C02_H_#include "stm32f10x.h" // Device header//void AT24C02_WriteByte(uint8_t WordAddress,uint8_t data);
//uint8_t AT24C02_ReadByte(uint8_t WordAddress);
uint8_t AT24C02_WriteByte(uint8_t address,uint8_t data);
uint8_t AT24C02_ReadByte(uint8_t address,uint8_t *data );
uint8_t AT24C02_PageWrite(uint8_t address,uint8_t *str,uint8_t count);
uint8_t AT24C02_NPageWrite(uint8_t address,uint8_t *str,uint8_t count);void AT24C02_Init(void);u8 AT24C02_NReadData(u8 page_Address,u8 *buf,u8 Datalen);#endif