《STM32从零开始学习历程》——I2C向EEPROM读取一字节数据(I2C硬件)

24 篇文章 0 订阅
8 篇文章 1 订阅

《STM32从零开始学习历程》@EnzoReventon

I2C向EEPROM读取一字节数据(I2C硬件)

相关链接:
I2C物理层介绍
I2C协议层介绍
I2C固件库介绍
STM32的I2C特性及架构介绍
STM32的EEPROM简介
I2C向EEPROM写入一字节数据(I2C硬件)

参考资料:
[野火EmbedFire]《STM32库开发实战指南——基于野火霸天虎开发板》
[正点原子]STM32F4开发指南-库函数版本_V1.2
[ST]《STM32F4xx中文参考手册》
[ATMEL]《AT24C02说明书》
开发板硬件原理图;EEPROM原理图。

0 引言

在上文《I2C向EEPROM写入一字节数据(I2C硬件)》中,我们介绍了使用I2C向EEPROM写入一个字节的实验,但是这个写过程是开环的,你不知道这个数据是否写入到你想要写入的地址或者写入的数据是不是你想要写入的,因此这种写入方式具有一定的局限性,本文在上文的基础上增加了读一个字节的功能,实现检查写入的数据。

1 实现功能

在写入一个字节的基础上,增加读一个字节的功能。
并通过串口调试助手显示读取的数据。

2 硬件设计

本实验采用的开发板为“正点原子”探索者F4开发板,核心芯片为F407ZGT6。
使用到的外设及硬件为:USART1,I2C1,EEPROM
USART1:PA9(T)—》RXE;PA10(R) —》TXE,将引脚使用跳线帽相连接即可。

3 软件设计流程

  1. GPIO功能复用
  2. 初始化GPIO
  3. I2C初始化
  4. I2C使能
  5. 定义写入数据函数
  6. 根据EEPROM写数据流程调用函数。
    EEPROM写数据流程请参考:《STM32从零开始学习历程》——STM32的EEPROM简介。此处不做过多的详解。
  7. 定义随机读取函数
  8. 定义等待ERROR内部写入操作完成函数
  9. 编写主函数,并发送数据。
  10. 优化代码(超时,故障代码)

4 代码分析

此处还是将程序所有代码完整的展示。

  1. I2C初始化函数
  • 宏定义及函数申明
//定义一个超时常数
#define TIME_OUT 0x000FFFFF

uint32_t count_wait = TIME_OUT;
//申明故障代码返回函数
uint8_t Error_Back(uint8_t error_code);

  • 初始化I2C
void IIC_Init(void)
{			
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	I2C_InitTypeDef   I2C_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);				//使能GPIOB时钟
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);				//使能I2C1时钟
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_I2C1);
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_I2C1);
	

	//GPIOB8初始化SCL
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;						//普通输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;						//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;					//100MHz
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;						//上拉
	GPIO_Init(GPIOB, &GPIO_InitStructure);								//初始化

	
	//GPIOB9初始化SDA
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;						//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;						//开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;					//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;						//上拉
    GPIO_Init(GPIOB, &GPIO_InitStructure);								//初始化
	
	//初始化I2C结构体
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;												//使能ask响应
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;				//设定7为地址
	I2C_InitStructure.I2C_ClockSpeed = 400000;												//设置时钟速度
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2  ;									//指定时钟占空比1/2与9/16选择
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;												//设置为I2C模式
	I2C_InitStructure.I2C_OwnAddress1 = 0x78;												//指定自身的I2C设备地址。另一种说法:作为主机时可以不用写?
	
	I2C_Init(I2C1,&I2C_InitStructure);														//初始化结构体
	
	I2C_Cmd(I2C1,ENABLE);																	//I2C使能
	}
  • EEPROM写入一个字节函数
//addr:是要写入存储单元的地址

//date:是要写入的数据

uint8_t EEPROM_Byte_Write(uint8_t addr, uint8_t date)
{
	//产生起始条件
	I2C_GenerateSTART(I2C1,ENABLE);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV5事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
	{
		
		count_wait--;
				
		if(count_wait ==  0)
		{
			//表示程序执行卡在EV5,发送开始信号阶段
			return Error_Back(1);
		}
	}
	
//========================================================================================
	
	//要发送的EEPROM设别地址 高四位:1010固定 低四位:A2 A1 A0 R/W 0 0 0 0  1010 0000 = 0xa0
	I2C_Send7bitAddress(I2C1,0xa0,I2C_Direction_Transmitter);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV6事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(2);
		}
	}
	
//========================================================================================
	
	//发送要写入的存储单元的地址
	I2C_SendData(I2C1,addr);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV8_2事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(3);
		}
	}
	
//========================================================================================
	
	//发送要写入的数据
	I2C_SendData(I2C1,date);
	
	//重置count_wait
	count_wait = TIME_OUT;	
	
	//等待EV8_2事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(4);
		}		
	}
		
