标签:add png 连续 实现 else 读取数据 mcu stm32 不能
本文介绍如何使用STM32标准外设库驱动EEPROM,本例程驱动的EEPROM为AT24C02,通讯协议为IIC,使用IO口模拟方式。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。
EEPROM全称为EEPROM(Electrically Erasable Programmable Read Only Memory)是电可擦除可编程只读存储器。虽然名称为只读存储器,但是擦除和写入都是直接使用电路控制,不需要再使用外部设备来擦写,即设备在运行过程中即可随时擦除和写入。可以按字节为单位修改数据,无需整个芯片擦除,且掉电后数据不丢失,一般用来存储一些配置信息,以便系统重新上电的时候加载。
一般常用的EEPROM为ATMEL公司(已被Microchip收购)的AT24Cxx系列,常用容量从1K到64Kbit不等,换算成字节为128到8K Bytes,可以根据项目需求和价格综合考虑选型。
AT24C02容量为2Kbit,即256Byte,地址范围为0~255,即0~0xFF,使用1个字节即可表示,因此地址长度为1字节。
AT24C02使用IIC协议跟MCU通讯。
如果仅接入一个AT24C02,可以将设备的A0、A1、A2引脚全部接入低电平,那么此时该设备的地位为0x50,再增加一位读写标志位,最终读取操作时地址为0xA1,写入操作时地址为0xA0。
1 uint8_t EE_Read(uint8_t addr, uint8_t *pBuffer, uint8_t numToRead) 2 { 3 uint8_t i; 4 5 if(EE_WaitReady()){ 6 goto iic_fail; 7 } 8 9 IIC_Start(); 10 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR); 11 if(IIC_WaitAck()){ 12 goto iic_fail; 13 } 14 IIC_WriteByte(addr); 15 if(IIC_WaitAck()){ 16 goto iic_fail; 17 } 18 IIC_Start(); 19 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_RD); 20 if(IIC_WaitAck()){ 21 goto iic_fail; 22 } 23 24 for(i = numToRead; i > 0; i--) 25 { 26 *pBuffer++ = ((i == 1) ? IIC_ReadByte(0) : IIC_ReadByte(1)); 27 } 28 29 IIC_Stop(); 30 return 0; 31 32 iic_fail: 33 IIC_Stop(); 34 return 1; 35 }
1 //AT24C02页写入函数,不得超过当前页的最后地址,每页可写入8个字节 2 //addr: 写入数据的目的地址 3 //pBuffer: 要写入的数据 4 //numToWrite: 要写入数据的长度 5 //返回值: 1,写入失败 6 // 0,写入成功 7 uint8_t EE_PageWrite(uint8_t addr, uint8_t *pBuffer, uint8_t numToWrite) 8 { 9 uint8_t i; 10 11 if(EE_WaitReady()){ 12 goto iic_fail; 13 } 14 15 IIC_Start(); 16 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR); 17 if(IIC_WaitAck()){ 18 goto iic_fail; 19 } 20 IIC_WriteByte(addr); 21 if(IIC_WaitAck()){ 22 goto iic_fail; 23 } 24 for(i = 0; i < numToWrite; i++){ 25 IIC_WriteByte(*pBuffer++); 26 if(IIC_WaitAck()){ 27 goto iic_fail; 28 } 29 } 30 31 IIC_Stop(); 32 return 0; 33 34 iic_fail: 35 IIC_Stop(); 36 return 1; 37 } 38 39 //在AT24C02里面的指定地址开始写入指定个数的数据 40 //addr: 写入数据的目的地址 41 //pBuffer: 要写入的数据 42 //numToWrite: 要写入数据的长度 43 //返回值: 1,读取失败 44 // 0,读取成功 45 uint8_t EE_Write(uint8_t addr, uint8_t *pBuffer, uint8_t numToWrite) 46 { 47 uint8_t i; 48 uint8_t firstPageLen, lastPageLen, pageNum; 49 50 EE_GetWritePages(addr, numToWrite, EEPROM_PAGE_SIZE, 51 &firstPageLen, &lastPageLen, &pageNum); 52 53 for(i = 0; i < pageNum; i++) 54 { 55 if(i == 0){ //首页写入长度为firstPageLen 56 if(EE_PageWrite(addr, pBuffer, firstPageLen)){ 57 goto iic_fail; 58 } 59 addr += firstPageLen; 60 pBuffer += firstPageLen; 61 }else if(i == pageNum - 1){ //尾页写入长度为lastPageLen 62 if(EE_PageWrite(addr, pBuffer, lastPageLen)){ 63 goto iic_fail; 64 } 65 addr += lastPageLen; 66 pBuffer += lastPageLen; 67 }else{ //除首页和尾页外写入长度为EEPROM_PAGE_SIZE 68 if(EE_PageWrite(addr, pBuffer, EEPROM_PAGE_SIZE)){ 69 goto iic_fail; 70 } 71 addr += EEPROM_PAGE_SIZE; 72 pBuffer += EEPROM_PAGE_SIZE; 73 } 74 } 75 76 return 0; 77 78 iic_fail: 79 IIC_Stop(); 80 return 1; 81 }
EEPROM调用写入操作后,需要一段时间才能将数据更新,通过查看AT24C02的datasheet,可以看到写入循环最大为5ms,数据更新期间对设备无法进行读写操作,程序具体实现为向AT24C02发送START+设备地址(写命令),如果AT24C02返回ACK信号,表示AT24C02目前已经可以正常读写,否则需要继续等待,等待过程中可以继续发送START+设备地址(写命令),直到返回ACK信号。
为防止设备故障导致一直无法返回ACK信号,需要设定一个最大检测次数,因为AT24C02最大写入循环为5ms,因此可以将每次检测时间间隔设定为100us,循环次数为50次,如果检测50次均没有返回ACK信号,此时可以放弃读写操作,返回错误。
1 //查看AT24C02是否空闲 2 //返回值: 1,EEPROM忙,无法读写 3 // 0,EEPROM空闲,可以读写 4 uint8_t EE_WaitReady(void) 5 { 6 uint8_t i; 7 uint8_t ret = 1; 8 9 for(i = 0; i < 50; i++){ 10 IIC_Start(); 11 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR); //发送写命令 12 if(!IIC_WaitAck()){ 13 ret = 0; 14 break; 15 } 16 delay_us(100); 17 } 18 IIC_Stop(); 19 return ret; 20 }
AT24C02存储数据时把整个存储区会分成若干页,每页8个字节,一次写操作不能跨页写入,因此如果连续写入时必须要使用一定的算法将数据按页将数据分割成若干块,然后按块进行写入。
如果需要连续写入,那么需要分成若干次页写入,除首页和尾页可能不是整页写入之外,中间页均可以整页写入,那么只要能够计算出首页需要写入的字节数、尾页需要写入的字节数以及需要写入的总页数n,然后调用n次页写入函数,即可实现数据的全部写入,第一次调用时写入长度为首页字节数,最后一次调用时写入长度为尾页字节数,中间的写入长度为整页长度,每次调用完毕后地址增加实际写入的长度。
具体计算过程:
首块偏移 = 起始地址 % 页大小,
首块写入长度 = 页大小 - 首块偏移,
如果写入长度 < 首块写入长度,首块写入长度 = 写入长度,
剩余长度 = 写入长度 - 首块写入长度,
剩余整数块数 = 剩余长度 / 页大小,
尾块长度 = 剩余长度 % 页大小,
总块数 = 1 + 剩余整数块数,
如果尾块长度不为0,总块数加1。
举例:写入起始地址为2,写入长度为18,那么通过计算,可以得出首页写入长度为6,尾页写入长度为4,写入页数为3,需要调用3次页写入函数EE_PageWrite()。该函数第一个参数为写入的地址,第二个参数为写入数据指针,第三个参数为写入的字节数。每次调用完毕后数据指针(pBuffer)也需要增加写入的长度。
1 EE_PageWrite(2, pBuffer, 6); 2 pBuffer += 6; 3 EE_PageWrite(8, pBuffer, 8); 4 pBuffer += 8; 5 EE_PageWrite(16, pBuffer, 4); 6 pBuffer += 4;
1 /* 2 根据要写入的地址、长度、页大小计算如何分页 3 输入参数:addr: 写入起始地址 4 len: 写入数据长度 5 pageSize:每页存储的数据,对于AT24C02来说,该值为8 6 要写入参数:pFirstPageLen: 首页要写入的字节 7 pLastPageLen: 尾页要写入的字节 8 pPageNum: 总共要写入的页数 9 */ 10 void EE_GetWritePages(uint8_t addr, uint8_t len, uint8_t pageSize, 11 uint8_t * pFirstPageLen, uint8_t * pLastPageLen, uint8_t * pPageNum) 12 { 13 uint8_t firstPageOffset; //首页偏移 14 uint8_t otherLen; //去除首页之后剩余长度 15 uint8_t otherPageNum; //去除首页之后剩余整数页数量 16 17 firstPageOffset = addr % pageSize; 18 *pFirstPageLen = pageSize - firstPageOffset; 19 20 if(len < *pFirstPageLen){ 21 *pFirstPageLen = len; 22 } 23 24 otherLen = len - *pFirstPageLen; 25 otherPageNum = otherLen / pageSize; 26 *pLastPageLen = otherLen % pageSize; 27 28 *pPageNum = otherPageNum + 1; 29 30 if(*pLastPageLen){ 31 (*pPageNum)++; 32 } 33 }
https://files.cnblogs.com/files/greatpumpkin/IIC_soft.rar
标签:add png 连续 实现 else 读取数据 mcu stm32 不能
原文地址:https://www.cnblogs.com/greatpumpkin/p/13546446.html