1use core::cell::Cell;
8use enum_primitive::cast::FromPrimitive;
9use enum_primitive::enum_from_primitive;
10use kernel::hil::i2c;
11use kernel::hil::time::{self, Alarm, ConvertTicks};
12use kernel::utilities::cells::{OptionalCell, TakeCell};
13use kernel::ErrorCode;
14
15pub static BASE_ADDR: u8 = 0x44;
16
17enum_from_primitive! {
18 enum Registers {
19 MEASHIGHREP = 0xFD,
22 MEASMEDREP = 0xF6,
24 MEASLOWREP = 0xE0,
26 READSERIALNUM = 0x89,
28 SOFTRESET = 0x94,
30 HEATER200MW1S = 0x39,
32 HEATER200MW01S = 0x32,
34 HEATER110MW1S = 0x2F,
36 HEATER110MW01S = 0x24,
38 HEATER20MW1S = 0x1E,
40 HEATER20MW01S = 0x15,
42 }
43}
44
45#[derive(Clone, Copy, PartialEq)]
46enum State {
47 Idle,
48 Read,
49 ReadData,
50}
51
52fn crc8(data: &[u8]) -> u8 {
53 let polynomial = 0x31;
54 let mut crc = 0xff;
55
56 for x in 0..data.len() {
57 crc ^= data[x];
58 for _i in 0..8 {
59 if (crc & 0x80) != 0 {
60 crc = crc << 1 ^ polynomial;
61 } else {
62 crc <<= 1;
63 }
64 }
65 }
66 crc
67}
68
69pub struct SHT4x<'a, A: Alarm<'a>, I: i2c::I2CDevice> {
70 i2c: &'a I,
71 humidity_client: OptionalCell<&'a dyn kernel::hil::sensors::HumidityClient>,
72 temperature_client: OptionalCell<&'a dyn kernel::hil::sensors::TemperatureClient>,
73 state: Cell<State>,
74 buffer: TakeCell<'static, [u8]>,
75 read_temp: Cell<bool>,
76 read_hum: Cell<bool>,
77 alarm: &'a A,
78}
79
80impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> SHT4x<'a, A, I> {
81 pub fn new(i2c: &'a I, buffer: &'static mut [u8], alarm: &'a A) -> SHT4x<'a, A, I> {
82 SHT4x {
83 i2c,
84 humidity_client: OptionalCell::empty(),
85 temperature_client: OptionalCell::empty(),
86 state: Cell::new(State::Idle),
87 buffer: TakeCell::new(buffer),
88 read_temp: Cell::new(false),
89 read_hum: Cell::new(false),
90 alarm,
91 }
92 }
93
94 fn read_humidity(&self) -> Result<(), ErrorCode> {
95 if self.read_hum.get() {
96 Err(ErrorCode::BUSY)
97 } else {
98 if self.state.get() == State::Idle {
99 let result = self.read_temp_hum();
100 if result.is_ok() {
101 self.read_hum.set(true);
102 }
103 result
104 } else {
105 self.read_hum.set(true);
106 Ok(())
107 }
108 }
109 }
110
111 fn read_temperature(&self) -> Result<(), ErrorCode> {
112 if self.read_temp.get() {
113 Err(ErrorCode::BUSY)
114 } else {
115 if self.state.get() == State::Idle {
116 let result = self.read_temp_hum();
117 if result.is_ok() {
118 self.read_temp.set(true);
119 }
120 result
121 } else {
122 self.read_temp.set(true);
123 Ok(())
124 }
125 }
126 }
127
128 fn read_temp_hum(&self) -> Result<(), ErrorCode> {
129 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
130 self.state.set(State::Read);
131 self.i2c.enable();
132
133 buffer[0] = Registers::MEASHIGHREP as u8;
134
135 let _res = self.i2c.write(buffer, 1);
136 match _res {
137 Ok(()) => Ok(()),
138 Err((error, data)) => {
139 self.buffer.replace(data);
140 self.state.set(State::Idle);
141 self.i2c.disable();
142 Err(error.into())
143 }
144 }
145 })
146 }
147}
148
149impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> time::AlarmClient for SHT4x<'a, A, I> {
150 fn alarm(&self) {
151 let state = self.state.get();
152 match state {
153 State::Read => {
154 self.state.set(State::ReadData);
155 self.buffer.take().map(|buffer| {
156 let _res = self.i2c.read(buffer, 6);
157 });
158 }
159 _ => {
160 panic!("SHT4x Invalid alarm!");
162 }
163 }
164 }
165}
166
167impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> i2c::I2CClient for SHT4x<'a, A, I> {
168 fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
169 match status {
170 Ok(()) => {
171 let state = self.state.get();
172
173 match state {
174 State::ReadData => {
175 let read_temp_res = if self.read_temp.get() {
176 self.read_temp.set(false);
177 if crc8(&buffer[0..2]) == buffer[2] {
178 let mut stemp = buffer[0] as u32;
179 stemp <<= 8;
180 stemp |= buffer[1] as u32;
181 let stemp = ((4375 * stemp) >> 14) as i32 - 4500;
182 Some(Ok(stemp))
183 } else {
184 Some(Err(ErrorCode::FAIL))
185 }
186 } else {
187 None
188 };
189
190 let read_hum_res = if self.read_hum.get() {
191 self.read_hum.set(false);
192 if crc8(&buffer[3..5]) == buffer[5] {
193 let mut shum = buffer[3] as u32;
194 shum <<= 8;
195 shum |= buffer[4] as u32;
196 shum = (625 * shum) >> 12;
197 Some(shum as usize)
198 } else {
199 Some(usize::MAX)
200 }
201 } else {
202 None
203 };
204
205 self.buffer.replace(buffer);
206 self.state.set(State::Idle);
207
208 read_temp_res.map(|res| {
209 self.temperature_client.map(|cb| cb.callback(res));
210 });
211
212 read_hum_res.map(|res| {
213 self.humidity_client.map(|cb| cb.callback(res));
214 });
215 }
216 State::Read => {
217 self.buffer.replace(buffer);
218 let interval = self.alarm.ticks_from_ms(20);
219 self.alarm.set_alarm(self.alarm.now(), interval);
220 }
221 _ => {}
222 }
223 }
224 Err(i2c_err) => {
225 self.buffer.replace(buffer);
226 self.i2c.disable();
227 self.state.set(State::Idle);
228 if self.read_temp.get() {
229 self.read_temp.set(false);
230 self.temperature_client
231 .map(|cb| cb.callback(Err(i2c_err.into())));
232 }
233 if self.read_hum.get() {
234 self.read_hum.set(false);
235 self.humidity_client.map(|cb| cb.callback(usize::MAX));
236 }
237 }
238 }
239 }
240}
241
242impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::HumidityDriver<'a>
243 for SHT4x<'a, A, I>
244{
245 fn set_client(&self, client: &'a dyn kernel::hil::sensors::HumidityClient) {
246 self.humidity_client.set(client);
247 }
248
249 fn read_humidity(&self) -> Result<(), ErrorCode> {
250 self.read_humidity()
251 }
252}
253
254impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::TemperatureDriver<'a>
255 for SHT4x<'a, A, I>
256{
257 fn set_client(&self, client: &'a dyn kernel::hil::sensors::TemperatureClient) {
258 self.temperature_client.set(client);
259 }
260
261 fn read_temperature(&self) -> Result<(), ErrorCode> {
262 self.read_temperature()
263 }
264}