一、温湿度传感器AHT20–IIC通讯轮询模式
1、创建新项目,芯片选用STM32F103C8T6,项目名为iic。
2、根据AHT20所连接的引脚,开启IIC1外设。
3、项目管理,为每个外设生成一个.c、.h文件,这样就可以include头文件,拿到huart1或者hi2c这类外设的操作句柄了,之后保存生成代码。
4、创建自己的.c、.h文件
Core→Inc右键→New→Hreader File,命名aht20.h Core→src右键→New→Source File,命名aht20.c
5、代码部分
根据数据手册的传感器读取流程1,可以写出AHT20的初始化函数。
# define AHT20_ADDRESS 0X70
void AHT20_Init ( void ) { uint8_t readBuffer; HAL_Delay ( 40 ) ; HAL_I2C_Master_Transmit ( & hi2c1, AHT20_ADDRESS, & readBuffer, 1 , HAL_MAX_DELAY) ; if ( ( readBuffer & 0x08 ) == 0x00 ) { uint8_t sendBuffer[ 3 ] = { 0xBE , 0x08 , 0x00 } ; HAL_I2C_Master_Receive ( & hi2c1, AHT20_ADDRESS, sendBuffer, 3 , HAL_MAX_DELAY) ; }
}
根据数据手册的传感器读取流程2跟3,还有数据表可以写出AHT20的读取测量数据的函数。
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.0f / ( 1 << 20 ) ; data = ( ( uint32_t ) readBuffer[ 5 ] ) + ( ( uint32_t ) readBuffer[ 4 ] << 8 ) + ( ( ( uint32_t ) readBuffer[ 3 ] & 0x0F ) << 16 ) ; * Temperature = ( data * 200.0f / ( 1 << 20 ) ) - 50 ; } }
由于我们使用到浮点数,Cube默认没有启用编译器对浮点数输出的支持,在菜单栏project→properties进入设置页面勾选图纸选项开启此功能。 主函数部分。
先将AHT20初始化,创建两个变量和一个用来临时存放数据的数组。
AHT20_Init ( ) ; float temperature, humidity; char dataArray[ 50 ] ;
while ( 1 ) { AHT20_Read ( & temperature, & humidity) ; sprintf ( dataArray, "温度:%.f ℃, 湿度:%.f %%\r\n" , temperature, humidity) ; HAL_UART_Transmit ( & huart1, ( uint8_t * ) dataArray, strlen ( dataArray) , HAL_MAX_DELAY) ; HAL_Delay ( 1000 ) ; }
编译代码并烧录,再通过连接串口获取单片机发送的数据。
二、IIC中断模式与状态机
1、IIC中断模式
打开IIC的中断向量并保存生成代码。
2、利用状态机编程代码。
前面我们使用的轮询的方式,读取模块的数据时,有等待时间,等待读取成功之后才发送到串口显示出来。如果我们只是简单的将轮询模式中IIC的发送和接受改成中断模式,会导致数据的读取还未完成时就将数据给发送出去,造成数据的错误,所以我们需要对代码进行改造,利用状态机来编码,不同的状态做不同的事。
1.因为状态机的原理就是不同的状态做不同的事情,所以我们将上面读取数据的函数AHT20_Read()拆分为三个单独的函数,如下:
发送测量指令:
void AHT20_Measure ( void ) { static uint8_t sendBuffer[ 3 ] = { 0xAC , 0x33 , 0x00 } ; HAL_I2C_Master_Transmit_IT ( & hi2c1, AHT20_ADDRESS, sendBuffer, 3 ) ;
}
获取模块的数据:
void AHT20_GetData ( void ) { HAL_I2C_Master_Receive_IT ( & hi2c1, AHT20_ADDRESS, readBuffer, 6 ) ;
}
解析获取的数据:
uint8_t readBuffer[ 6 ] = { 0 } ;
void AHT20_AnalysisData ( 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[ 5 ] ) + ( ( uint32_t ) readBuffer[ 4 ] << 8 ) + ( ( ( uint32_t ) readBuffer[ 3 ] & 0x0F ) << 16 ) ; * Temperature = ( data * 200.0f / ( 1 << 20 ) ) - 50 ; }
}
函数注意要在头文件中申明,方便其他文件使用。
2.创建状态机模型
uint8_t aht20State = 0 ;
0:初始状态,发送测量指令,然后将状态变为1 1:正在发送测量指令中 2:发送完成,按照手册中需要等75ms后才可以进行读取 3:读取中 4:读取完成,对数据进行解析并展示数据并且需要将状态恢复到初始状态,进行下一个循环。
3.代码部分
主函数的循环语句中,我们发现没有状态1切换到状态2、状态3切换到状态4的语句,我们需要所以我们需要借助IIC中断的回调函数来实现切换状态的功能。也因为状态aht20State需要在其他函数中使用,所以我们需要将次变量在头文件中加上extern来声明,其他文件中才可以使用。 main.h文件中
extern uint8_t aht20State;
while ( 1 ) { if ( aht20State == 0 ) { AHT20_Measure ( ) ; aht20State = 1 ; } else if ( aht20State == 2 ) { HAL_Delay ( 75 ) ; AHT20_GetData ( ) ; aht20State = 3 ; } else if ( aht20State == 4 ) { AHT20_AnalysisData ( Temperature, Humidity) ; sprintf ( dataArray, "温度:%.f ℃, 湿度:%.f %%\r\n" , temperature, humidity) ; HAL_UART_Transmit ( & huart1, ( uint8_t * ) dataArray, strlen ( dataArray) , HAL_MAX_DELAY) ; HAL_Delay ( 5000 ) ; aht20State = 0 ; }
在i2c.c文件中,发送完成和接受完成的回调函数。
void HAL_I2C_MasterTxCpltCallback ( I2C_HandleTypeDef * hi2c) { if ( hi2c == & hi2c1) { aht20State = 2 ; }
}
void HAL_I2C_MasterRxCpltCallback ( I2C_HandleTypeDef * hi2c) { if ( hi2c == & hi2c1) { aht20State = 4 ; }
}
在上个实例中我们用得是阻塞式IIC通讯,关于AHT20_Init()的函数中也要修改成中断模式:
void AHT20_Init ( void ) { uint8_t readBuffer; HAL_Delay ( 40 ) ; HAL_I2C_Master_Transmit_IT ( & hi2c1, AHT20_ADDRESS, & readBuffer, 1 ) ; if ( ( readBuffer & 0x08 ) == 0x00 ) { uint8_t sendBuffer[ 3 ] = { 0xBE , 0x08 , 0x00 } ; HAL_I2C_Master_Receive_IT ( & hi2c1, AHT20_ADDRESS, sendBuffer, 3 ) ; }
}
烧录后通过串口发送数据,一切正常。
三、IIC通讯DMA模式
1、开启DMA模式
开启IIC的DMA模式,添加两个通道,一个RX一个TX,其他设置无需修改,保持默认。保存生成代码
2、将中断模式的代码修改为DMA模式的代码。
void AHT20_Init ( void ) { uint8_t readBuffer; HAL_Delay ( 40 ) ; HAL_I2C_Master_Transmit_DMA ( & hi2c1, AHT20_ADDRESS, & readBuffer, 1 ) ; if ( ( readBuffer & 0x08 ) == 0x00 ) { uint8_t sendBuffer[ 3 ] = { 0xBE , 0x08 , 0x00 } ; HAL_I2C_Master_Receive_DMA ( & hi2c1, AHT20_ADDRESS, sendBuffer, 3 ) ; }
}
void AHT20_Measure ( void ) { static uint8_t sendBuffer[ 3 ] = { 0xAC , 0x33 , 0x00 } ; HAL_I2C_Master_Transmit_DMA ( & hi2c1, AHT20_ADDRESS, sendBuffer, 3 ) ;
}
void AHT20_GetData ( void ) { HAL_I2C_Master_Receive_DMA ( & hi2c1, AHT20_ADDRESS, readBuffer, 6 ) ;
}
完成修改,功能没变,只是IIC的模式改变而已。 此文章仅自己学习记录用,是参考小破站keysking的教程。