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. 优化和重构

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

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

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即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值