1use core::cell::Cell;
72use kernel::hil::i2c::{self, I2CClient, I2CDevice};
73use kernel::hil::sensors::{HumidityClient, HumidityDriver, TemperatureClient, TemperatureDriver};
74use kernel::utilities::cells::{OptionalCell, TakeCell};
75use kernel::ErrorCode;
76
77const REG_AUTO_INCREMENT: u8 = 1 << 7;
78const CTRL_REG1: u8 = 0x20;
79const STATUS_REG: u8 = 0x27;
80const HUMID0_REG: u8 = 0x28;
81const CALIB_REG_1ST: u8 = 0x30;
82
83#[derive(Copy, Clone, Debug)]
84struct CalibrationData {
85 temp_slope: f32,
86 temp_intercept: f32,
87 humidity_slope: f32,
88 humidity_intercept: f32,
89}
90
91pub struct Hts221<'a, I: I2CDevice> {
92 buffer: TakeCell<'static, [u8]>,
93 i2c: &'a I,
94 temperature_client: OptionalCell<&'a dyn TemperatureClient>,
95 humidity_client: OptionalCell<&'a dyn HumidityClient>,
96 state: Cell<State>,
97 pending_temperature: Cell<bool>,
98 pending_humidity: Cell<bool>,
99}
100
101impl<'a, I: I2CDevice> Hts221<'a, I> {
102 pub fn new(i2c: &'a I, buffer: &'static mut [u8]) -> Self {
103 Hts221 {
104 buffer: TakeCell::new(buffer),
105 i2c,
106 temperature_client: OptionalCell::empty(),
107 humidity_client: OptionalCell::empty(),
108 state: Cell::new(State::Reset),
109 pending_temperature: Cell::new(false),
110 pending_humidity: Cell::new(false),
111 }
112 }
113
114 fn start_reading(&self) -> Result<(), ErrorCode> {
121 self.buffer
122 .take()
123 .map(|buffer| {
124 self.i2c.enable();
125 match self.state.get() {
126 State::Reset => {
127 buffer[0] = REG_AUTO_INCREMENT | CALIB_REG_1ST;
128
129 if let Err((_error, buffer)) = self.i2c.write_read(buffer, 1, 16) {
130 self.buffer.replace(buffer);
131 self.i2c.disable();
132 } else {
133 self.state.set(State::Calibrating);
134 }
135 }
136 State::Idle(calibration_data, _, _) => {
137 buffer[0] = REG_AUTO_INCREMENT | CTRL_REG1;
138 buffer[1] = 1 << 2 | 1 << 7; buffer[2] = 1; if let Err((_error, buffer)) = self.i2c.write(buffer, 3) {
142 self.buffer.replace(buffer);
143 self.i2c.disable();
144 } else {
145 self.state.set(State::InitiateReading(calibration_data));
146 }
147 }
148 _ => {} }
150 })
151 .ok_or(ErrorCode::BUSY)
152 }
153}
154
155impl<'a, I: I2CDevice> TemperatureDriver<'a> for Hts221<'a, I> {
156 fn set_client(&self, client: &'a dyn TemperatureClient) {
157 self.temperature_client.set(client);
158 }
159
160 fn read_temperature(&self) -> Result<(), ErrorCode> {
161 self.pending_temperature.set(true);
162 if !self.pending_humidity.get() {
163 self.start_reading()
164 } else {
165 Ok(())
166 }
167 }
168}
169
170impl<'a, I: I2CDevice> HumidityDriver<'a> for Hts221<'a, I> {
171 fn set_client(&self, client: &'a dyn HumidityClient) {
172 self.humidity_client.set(client);
173 }
174
175 fn read_humidity(&self) -> Result<(), ErrorCode> {
176 self.pending_humidity.set(true);
177 if !self.pending_temperature.get() {
178 self.start_reading()
179 } else {
180 Ok(())
181 }
182 }
183}
184
185#[derive(Clone, Copy, Debug)]
186enum State {
187 Reset,
188 Calibrating,
189 InitiateReading(CalibrationData),
190 CheckStatus(CalibrationData),
191 Read(CalibrationData),
192 Idle(CalibrationData, i32, usize),
193}
194
195impl<I: I2CDevice> I2CClient for Hts221<'_, I> {
196 fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
197 if let Err(i2c_err) = status {
198 self.state.set(State::Idle(
199 CalibrationData {
200 temp_slope: 0.0,
201 temp_intercept: 0.0,
202 humidity_slope: 0.0,
203 humidity_intercept: 0.0,
204 },
205 0,
206 0,
207 ));
208 self.buffer.replace(buffer);
209 self.temperature_client
210 .map(|client| client.callback(Err(i2c_err.into())));
211 self.humidity_client.map(|client| client.callback(0));
212 return;
213 }
214
215 match self.state.get() {
216 State::Calibrating => {
217 let h0rh = buffer[0] as f32;
218 let h1rh = buffer[1] as f32;
219 let h0t0out = ((buffer[6] as i16) | ((buffer[7] as i16) << 8)) as f32;
220 let h1t0out = ((buffer[10] as i16) | ((buffer[11] as i16) << 8)) as f32;
221
222 let humidity_slope = (h1rh - h0rh) / (2.0 * (h1t0out - h0t0out));
223 let humidity_intercept = (h0rh / 2.0) - humidity_slope * h0t0out;
224
225 let t0deg_c = ((buffer[2] as i16) | (((buffer[5] & 0b11) as i16) << 8)) as f32;
226 let t1deg_c = ((buffer[3] as i16) | (((buffer[5] & 0b1100) as i16) << 6)) as f32;
227
228 let t0out = ((buffer[12] as i16) | ((buffer[13] as i16) << 8)) as f32;
229 let t1out = ((buffer[14] as i16) | ((buffer[15] as i16) << 8)) as f32;
230
231 let temp_slope = (t1deg_c - t0deg_c) / (8.0 * (t1out - t0out));
232 let temp_intercept = (t0deg_c / 8.0) - temp_slope * t0out;
233
234 buffer[0] = REG_AUTO_INCREMENT | CTRL_REG1;
235 buffer[1] = 1 << 2 | 1 << 7; buffer[2] = 1; if let Err((error, buffer)) = self.i2c.write(buffer, 3) {
239 self.state.set(State::Idle(
240 CalibrationData {
241 temp_slope: 0.0,
242 temp_intercept: 0.0,
243 humidity_slope: 0.0,
244 humidity_intercept: 0.0,
245 },
246 0,
247 0,
248 ));
249 self.buffer.replace(buffer);
250 self.temperature_client
251 .map(|client| client.callback(Err(error.into())));
252 self.humidity_client.map(|client| client.callback(0));
253 } else {
254 self.state.set(State::InitiateReading(CalibrationData {
255 temp_slope,
256 temp_intercept,
257 humidity_slope,
258 humidity_intercept,
259 }));
260 }
261 }
262 State::InitiateReading(calibration_data) => {
263 buffer[0] = STATUS_REG;
264
265 if let Err((error, buffer)) = self.i2c.write_read(buffer, 1, 1) {
266 self.state.set(State::Idle(
267 CalibrationData {
268 temp_slope: 0.0,
269 temp_intercept: 0.0,
270 humidity_slope: 0.0,
271 humidity_intercept: 0.0,
272 },
273 0,
274 0,
275 ));
276 self.buffer.replace(buffer);
277 self.temperature_client
278 .map(|client| client.callback(Err(error.into())));
279 self.humidity_client.map(|client| client.callback(0));
280 } else {
281 self.state.set(State::CheckStatus(calibration_data));
282 }
283 }
284 State::CheckStatus(calibration_data) => {
285 if buffer[0] & 0b11 == 0b11 {
286 buffer[0] = REG_AUTO_INCREMENT | HUMID0_REG;
287
288 if let Err((error, buffer)) = self.i2c.write_read(buffer, 1, 4) {
289 self.state.set(State::Idle(
290 CalibrationData {
291 temp_slope: 0.0,
292 temp_intercept: 0.0,
293 humidity_slope: 0.0,
294 humidity_intercept: 0.0,
295 },
296 0,
297 0,
298 ));
299 self.buffer.replace(buffer);
300 self.temperature_client
301 .map(|client| client.callback(Err(error.into())));
302 self.humidity_client.map(|client| client.callback(0));
303 } else {
304 self.state.set(State::Read(calibration_data));
305 }
306 } else {
307 buffer[0] = STATUS_REG;
308
309 if let Err((error, buffer)) = self.i2c.write_read(buffer, 1, 1) {
310 self.state.set(State::Idle(
311 CalibrationData {
312 temp_slope: 0.0,
313 temp_intercept: 0.0,
314 humidity_slope: 0.0,
315 humidity_intercept: 0.0,
316 },
317 0,
318 0,
319 ));
320 self.buffer.replace(buffer);
321 self.temperature_client
322 .map(|client| client.callback(Err(error.into())));
323 self.humidity_client.map(|client| client.callback(0));
324 }
325 }
326 }
327 State::Read(calibration_data) => {
328 let humidity_raw = ((buffer[0] as i16) | ((buffer[1] as i16) << 8)) as f32;
329 let humidity = ((humidity_raw * calibration_data.humidity_slope
330 + calibration_data.humidity_intercept)
331 * 100.0) as usize;
332
333 let temperature_raw = ((buffer[2] as i16) | ((buffer[3] as i16) << 8)) as f32;
334 let temperature = ((temperature_raw * calibration_data.temp_slope
335 + calibration_data.temp_intercept)
336 * 100.0) as i32;
337 buffer[0] = CTRL_REG1;
338 buffer[1] = 1 << 7; if let Err((error, buffer)) = self.i2c.write(buffer, 2) {
346 self.state.set(State::Idle(
347 CalibrationData {
348 temp_slope: 0.0,
349 temp_intercept: 0.0,
350 humidity_slope: 0.0,
351 humidity_intercept: 0.0,
352 },
353 0,
354 0,
355 ));
356 self.buffer.replace(buffer);
357 self.temperature_client
358 .map(|client| client.callback(Err(error.into())));
359 self.humidity_client.map(|client| client.callback(0));
360 } else {
361 self.state
362 .set(State::Idle(calibration_data, temperature, humidity));
363 }
364 }
365 State::Idle(_, temperature, humidity) => {
366 self.buffer.replace(buffer);
367 self.i2c.disable();
368 if self.pending_temperature.get() {
369 self.pending_temperature.set(false);
370 self.temperature_client
371 .map(|client| client.callback(Ok(temperature)));
372 }
373 if self.pending_humidity.get() {
374 self.pending_humidity.set(false);
375 self.humidity_client.map(|client| client.callback(humidity));
376 }
377 }
378 State::Reset => {} }
380 }
381}