stm32硬件I2C event解析

在这里插入图片描述
STM32 中,硬件I2C每次动作都会伴随着事件的产生,本次笔记是对I2C作为主机时产生的几个事件进行讲解。
以I2C硬件发送为例:

void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead){ 
//I2C发送字符串
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
 //读取BUSY标志位,等待I2C空闲后进入下列程序
	I2C_GenerateSTART(I2C1,ENABLE);//I2C发送起始信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
 //I2C_CheckEvent:检查最近一次 I2C 事件是否是输入的事件 本次事件为EV5
 //对照主发送器传送序列图,本次事件在开始发生在之后
 //在发送起始信号后,函数就开始了EV5的检测,产生EV5的条件就是开始信号发送完成
 //当开始信号未发送完成,ev5事件不会产生,会一直卡在while循环中
 //本次事件可以确保起始信号正常发送,并且事件会在结束while循环后写地址操作时,对事件进行重置
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); 
 //(I2Cx,从机地址,传送数据方向): 此功能为发送从机7位地址
 //并且当地址数据填入DR数据寄存器时,硬件自动清除EV5事件
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
 //本次事件为EV6,事件发生在主机发送地址,从机接收地址并且答应后
 //在我们发送地址数据后,就开始了事件检测,当检测到从设备产生答应信号时,事件产生并跳出循环 
	I2C_Cmd(I2C1,ENABLE);
 //例程自带,删了也对程序无影响......不知其功能,233
	I2C_SendData(I2C1,readAddr);
 //发送从设备上的内存的地址,本质也就是发送一个字节的数据
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  //事件EV8,当位移寄存器非空,数据寄存器空时产生,也就是在数据发送时候就产生了
  //此事件的产生代表着I2C已经将数据从数据寄存器发送到位移寄存器也就是SDA线上了
  //也就是数据发送完成,标志位置位了,主机正常发送,当然从机有没有正常接收就是未知了
  //当重新开始需要发送新的数据时,EV8事件自动重置   
  //简单解释为数据发送成功,标志位值位,数据未发送完成,会卡在while循环,等待发送完成   
	I2C_GenerateSTART(I2C1,ENABLE);
  //LM75A,EEPROM之类设备都有类似操作,将写操作转换为读操作
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //ev5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); 
 //(I2C1,从机地址,方向为读)
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); 
 //EV6,确保从机接收到数据并答应后跳出
	while(NumByteToRead){     //NumByteToRead表示剩余所要读取的字节数量
		if(NumByteToRead == 1){ 
  //当最后一个字节时候,主设备要关闭答应
			I2C_AcknowledgeConfig(I2C1,DISABLE);
			I2C_GenerateSTOP(I2C1,ENABLE);	 //产生停止信号
		}
		if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){ 
  //事件7,表示数据都已经接收到了并保存在数据寄存器中了
  //关于事件7,可以看下图,事件7产生在上个数据接收完成并答应之后
  //产生数据7时候,数据寄存器非空,当对DR读数据时,ev7重置
  //文章末尾还有对此事件的讲解  
			*pBuffer = I2C_ReceiveData(I2C1);
   //保存数据到数组中
			pBuffer++; //数组加1
			NumByteToRead--; //还需读取字节数减1;
		}
	}
	I2C_AcknowledgeConfig(I2C1,ENABLE);
 //重启答应位,保证下次能够正常使用I2C.
}

在这里插入图片描述

刚开始我对于为什么在接收最后一个字节前就先直接关闭答应位,先关闭答应位和发出停止信号,不是会对之后接收最后要给字节的数据有影响吗?
后来,我参考图246,解开了一些困惑,当你对本次接收到的数据从DR中读取时,下一组数据已经在总线上进行发送了,但是看上图发现,EV7_1 事件,当你在DR读取上一组数据,下一组数据还在位移寄存器中,当新的数据还没完全移送到DR中是不会产生答应的…I2C的最大传输速率也就在 400K,而单片机频率达到了Mhz的级别,所以当你处理完接收到上组数据,新的数据还没完全接收成功,这中间足够有事件关闭答应位了.
至于停止位使能为什么也会放在读取最后一个函数前处理,就在刚刚我也有了自己的理解:停止位使能后,并不是马上将总线停止的,他需要等到总线上发出了非答应位后,才会产生停止信号.
说实话,感觉有点绕,但是最简单的办法起始就是直接参考时序图进行编程就是了.

添加要给刚刚对接收数据答应位的一个误解,我刚开始以为答应位是在你读取数据后主机才发送的答应位,结果突然发现好像不是这么理解的,正确的应该是当你接收到EV7之前,硬件I2C就已经对从机发出答应信号了,当你使能了答应位,每次完整接收到数据,主机都会自动答应,而程序在while函数中用if函数一直检测EV7的产生.
也就是说,当你主机自动答应后,新的数据是马上开始传送过来的,所以要实时检测ev7事件的发生,并对事件进程处理.

其实你会发现,如果去了事件的产生,那么他和软件I2C时序看起来没什么区别,但是软件I2C中通常我们会对时序的把控更加自如,而对于硬件I2C我们需要对其产生的事件进行检测处理,以确保能够数据的准确性.
就拿EV7事件来说,如果你检测到EV7事件后不马上对其进行处理,可以做个实验,如果从机一直发送数据过来,不马上处理DR中的数据,延迟个1s后再进行读取,会发现你读到的不是第一个接收到的数据,而是最后一个......你软件I2C对于时序的调控就会比硬件更加自如一点.

以上是对硬件I2C基础应用的个人的一些见解…

  • 7
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一系列由意法半导体(STMicroelectronics)开发的32位微控制器产品线。STM32系列提供了丰富的硬件外设,其中包括硬件I2C(Inter-Integrated Circuit)接口。 硬件I2C是一种用于在微控制器和其他外设之间进行通信的串行通信协议。它使用两根线路,即SDA(数据线)和SCL(时钟线),允许多个设备在同一总线上进行通信。 在STM32上使用硬件I2C,您需要首先配置和初始化相关的GPIO引脚,并设置I2C的时钟频率、地址等参数。然后,您可以使用STM32提供的I2C库函数来发送和接收数据。 以下是一个示例代码,用于在STM32上使用硬件I2C发送数据: ```c #include "stm32f4xx.h" void I2C_Init(void) { // 配置相关GPIO引脚为I2C功能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置I2C时钟和地址 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_Init(I2C1, &I2C_InitStructure); // 使能I2C I2C_Cmd(I2C1, ENABLE); } void I2C_SendData(uint8_t data) { // 等待I2C准备就绪 while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 设置要发送的数据 I2C_SendData(I2C1, data); // 等待数据发送完成 while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } int main(void) { // 初始化I2C I2C_Init(); // 发送数据 uint8_t data = 0xAA; I2C_SendData(data); while (1) { // 保持程序运行 } } ``` 这只是一个简单的示例代码,具体的实现方式可能会因为所使用的STM32型号和开发工具不同而有所差异。在实际开发中,您可能还需要处理其他I2C事件、配置中断等。建议您查阅相关的STM32参考手册和开发文档,以获取更详细的信息和示例代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值