//========================================================================================
	
	//产生结束信号
	I2C_GenerateSTOP(I2C1,ENABLE);

	//表示程序执行正常	
	return 0; 

}
  • 随机读取一个字节函数
//addr:是要读取存储单元的地址
//date:用来存储 读取到的数据 的指针
//returen:表示正常,非0为失败
uint8_t EEPROM_Random_Read(uint8_t addr, uint8_t *date)
{
//========================================================================================
	
	//产生第一次起始条件
	I2C_GenerateSTART(I2C1,ENABLE);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV5事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
	{
		count_wait--;
				
		if(count_wait ==  0)
		{
			return Error_Back(5);//表示程序执行卡在EV5,发送开始信号阶段
		}
	}
	
//==========================================写方向===========================================
	
	//要发送的EEPROM设别地址 高四位:1010固定 低四位:A2 A1 A0 R/W 0 0 0 0  1010 0001 = 0xa1 写方向
	I2C_Send7bitAddress(I2C1,0xa0,I2C_Direction_Transmitter);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV6事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(6);
		}
	}
	
//========================================================================================
	
	//发送要写入的存储单元的地址
	I2C_SendData(I2C1,addr);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV8_2事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(7);
		}
	}
	
//========================================================================================
	//产生第二次起始信号
	
	//产生起始条件
	I2C_GenerateSTART(I2C1,ENABLE);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV5事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
	{
		count_wait--;
				
		if(count_wait ==  0)
		{
			return Error_Back(8);//表示程序执行卡在EV5,发送开始信号阶段
		}
	}
	
//============================================读方向=========================================
	
	//发送要读取的EEPROM设别地址 高四位:1010固定 低四位:A2 A1 A0 R/W 0 0 0 0  1010 0000 = 0xa0 读方向
	I2C_Send7bitAddress(I2C1,0xa0,I2C_Direction_Receiver);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV6事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(9);
		}
	}
	//做出非应答信号 这个函数要放在receivedata前面,否则在接受数据的时候,会多接收一个数据才结束
	I2C_AcknowledgeConfig(I2C1,DISABLE);
	
	//重置count_wait
	count_wait = TIME_OUT;	
	
	//等待EV8_2事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS )
	{
		count_wait--;
		
		if(count_wait == 0)
		{
			return Error_Back(10);
		}		
	}
	
	//接收读取到的数据
	*date = I2C_ReceiveData(I2C1);
	
	//产生结束信号
	I2C_GenerateSTOP(I2C1,ENABLE);

	//表示程序执行正常	
	return 0; 
}
  • 等待EEPROM内部写入操作完成函数。
//等待ERROR内部写入操作完成
uint8_t Wait_For_Standby(void)
{
	uint32_t check_count = 0xFFFFF;
	
 while(check_count--)
  {
	
	//产生起始条件
	I2C_GenerateSTART(I2C1,ENABLE);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV5事件,直到成功
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
	{
		
		count_wait--;
				
		if(count_wait ==  0)
		{
			return Error_Back(11);//表示程序执行卡在EV5,发送开始信号阶段
		}
	}

	//要发送的EEPROM设别地址 高四位:1010固定 低四位:A2 A1 A0 R/W 0 0 0 0  1010 0001 = 0xa1 写方向
	I2C_Send7bitAddress(I2C1,0xa0,I2C_Direction_Transmitter);
	
	//重置count_wait
	count_wait = TIME_OUT;
	
	//等待EV6事件,直到成功
	while(count_wait--)
	 {
	//若检测到响应,说明内部写时序完成,跳出循环
		if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
		{
			//产生结束信号
			I2C_GenerateSTOP(I2C1,ENABLE);
			return 0;
		}
	  }
	}

	//产生结束信号
	I2C_GenerateSTOP(I2C1,ENABLE);

	//表示程序执行正常	
	return 1; 
}
  • 错误代码返回函数
uint8_t Error_Back(uint8_t error_code)
{
	printf("\r\n I2C ERROR OCCOR! THE ERROR NUMBER IS %d!\r\n",error_code);
	
	return error_code;
}

  1. 主函数
int main(void)
{ 

	uint8_t data;	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	
	delay_init(168);    		//初始化延时函数
	
	uart_init(115200);			//初始化串口波特率为115200
	
	LED_Init();					//初始化LED 
	
 	LCD_Init();					//LCD初始化 
	
	KEY_Init(); 				//按键初始化  
	
	IIC_Init();					//I2C初始化
	
	EEPROM_Byte_Write(0x05, 0x22);		//向EEPROM写入数据
	
	Wait_For_Standby();					//等待传输完成
	
	EEPROM_Random_Read(0x05,&data);		//随机读取数据
		
	printf("\r\nox%x\r\n",data);
}

5 结果展示

使用串口调试助手,显示数据已经读取并显示读取到的数据。

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EnzoReventon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值