一、I2C总线通信协议
软件I2C
软件I2C,也称为模拟I2C或bit-bang I2C,是一种通过微控制器的通用输入输出(GPIO)引脚来模拟I2C总线通信的方式。它不依赖于专门的硬件I2C接口,而是通过编程控制GPIO引脚的电平状态来实现I2C协议的数据传输。在软件I2C中,通常需要两个GPIO引脚:一个是SCL(时钟线),另一个是SDA(数据线)。通过精确地控制这两个引脚上的电压变化,可以发送和接收数据。
硬件I2C
硬件I2C是一种利用微控制器或其他设备上的专用硬件模块来进行I2C通信的方法。这种硬件模块内置了I2C协议的逻辑,能够自动处理时钟信号(SCL)和数据线(SDA)的操作,从而允许微控制器以更高效和可靠的方式与其他I2C兼容设备通信。
二、AHT20芯片
了解AHT20芯片的相关信息,具体信息请到官方下载对应产品介绍文档。
资料链接:http://www.aosong.com/class-36.html
官方代码使用的是PB14,PB15引脚需要修改对应的引脚才可以正常使用。
下图为AHT20芯片的典型电路(芯片正面有孔)。
三、工程配置
要求:每隔2秒钟采集一次温湿度数据,显示到OLED上,同时通过串口发送到上位机的“串口助手”软件。
首先进入CubeMX新疆工程,选择STM32F103C8T6芯片。
1.RCC配置。
2.SYS配置。
3.TIM1配置。
4.UASART串口配置。
5.I2C1配置(用于AHT20)。
6..I2C2配置(用于u8g2)。
7.时钟树配置。
8.代码配置。
四、代码修改
首先参考作者上篇文章进行u8g2的基础配置,以及字模的获取。
STM32—U8g2图形库练习
然后在usart.c文件中添加如下代码(添加头文件#include "stdio.h")。
/* USER CODE BEGIN 1 */int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);return ch;}/* USER CODE END 1 */
main.c文件修改
#include "main.h"#include "dma.h"#include "i2c.h"#include "tim.h"#include "usart.h"#include "gpio.h"#include "u8g2.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include<stdio.h>/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *///温static const unsigned char wen[] ={0x00,0x00,0xC4,0x1F,0x48,0x10,0x48,0x10,0xC1,0x1F,0x42,0x10,0x42,0x10,0xC8,0x1F,0x08,0x00,0xE4,0x3F,0x27,0x25,0x24,0x25,0x24,0x25,0x24,0x25,0xF4,0x7F,0x00,0x00};//湿static const unsigned char shi[] ={0x00,0x00,0xE4,0x1F,0x28,0x10,0x28,0x10,0xE1,0x1F,0x22,0x10,0x22,0x10,0xE8,0x1F,0x88,0x04,0x84,0x04,0x97,0x24,0xA4,0x14,0xC4,0x0C,0x84,0x04,0xF4,0x7F,0x00,0x00};//度 static const unsigned char du[] ={0x80,0x00,0x00,0x01,0xFC,0x7F,0x44,0x04,0x44,0x04,0xFC,0x3F,0x44,0x04,0x44,0x04,0xC4,0x07,0x04,0x00,0xF4,0x0F,0x24,0x08,0x42,0x04,0x82,0x03,0x61,0x0C,0x1C,0x70};//待 static const unsigned char dai[]={0x10,0x02,0x10,0x02,0x08,0x02,0xC4,0x3F,0x12,0x02,0x10,0x02,0xE8,0x7F,0x0C,0x08,0x0A,0x08,0xE9,0x7F,0x08,0x08,0x48,0x08,0x88,0x08,0x88,0x08,0x08,0x0A,0x08,0x04};//检 static const unsigned char jian[]={0x08,0x02,0x08,0x02,0x08,0x05,0x08,0x05,0xBF,0x08,0x48,0x10,0xAC,0x6F,0x1C,0x00,0x2A,0x11,0x0A,0x12,0x49,0x12,0x88,0x0A,0x88,0x08,0x08,0x04,0xE8,0x7F,0x08,0x00};//测 static const unsigned char che[]={0x00,0x20,0xE4,0x23,0x28,0x22,0x28,0x2A,0xA1,0x2A,0xA2,0x2A,0xA2,0x2A,0xA8,0x2A,0xA8,0x2A,0xA4,0x2A,0xA7,0x2A,0x84,0x20,0x44,0x21,0x44,0x22,0x24,0x28,0x10,0x10};/* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void oled_write(int temperature,int humidity){char t[5]; // 创建一个足够大的字符数组来存储转换后的数字char h[5];double t1=( double)temperature;double h1=(double)humidity;sprintf(t, "%.2f",t1/10 ); // 使用sprintf将int变量转换为字符串sprintf(h, "%.2f",h1/10);u8g2_t u8g2;u8g2Init(&u8g2); u8g2_ClearBuffer(&u8g2); u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//设置字体格式u8g2_DrawXBMP(&u8g2,16,0,16,16,wen);//(参数顺序依次是,结构体、x、y、字宽、字高、储存要显示的字点阵的数组)u8g2_DrawXBMP(&u8g2,32,0,16,16,du);u8g2_DrawStr(&u8g2,48,16,":");u8g2_DrawXBMP(&u8g2,16,32,16,16,shi);u8g2_DrawXBMP(&u8g2,32,32,16,16,du);u8g2_DrawStr(&u8g2,48,48,":");u8g2_DrawUTF8(&u8g2,55,16,t);u8g2_DrawUTF8(&u8g2,55,48,h);u8g2_SendBuffer(&u8g2);}void oled_write_init(){u8g2_t u8g2;u8g2Init(&u8g2); u8g2_ClearBuffer(&u8g2); u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//设置字体格式u8g2_DrawXBMP(&u8g2,16,0,16,16,wen);//(参数顺序依次是,结构体、x、y、字宽、字高、储存要显示的字点阵的数组)u8g2_DrawXBMP(&u8g2,32,0,16,16,du);u8g2_DrawStr(&u8g2,48,16,":");u8g2_DrawXBMP(&u8g2,16,32,16,16,shi);u8g2_DrawXBMP(&u8g2,32,32,16,16,du);u8g2_DrawStr(&u8g2,48,48,":");u8g2_DrawXBMP(&u8g2,58,0,16,16,dai);u8g2_DrawXBMP(&u8g2,74,0,16,16,jian);u8g2_DrawXBMP(&u8g2,90,0,16,16,che);u8g2_DrawXBMP(&u8g2,58,32,16,16,dai);u8g2_DrawXBMP(&u8g2,74,32,16,16,jian);u8g2_DrawXBMP(&u8g2,90,32,16,16,che);u8g2_SendBuffer(&u8g2);}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/int main(void){/* USER CODE BEGIN 1 */uint32_t CT_data[2]={0,0};volatile int c1,t1;Delay_1ms(500);/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_I2C1_Init();MX_TIM1_Init();MX_I2C2_Init();/* USER CODE BEGIN 2 *///初始化AHT20AHT20_Init();Delay_1ms(500);u8g2_t u8g2;u8g2Init(&u8g2);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */oled_write_init();while (1){/* USER CODE END WHILE */AHT20_Read_CTdata(CT_data); //不经过CRC校验,直接读取AHT20的温度和湿度数据 推荐每隔大于1S读一次//AHT20_Read_CTdata_crc(CT_data); //crc校验后,读取AHT20的温度和湿度数据 c1 = CT_data[0]*1000/1024/1024; //计算得到湿度值c1(放大了10倍)t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)printf("正在检测");for(int i=0;i<10;i++){HAL_Delay(100);printf(".");}printf("\r\n");HAL_Delay(1000);printf("温度:%d%d.%d",t1/100,(t1/10)%10,t1%10);printf("湿度:%d%d.%d",c1/100,(c1/10)%10,c1%10);printf("\r\n");printf("等待");for(int i=0;i<10;i++){HAL_Delay(100);printf(".");}printf("\r\n");HAL_Delay(1000);oled_write(t1,c1);/* USER CODE BEGIN 3 */}/* USER CODE END 3 */}
五、实验结果
六、总结
本次STM32编程练习结合了串口通信、AHT20的温湿度检测和u8g2的oled显示,让我学到了很多。
七、参考资料
stm32通过I2C接口实现温湿度(AHT20)的采集
I2C总线通信协议及实操stm32通过I2C实现温湿度(AHT20)采集
STM32F103基于I2C协议的AHT20温湿度传感器的数据采集(HAL库,提供源码)