MCU模拟IIC读写,以E2PROM AT24C256为例
/* 模拟IIC */ #define EEPROM_WP_Pin GPIO_PIN_15 #define EEPROM_WP_GPIO_Port GPIOE #define WP_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() #define EEPROM_I2C2_SCL_Pin GPIO_PIN_10 #define EEPROM_I2C2_SCL_GPIO_Port GPIOB #define SCL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define EEPROM_I2C2_SDA_Pin GPIO_PIN_11 #define EEPROM_I2C2_SDA_GPIO_Port GPIOB #define SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define SCL_EEPROM_H() HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_SET) #define SCL_EEPROM_L() HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_RESET) #define SDA_EEPROM_H() HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_SET) #define SDA_EEPROM_L() HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_RESET) #define SDA_EEPROM_READ() HAL_GPIO_ReadPin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin) /* IIC设备常量 */ #define EXTEEPROM_HW_ADDRESS 0xA0 // EXTEEPROM地址 #define AT24C256 1 //使用AT24C256使能 #define AT24C64 0 //使用AT24C64使能 #if AT24C256 #define Page_Bite 64 #define EXTEEPROM_STORAGE_SIZE 0x8000 #endif #if AT24C64 #define Page_Bite 32 //页大小 #define EXTEEPROM_STORAGE_SIZE 0x2000 //EEPROM大小 #endif #define EEPROM_MAX_WRITE_LEN 1024 // 最大写入长度1024=1K unsigned char HAL_ReadDataFromEEPROM(unsigned short eeprom_add, unsigned short len, unsigned char *ram_add); unsigned char HAL_WriteDataToEEPROM(unsigned short eeprom_add, unsigned short len, unsigned char *ram_add); unsigned char ReadDataFromExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff); unsigned char WriteDataToExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff);
void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ WP_GPIO_CLK_ENABLE(); SCL_GPIO_CLK_ENABLE(); SCL_GPIO_CLK_ENABLE(); /*eeprom*/ GPIO_InitStruct.Pin = EEPROM_WP_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /*推挽*/ GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); HAL_GPIO_WritePin(EEPROM_WP_GPIO_Port, EEPROM_WP_Pin, GPIO_PIN_RESET); GPIO_InitStruct.Pin = EEPROM_I2C2_SCL_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /*开漏*/ GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_SET); GPIO_InitStruct.Pin = EEPROM_I2C2_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /*开漏*/ GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_SET); } /******************************************************************************* * 函数名称: HAL_WriteWPState * 函数功能: 设置写保护状态 -->针对eeprom * 输入参数: 使能,静止 * 输出参数: 无 * 返 回 值: 无 *******************************************************************************/ void HAL_WriteWPState(GPIO_PinState PinState) { HAL_GPIO_WritePin(EEPROM_WP_GPIO_Port,EEPROM_WP_Pin,PinState); } static void EEPROM_I2C_delay(void) //写引脚延时 { volatile uint32_t i = 40; //这里可以优化速度 ,经测试最低到5还能写入 for(i=0; i<40; i++) { ; } } void udelay(uint32_t usec) //usec延时 可以优化 { volatile uint32_t i; for(i=(usec*4); i!=0; i--) { asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); } } static uint8 EEPROM_I2C_Start(void) { SDA_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_H(); EEPROM_I2C_delay(); SDA_EEPROM_L(); EEPROM_I2C_delay(); SCL_EEPROM_L(); EEPROM_I2C_delay(); } static void EEPROM_I2C_Stop(void) { SCL_EEPROM_L(); EEPROM_I2C_delay(); SDA_EEPROM_L(); EEPROM_I2C_delay(); SCL_EEPROM_H(); EEPROM_I2C_delay(); SDA_EEPROM_H(); EEPROM_I2C_delay(); } static void EEPROM_I2C_Ack(void) { SDA_EEPROM_L(); EEPROM_I2C_delay(); SCL_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_L(); EEPROM_I2C_delay(); SDA_EEPROM_H(); EEPROM_I2C_delay(); } static void EEPROM_I2C_NoAck(void) { SDA_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_L(); EEPROM_I2C_delay(); } static uint8 EEPROM_I2C_WaitAck(void) //返回为:=1有ACK { SCL_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_L(); EEPROM_I2C_delay(); return 1; } static void EEPROM_I2C_SendByte(uint8_t SendByte) //数据从高位到低位// { uint8 i = 8; while(i--) { if(SendByte&0x80) //1000 0000 SDA_EEPROM_H(); else SDA_EEPROM_L(); SendByte<<=1; EEPROM_I2C_delay(); SCL_EEPROM_H(); EEPROM_I2C_delay(); SCL_EEPROM_L(); EEPROM_I2C_delay(); } } static uint8 EEPROM_I2C_ReceiveByte(void) //数据从高位到低位// { uint8 i = 8; uint8 ReceiveByte = 0; while(i--) { ReceiveByte<<=1; //0000 0000 SCL_EEPROM_H(); EEPROM_I2C_delay(); if(SDA_EEPROM_READ()) { ReceiveByte|=0x01;//0000 0001 移位8次 } SCL_EEPROM_L(); EEPROM_I2C_delay(); } return ReceiveByte; } /******************************************************************************* * 函数名称: ReadbytefromEEPROM * 函数功能: 读从设备指定地址指定长度字节 * 输入参数: slave_addr:从设备地址 addr:设备内部地址,读字节的起始地址 len:读字节长度 * 输出参数: *data:接收数据指针 * 返 回 值: 1 -->读结束 *******************************************************************************/ static uint8 ReadbytefromEEPROM(uint8_t slave_addr, uint16_t addr, uint8_t *data, uint16_t len) { unsigned int i; EEPROM_I2C_Start(); EEPROM_I2C_SendByte(slave_addr); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } EEPROM_I2C_SendByte(addr>>8); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } EEPROM_I2C_SendByte(addr); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } EEPROM_I2C_Start(); EEPROM_I2C_SendByte(slave_addr|0x01); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } for(i = 0; i < len - 1; i++) { data[i] = EEPROM_I2C_ReceiveByte(); EEPROM_I2C_Ack(); } data[i] = EEPROM_I2C_ReceiveByte(); EEPROM_I2C_NoAck(); EEPROM_I2C_Stop(); return 1; } /******************************************************************************* * 函数名称: ReadbytefromEEPROM * 函数功能: 读从设备指定地址指定长度字节 * 输入参数: slave_addr:从设备地址 addr:设备内部地址,写字节的起始地址 len:要写的字节长度 *data:要写的数据 * 输出参数: * 返 回 值: 1 -->写结束 *******************************************************************************/ static uint8 WritebytetoEEPROM(uint8_t slave_addr, uint16_t addr, uint8_t *data, uint16_t len) { unsigned int i; EEPROM_I2C_Start(); EEPROM_I2C_SendByte(slave_addr); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } EEPROM_I2C_SendByte(addr>>8); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } EEPROM_I2C_SendByte(addr); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } for(i = 0; i < len; i++) { EEPROM_I2C_SendByte(data[i]); if(EEPROM_I2C_WaitAck() != 1) { //return IIC_ERR_WADDR; } } EEPROM_I2C_Stop(); return 1; } /*************************************************************************************** * 名称: HAL_WriteDataToEEPROM * 功能: 向EEPROM写数据 * 输入: EEPROM_add 数据地址 * len 数据项长度 * data 数据缓存 * 输出: * 处理: * 返回: 1 写入成功 * 0 写入失败 ***************************************************************************************/ uint8 HAL_WriteDataToEEPROM(uint16 eeprom_add, uint16 len, uint8 *data) { unsigned short WriteLen = len; unsigned short DateOffect = 0; unsigned short size = 0; unsigned short PageOffect = eeprom_add % Page_Bite; // if((eeprom_add + len > EXTEEPROM_STORAGE_SIZE)||(len>EEPROM_MAX_WRITE_LEN)) { return FALSE; } HAL_WriteWPState(GPIO_PIN_RESET);//关闭写保护 udelay(1); //EEPROM支持以32字节为单位的页写,而实际是EPPROM按照每16字节进行页的划分 //写到页的边界后,要往后写必须重新调用写入 while(WriteLen > 0) { if(PageOffect + WriteLen > Page_Bite) { size = Page_Bite - PageOffect; } else { size = WriteLen; } if(WritebytetoEEPROM(EXTEEPROM_HW_ADDRESS, eeprom_add + DateOffect, data+DateOffect, size) != 1) { return 0; } PageOffect = 0; DateOffect += size; WriteLen -= size; udelay(6000); } HAL_WriteWPState(GPIO_PIN_SET);//打开写保护 return 1; } /*************************************************************************************** * 名称: HAL_ReadDataFromEEPROM * 功能: 向EEPROM写数据 * 输入: eeprom_add 数据偏移 * len 数据项长度 * data 数据缓存 * 输出: * 处理: * 返回: > 0 写入成功 * = 0 写入失败 ***************************************************************************************/ uint8 HAL_ReadDataFromEEPROM(uint16 eeprom_add, uint16 len, uint8 *data) { unsigned short ReadLen = len; unsigned short DateOffect = 0; unsigned short size = 0; unsigned short PageOffect = eeprom_add % Page_Bite; if((eeprom_add + len > EXTEEPROM_STORAGE_SIZE)||(len > EEPROM_MAX_WRITE_LEN)) { return 0; } //EEPROM支持以32字节为单位的页写,而实际是EPPROM按照每16字节进行页的划分 //写到页的边界后,要往后写必须重新调用写入 while(ReadLen > 0) { if(PageOffect + ReadLen > Page_Bite) { size = Page_Bite - PageOffect; } else { size = ReadLen; } if(ReadbytefromEEPROM(EXTEEPROM_HW_ADDRESS, eeprom_add + DateOffect, data + DateOffect, size) == 1) { //return size; } PageOffect = 0; DateOffect += size; ReadLen -= size; udelay(6000); } return 1; } unsigned char WriteDataToExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff)//补校验和 { uint32 i; uint8 TempBuff[1025]= {0}; uint8 CS = 0; if(Len > 1024) { return FALSE; } //数据暂存到缓冲区 memcpy(TempBuff, DataBuff, Len); //计算校验和 CS = 0x33; for (i=0; i < Len; i++) { CS += TempBuff[i]; } //将校验和追加到缓冲区 TempBuff[Len] = CS; HAL_WriteDataToEEPROM(Addr, Len+1, TempBuff); return TRUE; } unsigned char ReadDataFromExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff) { uint32 i; uint8 TempBuff[1025]= {0}; uint8 CS = 0; if(Len > 1024) { return FALSE; } HAL_ReadDataFromEEPROM(Addr, Len+1, TempBuff); //计算校验和 CS = 0x33; for (i=0; i < Len; i++) { CS += TempBuff[i]; } //校验和相等,数据正确,直接返回 if (TempBuff[Len] == CS) { //从缓冲区取数据返回 memcpy(DataBuff, TempBuff, Len); return 1; } return 0; }