STM32内部自带有IIC通讯,可以直接使用它的固件操作EEPROM
IIC的初始化函数
#include "my_eeprom.h"
#define STM32_I2C_OWN_ADDR 0x1F
#define EEPROM_I2C_ADDR 0xA0
#define TIME_OUT 0xFF
uint16_t error_callbak(uint16_t no);
uint8_t Wait_For_Standby(void);
/* i2c
PB8 I2C1_SCL
PB9 I2C1_SDA
*/
void my_eeprom_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //使能 I2C的时钟
GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_I2C1); //配置GPIO的复用功能
/* GPIO SCL初始化 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 300000;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = STM32_I2C_OWN_ADDR;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);//使能
}
本案例中EEPROM的地址:
#define EEPROM_I2C_ADDR 0xA0
0xA0的地址原因:
首先官方手册地址规定:
该板子使用的是2K的EEPROM,而且电路图中,A0 A1 A2都接地,所以都为0,
所以EEPROM的地址最终为0xA0
ERPROM 写一个字节
向EEPROM中任何一个地址写一个字节数据:
uint8_t my_EEPROM_Byte_Write(uint8_t addr,uint8_t data)
{
uint32_t count_wait;
I2C_GenerateSTART(I2C1,ENABLE); //产生起始信号
count_wait = TIME_OUT;
//等待EV5事件,这是一个宏。STM32在起始信号稳定后会设置该宏
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(1);
}
}
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Transmitter); //发送 EEPROM 地址,并指明是写操作
count_wait = TIME_OUT;
//等待事件 EV6,成功发送地址后,有EV6事件触发,表示发送成功
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(2);
}
}
//发送要写入的存储单元地址
I2C_SendData(I2C1,addr);
//等待事件 EV8,成功发送数据后,有EV8事件触发,表示发送成功
count_wait = TIME_OUT;
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(3);
}
}
I2C_SendData(I2C1,data); //发送要写入的数据
//等待事件 EV8
count_wait = TIME_OUT;
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(4);
}
}
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
return Wait_For_Standby();
}
这里需要注意,当发送完写数据给EEPROM完成后,EEPROM此时才开始操作内部寄存器存储数据,会消耗一定时间,在这段时间内如果再次访问EEPROM,会访问失败。这就是最后wait_for_standby()函数的作用。
代码如下:
一直发送访问请求,直到访问成功。
uint8_t Wait_For_Standby(void)
{
uint32_t check_count = 0xFFFFF;
while(check_count--)
{
//产生起始信号
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到检测成功
while(I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return Error_CallBack(11);
}
}
//要发送的EEPROM设备地址,并设置为写方向
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到检测成功
while(count_wait--)
{
//若检测到响应,说明内部写时序完成,跳出等待函数
if(I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
{
//退出前停止本次通讯
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
return 0;
}
}
}
//退出前停止本次通讯
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
return 1;
}
ERPROM 读一个字节
EEPROM中一般使用任意地址读功能,而不使用当前地址读。代码实现如下:
读取任意一个地址的一个字节数据:
uint8_t my_EEPROM_Random_Read(uint8_t addr, uint8_t *data)
{
uint32_t count_wait;
//产生起始信号
I2C_GenerateSTART(I2C1, ENABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(5);
}
}
//要发送的EEPROM设备地址,并设置为写方向
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(6);
}
}
//发送要读取的存储单元地址
I2C_SendData(I2C1,addr);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(7);
}
}
//------------------------------------------------------------------------
//产生第二次起始信号
I2C_GenerateSTART(I2C1, ENABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(8);
}
}
//************************读方向**********************************************
//要发送的EEPROM设备地址,并设置为读方向
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Receiver);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(9);
}
}
//作出非应答信号, 此处如果是应答,EEPROM就会一直发数据了
I2C_AcknowledgeConfig(I2C1, DISABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(10);
}
}
//接收数据
*data = I2C_ReceiveData(I2C1);
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
return 0;
}
按页写入数据
uint8_t my_eeprom_page_write(uint16_t addr,uint8_t* data,uint8_t size)
{
uint32_t count_wait;
if(size > 8) {
return error_callbak(10);
}
I2C_GenerateSTART(I2C1,ENABLE); //产生起始信号
count_wait = TIME_OUT;
//等待EV5事件
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(1);
}
}
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Transmitter); //发送 EEPROM 地址,写方向
count_wait = TIME_OUT;
//等待事件 EV6
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(2);
}
}
//发送要写入的存储单元地址
I2C_SendData(I2C1,addr);
//等待事件 EV8
count_wait = TIME_OUT;
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(3);
}
}
while(size--) {
I2C_SendData(I2C1,*data); //发送要写入的数据
//等待事件 EV8
count_wait = TIME_OUT;
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0) {
return error_callbak(4);
}
}
data++;
}
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
return Wait_For_Standby();
写入任意大小数据
uint8_t my_eeprom_buffer_write(uint16_t addr,uint8_t* data,uint16_t size)
{
//本次使用的EEPROM 是8字节 为一页对其
if(size > EEPROM_MAX_SIZE)
{
return error_callbak(22);
}
uint8_t addr_page = addr % 8; //检测地址是否对其
uint16_t num_page,surplus_page;
if(addr_page == 0)
{
surplus_page = size % 8; //数据是否对其,多出多少
num_page = size / 8; //算出数据是多少页
while(num_page--)
{
my_eeprom_page_write(addr,data,8);
addr += 8;
data += 8;
}
my_eeprom_page_write(addr,data,surplus_page); //发送不满一页的数据
}
else{ //地址不对齐
uint16_t first_page = 8 - addr_page; //还差这么多地址才对齐
my_eeprom_page_write(addr,data,first_page);//先发这么多,让地址对齐
addr += first_page;
data += first_page;
size -= first_page;
surplus_page = size % 8;
num_page = size / 8;
while(num_page--)
{
my_eeprom_page_write(addr,data,8);
addr += 8;
data += 8;
}
my_eeprom_page_write(addr,data,surplus_page);
}
return 0;
}
读取EEPROM中所有数据
uint8_t my_eeprom_buffer_read(uint16_t addr, uint8_t *data,uint16_t size)
{
uint32_t count_wait;
//产生起始信号
I2C_GenerateSTART(I2C1, ENABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(16);
}
}
//要发送的EEPROM设备地址,并设置为写方向
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(17);
}
}
//发送要读取的存储单元地址
I2C_SendData(I2C1,addr);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(18);
}
}
//------------------------------------------------------------------------
//产生第二次起始信号
I2C_GenerateSTART(I2C1, ENABLE);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(19);
}
}
//************************读方向**********************************************
//要发送的EEPROM设备地址,并设置为读方向
I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDR, I2C_Direction_Receiver);
//重置 count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(20);
}
}
while(size--)
{
if(size == 0)
{
I2C_AcknowledgeConfig(I2C1, DISABLE);
}else {
I2C_AcknowledgeConfig(I2C1,ENABLE);
}
//重置 count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到检测成功
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
{
count_wait--;
if(count_wait == 0 )
{
return error_callbak(21);
}
}
//接收数据
*data = I2C_ReceiveData(I2C1);
data++;
}
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
return 0;
}