《STM32从零开始学习历程》@EnzoReventon
相关链接:
I2C物理层介绍
I2C协议层介绍
I2C固件库介绍
STM32的I2C特性及架构介绍
STM32的EEPROM简介
I2C向EEPROM写入一字节数据(I2C硬件)
I2C向EEPROM读取一字节数据(I2C硬件
参考资料:
[野火EmbedFire]《STM32库开发实战指南——基于野火霸天虎开发板》
[正点原子]STM32F4开发指南-库函数版本_V1.2
[ST]《STM32F4xx中文参考手册》
[ATMEL]《AT24C02说明书》
开发板硬件原理图;EEPROM原理图。
程序源码:
【下载地址】
程序总体与《I2C向EEPROM写入一字节数据(I2C硬件)》、《I2C向EEPROM读取一字节数据(I2C硬件》基本一致,为了适应多页数据的连续读写,在上文程序的基础上加入了连续读写的函数。
写入EEPROM多个数据,并通过串口调试助手显示读取的数据。
本实验采用的开发板为“正点原子”探索者F4开发板,核心芯片为F407ZGT6。
使用到的外设及硬件为:USART1,I2C1,EEPROM。
USART1:PA9(T)—》RXE;PA10(R) —》TXE,将引脚使用跳线帽相连接即可。
本文主要展示新增的代码。
//addr:要写入的存储单元首地址
//data:要写入的数据的指针
//size:要写入多少个数据(size小于等于8)
//return:0表示正常,非0为失败
uint8_t EEPROM_Page_Write(uint8_t addr, uint8_t* data, uint8_t size)
{
//产生起始信号
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_Back(12);
}
}
//要发送的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_Back(13);
}
}
//发送要写入的存储单元地址
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_Back(14);
}
}
while(size--)
{
//发送要写入的数据
I2C_SendData(I2C1,*data);
//重置 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_Back(15);
}
}
data++;
}
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
//等待写入完成
return Wait_For_Standby();
}
//addr:要写入的存储单元首地址
//data:要写入的数据的指针
//size:要写入多少个数据
//return:0表示正常,非0为失败
uint8_t EEPROM_Buffer_Write(uint8_t addr, uint8_t* data, uint16_t size)
{
// single_adrr = 1 = 1 % 8时 写入 EEPROM_PAGE_SIZE-single_adrr = 7
uint8_t single_adrr = addr%EEPROM_PAGE_SIZE;
if(single_adrr == 0)
{
uint8_t num_of_page = size/EEPROM_PAGE_SIZE;
uint8_t single_byte = size%EEPROM_PAGE_SIZE; //取模 9/8余1
while(num_of_page--)
{
//调用页写入函数
EEPROM_Page_Write(addr, data, EEPROM_PAGE_SIZE);
//等待写入完成
Wait_For_Standby();
addr += EEPROM_PAGE_SIZE;
data += EEPROM_PAGE_SIZE;
}
//调用页写入函数
EEPROM_Page_Write(addr, data, single_byte);
//等待写入完成
Wait_For_Standby();
}
else //addr不对齐
{
uint8_t num_of_page,single_byte,left_size;
//第一次写入不对齐的数据
uint8_t first_size = EEPROM_PAGE_SIZE - single_adrr;
//调用页写入函数
EEPROM_Page_Write(addr, data, first_size);
//等待写入完成
Wait_For_Standby();
addr += first_size;
data += first_size;
//剩下要写入的数据
left_size = size - first_size;
num_of_page = left_size/EEPROM_PAGE_SIZE;
single_byte = left_size%EEPROM_PAGE_SIZE;
while(num_of_page--)
{
//调用页写入函数
EEPROM_Page_Write(addr, data, EEPROM_PAGE_SIZE);
//等待写入完成
Wait_For_Standby();
addr += EEPROM_PAGE_SIZE;
data += EEPROM_PAGE_SIZE;
}
//调用页写入函数
EEPROM_Page_Write(addr, data, single_byte);
//等待写入完成
Wait_For_Standby();
}
return 0;
}
该功能的函数主要在于连续的写入多个字节。本程序段的主要功能是实现对输入的数据进行分页,并且对没有完整占用整个字节的数据进行对其。
通过“整除”计算要写入的数据Size 能写满多少“完整的页”,计算得的值存储在num_of_page 中,但有时数据不是刚好能写满完整页的,会多一点出来,通过“求余”计算得出“不满一页的数据个数”就存储在single_byte 中。计算后通过按页传输num_of_page 次整页数据及最后的single_byte 个数据,使用页传输,比之前的单个字节数据传输要快很多。
考虑好关于分页的概念之后,我们对数据进行对齐的时候通常还需要考虑首地址的问题。若首地址不是刚好对齐到页的首地址,会需要一个first_size值,用于存储从该首地址开始写满该地址所在的页,还能写多少个数据。实际传输时,先把这部分first_size个数据先写入,填满该页,然后把剩余的数据(num_of_page),再重复上述求出num_of_page 及single_byte的过程,按页传输到EEPROM。
//addr:要读取的存储单元首地址
//data:用来存储 读取到的数据 的指针
//size:要读取多少个数据
//return:0表示正常,非0为失败
uint8_t EEPROM_Buffer_Read(uint8_t addr, uint8_t *data, uint16_t size)
{
//产生起始信号
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_Back(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_Back(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_Back(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_Back(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_Back(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_Back(21);
}
}
//接收数据
*data = I2C_ReceiveData(I2C1);
data++;
}
//产生结束信号
I2C_GenerateSTOP(I2C1, ENABLE);
return 0;
}
#define TEST_SIZE 255 //定义测试数据的大小
int main(void)
{
uint8_t data[TEST_SIZE];
uint8_t buff[TEST_SIZE];
uint16_t i;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
//给buff赋值
for(i=0;i<TEST_SIZE;i++)
{
buff[i] = i;
}
IIC_Init();
EEPROM_Byte_Write(0x01, 0x12);
EEPROM_Random_Read(0x01,data);
printf("\r\nox%x\r\n",data[0]);
// EEPROM_Page_Write(0x00,buff,TEST_SIZE);
EEPROM_Buffer_Write(0x01,buff,TEST_SIZE);
EEPROM_Buffer_Read(0x01,data,TEST_SIZE);
for(i=0;i<TEST_SIZE;i++)
{
printf("0x%02x ",data[i]);
}
while(1)
{
}
}