本人自己想做一个项目,做到AHT10获取温湿度时,由于开发经验不足,在网上不断查找资料,但都没有完整详细的步骤讲解,在此卡了好几天。经历几天的摸索,最终一步步实现软件模拟IIC通讯,读取AHT10温湿度数据。在此做个记录分享,给跟我一样的初学者们一点借鉴。

       首先想要跟I2C设备通讯实现方式上有两种,一种是软件模拟I2C协议,就是本文所讲的内容,另一种就是硬件I2C,通过STM32CubeMX配置I2C,再进行一些应用编程。我还没有尝试过。

      基于HAL库软件实现I2C的话,要注意的一点是微秒延时。HAL库只有毫秒延时,没有微秒延时。具体一些内容可以百度看看。

    手册的内容我就不做分享了,百度下载手册,代码结合手册内容和I2C协议内容理解。

    我的MCU是STM32L151C8T6。

    先贴微秒延时代码。这段代码没有实测过到底准不准确,建议只在I2C通讯中使用,是没什么问题的。

void Delay_us(uint16_t usec)
{
    uint16_t i=4;
    
    usec >>= 1;
    
    while (usec--)
    {
        while(i--)
        {
            ;
        }
    }    
}

  第一步就是要对我们的I2C接口进行初始化了。我选用的是PB6/PB7引脚。

1 static void AHT10_IIC_Init(void)
 2 {                         
 3     GPIO_InitTypeDef GPIO_InitStruct;
 4     __HAL_RCC_GPIOB_CLK_ENABLE();
 5 
 6     GPIO_InitStruct.Pin = AHT10_SDA_GPIO_PIN;
 7         GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 8         GPIO_InitStruct.Pull = GPIO_NOPULL;
 9         GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
10         HAL_GPIO_Init(AHT10_SDA_GPIO_PORT, &GPIO_InitStruct);
11     
12     GPIO_InitStruct.Pin = AHT10_SCL_GPIO_PIN;
13         GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
14         GPIO_InitStruct.Pull = GPIO_NOPULL;
15         GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
16         HAL_GPIO_Init(AHT10_SCL_GPIO_PORT, &GPIO_InitStruct);
17     
18     AHT10_SDA_H;
19     AHT10_SCL_H;
20 }

然后我们定义一个SDA初始化的函数,设置入口参数,方便配置各种参数配置。

void AHT10_SDA_INIT(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin, uint32_t Mode, uint32_t Pull)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_Pin;
    GPIO_InitStruct.Mode = Mode;
    GPIO_InitStruct.Pull = Pull;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOx, &GPIO_InitStruct);
}

 设置几个读写函数

1 void AHT10_SDA_State(GPIO_PinState PinState)
 2 {
 3     HAL_GPIO_WritePin(AHT10_SDA_GPIO_PORT,AHT10_SDA_GPIO_PIN,PinState);
 4 }
 5 
 6 void AHT10_SCL_State(GPIO_PinState PinState)
 7 {
 8     HAL_GPIO_WritePin(AHT10_SCL_GPIO_PORT,AHT10_SCL_GPIO_PIN,PinState);
 9 }
10 
11 uint8_t read_sda_data(void)
12 {
13     return HAL_GPIO_ReadPin(AHT10_SDA_GPIO_PORT,AHT10_SDA_GPIO_PIN);
14 }

I2C启动函数。I2C启动,SCL为高时,SDA从高变低。(结合通讯协议来理解。)

1 void AHT10_IIC_Start(void)
 2 {
 3     AHT10_SDA_INIT_OUT;
 4     AHT10_SDA_H;
 5     AHT10_SCL_H;
 6     AHT10_Delay_us(4);
 7   AHT10_SDA_L;
 8   AHT10_Delay_us(4);
 9   AHT10_SCL_L;
10 }

I2C停止函数,SCL为高时,SDA从低变高

1 void AHT10_IIC_Stop(void)
 2 {
 3     AHT10_SDA_INIT_OUT;
 4     AHT10_SDA_L;
 5     AHT10_SCL_L;
 6     AHT10_Delay_us(4);
 7     AHT10_SCL_H;
 8     AHT10_SDA_H;
 9     AHT10_Delay_us(4);
10 }

启动和停止写完了。我们来写等待从机设备响应ACK部分。

SCL为高时,SDA为高则未响应,反之。

1 static uint8_t AHT10_IIC_Wait_Ack(void)
 2 {
 3     uint8_t ucErrTime=0;
 4     AHT10_SDA_INIT_IN;
 5     AHT10_SDA_H;
 6     AHT10_Delay_us(1);
 7     AHT10_SCL_H;
 8     AHT10_Delay_us(1);
 9     while(read_sda_data())
10    {
11         ucErrTime++;
12       if(ucErrTime>250)
13         {
14             AHT10_IIC_Stop();
15             return 1;
16         }
17     }
18     AHT10_SCL_L;
19     return 0; 
20 }

