一、htu21d介绍
2.1 基本介绍
HTU21D (F)是一种新型的MEMS工艺的带温度输出的数字湿度传感器。它在尺寸和智能方面设置了新的标准,它嵌入在一个可回流焊的双平无导线(DFN)包中,具有一个小的3 x 3 x 0.9 mm的封装。该传感器提供经过校准的数字的I²C格式的线性化信号。
HTU21D (F)数字湿度传感器是专用的湿度和温度即插即用传感器,用于需要可靠和准确测量OEM应用中。与微控制器的直接接口可以与模块的湿度和温度数字输出。这些低功率传感器是为高容量和高成本敏感的应用程序与紧密的空间限制而设计的。
1.2 引脚和应用电路
1.2.1 引脚

- VDD
HTU21D (F)传感器的电源电压必须在1.5VDC - 3.6VDC的范围内。推荐电源电压为3VDC(调节)
- SCK
SCK用于同步微控制器与HTU21D (F)传感器之间的通信。由于该接口由完全静态的逻辑组成,因此没有最小的SCK频率。
- SDA
数据线用于在设备内外传输数据。对于向HTU21D (F)传感器发送命令,DATA在SCK的上升边缘有效,在SCK较高时必须保持稳定。在SCK下降沿后,可以改变DATA值。对于安全通信,数据应分别为SCK上升边缘前和下降边缘后有效的tSU和tHD。对于从HTU21D (F)传感器读取数据,data在SCK下降后为有效的tVD,并一直有效到SCK的下一个下降边缘。

1.2.2 应用电路

1.3 命令
传感器支持的命令有:温湿度测量命令、读写用户寄存器命令、软件复位命令。

1.4 温湿度测量
1.4.1 保持模式
在保持模式下,HTU21D (F)在测量时向下拉下SCK线,以迫使i2c主机进入等待状态。通过释放SCK线路,HTU21D (F)传感器表明内部处理已经完成,并且可以继续进行i2c通信。
1.4.2 非保持模式
对于非保持模式,如果没有完成测量,MCU发送“读”命令时传感器在不返回ACK,MCU需要轮询读取直到传感器返回ACK。

1.4.3 读取数据
数据最高有效位是14bit,所以最低2bit有特别的含义,bit1用于区分读取的数据是温度还是湿度,为0表示温度,为1表示湿度。
1.4.4 CRC校验
HTU21D (F)传感器提供CRC-8校验和。所使用的多项式是X8 + X5 + X4 + 1。
1.4.5 温湿度计算
湿度计算,单位%RH,式中的Srh是从i2c读取的寄存器的值。

温度计算,单位℃,式中的Stemp是从i2c读取的寄存器的值。

二、n32g457的i2c外设
2.1 n32g457 i2c介绍
- 多主机功能:同一接口既可实现主机功能又可实现从机功能
- 是并行总线到 I 2C 总线协议的转换器
- 支持 7 位和 10 位的地址模式和广播寻址
- 作为 I 2C 主设备可以产生时钟、起始信号和停止信号
- 作为 I 2C 从设备具有可编程的 I 2C 地址检测、停止位检测的功能,以及可响应两个从地址的双地址能
力
- 支持标速(最高 100 kHz)和快速(最高 400 kHz)
- 支持多种状态标志,比如:发送器/接收器模式标志、字节发送结束标志以及 I 2C 总线忙标志。
- 支持多种错误标志,比如:主模式时的仲裁丢失标志、地址/数据传输后的应答(ACK/NAK)标志、
- 检测到错位的起始或停止条件标志以及禁止拉长时钟功能时的上溢或下溢标志。
- 支持中断向量,字节成功发送中断和错误事件中断
- 可选的拉长时钟功能
- 支持 DMA 模式
- 可选择的 PEC(报文错误检测)生成和校验:发送模式中生成 PEC 并作为最后一个字节传输,接收
- 模式中 PEC 作为最后一个接收字节来校验错误
- 兼容 SMBus 2.0 和 PMBus
2.2 i2c初始化

