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 .具体时序参考数据手册。

我用的是I2C1接口。

宏定义如下:

#define EEPROM_Block_ADDRESS 0xA0   /* Device Address */


#define I2C1_SLAVE_ADDRESS7  	  0xA0
#define I2C_PageSize         	  16
#define I2C_BUF_LEN   	      	  256

#define EEPROM_I2C                          I2C1
#define EEPROM_I2C_CLK                      RCC_APB1Periph_I2C1
#define EEPROM_I2C_SCL_PIN                  GPIO_Pin_6                 /* PB.6 */
#define EEPROM_I2C_SCL_GPIO_PORT            GPIOB                       /* GPIOB */
#define EEPROM_I2C_SCL_GPIO_CLK             RCC_APB2Periph_GPIOB
#define EEPROM_I2C_SDA_PIN                  GPIO_Pin_7                  /* PB.7 */
#define EEPROM_I2C_SDA_GPIO_PORT            GPIOB                       /* GPIOB */
#define EEPROM_I2C_SDA_GPIO_CLK             RCC_APB2Periph_GPIOB

#define I2C_SPEED 200000

extern uint8_t I2C_Buf[I2C_BUF_LEN];

配置和功能函数如下:

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>


/* 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--) ;
}

/**
  * @brief  Configure the used I/O ports pin
  * @param  None
  * @retval : None
  */
void GPIO_Configuration(void)
{
#if 1
  GPIO_InitTypeDef  GPIO_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
}

/**
  * @brief  I2C Configuration
  * @param  None
  * @retval : None
  */
void I2C_Configuration(void)
{
#if 1
  I2C_InitTypeDef  I2C_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
}

/**
  * @brief  Initializes peripherals used by the I2C EEPROM driver.
  * @param  None
  * @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;
}

	
/**
  * @brief  Writes one byte to the I2C EEPROM.
  * @param pBuffer : pointer to the buffer  containing 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);
}

/**
  * @brief  Writes 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 5  Send 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 6  Disable 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 data  0x%x\r\n",*pBuffer); 
}


uint8_t I2C_EE_CurrentRead(void)
{  
  uint8_t recdata = 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 5  Send 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 6  Disable 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字节操作。

实际波形如下:

发布了12 篇原创文章 · 获赞 4 · 访问量 6355

猜你喜欢

转载自blog.csdn.net/yilizhihu/article/details/104667368