/**********************************************************************
|
* Copyright: (C)2024 LingYun IoT System Studio
|
* Author: GuoWenxue<guowenxue@gmail.com>
|
*
|
* Description: SHT20 Temperature & humidity sensor driver on ISKBoard
|
*
|
* ChangeLog:
|
* Version Date Author Description
|
* V1.0.0 2024.08.29 GuoWenxue Release initial version
|
*
|
***********************************************************************/
|
|
#include <stdio.h>
|
#include "sht20.h"
|
#include "miscdev.h" /* udelay() / mdelay() */
|
|
/* SHT20 7位标准 I2C 从机地址 */
|
#define SHT20_I2C_ADDR 0x40
|
|
/* SHT20 控制指令 (No Hold Master) */
|
#define SHT20_CMD_TRIG_T 0xF3 /* 触发温度测量 (非阻塞) */
|
#define SHT20_CMD_TRIG_RH 0xF5 /* 触发湿度测量 (非阻塞) */
|
#define SHT20_CMD_SOFT_RESET 0xFE /* 软件复位 */
|
|
/* I2C 设备 SHT20 */
|
static i2c_dev_t sht20;
|
|
/* 前置静态辅助函数声明 */
|
static uint8_t sht20_check_crc(uint8_t *data, uint8_t nbytes, uint8_t checksum);
|
i2c_status_t sht20_read_value(i2c_dev_t *sht20, uint8_t cmd, float *value);
|
|
/**
|
* @brief SHT20 初始化并软复位芯片
|
* @param bus: 绑定的 I2C 物理总线指针
|
* @retval 0: 成功; -1: 失败
|
*/
|
int sht20_init(i2c_bus_t *bus)
|
{
|
uint8_t cmd = SHT20_CMD_SOFT_RESET;
|
int rv = 0;
|
|
if (!bus)
|
{
|
return -1;
|
}
|
|
mdelay(20);
|
|
/* 绑定设备及其设备号到 I2C 总线上 */
|
i2c_bind_dev(&sht20, bus, SHT20_I2C_ADDR);
|
|
/* 将I2C总线加锁让设备独占总线 */
|
i2c_lock_dev(&sht20);
|
|
/* 向 SHT20 发送软件复位指令,并等待复位完成 */
|
i2c_write_no_reg(sht20.bus, sht20.dev_addr, &cmd, 1);
|
mdelay(20);
|
|
/* 释放设备的 I2C 总线锁 */
|
i2c_free_dev(&sht20);
|
return rv;
|
}
|
|
/**
|
* @brief 应用层温湿度采样接口函数 (带总线锁)
|
* @param temperature: 存放输出温度结果的指针
|
* @param humidity: 存放输出湿度结果的指针
|
* @retval 0: 采样成功且CRC通过; -1: 采样或校验失败
|
*/
|
int sht20_sample_TrH(float *temperature, float *humidity)
|
{
|
int rv = -1;
|
|
if( !temperature || !humidity )
|
{
|
return -1;
|
}
|
|
/* 将I2C总线加锁让设备独占总线 */
|
i2c_lock_dev(&sht20);
|
|
/* 2. 采集温度 */
|
if (sht20_read_value(&sht20, SHT20_CMD_TRIG_T, temperature) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 3. 采集湿度 */
|
if (sht20_read_value(&sht20, SHT20_CMD_TRIG_RH, humidity) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
rv = 0;
|
|
cleanup:
|
/* 4. 释放总线并解锁 */
|
i2c_free_dev(&sht20);
|
return rv;
|
}
|
|
/**
|
* @brief SHT20 硬件标准 CRC-8 校验
|
* @note 多项式 P(x) = x^8 + x^5 + x^4 + 1 = 1 0011 0001 = 0x31
|
* @param data: 待校验的数据缓冲区指针 (含 High Byte 和 Low Byte)
|
* @param nbytes: 校验的数据长度 (固定为 2 字节)
|
* @param checksum: SHT20 芯片硬件返回的 CRC 结果字节
|
* @retval 0: 校验通过; 其余值: 校验失败
|
*/
|
static uint8_t sht20_check_crc(uint8_t *data, uint8_t nbytes, uint8_t checksum)
|
{
|
uint8_t crc = 0x00; /* 初始值为 0 */
|
uint8_t byte_ctr;
|
uint8_t bit;
|
|
/* 逐个字节计算 */
|
for (byte_ctr = 0; byte_ctr < nbytes; ++byte_ctr)
|
{
|
crc ^= (data[byte_ctr]);
|
|
/* 按位进行模二除法 */
|
for (bit = 8; bit > 0; --bit)
|
{
|
if (crc & 0x80)
|
{
|
crc = (uint8_t)((crc << 1) ^ 0x31);
|
}
|
else
|
{
|
crc = (uint8_t)(crc << 1);
|
}
|
}
|
}
|
|
if (crc != checksum)
|
{
|
return 1; /* 校验不匹配 */
|
}
|
|
return 0; /* 成功 */
|
}
|
|
/**
|
* @brief 读取温湿度的底层通用核心函数 (加入完备的 CRC-8 硬件校验)
|
*/
|
i2c_status_t sht20_read_value(i2c_dev_t *dev, uint8_t cmd, float *value)
|
{
|
uint8_t raw_data[2];
|
uint8_t crc_byte;
|
uint8_t rx_buf[3];
|
uint16_t raw_result;
|
uint32_t poll_timeout = 2000;
|
i2c_status_t status;
|
|
if ( !value )
|
{
|
return I2C_ERROR;
|
}
|
|
/* 1. 发送触发测量命令 (无寄存器写模式) */
|
status = i2c_write_no_reg(dev->bus, dev->dev_addr, &cmd, 1);
|
if (status != I2C_OK)
|
{
|
return I2C_ERROR;
|
}
|
|
/* 根据测量指令进行预留的 ADC 硬件转换安全延时 */
|
if (cmd == SHT20_CMD_TRIG_T)
|
{
|
mdelay(65);
|
}
|
else
|
{
|
mdelay(25);
|
}
|
|
/* 2. 利用高级读 API 的内部寻址应答机制等待芯片转换完成 */
|
while (poll_timeout--)
|
{
|
/* SHT20 在进行 ADC 转换时,它会对读模式寻址返回 NACK。
|
* i2c_read_no_reg() 发现地址无应答,会自动发 Stop 并返回错误。
|
* 我们直接利用这一机制作为非阻塞就绪判断!
|
*/
|
status = i2c_read_no_reg(dev->bus, dev->dev_addr, rx_buf, 3);
|
if (status == I2C_OK)
|
{
|
/* 读取成功,3 字节(高、低、CRC)*/
|
break;
|
}
|
|
udelay(100);
|
}
|
|
if (poll_timeout == 0)
|
{
|
return I2C_ERROR;
|
}
|
|
/* 3. 分离出读回的 High Byte, Low Byte 以及 CRC 校验码 */
|
raw_data[0] = rx_buf[0];
|
raw_data[1] = rx_buf[1];
|
crc_byte = rx_buf[2];
|
|
/* 4. 执行 CRC-8 校验 */
|
if (sht20_check_crc(raw_data, 2, crc_byte) != 0)
|
{
|
printf("SHT20 CRC Check Failed!\r\n");
|
return I2C_ERROR;
|
}
|
|
/* 5. 清除低 2 位状态位 */
|
raw_result = (uint16_t)((raw_data[0] << 8) | raw_data[1]);
|
raw_result &= ~0x0003;
|
|
/* 6. 代入物理公式转换 */
|
if (cmd == SHT20_CMD_TRIG_T)
|
{
|
*value = -46.85f + 175.72f * ((float)raw_result / 65536.0f);
|
}
|
else
|
{
|
*value = -6.0f + 125.0f * ((float)raw_result / 65536.0f);
|
}
|
|
return I2C_OK;
|
}
|