嵌入式|STM32硬件IIC读写EEPROM

前面一篇写了软件模拟IIC读写EEPROM。
本篇介绍硬件IIC读写EEPROM。平台是STM32F103+AT24C04N。SDA和SCL接5K上拉电阻到3.3v。
首先介绍AT24C04N的基本特性。512byte。支持1.8v~5.5v供电。支持5种读写模式。BYTE WRITE(字节写),PAGE WRITE(按页写)。RANDOM READ(随机读),SEQUENTIAL READ(顺序读)和CURRENT ADDRESS READ .具体时序参考数据手册。
嵌入式|STM32硬件IIC读写EEPROM
文章图片

【嵌入式|STM32硬件IIC读写EEPROM】嵌入式|STM32硬件IIC读写EEPROM
文章图片



我用的是I2C1接口。
宏定义如下:

#define EEPROM_Block_ADDRESS 0xA0/* Device Address */#define I2C1_SLAVE_ADDRESS70xA0 #define I2C_PageSize16 #define I2C_BUF_LEN256#define EEPROM_I2CI2C1 #define EEPROM_I2C_CLKRCC_APB1Periph_I2C1 #define EEPROM_I2C_SCL_PINGPIO_Pin_6/* PB.6 */ #define EEPROM_I2C_SCL_GPIO_PORTGPIOB/* GPIOB */ #define EEPROM_I2C_SCL_GPIO_CLKRCC_APB2Periph_GPIOB #define EEPROM_I2C_SDA_PINGPIO_Pin_7/* PB.7 */ #define EEPROM_I2C_SDA_GPIO_PORTGPIOB/* GPIOB */ #define EEPROM_I2C_SDA_GPIO_CLKRCC_APB2Periph_GPIOB#define I2C_SPEED 200000extern uint8_t I2C_Buf[I2C_BUF_LEN];


