一. 使用STM32CubeMX实现 IIC 通信
打开STM32CubeMX,选择STM32103C8芯片,设置基本配置,设置时钟配置,打开USART1串口通信(使用轮询模式)。
设置好之后,设置存储路径,选择所用IDE,然后点击GENERATE CODE创建工程,在弹出的open project打开工程进行全局编译。
二. HAL库中IIC通信中断模式和DMA模式的重要函数
1. 主设备发送数据到从设备函数 中断模式:HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
@简介 使用中断模式在主模式下以非阻塞模式传输一定数量的数据
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@参数 DevAddress Target device address(目标设备地址)
@参数 pData 指向数据缓冲区的指针
@参数 Size 要发送的数据量
@返回值 HAL status
2. 主设备发送数据到从设备函数 DMA模式:HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
@简介 使用DMA模式在主模式下以非阻塞模式传输一定数量的数据
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@参数 DevAddress Target device address(目标设备地址)
@参数 pData 指向数据缓冲区的指针
@参数 Size 要发送的数据量
@返回值 HAL status
3. 主设备接收从设备发送来的数据函数 中断模式:HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
@简介 使用中断在主模式下接收非阻塞模式下的一定数量的数据
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@参数 DevAddress Target device address(目标设备地址)
@参数 pData 指向数据缓冲区的指针
@参数 Size 要发送的数据量
@返回值 HAL status
4. 主设备接收从设备发送来的数据函数 DMA模式:HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
@简介 使用DMA模式在主模式下接收非阻塞模式下的一定数量的数据
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@参数 DevAddress Target device address(目标设备地址)
@参数 pData 指向数据缓冲区的指针
@参数 Size 要发送的数据量
@返回值 HAL status
5. 主设备发送数据到从设备传输完成中断回调函数:HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);
@简介 主模式TX传输完成回调函数
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@返回值 空
__weak void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_MasterTxCpltCallback could be implemented in the user file
*/
}
6. 主设备接收从设备发送来的数据传输完成中断回调函数:HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);
@简介 主模式RX传输完成回调函数
@参数 hi2c 指向I2C_HandleTypeDef结构体的指针,该结构体包含指定I2C的配置信息。
@返回值 空
__weak void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_MasterRxCpltCallback could be implemented in the user file
*/
}
以上函数在stm32f1xx_hal_i2c.c文件中:
三. 实现 IIC 通信(中断模式)
这个时候可以引入状态模式(状态机)概念,状态模式是嵌入式软件开发中非常重要的也是非常核心的设计模式之一,具体概念自行百度。
在这里我们可以将AHT20测量温湿度数据流程剖析开来。
1. 最开始初始化AHT20模块
2. 向AHT20发送测量命令—(这里可以设置为状态位为0)
3. 发送测量命令中—(这里可以设置为状态位为1)
4. 发送测量命令完成后开始读取测量数据—(这里可以设置为状态位为2)
5. 读取测量数据中—(这里可以设置为状态位为3)
6. 读取数据完成后开始处理获取到的温湿度数据—(这里可以设置为状态位为4),最后将状态位置为初始状态位0
这里画一个流程图直观的展示
到这里为止,一个状态机模型就构造好了,接下来开始根据该模型编写代码
在AHT20.c文件中添加如下代码
#include "AHT20.h"
#define AHT20 0x70
unsigned char readbuffer[6];
/* AHT20初始化
* 进行AHT20温湿度模块的初始化,上电后等待40ms,发送0x71可以获取一个字节的状态字
* 如果返回的数据(装载在readbuffer中)的校准使能位Bit[3]是否为1,即(xxxx 1xxx)
* 若不为1,则需要发送0xBE命令(初始化)包含有两个字节0x08、0x00然后在等待10ms
*/
void AHT20_Init(){
unsigned char readbuffer; // 装取数据
HAL_Delay(40);
// HAL_MAX_DELAY 永久等待 主机读取从机数据 读:函数会自动在设备地址基础上加1
HAL_I2C_Master_Receive(&hi2c1,AHT20,&readbuffer,1,HAL_MAX_DELAY);
if((readbuffer & 0x08) == 0x00){
unsigned char sendbuffer[3] = {0xBE, 0x08, 0x00};
// 主机向从机写入数据函数 写:函数会自动在设备地址基础上加0
HAL_I2C_Master_Transmit(&hi2c1,AHT20,sendbuffer,1,HAL_MAX_DELAY);
}
}
/* AHT20触发测量 中断模式下 以及 DMA模式下
*
*/
void AHT20_Measure(){
static unsigned char sendbuffer[3] = {0xAC, 0x33, 0x00};
HAL_I2C_Master_Transmit_IT(&hi2c1,AHT20,sendbuffer,3);// 向从机写入数据函数
}
/* AHT20开始读取温湿度数据
*
*/
void AHT20_Read_Data(){
HAL_I2C_Master_Receive_IT(&hi2c1,AHT20,readbuffer,6); // 向从机读取数据函数
}
/* AHT20处理发送来的数据,根据AHT20返回数据的时序,对返回来的数据进行处理
*
*/
void AHT20_Processing_Data(float *tm,float *hm){
static unsigned int data;
if((readbuffer[0] & 0x80) == 0x00){
data = (readbuffer[1]<<12)+(readbuffer[2]<<4)+(readbuffer[3]>>4);
*hm = data * 100.0f / (1<<20);
data = (((uint32_t)readbuffer[3]&0x0f)<<16)+(readbuffer[4]<<8)+(readbuffer[5]);
*tm = data * 200.0f /(1<<20) - 50;
}
}
在AHT20.h文件中添加如下代码
#ifndef __AHT20_H
#define __AHT20_H
#include "i2c.h"
// 中断状态下
void AHT20_Measure(void);
void AHT20_Read_Data(void);
void AHT20_Processing_Data(float *tm,float *hm);
#endif
在main.c文件中的Private variables下的USER CODE BEGIN PV注释对里添加如下代码
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
float temperature,humidity;
char message[50];
int i2c_flag = 0;
/* USER CODE END PV */
在main.c文件中的Private user code下的USER CODE BEGIN 0注释对里添加如下代码
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// IIC发送数据完成中断回调函数
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
if(hi2c == &hi2c1){ // 若发送完成,状态位改变
i2c_flag = 2;
}
}
// IIC接收数据完成中断回调函数
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){
if(hi2c == &hi2c1){ // 若接收完成,状态位改变
i2c_flag = 4;
}
}
/* USER CODE END 0 */
在main.c文件main函数下的USER CODE BEGIN 2注释对里添加如下代码
/* USER CODE BEGIN 2 */
AHT20_Init();
/* USER CODE END 2 */
在main.c文件main函数中Infinite loop下的USER CODE BEGIN WHILE注释对里添加如下代码
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(i2c_flag == 0){
AHT20_Measure();
i2c_flag = 1;
}else if(i2c_flag == 2){
HAL_Delay(75);
AHT20_Read_Data();
i2c_flag = 3;
}else if(i2c_flag == 4){
AHT20_Processing_Data(&temperature,&humidity);
sprintf((char *)message,"温度:%0.1f ℃,湿度:%.1f %% \r\n",temperature,humidity);
HAL_UART_Transmit(&huart1,(uint8_t *)message,strlen(message),HAL_MAX_DELAY);
HAL_Delay(1000);
i2c_flag = 0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
完成以上代码的编写后,将开发板连接好所需要的对应的设备(AHT20,USB转TTL模块)。将工程下载到开发板上,打开串口调试助手,这里我使用的波特率是115200Bit/s。
至此,基于IIC通信(中断模式)实现AHT20测量温湿度数据基本上就已经实现了。
关于DMA模式下测量温湿度数据,只需要将AHT20.c文件中的HAL_I2C_Master_Transmit_及HAL_I2C_Master_Receive_后的IT改为DMA,即可使用DMA模式(前提是要在STM32CubeMX中开启I2C1的DMA通道)