STM32硬件I2C测试读写EEPROM


一、测试环境

使用正点原子STM32F103,精英V2开发板上 I2C1(B6,B7)测试eeprom 读写数据,网上都说stm32硬件I2C有问题,不提倡使用硬件I2C。

在这里插入图片描述

二、程序测试

1.主程序实现

main.c代码如下:

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "i2c.h"


uint8_t I2C_EE_Test(void);

int main(void)
{
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口
	I2C1_Init();

	printf("stm32 i2c master test:\r\n");

	I2C_EE_Test();

	while(1)
	{
		LED0=!LED0;//提示系统正在运行	
		delay_ms(300);	   
	}
}

uint8_t I2C_EE_Test(void)
{	
	uint8_t ReadData[256]={0};
	uint8_t WriteDdta[256]={0};
	uint16_t i;

	//初始化写入数组
	for(i = 0; i < 256; i++)
	{
		WriteDdta[i]=i; 
	}

	//向EEPROM从地址为0开始写入256个字节的数据 
	I2C_EE_BufferWrite(WriteDdta, 0, 256);
	//等待EEPROM写入数据完成 
	EEPROM_WaitForWriteEnd();	 
	//向EEPROM从地址为0开始读出256个字节的数据
	EEPROM_Read(ReadData, 0, 256);

	for (i=0; i<256; i++)
	{	
		if(ReadData[i] != WriteDdta[i])
		{
			EEPROM_ERROR("0x%02X ", ReadData[i]);
			EEPROM_ERROR("错误:I2C EEPROM写入与读出的数据不一致\n\r");
			return 0;
		}
		
		printf("0x%02X ", ReadData[i]);
		if(i%16 == 15)    
			printf("\n\r");   
	}
	
	EEPROM_INFO("I2C(AT24C02)读写测试成功\n\r");
	return 1;
}

void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}




2.I2C1驱动

i2c.c代码如下:

#include "i2c.h"

//设置等待时间
static __IO uint32_t  I2CTimeout = I2CT_LONG_TIMEOUT;   

//等待超时,打印错误信息
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);

void I2C1_Init(void)
{
	GPIO_InitTypeDef    GPIO_InitStuctrue;
	I2C_InitTypeDef     I2C_InitStuctrue;

	//开启GPIO外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	//开启IIC外设时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

	//SCL引脚-复用开漏输出
	GPIO_InitStuctrue.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStuctrue.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStuctrue.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStuctrue);

	//SDA引脚-复用开漏输出
	GPIO_InitStuctrue.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStuctrue.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStuctrue.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStuctrue);

	//IIC结构体成员配置
	I2C_InitStuctrue.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStuctrue.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStuctrue.I2C_ClockSpeed = EEPROM_I2C_BAUDRATE;
	I2C_InitStuctrue.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStuctrue.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStuctrue.I2C_OwnAddress1 = STM32_I2C_OWN_ADDR;
	I2C_Init(I2C1, &I2C_InitStuctrue);
	I2C_Cmd(I2C1, ENABLE);

}

//向EEPROM写入一个字节
void  EEPROM_Byte_Write(uint8_t addr,uint8_t data)
{
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//发送设备写地址
	I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	//发送要操作设备内部的地址
	I2C_SendData(EEPROM_I2C,addr);
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
  	I2C_SendData(EEPROM_I2C,data);
	//检测EV8_2事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR);
	//发送停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	
}

//向EEPROM写入多个字节
uint32_t  EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint16_t Num_ByteToWrite)
{
	
	 I2CTimeout = I2CT_LONG_TIMEOUT;
	//判断IIC总线是否忙碌
	while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
	} 
	//重新赋值
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR)
	{
		 if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
	} 
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送设备写地址
	I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR)
	{
		 if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
	} 

	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送要操作设备内部的地址
	I2C_SendData(EEPROM_I2C,addr);
	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
	{
		 if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
	} 

	while(Num_ByteToWrite)
	{
		I2C_SendData(EEPROM_I2C,*data);
		I2CTimeout = I2CT_FLAG_TIMEOUT;
		while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
		{
				if((I2CTimeout--) == 0) return   I2C_TIMEOUT_UserCallback(5);
		} 
		 Num_ByteToWrite--;
		 data++;
	}

	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//检测EV8_2事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
	} 
	//发送停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	 return 1;
}