配置和功能函数如下:
/* Includes ------------------------------------------------------------------*/ #include /* Private macro -------------------------------------------------------------*/ #define TIMEOUT 1000/* Private variables ---------------------------------------------------------*/ uint8_t EEPROM_ADDRESS = EEPROM_Block_ADDRESS; u32 I2C_Timeout =1000; static void delay_ms(u32 t) { u16 i = 8000; while(t--) { i = 8000; while(i--); } }static void delay_us(u32 d) { while(d--) ; }/** * @briefConfigure the used I/O ports pin * @paramNone * @retval : None */ void GPIO_Configuration(void) { #if 1 GPIO_InitTypeDefGPIO_InitStructure; RCC_APB2PeriphClockCmd( EEPROM_I2C_SCL_GPIO_CLK | EEPROM_I2C_SDA_GPIO_CLK, ENABLE ); /* Configure I2C1 pins: SCL and SDA */ GPIO_InitStructure.GPIO_Pin=EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; //GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT | EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); #endif }/** * @briefI2C Configuration * @paramNone * @retval : None */ void I2C_Configuration(void) { #if 1 I2C_InitTypeDefI2C_InitStructure; RCC_APB1PeriphClockCmd( EEPROM_I2C_CLK, ENABLE ); /* I2C configuration */ I2C_InitStructure.I2C_Mode= I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle= I2C_DutyCycle_2; //I2C_InitStructure.I2C_OwnAddress1 = I2C1_SLAVE_ADDRESS7; I2C_InitStructure.I2C_Ack= I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed= I2C_SPEED; /* Apply I2C configuration after enabling it */ I2C_Init(EEPROM_I2C, &I2C_InitStructure); /* I2C Peripheral Enable */ I2C_Cmd(EEPROM_I2C, ENABLE); #endif }/** * @briefInitializes peripherals used by the I2C EEPROM driver. * @paramNone * @retval : None */ void I2C_EE_Init() { /* GPIO configuration */ GPIO_Configuration(); /* I2C configuration */ I2C_Configuration(); /* depending on the EEPROM Address selected in the i2c_ee.h file */ /* Select the EEPROM Block0 to write on */ EEPROM_ADDRESS = EEPROM_Block_ADDRESS; } /** * @briefWrites one byte to the I2C EEPROM. * @param pBuffer : pointer to the buffercontaining the data to be *written to the EEPROM. * @param WriteAddr : EEPROM's internal address to write to. * @retval : None */ void I2C_EE_ByteWrite(uint8_t* pBuffer, uint8_t WriteAddr) { /* While the bus is busy */ while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)); /* Send STRAT condition */ I2C_GenerateSTART(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if(I2C_Timeout-- == 0) break; }/* Send EEPROM address for write */ I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter); I2C_Timeout = TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if(I2C_Timeout-- == 0) break; }/* Send the EEPROM's internal address to write to */ I2C_SendData(EEPROM_I2C, WriteAddr); I2C_Timeout = TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if(I2C_Timeout-- == 0) break; }/* Send the byte to be written */ I2C_SendData(EEPROM_I2C, *pBuffer); I2C_Timeout = TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if(I2C_Timeout-- == 0) break; }/* Send STOP condition */ I2C_GenerateSTOP(EEPROM_I2C, ENABLE); /* 延时不得小于5ms write cycle time*/ delay_ms(15); }/** * @briefWrites more than one byte to the EEPROM with a single WRITE *cycle. The number of byte can't exceed the EEPROM page size. * @param pBuffer : pointer to the buffer containing the data to be *written to the EEPROM. * @param WriteAddr : EEPROM's internal address to write to. * @param NumByteToWrite : number of bytes to write to the EEPROM. * @retval : None */ void I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr) { #if 1 uint8_t NumByteToWrite; NumByteToWrite = I2C_PageSize; /* While the bus is busy */ while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)); /* Send START condition */ I2C_GenerateSTART(EEPROM_I2C, ENABLE); /* Test on EV5 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)); /* Send EEPROM address for write */ I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); /* Send the EEPROM's internal address to write to */ I2C_SendData(EEPROM_I2C, WriteAddr); /* Test on EV8 and clear it */ while(! I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* While there is data to be written */ while(NumByteToWrite--) { /* Send the current byte */ I2C_SendData(EEPROM_I2C, *pBuffer); /* Point to the next byte to be written */ pBuffer++; /* Test on EV8 and clear it */ while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); }/* Send STOP condition */ I2C_GenerateSTOP(EEPROM_I2C, ENABLE); /* 延时不得小于5ms write cycle time*/ delay_ms(20); #endif }void I2C_EE_RandomrRead(uint8_t* pBuffer, uint8_t ReadAddr) {/* While the bus is busy */ while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)); /* step 1 Send START condition */ I2C_GenerateSTART(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if(I2C_Timeout-- == 0) break; }/* step 2 Send EEPROM address for write */ I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter); I2C_Timeout = TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if(I2C_Timeout-- == 0) break; }/* Clear EV6 by setting again the PE bit */ I2C_Cmd(EEPROM_I2C, ENABLE); //printf("\r\nstep 3:IIC reg addr \r\n"); /* step 3 Send the EEPROM's internal address to write to */ I2C_SendData(EEPROM_I2C, ReadAddr); I2C_Timeout = TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if(I2C_Timeout-- == 0) break; }/* step 4 Send STRAT condition a second time */ I2C_GenerateSTART(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if(I2C_Timeout-- == 0) break; }/*step 5Send EEPROM address for read */ I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver); I2C_Timeout = TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if(I2C_Timeout-- == 0) break; }/*step 6Disable Acknowledgement */ I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE); /*step 7 Send STOP Condition ,如果不在此处发stop信号, 而在接收数据后,会多收一个字节*/ I2C_GenerateSTOP(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_RXNE) == RESET) { if(I2C_Timeout-- == 0) break; } *pBuffer = I2C_ReceiveData(EEPROM_I2C); /*step 7 Send STOP Condition */ //I2C_GenerateSTOP(EEPROM_I2C, ENABLE); //printf("\r\nIIC rec data0x%x\r\n",*pBuffer); }uint8_t I2C_EE_CurrentRead(void) { uint8_t recdata = https://www.it610.com/article/0x0; #if 1 /* While the bus is busy */ while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)); /* step 4 Send STRAT condition a second time */ I2C_GenerateSTART(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if(I2C_Timeout-- == 0) break; }/*step 5Send EEPROM address for read */ I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver); I2C_Timeout = TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if(I2C_Timeout-- == 0) break; }/*step 6Disable Acknowledgement */ I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE); /*step 7 Send STOP Condition ,如果不在此处发stop信号, 而在接收数据后,会多收一个字节*/ I2C_GenerateSTOP(EEPROM_I2C, ENABLE); I2C_Timeout = TIMEOUT; while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_RXNE) == RESET) { if(I2C_Timeout-- == 0) break; }recdata = I2C_ReceiveData(EEPROM_I2C); /*step 7 Send STOP Condition */ //I2C_GenerateSTOP(EEPROM_I2C, ENABLE); #endif return recdata; }

代码参考了普中科技的ARM板子例程。板子例程里有很多bug不太好用。如果有源码大家可以对比研究。我实现了随机读,顺序读,按字节写,按页写等4种标准操作。请注意注释部分,这些代码都经过平台验证,可以直接使用,因为我的板子A0~A2都接到GND上了,所以,我的代码支持前256字节操作。
实际波形如下:
嵌入式|STM32硬件IIC读写EEPROM
文章图片


    推荐阅读