STM32-Cube-IIC通讯
一、温湿度传感器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
/* HAL库会根据用户发送或者接受数据将地址最后一位置1或者置0 */
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_Init();
float temperature, humidity;
char dataArray[50];
while (1)
{
AHT20_Read(&temperature, &humidity);
sprintf(dataArray, "温度:%.f ℃, 湿度:%.f %%\r\n", temperature, humidity);
/* sprintf() 函数只是将特定格式存于数组dataArray中,再通过串口将数组发送出去 */
HAL_UART_Transmit(&huart1, (uint8_t*)dataArray, strlen(dataArray), HAL_MAX_DELAY);
HAL_Delay(1000);
}
- 编译代码并烧录,再通过连接串口获取单片机发送的数据。

二、IIC中断模式与状态机
1、IIC中断模式
- 打开IIC的中断向量并保存生成代码。

2、利用状态机编程代码。
1.因为状态机的原理就是不同的状态做不同的事情,所以我们将上面读取数据的函数AHT20_Read()拆分为三个单独的函数,如下:
- 发送测量指令:
/* 测量函数 */
void AHT20_Measure(void) {
//因为函数用的是sendBuffer指针的地址,函数运行结束之后就会被释放,被其他变量所使用,所以这里需要使用static来创建数组,保证数组的地址一直存在。
static uint8_t sendBuffer[3] = {0xAC, 0x33, 0x00};
HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
}
- 获取模块的数据:
/* 获取数据 */
void AHT20_GetData(void) {
//将获取的数据存于readBuffer数组之中,readBuffer为全局变量,方便其他函数使用
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.创建状态机模型
/* USER CODE BEGIN PV */
uint8_t aht20State = 0;
/* USER CODE END PV */

3.代码部分
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
extern uint8_t aht20State;
/* USER CODE END EC */
while (1)
{
if (aht20State == 0) { //初始状态,发送测量指令
AHT20_Measure();
aht20State = 1;
} else if (aht20State == 2) { //发送完成,等75ms后才可以进行读取
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;
}
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
if (hi2c == &hi2c1) {
aht20State = 2;
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
if (hi2c == &hi2c1) {
aht20State = 4;
}
}
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模式

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) {
//因为函数用的是sendBuffer指针的地址,函数运行结束之后就会被释放,被其他变量所使用,所以这里需要使用static来创建数组,保证数组的地址一直存在。
static uint8_t sendBuffer[3] = {0xAC, 0x33, 0x00};
HAL_I2C_Master_Transmit_DMA(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
}
/* 获取数据 */
void AHT20_GetData(void) {
//将获取的数据存于readBuffer数组之中,readBuffer为全局变量,方便其他函数使用
HAL_I2C_Master_Receive_DMA(&hi2c1, AHT20_ADDRESS, readBuffer, 6);
}
作者:人生若只如初见645