IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32中I2C与EEPROM字写读写实现(硬件方式) -> 正文阅读

[嵌入式]STM32中I2C与EEPROM字写读写实现(硬件方式)

初始化

初始化就不解释的太详细了,多看看数据手册,这里我直接贴代码加注释

 /**
  * @brief  EEPROM_I2C_Config初始化GPIO及I2C的模式
  * @param  无
  * @retval 无
  */
void EEPROM_I2C_GPIO_Config(void)
{
	//GPIO初始化结构体
  GPIO_InitTypeDef GPIO_InitStructure;			
	
	/* 使能 I2C 外设时钟 */
	RCC_APB1PeriphClockCmd(EEPROM_I2C_CLK, ENABLE);
	
	//使能AHB1总线上SCL和SDA所在的时钟
  RCC_AHB1PeriphClockCmd(EEPROM_I2C_SCL_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
	
	/* 备用功能配置(除ADC和DAC外的所有非GPIO功能),使用该函数GPIO_PinAFConfig(); */
	/*  连接 PXx  到 I2C_SCL */
  GPIO_PinAFConfig(EEPROM_I2C_SCL_GPIO_PORT,EEPROM_I2C_SCL_SOURCE,EEPROM_I2C_SCL_AF);			//把引脚连接到外设
	/*  连接 PXx 到 I2C_SDA  */
  GPIO_PinAFConfig(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_SOURCE,EEPROM_I2C_SDA_AF);			//把引脚连接到外设
	
  
  /* GPIO初始化 */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;			//i2c GPIO口必须使用开漏输出 不能使用推挽输出 开漏(OD)与推挽(PP)电路
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  			//可以选择UP或者NOPULL 因为外部电路有上拉电阻  内部弱上拉意义不大
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//引脚速率
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;							//引脚复用功能正常开启
  
  /* 配置SCL引脚 */
  GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN  ;  			//配置SCL对应的引脚号   SCL:PB8   SDA:PB9  在stm32f407英文手册查询,引脚不唯一
  GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);		//初始化GPIO
	
  /* 配置SDA引脚 */	
  GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN;					//配置SDA引脚号
  GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);	//初始化GPIO
	
		  	 
}


/**
  * @brief  I2C 工作模式配置
  * @param  无
  * @retval 无
  */
void I2C_Mode_Config(void)
{
  I2C_InitTypeDef  I2C_InitStructure; 

/* --------------------I2C 配置 ---------------------------*/
	/* I2C 模式配置  */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;	
	/* I2C 占空比周期配置  low/high=2/1 */
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;		                    /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
	/* I2C STM32 的 I2C 设备自己的地址长度配置 */
	I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; 
	/* I2C 应答使能配置 */
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	
	/* I2C 寻址长度配置  7bit|10bit */
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;			/* I2C的寻址模式 */
	/* I2C 速率配置 */
	I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;	                            /* 通信速率 */
	/* I2C 初始化 */
  I2C_Init(EEPROM_I2C, &I2C_InitStructure);	                                      /* I2C1 初始化 */
	/* I2C 配置 */
	I2C_Cmd(EEPROM_I2C, ENABLE);  	                                                /* 使能 I2C1 */

	/* I2C 应答使能配置 */
  I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);  
}


void I2C_EE_init(void)
{
	/*  I2C GPIO配置  */
	EEPROM_I2C_GPIO_Config();
	
	/*  I2C配置  */
	I2C_Mode_Config();
	
}

读写过程原理

使用 I2C 外设通讯时,在通讯的不同阶段它会对“状态寄存器 (SR1及SR2) ”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。

发送

接收

?下面的EV事件是必须等待响应后才能继续执行下一步

EV事件看不明白的看STM32数据手册? 有对I2C的寄存器的每位的作用都有讲解

?关于怎么检测EV事件响应I2C库中给了I2C_CheckEvent()函数非常方便

上面的注释给了各种EV事件应该对应选择什么参数

/**
  * @brief  Checks whether the last I2Cx Event is equal to the one passed
  *         as parameter.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  I2C_EVENT: specifies the event to be checked. 
  *          This parameter can be one of the following values:
  *            @arg I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: EV1
  *            @arg I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: EV1
  *            @arg I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED: EV1
  *            @arg I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED: EV1
  *            @arg I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED: EV1
  *            @arg I2C_EVENT_SLAVE_BYTE_RECEIVED: EV2
  *            @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF): EV2
  *            @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL): EV2
  *            @arg I2C_EVENT_SLAVE_BYTE_TRANSMITTED: EV3
  *            @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF): EV3
  *            @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL): EV3
  *            @arg I2C_EVENT_SLAVE_ACK_FAILURE: EV3_2
  *            @arg I2C_EVENT_SLAVE_STOP_DETECTED: EV4
  *            @arg I2C_EVENT_MASTER_MODE_SELECT: EV5
  *            @arg I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: EV6     
  *            @arg I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: EV6
  *            @arg I2C_EVENT_MASTER_BYTE_RECEIVED: EV7
  *            @arg I2C_EVENT_MASTER_BYTE_TRANSMITTING: EV8
  *            @arg I2C_EVENT_MASTER_BYTE_TRANSMITTED: EV8_2
  *            @arg I2C_EVENT_MASTER_MODE_ADDRESS10: EV9
  *     
  * @note   For detailed description of Events, please refer to section I2C_Events
  *         in stm32f4xx_i2c.h file.
  *    
  * @retval An ErrorStatus enumeration value:
  *           - SUCCESS: Last event is equal to the I2C_EVENT
  *           - ERROR: Last event is different from the I2C_EVENT
  */
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
  uint32_t lastevent = 0;
  uint32_t flag1 = 0, flag2 = 0;
  ErrorStatus status = ERROR;

  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_I2C_EVENT(I2C_EVENT));

  /* Read the I2Cx status register */
  flag1 = I2Cx->SR1;
  flag2 = I2Cx->SR2;
  flag2 = flag2 << 16;

  /* Get the last event value from I2C status register */
  lastevent = (flag1 | flag2) & FLAG_MASK;

  /* Check whether the last event contains the I2C_EVENT */
  if ((lastevent & I2C_EVENT) == I2C_EVENT)
  {
    /* SUCCESS: last event is equal to I2C_EVENT */
    status = SUCCESS;
  }
  else
  {
    /* ERROR: last event is different from I2C_EVENT */
    status = ERROR;
  }
  /* Return status */
  return status;
}

