使用STM32 HAL库函数实现硬件IIC通信

/*出处:【STM32入门教程-2024】第12集 IIC通信与温湿度传感器AHT20(DHT20)_哔哩哔哩_bilibili

*/

AHT20驱动

这篇笔记我主要介绍代码实现,想要了解原理的请自己看视频,我不过多赘述了。

AHT20通信数据帧格式:

①对照手册上的通信流程写初始化函数

关键API介绍:

主机接收函数

HAL_StatusTypeDef HAL_I2C_Master_Receive
(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, 
uint8_t *pData, uint16_t Size, uint32_t Timeout)
参数名称 介绍
I2C_HandleTypeDef *hi2c 想要操作的I2C函数句柄,eg:&hi2c1
uint16_t DevAddress 设备地址
uint8_t *pData 接收数据的变量的指针
uint16_t Size 读取的目标位数,单位字节
uint32_t Timeout 超时时间

主机发送函数

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, 
uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
参数名称 介绍
I2C_HandleTypeDef *hi2c 想要操作的I2C函数句柄,eg:&hi2c1
uint16_t DevAddress 设备地址
uint8_t *pData 接收数据的变量的指针
uint16_t Size 读取的目标位数,单位字节
uint32_t Timeout 超时时间

初始化函数:

void AHT20_Init(void)
{
   uint8_t readBuffer;
	 HAL_Delay(40);
	
	/*读是写加一,这里地址实际上自动变成了0X71*/
	 HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DELAY);
	
	/*检查状态字的校准使能位Bit[3]是否为1*/
	if((readBuffer&0x08)==0x00)
  {
	  uint8_t sendBuffer[3]={0xBE,0x80,0x00};
		HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DELAY);
	}
}

②根据手册封装数据读取函数

按照老师讲解的步骤进行数据切割和移位:

void AHT20_Read(float *Temperature,float *Humidity)
{
  uint8_t sendBuffer[3]={0xAC,0x33,0x00};
  uint8_t readBuffer[6];
	
	HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DELAY);
  HAL_Delay(75);
	HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,readBuffer,6,HAL_MAX_DELAY);
	
	if((readBuffer[0]&0x80)==0x00)
	{
	  uint32_t data=0;
		data=(uint32_t)(readBuffer[3]>>4)+(uint32_t)(readBuffer[2]<<4)+(uint32_t)(readBuffer[1]<<12);
	   *Humidity=data*100.f/(1<<20);
		
		data=(uint32_t)((readBuffer[3]&0x0F)<<16)+(uint32_t)(readBuffer[4]<<8)+(uint32_t)(readBuffer[5]);
		*Temperature=data*200.0f/(1<<20)-50;
	}
}

IIC中断、DMA&状态机编程

中断模式:

①在选项卡中开启事件中断向量与错误中断向量

②HAL_I2C_Master_Transmit_IT中断函数无需设置等待时间;但与此同时由于轮询、中断、DMA三者的工作模式的区别,并不能直接在驱动程序中替代函数名。轮询带有强制阻塞机制,程序会等待所有数据发送\接受完成才会接着向下执行,但中断\DMA是非阻塞模式,并不会进行等待。

为了使用I2C的中断机制,我们需要重构AHT20的读取过程,拆解为测量(发送测量指令)、获取(传输读取来的数据)、分析(运算出温湿度值)三大步骤。

void AHT20_measure(void)
{
	static uint8_t sendBuffer[3]={0xAC,0x33,0x00};
  HAL_I2C_Master_Transmit_IT(&hi2c1,AHT20_ADDRESS,sendBuffer,3);
}
	
void AHT20_Get(void)
{ 
  HAL_I2C_Master_Receive_IT(&hi2c1,AHT20_ADDRESS,readBuffer,6);
}

void AHT20_Analysis(float *Temperature,float *Humidity)
{
  if((readBuffer[0]&0x80)==0x00)
	{
	  uint32_t data=0;
		data=((uint32_t)readBuffer[3]>>4)+((uint32_t)readBuffer[2]<<4)+((uint32_t)readBuffer[1]<<12);
	   *Humidity=data*100.0f/(1<<20);
		
		data=(((uint32_t)readBuffer[3]&0x0F)<<16)+((uint32_t)readBuffer[4]<<8)+((uint32_t)readBuffer[5]);
		*Temperature=data*200.0f/(1<<20)-50;
	}
}

状态机编程:

在单片机开发领域内中,状态机编程是一种非常常见且强大的设计模式。状态机允许你将系统的行为分解为一系列离散的状态,并根据输入或事件在这些状态之间转移。

以下是使用状态机编程时的一些基本步骤和概念:

  1. 定义状态

  2. 首先,你需要确定你的系统有哪些可能的状态。
  3. 每个状态都应该有一个唯一的标识符(如枚举值、常量或字符串)。
  4. 定义状态转移

  5. 确定从一个状态转移到另一个状态的条件。
  6. 这些条件通常基于外部输入、内部事件或定时器。
  7. 实现状态机

  8. 使用循环或中断服务程序来检测状态转移条件。
  9. 当条件满足时,更新当前状态并执行与该状态关联的任何操作。
  10. 编写状态处理函数

  11. 对于每个状态,编写一个处理函数来执行该状态下的操作。
  12. 这些函数可能包括读取传感器、控制执行器、更新显示等。
  13. 管理状态数据

  14. 如果状态机需要跟踪数据(如计数器、标志等),请确保在状态转移时正确地保存和恢复这些数据。
  15. 错误处理

  16. 实现错误处理机制以处理无效状态转移或意外事件。
  17. 这可能包括将系统重置为默认状态、记录错误或触发警报。
  18. 测试和验证

  19. 在开发过程中,通过模拟输入和事件来测试状态机的行为。
  20. 使用调试工具来监视状态转移和数据流。
  21. 优化和重构

  22. 如果状态机变得复杂或难以维护,请考虑重构它以简化结构或提高性能。
  23. 使用设计模式和最佳实践来保持代码清晰和可维护。

这次的需求需要五种状态轮换:

0 初始状态 发送测量命令
1 正在发送测量指令
2 测量指令发送完成 等待75ms后开始通信
3 数据读取中
4 读取完成 进行数据解析与输出

因此主函数的逻辑:

  /* USER CODE BEGIN WHILE */
  while (1)
  {
			if(aht20State==0){
			  AHT20_measure();
				aht20State=1;
			}else if(aht20State==2){
			  HAL_Delay(75);
				AHT20_Get();
				aht20State=3;
			}else if(aht20State==4){
			  AHT20_Analysis(&temperature,&humidity);
				sprintf(message,"温度:%.1f,湿度:%.1f%%\r\n",temperature,humidity);
				HAL_UART_Transmit(&huart2,(uint8_t*)message,strlen(message),HAL_MAX_DELAY);
				HAL_Delay(1000);
				aht20State=0;
			}
				
		 
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

其中1->2,3->4的逻辑处理交给I2C的中断处理回调函数:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
  if(hi2c==&hi2c1)
	{
		aht20State=2;
	}
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if(hi2c==&hi2c1)
	{
		aht20State=4;
	}
}

DMA模式:

①在CubeMX选项卡中开启DMA设置。

⑤把传递函数的_IT改成_DMA即可。

作者:番茄灭世神

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32 HAL库函数实现硬件IIC通信

发表回复