//向EEPROM读取多个字节
uint32_t EEPROM_Read(uint8_t *data,uint8_t addr,uint16_t Num_ByteToRead)
{
	 I2CTimeout = I2CT_LONG_TIMEOUT;
	//判断IIC总线是否忙碌
	while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
	} 
	
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
	} 
	
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送设备写地址
	I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
	//检测EV6事件等待从机应答
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
	}
  
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送要操作设备内部存储器的地址
	I2C_SendData(EEPROM_I2C,addr);
	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
	}
	I2CTimeout = I2CT_FLAG_TIMEOUT;
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
	}
	I2CTimeout = I2CT_FLAG_TIMEOUT;	 
	//发送设备读地址
	I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Receiver);
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
	}
	 
	while(Num_ByteToRead--)
	{
		//是否是最后一个字节,若是则发送非应答信号
	if( Num_ByteToRead==0)
	{
		//发送非应答信号
		I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);
		//发送停止信号
		I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	}
	 
	I2CTimeout = I2CT_FLAG_TIMEOUT;	 
	//检测EV7事件
   	while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED )==ERROR)
	{
		if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
	}
	 
	*data=I2C_ReceiveData(EEPROM_I2C);
	data++; 
	 
	}
	
	//重新开启应答信号
	I2C_AcknowledgeConfig(EEPROM_I2C,ENABLE);
  	return 1;
}
void I2C_EE_BufferWrite(uint8_t* pBuffer,uint8_t WriteAddr, uint16_t NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
  //I2C_PageSize=8
  Addr = WriteAddr % I2C_PageSize;
  count = I2C_PageSize - Addr;
  NumOfPage =  NumByteToWrite / I2C_PageSize;
  NumOfSingle = NumByteToWrite % I2C_PageSize;
 
  /* 写入数据的地址对齐,对齐数为8 */
  if(Addr == 0) 
  {
    /* 如果写入的数据个数小于8 */
    if(NumOfPage == 0) 
    {
      EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
      EEPROM_WaitForWriteEnd();
    }
    /* 如果写入的数据个数大于8 */
    else  
    {
			//按页写入
      while(NumOfPage--)
      {
        EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PageSize); 
    	  EEPROM_WaitForWriteEnd();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;
      }
      //不足一页(8个)单独写入
      if(NumOfSingle!=0)
      {
        EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
        EEPROM_WaitForWriteEnd();
      }
    }
  }
  /*写的数据的地址不对齐*/
  else 
  {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / I2C_PageSize;
      NumOfSingle = NumByteToWrite % I2C_PageSize;	
      
      if(count != 0)
      {  
        EEPROM_Page_Write(WriteAddr, pBuffer, count);
        EEPROM_WaitForWriteEnd();
        WriteAddr += count;
        pBuffer += count;
      } 
      
      while(NumOfPage--)
      {
        EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PageSize);
        EEPROM_WaitForWriteEnd();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;  
      }
      if(NumOfSingle != 0)
      {
        EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle); 
        EEPROM_WaitForWriteEnd();
      }
    } 
}

uint32_t EEPROM_WaitForWriteEnd(void)
{
	I2CTimeout = I2CT_FLAG_TIMEOUT;	
	
	do
	{
		I2CTimeout = I2CT_FLAG_TIMEOUT;
		//发送起始信号
		I2C_GenerateSTART(EEPROM_I2C,ENABLE);
		//检测EV5事件
		while( I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_SB )==RESET)
		{
					if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
			}
		I2CTimeout = I2CT_FLAG_TIMEOUT;	
		//发送设备写地址
		I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
		
	}while( (I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_ADDR )==RESET) && (I2CTimeout--) );
	
	//发送停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	return 1;
}



static  uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* Block communication and all processes */
  EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);
  
  return 0;
}

i2c.h代码如下:

#ifndef __I2C_H
#define __I2C_H
#include <stdio.h>
#include "sys.h"

//IIC1
#define  EEPROM_I2C                       I2C1
#define  EEPROM_I2C_BAUDRATE              400000

//STM32自身地址1 与从机设备地址不相同即可(7位地址)
#define   STM32_I2C_OWN_ADDR             0x6f
//EEPROM设备地址
#define   EEPROM_I2C_Address             0XA0
#define   I2C_PageSize                     8

//等待次数
#define I2CT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT         ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))

/*信息输出*/
#define EEPROM_DEBUG_ON                    0
#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

void I2C1_Init(void);
void EEPROM_Byte_Write(uint8_t addr,uint8_t data);	
uint32_t  EEPROM_WaitForWriteEnd(void);	
uint32_t  EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint16_t Num_ByteToWrite);																					
uint32_t  EEPROM_Read(uint8_t *data,uint8_t addr,uint16_t Num_ByteToRead);
void I2C_EE_BufferWrite(uint8_t* pBuffer,uint8_t WriteAddr, uint16_t NumByteToWrite);

#endif

3.使用逻分抓I2C数据

在这里插入图片描述

4.测试结果

在这里插入图片描述

三、gitee路径

https://gitee.com/sugang2022/stm32f103zet6.git
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用STM32的HAL库来实现I2C读写EEPROM。首先,确保已经正确配置了I2C外设和相关引脚。然后,按照以下步骤进行操作: 1. 初始化I2C外设:使用`HAL_I2C_Init()`函数初始化I2C外设。 2. 配置EEPROM地址:在发送I2C读写请求之前,需要设置EEPROM的地址。这可以通过向I2C外设发送一个写入EEPROM地址的I2C Start信号,然后发送EEPROM地址字节来实现。 3. 发送数据:如果要向EEPROM写入数据,可以使用`HAL_I2C_Master_Transmit()`函数发送数据。如果要从EEPROM读取数据,可以使用`HAL_I2C_Master_Receive()`函数接收数据。 下面是一个示例代码,演示如何使用HAL库进行I2C读写EEPROM: ```c #include "stm32xxxx.h" // 根据具体的芯片型号选择头文件 #define EEPROM_ADDRESS 0xA0 // EEPROM的I2C地址 // 初始化I2C外设 void I2C_Init(void) { // I2C外设初始化代码 // ... } // 向EEPROM写入数据 void EEPROM_Write(uint16_t address, uint8_t* data, uint16_t size) { // 发送I2C Start信号和EEPROM地址 HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDRESS, (uint8_t*)&address, 2, HAL_MAX_DELAY); // 发送数据到EEPROM HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDRESS, data, size, HAL_MAX_DELAY); } // 从EEPROM读取数据 void EEPROM_Read(uint16_t address, uint8_t* data, uint16_t size) { // 发送I2C Start信号和EEPROM地址 HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDRESS, (uint8_t*)&address, 2, HAL_MAX_DELAY); // 从EEPROM接收数据 HAL_I2C_Master_Receive(&hi2c1, EEPROM_ADDRESS, data, size, HAL_MAX_DELAY); } ``` 请注意,上述代码中的`hi2c1`是I2C外设的句柄,具体根据你的芯片型号和使用的外设进行修改。此外,还需要根据EEPROM的具体规格和通信协议进行相应的配置。 希望以上信息对你有所帮助!如有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值