写代码讲解

其中对EV事件响应设置了超时等待,如果超时了还未响应则返回回调函数打印出报错信息

/* ------------------对EEPROM写入1字节数据----------------------- */
//Buffer :要写入的数据
//Addr   :要写入EEPROM的单元格地址
uint32_t EEPROM_byte_write(u8 Buffer , u8 Addr)
{
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	
	//等待EV5事件响应
	TimeOut_count = I2C_time_out;
	while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(0);   
	}
	
	//发送要写入的EEPROM的地址和读写方向
	I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(1);   
	}
	
	//发送要写入EEPROM的单元地方
	I2C_SendData(EEPROM_I2C,Addr);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(2);   
	}
	
	//发送要写入的数据
	I2C_SendData(EEPROM_I2C , Buffer);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(3);   
	}
	
	//发送停止信号
	I2C_GenerateSTOP(I2C1,ENABLE);
	
	return 0;
}

读代码

//addr : 要读取的单元格地址
//DATA :读取的内容存放区
uint32_t EPPROM_Random_Read(u8 addr , u8 *Data)
{
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	
	//等待EV5事件响应
	TimeOut_count = I2C_time_out;
	while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(4);   
	}
	
	//发送要写入的EEPROM的地址和读写方向   先选择写方向写入单元格地址
	I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
	
	//等待EV6事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(5);   
	}
	
	//发送要读取EEPROM的单元地方
	I2C_SendData(EEPROM_I2C,addr);
	
	//等待EV8_2事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(6);   
	}
	
	//---------------------------------------------------------------------------------------------------------
	//-----------------------产生第二次起始信号---------------------------------------------------------
	
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C,ENABLE);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;	
	while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(7);   
	}
	
	//*********************************读方向************************************************
	//发送要写入的EEPROM的地址和读方向
	I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Receiver);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(8);   
	}
	
	I2C_AcknowledgeConfig(EEPROM_I2C , DISABLE);
	
	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
	{
		if((TimeOut_count--) == 0)   return I2C_timeout_callback(9);   
	}
	
	*Data = I2C_ReceiveData(EEPROM_I2C);	
	
	//发送停止信号
	I2C_GenerateSTOP(I2C1,ENABLE);
	
	return 0;
}

注意事项

STM32对EEPROM写入后立马读取是会报错的

必须等待写入事件响应后才能读取,因为在数据写入的时候

7 TxE:数据寄存器为空 (Data register empty)(发送器) 还没发送完毕的时候是不能读取的?

所以写了个wait函数用来等待发送完毕检测

//等待内部写入操作完成
//写入完成返回1  失败发送0
uint8_t Wait_for_StandBy(void)
{
	uint32_t wait_count = 0xFFFF;
	while(wait_count--)
	{
		//发送起始信号
		I2C_GenerateSTART(EEPROM_I2C,ENABLE);
		
		//等待EV事件响应
		TimeOut_count = I2C_time_out;
		while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
		{
			if((TimeOut_count--) == 0)   return I2C_timeout_callback(10);   
		}
		
		//发送要写入的EEPROM的地址和读写方向
		I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
				
		//等待EV事件响应
		TimeOut_count = I2C_time_out;
		while(TimeOut_count--)
		{
			if(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)    
			{
				//发送停止信号
				I2C_GenerateSTOP(I2C1,ENABLE);
				return 1;
			}
		}
	}
	//发送停止信号
	I2C_GenerateSTOP(I2C1,ENABLE);
	return 0;
}

注意在检测的时候也是需要起始信号和停止信号的,对EEPROM的地址一直写入直到成功代表STM32已经对EEPROM写入完成

这个函数只需要添加在读写函数之间就好了

int main(void)
{	
	
    /*初始化UART 配置模式为 115200 8-N-1,中断接收*/
    Debug_USART_Config();
	
	
	printf("这是一个EEPROM读写实验\n");
	
	printf("\r\n 欢迎使用野火  STM32 F407 开发板。\r\n");		 

	printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");
	
	I2C_EE_init();
	
	u8 Data;
	
	//在EEPROM的0x00单元地址 写入0x12
	EEPROM_byte_write(0x12 , 0x00);
	
	//写完马上读会出错  在写入时是不能读的
	Wait_for_StandBy();
	
	//读取EEPROM的单元格0x00地址数据存入  Data中
	EPPROM_Random_Read(0x00 , &Data);
	
	printf(" \r\n EEPROM读写实验结束 data = 0x%02x \n",Data);
	
  while(1)
	{	
		
	}	
}

?

?

报错回调函数

uint32_t I2C_timeout_callback(uint8_t errorCode)
{
	printf("I2C 等待超时 , 错误代码 errorCode = %d \r\n",errorCode);
	
	return 0;
}

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:50:48  更:2022-10-17 12:52:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 18:11:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码