主机向从机发送应答ACK部分和非应答NACK部分

1 static void AHT10_IIC_Ack(void)
 2 {    
 3     AHT10_SCL_L;
 4     AHT10_SDA_INIT_OUT;
 5     AHT10_SDA_L;
 6     AHT10_Delay_us(2);
 7     AHT10_SCL_H;
 8     AHT10_Delay_us(2);
 9     AHT10_SCL_L;
10 }
1 static void AHT10_IIC_NAck(void)
 2 {    
 3     AHT10_SCL_L;
 4     AHT10_SDA_INIT_OUT;
 5     AHT10_SDA_H;
 6     AHT10_Delay_us(2);
 7     AHT10_SCL_H;
 8     AHT10_Delay_us(2);
 9     AHT10_SCL_L;
10 }

主机向从机发送一个字节。

1 static void AHT10_IIC_Send_Byte(uint8_t txd)
 2 {
 3     //uint8_t t;
 4     AHT10_SDA_INIT_OUT;
 5     AHT10_SCL_L;
 6     for(uint8_t t=0;t<8;t++)
 7   {
 8         if((txd&0x80)>>7)//高位先传,&1000 0000后将第八位右移至第一位
 9             AHT10_SDA_H;
10         else
11             AHT10_SDA_L;
12         txd<<=1;//每传完一位后,低位向高位进1
13       AHT10_Delay_us(2);
14       AHT10_SCL_H;
15         AHT10_Delay_us(2); 
16         AHT10_SCL_L;
17         AHT10_Delay_us(2);
18     }
19 }

主机读取一个字节。

1 static uint8_t AHT10_IIC_Read_Byte(unsigned char ack)
 2 {
 3     unsigned char i,receive=0;
 4     AHT10_SDA_INIT_IN;
 5     for(i=0;i<8;i++)
 6   {
 7       AHT10_SCL_L;
 8       AHT10_Delay_us(2);
 9         AHT10_SCL_H;
10         receive<<=1;
11         if(read_sda_data())
12             receive++;
13         AHT10_Delay_us(1);
14     }
15     if (!ack)
16     AHT10_IIC_NAck();//发送nack
17   else
18     AHT10_IIC_Ack(); //发送ack 
19   return receive;
20 }

以上就是实现I2C通讯的几个基本流程,将其模块化,方便调用。

检查AHT10模块是否响应。

1 uint8_t AHT10Check(void)
2 {
3   uint8_t ack=0;
4   AHT10_IIC_Start();
5     AHT10_IIC_Send_Byte(AHT10_ADDRESS);
6     ack = AHT10_IIC_Wait_Ack();
7     AHT10_IIC_Stop();
8     return ack;//返回1则未检测到
9 }

AHT10的软件复位

1 void AHT10Reset(void)
2 {
3     AHT10_IIC_Start();
4     AHT10_IIC_Send_Byte(AHT10_WRITE);
5     AHT10_IIC_Wait_Ack();
6     AHT10_IIC_Send_Byte(0xba);
7     AHT10_IIC_Wait_Ack();
8     AHT10_IIC_Stop();    
9 }

接下来是AHT10的初始化。

1 void AHT10Init()
 2 {
 3     AHT10_IIC_Init();
 4     
 5     AHT10_IIC_Start();
 6     AHT10_IIC_Send_Byte(AHT10_ADDRESS);
 7     AHT10_IIC_Send_Byte(0xe1);    //AHT10启动的初始化指令
 8     AHT10_IIC_Send_Byte(0x08);
 9     AHT10_IIC_Send_Byte(0x00);
10     AHT10_IIC_Stop();    
11     AHT10_Delay_ms(40);//延时40MS让传感器稳定
12 }

AHT10读取温湿度数据

1 uint8_t AHT10ReadData(uint16_t *temperature,uint16_t *humidity)
 2 {
 3     uint8_t ack;
 4     uint32_t SRH=0,ST=0;
 5     uint8_t databuff[6];
 6     AHT10_IIC_Start();
 7     AHT10_IIC_Send_Byte(AHT10_WRITE);
 8     AHT10_IIC_Wait_Ack();
 9     AHT10_IIC_Send_Byte(0xac);
10     AHT10_IIC_Wait_Ack();
11     AHT10_IIC_Send_Byte(0x33);
12     AHT10_IIC_Wait_Ack();
13     AHT10_IIC_Send_Byte(0x00);
14     AHT10_IIC_Wait_Ack();
15     AHT10_IIC_Stop();      
16     AHT10_Delay_ms(80);//延时等待数据读取。
17     AHT10_IIC_Start();
18     AHT10_IIC_Send_Byte(AHT10_READ);
19     AHT10_IIC_Wait_Ack();
20     ack=AHT10_IIC_Read_Byte(1);
21     if((ack&0x40)==0)
22     {
23         databuff[0]=AHT10_IIC_Read_Byte(1);
24         databuff[1]=AHT10_IIC_Read_Byte(1);
25         databuff[2]=AHT10_IIC_Read_Byte(1);
26         databuff[3]=AHT10_IIC_Read_Byte(1);
27         databuff[4]=AHT10_IIC_Read_Byte(0);
28         AHT10_IIC_Stop();
29         SRH=(databuff[0]<<12)+(databuff[1]<<4)+(databuff[2]>>4);
30         ST=((databuff[2]&0X0f)<<16)+(databuff[3]<<8)+(databuff[4]);
31 
32         *humidity = SRH*1000/1024/1024;  
33             *temperature = ST *200*10/1024/1024-500;
34          return 1;
35     }
36     AHT10_IIC_Stop();    
37     return 0;
38 }

