在列出 HDC1080温湿度传感器驱动程序核心代码 之前,请允许我先多说几句:
- CSDN上有关HDC1080驱动代码的高阅读量博客只有两篇,且两篇博客内都不提供代码!而是在其中附上了需要积分才能下载代码的链接。吐了。
- GitHub上有关HDC1080驱动代码的STM32版只有两篇。一篇代码使用STM32的HAL库写的,在I2C的使用上封装程度太高,看不到通信中的关键细节。另一篇代码使用了大量的OLED界面操作,整体代码过于冗余,并且在HDC1080的I2C驱动上写的不清晰。
- TI公司的HDC1080官方芯片手册,在HDC1080驱动步骤中的第二步和第三步上写的及其令人费解,以至于我花了两天不断思考这两步究竟是要做什么操作。
下面进入正文。
温湿度数据
温湿度数据通过串口助手显示结果如下,temp温度在25℃左右,humi湿度在51%RH。当我将手指放在HDC1080表面时,temp温度逐渐升高至27℃左右,而humi湿度显示为0,这可能是由于HDC1080表面探测湿度的 金色元件 被我的手指完全遮挡所导致。之后,当我将手指移出HDC1080,temp温度和humi湿度逐渐回归正常数值。
驱动代码 HDC1080.h
首先,我使用的是 STM32F103C8T6 芯片,采用 Keil 5 编程软件,通过软件 I2C 协议和HDC1080 芯片进行通信。软件I2C中,SCL = PB0,SDA = PB1。在 HDC1080.h 中我做了如下三部分定义:
- HDC1080 软件 I2C 的接口定义
- HDC1080 各寄存器地址
- 软件 I2C 函数和 HDC1080 相关读取函数
代码如下:
#ifndef __HDC1080_H__
#define __HDC1080_H__
#include "sys.h"
// HDC1080 sensor port: PB0 - PB1
#define HDC1080_PORT GPIOB
#define HDC1080_SCL GPIO_Pin_0
#define HDC1080_SDA GPIO_Pin_1
#define HDC1080_SCL_H HDC1080_PORT->BSRR = HDC1080_SCL //set to 1
#define HDC1080_SCL_L HDC1080_PORT->BRR = HDC1080_SCL //reset to 0
#define HDC1080_SDA_H HDC1080_PORT->BSRR = HDC1080_SDA
#define HDC1080_SDA_L HDC1080_PORT->BRR = HDC1080_SDA
#define HDC1080_SCL_Status HDC1080_PORT->IDR & HDC1080_SCL //IDR是查看引脚电平状态
#define HDC1080_SDA_Status HDC1080_PORT->IDR & HDC1080_SDA
// The Chip parameters we use.
#define HDC1080_TempAddress 0x00
#define HDC1080_HumiAddress 0x01
#define HDC1080_Configuration 0x02
#define HDC1080_ManufactID 0xFE
#define HDC1080_DeviceID 0xFF
#define HDC1080_Write_Address 0x80
#define HDC1080_Read_Address 0x81
// I2C initial
void HDC1080_I2C_GPIO_Configuration(void);
void I2C4_delay(void);
int I2C4_Start(void);
void I2C4_Stop(void);
void I2C4_SDA_IN(void);
void I2C4_SDA_OUT(void);
static void I2C4_Ack(void);
static void I2C4_NoAck(void);
uint8_t I2C4_GetAck(void);
void I2C4_WriteByte(uint8_t Data);
uint8_t I2C4_ReadByte(uint8_t ack);
int HDC1080_I2C4_WriteByte(uint8_t Reg_Addr, uint8_t Data);
int HDC1080_I2C4_WriteBuffer(uint8_t Reg_Addr, uint8_t *pBuffer, uint8_t NumByteToWrite);
int HDC1080_I2C4_ReadByte(uint8_t Reg_Addr, uint8_t *Data);
int HDC1080_I2C4_ReadBuffer(uint8_t Reg_Addr, uint8_t *pBuffer, uint8_t NumByteToRead);
void HDC1080_Start(void);
uint16_t HDC1080_Result(char ch);
void HDC1080_Caculate(uint16_t ut, uint16_t uh, float *rt, float *rh);
#endif
驱动代码 HDC1080.c
下面介绍驱动 HDC1080 芯片的核心通信代码和逻辑,首先我们看一下 TI 公司给出的官方芯片手册中的通信过程:
对于第一步配置HDC1080的工作模式和第四步读取温湿度数据,这两步理解起来很容易。而最难以理解的是第二步和第三步,这两句话是什么意思?我们该怎么做通信?我本以为第二步和第三步是做两个独立的通信动作。但实际上,第二部和第三步合起来只是做了一个通信动作。我们来看如下通信流程图:
流程图中,“I2C写1次数据” 完成了芯片手册中提到的第一步,“I2C读一次数据” 完成了芯片手册中提到的的二、三、四步。我们都知道 I2C 在读/写设备数据的过程中,需要进行主从模式的转换。也就是说,STM32 使用 I2C 读数据前,首先进行写地址操作,将自己设置为主设备,也即流程图中的Write 0x80 和 Write 0x00。完成后,再次进行写操作,也就是流程图中的Write 0x81,这里需注意0x80和0x81的含义。同时将自己的工作模式转为从设备,开始进入数据接收状态。这时候,读取主设备发送来的数据。
下面回顾一下 HDC1080 芯片手册中提到的通信过程中的2,3,4条:
- Trigger the measurements by executing a pointer write transaction with the address pointer set to 0x00.
- Wait for the measurements to complete, based on the conversion time.
- Read the output data。
我们发现2,3,4条通信过程,即和流程图中的“I2C读一次数据”的过程完全对应。其中第3条通信动作,Wait for the measurements to complete,是夹在“I2C读一次数据”过程之中的,也就是流程图中红色字体标注的 Wait 20ms。 搞清楚这一步,下面再向寄存器读取温湿度数据就没有问题了。
下面提供 HDC1080.c 的核心通信代码,其中最重要的代码是 HDC1080_I2C4_ReadBuffer 函数,请多注意。
/ HDC1080 Coding //
void HDC1080_Start(void)
{
/* uint8_t ManuID[2];
HDC1080_I2C4_ReadBuffer(HDC1080_ManufactID,ManuID,2);//read 0x5449 is ok! */
uint8_t buf[4];
buf[0] = 0x10; //config temp and humi
buf[1] = 0x00; //both 14 bit
HDC1080_I2C4_WriteBuffer(HDC1080_Configuration,buf,2);
}
uint16_t HDC1080_Result(char ch)
{
uint16_t temp,humi;
uint8_t buf[4];
HDC1080_I2C4_ReadBuffer(HDC1080_TempAddress,buf,4);
temp = buf[0] << 8;
temp |= buf[1];
humi = buf[2] << 8;
humi |= buf[3];
if (ch == 'T') //ch字符用来进行温度和湿度的选择性输出
{
return temp;
}
else if (ch == 'H')
{
return humi;
}
else{
return 0;
}
}
void HDC1080_Caculate(uint16_t ut, uint16_t uh, float *rt, float *rh)
{
//ut 和 uh 为从HDC1080中读取出的原始温湿度值,这里根据芯片手册中的公式进行转换。
*rt = ut / pow(2.0, 16.0) * 165.0 - 40.0; //'C
*rh = uh / pow(2.0, 16.0) * 100.0; //%RH
}
int HDC1080_I2C4_ReadBuffer(uint8_t Reg_Addr, uint8_t *pBuffer, uint8_t NumByteToRead){
uint8_t i;
uint8_t tag;//选择性回应Ack
I2C4_Start();
I2C4_WriteByte(HDC1080_Write_Address);
if(!I2C4_GetAck())// 有slave ack的响应
{
I2C4_Stop();
return DISABLE;
}
I2C4_WriteByte(Reg_Addr);
if(!I2C4_GetAck())
{
I2C4_Stop();
return DISABLE;
}
delay_ms(20);//等待温湿度数据转换
I2C4_Start();//更改通信模式,主机从发送变为接收
I2C4_WriteByte(HDC1080_Read_Address);
if(!I2C4_GetAck())// 有slave ack的响应
{
I2C4_Stop();
return DISABLE;
}
for(i=0; i<NumByteToRead; i++)
{
tag = (i == NumByteToRead-1) ? 0 : 1;
pBuffer[i] = I2C4_ReadByte(tag);
}
I2C4_Stop(); //在这个位置统一停止
return 1;
}