i2c初始化主要完成以下工作:
- 使能I2C1和I2C1对应的GPIOB以及重映射的外设时钟
- 设置I2C1引脚重映射到PB8,PB9
- 初始化I2C1的SDA和SCL引脚为复用开漏输出
- 初始化I2C速率100k、7bit地址模式
int i2c_master_init(void)
{
I2C_InitType i2c1_master;
GPIO_InitType i2c1_gpio;
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
#ifdef I2C1_REMAP
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
GPIO_ConfigPinRemap(GPIO_RMP_I2C1, ENABLE);
#endif
i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2c1_gpio.GPIO_Speed = GPIO_Speed_2MHz;
i2c1_gpio.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitPeripheral(GPIOx, &i2c1_gpio);
I2C_DeInit(I2C1);
i2c1_master.BusMode = I2C_BUSMODE_I2C;
i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2c1_master.OwnAddr1 = I2C_MASTER_ADDR;
i2c1_master.AckEnable = I2C_ACKEN;
i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_master.ClkSpeed = 100000; // 100K
I2C_Init(I2C1, &i2c1_master);
I2C_Enable(I2C1, ENABLE);
return 0;
}
2.3 i2c发送
i2c发送主要完成以下工作:
- 调用I2C_GenerateStart产生i2c的起始信号
- 调用I2C_SendAddr7bit发送写方向的7位从机地址
- 调用I2C_SendData向i2c外设的数据寄存器写入数据,发送数据
- 调用I2C_GenerateStop产生i2c的停止信号
int i2c_master_send(uint8_t* data, int len)
{
uint8_t* sendBufferPtr = data;
#ifdef NON_REENTRANT
if (Mutex_Flag)
return -1;
else
Mutex_Flag = 1;
#endif
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_MODE);
}
}
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_TXMODE);
}
}
Comm_Flag = C_READY;
while (len-- > 0)
{
I2C_SendData(I2C1, *sendBufferPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDING);
}
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDED);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
Comm_Flag = C_READY;
#ifdef NON_REENTRANT
if (Mutex_Flag)
Mutex_Flag = 0;
else
return -2;
#endif
return 0;
}
2.4 i2c接收
i2c接收主要完成以下工作:
- 调用I2C_GenerateStart产生i2c的起始信号
- 调用I2C_SendAddr7bit发送读方向的7位从机地址
- 获取I2C_FLAG_RXDATNE标志判断是否有数据,有数据则接收
- 调用I2C_GenerateStop产生i2c的停止信号
int i2c_master_recv(uint8_t* data, int len)
{
uint8_t* recvBufferPtr = data;
#ifdef NON_REENTRANT
if (Mutex_Flag)
return -1;
else
Mutex_Flag = 1;
#endif
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
I2C_ConfigAck(I2C1, ENABLE);
// send start
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_MODE);
}
}
// send addr
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_RECV);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_RXMODE_FLAG)) // EV6
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_RXMODE);
}
}
Comm_Flag = C_READY;
if (len == 1)
{
I2C_ConfigAck(I2C1, DISABLE);
(void)(I2C1->STS1); /// clear ADDR
(void)(I2C1->STS2);
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_RXDATNE))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_RECVD);
}
}
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
}
else if (len == 2)
{
I2C1->CTRL1 |= 0x0800; /// set ACKPOS
(void)(I2C1->STS1);
(void)(I2C1->STS2);
I2C_ConfigAck(I2C1, DISABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_BYTEF))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_BYTEF);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
}
else
{
I2C_ConfigAck(I2C1, ENABLE);
(void)(I2C1->STS1);
(void)(I2C1->STS2);
while (len)
{
if (len == 3)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_BYTEF))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_BYTEF);
}
}
I2C_ConfigAck(I2C1, DISABLE);
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_BYTEF))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_BYTEF);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
break;
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_RECVD_FLAG)) // EV7
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_RECVD);
}
}
*recvBufferPtr++ = I2C_RecvData(I2C1);
len
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
Comm_Flag = C_READY;
#ifdef NON_REENTRANT
if (Mutex_Flag)
Mutex_Flag = 0;
else
return -2;
#endif
return 0;
}
三、htu21d驱动移植
移植主要实现以下接口
enum status_code i2c_master_write_packet_wait(struct i2c_master_packet *const packet)
enum status_code i2c_master_read_packet_wait(struct i2c_master_packet *const packet)
enum status_code i2c_master_write_packet_wait_no_stop(struct i2c_master_packet *const packet)
这三个接口按照n32g45x的i2c时序,调用固件库的接口,完成移植。以i2c_master_write_packet_wait举例
enum status_code i2c_master_write_packet_wait(struct i2c_master_packet *const packet)
{
#if 1
uint8_t* sendBufferPtr = packet->data;
uint8_t htu_address = packet->address;
uint8_t len = packet->data_length;
#ifdef NON_REENTRANT
if (Mutex_Flag)
return -1;
else
Mutex_Flag = 1;
#endif
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_MODE);
}
}
I2C_SendAddr7bit(I2C1, htu_address, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_TXMODE);
}
}
Comm_Flag = C_READY;
while (len-- > 0)
{
I2C_SendData(I2C1, *sendBufferPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDING);
}
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDED);
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
}
}
Comm_Flag = C_READY;
#ifdef NON_REENTRANT
if (Mutex_Flag)
Mutex_Flag = 0;
else
return -2;
#endif
#endif
return STATUS_OK;
}
四、测试
在log中可以看到实时显示温湿度的数值。