以上就是全部流程,将其放在一个新的.c文件中就行了。代码网上可以找到类似的,我就是在基础上进行了结合而已。

最后我们定义一个读取函数,在main里直接调用就行,不过要先定义缓存变量,把地址传进来存储温湿度数据。

1 void aht10_read(uint16_t *AHT10_tem,uint16_t *AHT10_hum)
2 {    
3   AHT10Init();
4   AHT10ReadData(AHT10_tem,AHT10_hum);  
5 }

 

.h文件的代码我也贴在下面。全部流程的代码都在这里面了,直接复制就可以用,注意GPIO口是否一致。

1 #ifndef _AHT10_H_
 2 #define _AHT10_H_
 3 
 4 #include "stm32l1xx_hal.h"
 5 
 6 #define AHT10_ADDRESS 0x70
 7 #define AHT10_WRITE 0x70
 8 #define AHT10_READ 0x71
 9 
10 
11 #define AHT10_Delay_us(time)                                Delay_us(time)
12 #define AHT10_Delay_ms(time)                                HAL_Delay(time)
13 #define AHT10_SDA_GPIO_PIN GPIO_PIN_7
14 #define AHT10_SCL_GPIO_PIN GPIO_PIN_6
15 #define AHT10_SDA_GPIO_PORT GPIOB
16 #define AHT10_SCL_GPIO_PORT GPIOB
17 
18 #define AHT10_SDA_INIT_OUT AHT10_SDA_INIT(AHT10_SDA_GPIO_PORT,AHT10_SDA_GPIO_PIN,GPIO_MODE_OUTPUT_PP,GPIO_NOPULL)
19 #define AHT10_SDA_INIT_IN  AHT10_SDA_INIT(AHT10_SDA_GPIO_PORT,AHT10_SDA_GPIO_PIN,GPIO_MODE_INPUT,GPIO_PULLUP)
20 #define AHT10_SDA_H        AHT10_SDA_State(GPIO_PIN_SET)
21 #define AHT10_SDA_L        AHT10_SDA_State(GPIO_PIN_RESET)
22 
23 #define AHT10_SCL_H        AHT10_SCL_State(GPIO_PIN_SET)
24 #define AHT10_SCL_L        AHT10_SCL_State(GPIO_PIN_RESET)
25 
26 
27 void AHT10_SDA_INIT(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin, uint32_t Mode, uint32_t Pull);
28 void AHT10_SDA_State(GPIO_PinState PinState);
29 void AHT10_SCL_State(GPIO_PinState PinState);
30 
31 
32 void AHT10Init(void);
33 uint8_t AHT10Check(void);
34 void AHT10Reset(void);
35 uint8_t AHT10ReadData(uint16_t *temperature,uint16_t *humidity);
36 void Delay_us(uint16_t usec);
37 void aht10_read(uint16_t *AHT10_tem,uint16_t *AHT10_hum);
38 #endif

 

结束啦~,没有对代码进行解释,是因为结合手册看就行了~,还是比较清楚的。

 

hal_i2c_mem_write_dma怎么使用_数据

1 static void AHT10_IIC_Init(void)
 2 {                         
 3     GPIO_InitTypeDef GPIO_InitStruct;
 4     __HAL_RCC_GPIOB_CLK_ENABLE();//ʹÄÜGPIOʱÖÓ
 5 
 6     GPIO_InitStruct.Pin = AHT10_SDA_GPIO_PIN;
 7   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 8   GPIO_InitStruct.Pull = GPIO_NOPULL;
 9   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
10   HAL_GPIO_Init(AHT10_SDA_GPIO_PORT, &GPIO_InitStruct);
11     
12     GPIO_InitStruct.Pin = AHT10_SCL_GPIO_PIN;
13   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
14   GPIO_InitStruct.Pull = GPIO_NOPULL;
15   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
16   HAL_GPIO_Init(AHT10_SCL_GPIO_PORT, &GPIO_InitStruct);
17     
18     AHT10_SDA_H;
19