1 /*
2 * Copyright (C) 2022 HiHope Open Source Organization .
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http:// www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 *
14 * limitations under the License.
15 */
16
17
18 #include "aht20.h"
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "wifiiot_i2c.h"
25 #include "wifiiot_errno.h"
26
27 #define AHT20_I2C_IDX WIFI_IOT_I2C_IDX_0
28
29 #define AHT20_STARTUP_TIME (20*1000) // 上电启动时间
30 #define AHT20_CALIBRATION_TIME (40*1000) // 初始化(校准)时间
31 #define AHT20_MEASURE_TIME (75*1000) // 测量时间
32
33 #define AHT20_DEVICE_ADDR 0x38
34 #define AHT20_READ_ADDR ((0x38<<1)|0x1)
35 #define AHT20_WRITE_ADDR ((0x38<<1)|0x0)
36
37 #define AHT20_CMD_CALIBRATION 0xBE // 初始化(校准)命令
38 #define AHT20_CMD_CALIBRATION_ARG0 0x08
39 #define AHT20_CMD_CALIBRATION_ARG1 0x00
40
41 /**
42 * 传感器在采集时需要时间,主机发出测量指令(0xAC)后,延时75毫秒以上再读取转换后的数据并判断返回的状态位是否正常。
43 * 若状态比特位[Bit7]为0代表数据可正常读取,为1时传感器为忙状态,主机需要等待数据处理完成。
44 **/
45 #define AHT20_CMD_TRIGGER 0xAC // 触发测量命令
46 #define AHT20_CMD_TRIGGER_ARG0 0x33
47 #define AHT20_CMD_TRIGGER_ARG1 0x00
48
49 // 用于在无需关闭和再次打开电源的情况下,重新启动传感器系统,软复位所需时间不超过20 毫秒
50 #define AHT20_CMD_RESET 0xBA // 软复位命令
51
52 #define AHT20_CMD_STATUS 0x71 // 获取状态命令
53
54 /**
55 * STATUS 命令回复:
56 * 1. 初始化后触发测量之前,STATUS 只回复 1B 状态值;
57 * 2. 触发测量之后,STATUS 回复6B: 1B 状态值 + 2B 湿度 + 4b湿度 + 4b温度 + 2B 温度
58 * RH = Srh / 2^20 * 100%
59 * T = St / 2^20 * 200 - 50
60 **/
61 #define AHT20_STATUS_BUSY_SHIFT 7 // bit[7] Busy indication
62 #define AHT20_STATUS_BUSY_MASK (0x1<<AHT20_STATUS_BUSY_SHIFT)
63
aht20_status_busy(uint8_t status)64 uint8_t aht20_status_busy(uint8_t status)
65 {
66 return ((status & AHT20_STATUS_BUSY_MASK) >> (AHT20_STATUS_BUSY_SHIFT));
67 }
68
69 #define AHT20_STATUS_MODE_SHIFT 5 // bit[6:5] Mode Status
70 #define AHT20_STATUS_MODE_MASK (0x3<<AHT20_STATUS_MODE_SHIFT)
71
aht20_status_mode(uint8_t status)72 uint8_t aht20_status_mode(uint8_t status)
73 {
74 return ((status & AHT20_STATUS_MODE_MASK) >> (AHT20_STATUS_MODE_SHIFT))
75 }
76 // bit[4] Reserved
77 #define AHT20_STATUS_CALI_SHIFT 3 // bit[3] CAL Enable
78 #define AHT20_STATUS_CALI_MASK (0x1<<AHT20_STATUS_CALI_SHIFT)
79
80 uint8_t aht20_status_cali(uint8_t status)
81 {
82 return ((status & AHT20_STATUS_CALI_MASK) >> (AHT20_STATUS_CALI_SHIFT))
83 }
84 // bit[2:0] Reserved
85
86 #define AHT20_STATUS_RESPONSE_MAX 6
87
88 #define AHT20_RESLUTION (1<<20) // 2^20
89
90 #define AHT20_MAX_RETRY 10
91 #define TWO 2
92 #define THREE 3
93 #define FOUR 4
94 #define FIVE 5
95 #define EIGHT 8
96 #define FIFTY 50
97 #define ONE_HUNDRED 100
98 #define TWO_HUNDRED 200
99
100 static uint32_t AHT20_Read(uint8_t* buffer, uint32_t buffLen)
101 {
102 WifiIotI2cData data = { 0 };
103 data.receiveBuf = buffer;
104 data.receiveLen = buffLen;
105 uint32_t retval = I2cRead(AHT20_I2C_IDX, AHT20_READ_ADDR, &data);
106 if (retval != WIFI_IOT_SUCCESS) {
107 printf("I2cRead() failed, %0X!\n", retval);
108 return retval;
109 }
110 return WIFI_IOT_SUCCESS;
111 }
112
113 static uint32_t AHT20_Write(uint8_t* buffer, uint32_t buffLen)
114 {
115 WifiIotI2cData data = { 0 };
116 data.sendBuf = buffer;
117 data.sendLen = buffLen;
118 uint32_t retval = I2cWrite(AHT20_I2C_IDX, AHT20_WRITE_ADDR, &data);
119 if (retval != WIFI_IOT_SUCCESS) {
120 printf("I2cWrite(%02X) failed, %0X!\n", buffer[0], retval);
121 return retval;
122 }
123 return WIFI_IOT_SUCCESS;
124 }
125
126 // 发送获取状态命令
127 static uint32_t AHT20_StatusCommand(void)
128 {
129 uint8_t statusCmd[] = { AHT20_CMD_STATUS };
130 return AHT20_Write(statusCmd, sizeof(statusCmd));
131 }
132
133 // 发送软复位命令
134 static uint32_t AHT20_ResetCommand(void)
135 {
136 uint8_t resetCmd[] = {AHT20_CMD_RESET};
137 return AHT20_Write(resetCmd, sizeof(resetCmd));
138 }
139
140 // 发送初始化校准命令
141 static uint32_t AHT20_CalibrateCommand(void)
142 {
143 uint8_t clibrateCmd[] = {AHT20_CMD_CALIBRATION, AHT20_CMD_CALIBRATION_ARG0, AHT20_CMD_CALIBRATION_ARG1};
144 return AHT20_Write(clibrateCmd, sizeof(clibrateCmd));
145 }
146
147 // 读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通过发送0x71可以获取一个字节的状态字),
148 // 如果不为1,要发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
149 uint32_t AHT20_Calibrate(void)
150 {
151 uint32_t retval = 0;
152 uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = { AHT20_CMD_STATUS };
153 memset_s(&buffer, sizeof(buffer), 0x0, sizeof(buffer));
154
155 retval = AHT20_StatusCommand();
156 if (retval != WIFI_IOT_SUCCESS) {
157 return retval;
158 }
159
160 retval = AHT20_Read(buffer, sizeof(buffer));
161 if (retval != WIFI_IOT_SUCCESS) {
162 return retval;
163 }
164
165 if (AHT20_STATUS_BUSY(buffer[0]) || !AHT20_STATUS_CALI(buffer[0])) {
166 retval = AHT20_ResetCommand();
167 if (retval != WIFI_IOT_SUCCESS) {
168 return retval;
169 }
170 usleep(AHT20_STARTUP_TIME);
171 retval = AHT20_CalibrateCommand();
172 usleep(AHT20_CALIBRATION_TIME);
173 return retval;
174 }
175
176 return WIFI_IOT_SUCCESS;
177 }
178
179 // 发送 触发测量 命令,开始测量
180 uint32_t AHT20_StartMeasure(void)
181 {
182 uint8_t triggerCmd[] = {AHT20_CMD_TRIGGER, AHT20_CMD_TRIGGER_ARG0, AHT20_CMD_TRIGGER_ARG1};
183 return AHT20_Write(triggerCmd, sizeof(triggerCmd));
184 }
185
186 // 接收测量结果,拼接转换为标准值
187 uint32_t AHT20_GetMeasureResult(float* temp, float* humi)
188 {
189 uint32_t retval = 0, i = 0;
190 if (temp == NULL || humi == NULL) {
191 return WIFI_IOT_FAILURE;
192 }
193
194 uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = { 0 };
195 memset_s(&buffer, sizeof(buffer), 0x0, sizeof(buffer));
196 retval = AHT20_Read(buffer, sizeof(buffer)); // recv status command result
197 if (retval != WIFI_IOT_SUCCESS) {
198 return retval;
199 }
200
201 for (i = 0; AHT20_STATUS_BUSY(buffer[0]) && i < AHT20_MAX_RETRY; i++) {
202 usleep(AHT20_MEASURE_TIME);
203 retval = AHT20_Read(buffer, sizeof(buffer)); // recv status command result
204 if (retval != WIFI_IOT_SUCCESS) {
205 return retval;
206 }
207 }
208 if (i >= AHT20_MAX_RETRY) {
209 printf("AHT20 device always busy!\r\n");
210 return WIFI_IOT_FAILURE;
211 }
212
213 uint32_t humiRaw = buffer[1];
214 humiRaw = (humiRaw << EIGHT) | buffer[TWO];
215 humiRaw = (humiRaw << FOUR) | ((buffer[THREE] & 0xF0) >> FOUR);
216 *humi = humiRaw / (float)AHT20_RESLUTION * ONE_HUNDRED;
217
218 uint32_t tempRaw = buffer[3] & 0x0F;
219 tempRaw = (tempRaw << EIGHT) | buffer[FOUR];
220 tempRaw = (tempRaw << EIGHT) | buffer[FIVE];
221 *temp = tempRaw / (float)AHT20_RESLUTION * TWO_HUNDRED - FIFTY;
222 return WIFI_IOT_SUCCESS;
223 }
224