guowenxue
4 days ago 53de825c4c4d8325c02341177f62ab84897eb5ee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/**********************************************************************
 *   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;
}