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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用HAL库实现硬件I2C,需要按照以下步骤: 1. 初始化I2C总线,包括I2C的GPIO管脚初始化和I2C外设初始化。 2. 配置I2C的工作模式,包括传输速率、地址长度等。 3. 发送I2C起始信号。 4. 发送I2C设备地址和读写标志。 5. 写入数据或读取数据。 6. 发送I2C停止信号。 以下是一个使用HAL库实现I2C读写的例程: ```c #include "stm32f4xx_hal.h" I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { GPIO_InitTypeDef GPIO_InitStruct; if(i2cHandle->Instance==I2C1) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle) { if(i2cHandle->Instance==I2C1) { __HAL_RCC_I2C1_FORCE_RESET(); __HAL_RCC_I2C1_RELEASE_RESET(); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6|GPIO_PIN_7); } } void I2C_Read(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) { HAL_I2C_Mem_Read(&hi2c1, address, reg, I2C_MEMADD_SIZE_8BIT, data, size, 100); } void I2C_Write(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) { HAL_I2C_Mem_Write(&hi2c1, address, reg, I2C_MEMADD_SIZE_8BIT, data, size, 100); } ``` 上述代码中,MX_I2C1_Init()函数进行I2C总线的初始化配置。I2C_Read()和I2C_Write()函数分别实现了I2C的读取和写入操作。具体实现可参考HAL库的API文档。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值