capsules_extra/
sht3x.rs
1use core::cell::Cell;
12use enum_primitive::cast::FromPrimitive;
13use enum_primitive::enum_from_primitive;
14use kernel::hil::i2c;
15use kernel::hil::time::{self, Alarm, ConvertTicks};
16use kernel::utilities::cells::{OptionalCell, TakeCell};
17use kernel::ErrorCode;
18
19pub static BASE_ADDR: u8 = 0x44;
20
21enum_from_primitive! {
22 enum Registers {
23 MEASHIGHREPSTRETCH = 0x2C06,
25 MEASMEDREPSTRETCH = 0x2C0D,
27 MEASLOWREPSTRETCH = 0x2C10,
29 MEASHIGHREP = 0x2400,
31 MEASMEDREP = 0x240B,
33 MEASLOWREP = 0x2416,
35 READSTATUS = 0xF32D,
37 CLEARSTATUS = 0x3041,
39 SOFTRESET = 0x30A2,
41 HEATEREN = 0x306D,
43 HEATERDIS = 0x3066,
45 REGHEATERBIT = 0x0d,
47 }
48}
49
50#[derive(Clone, Copy, PartialEq)]
51enum State {
52 Idle,
53 Read,
54 ReadData,
55}
56
57fn crc8(data: &[u8]) -> u8 {
58 let polynomial = 0x31;
59 let mut crc = 0xff;
60
61 for x in 0..data.len() {
62 crc ^= data[x];
63 for _i in 0..8 {
64 if (crc & 0x80) != 0 {
65 crc = crc << 1 ^ polynomial;
66 } else {
67 crc <<= 1;
68 }
69 }
70 }
71 crc
72}
73
74pub struct SHT3x<'a, A: Alarm<'a>, I: i2c::I2CDevice> {
75 i2c: &'a I,
76 humidity_client: OptionalCell<&'a dyn kernel::hil::sensors::HumidityClient>,
77 temperature_client: OptionalCell<&'a dyn kernel::hil::sensors::TemperatureClient>,
78 state: Cell<State>,
79 buffer: TakeCell<'static, [u8]>,
80 read_temp: Cell<bool>,
81 read_hum: Cell<bool>,
82 alarm: &'a A,
83}
84
85impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> SHT3x<'a, A, I> {
86 pub fn new(i2c: &'a I, buffer: &'static mut [u8], alarm: &'a A) -> SHT3x<'a, A, I> {
87 SHT3x {
88 i2c,
89 humidity_client: OptionalCell::empty(),
90 temperature_client: OptionalCell::empty(),
91 state: Cell::new(State::Idle),
92 buffer: TakeCell::new(buffer),
93 read_temp: Cell::new(false),
94 read_hum: Cell::new(false),
95 alarm,
96 }
97 }
98
99 fn read_humidity(&self) -> Result<(), ErrorCode> {
100 if self.read_hum.get() {
101 Err(ErrorCode::BUSY)
102 } else {
103 if self.state.get() == State::Idle {
104 self.read_hum.set(true);
105 self.read_temp_hum()
106 } else {
107 self.read_hum.set(true);
108 Ok(())
109 }
110 }
111 }
112
113 fn read_temperature(&self) -> Result<(), ErrorCode> {
114 if self.read_temp.get() {
115 Err(ErrorCode::BUSY)
116 } else {
117 if self.state.get() == State::Idle {
118 self.read_temp.set(true);
119 self.read_temp_hum()
120 } else {
121 self.read_temp.set(true);
122 Ok(())
123 }
124 }
125 }
126
127 fn read_temp_hum(&self) -> Result<(), ErrorCode> {
128 self.buffer.take().map_or_else(
129 || panic!("SHT3x No buffer available!"),
130 |buffer| {
131 self.state.set(State::Read);
132 self.i2c.enable();
133
134 buffer[0] = ((Registers::MEASHIGHREP as u16) >> 8) as u8;
135 buffer[1] = ((Registers::MEASHIGHREP as u16) & 0xff) as u8;
136
137 let _ = self.i2c.write(buffer, 2);
139
140 Ok(())
141 },
142 )
143 }
144}
145
146impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> time::AlarmClient for SHT3x<'a, A, I> {
147 fn alarm(&self) {
148 let state = self.state.get();
149 match state {
150 State::Read => {
151 self.state.set(State::ReadData);
152 self.buffer.take().map_or_else(
153 || panic!("SHT3x No buffer available!"),
154 |buffer| {
155 let _res = self.i2c.read(buffer, 6);
156 },
157 );
158 }
159 _ => {
160 panic!("SHT31 Invalid alarm!");
162 }
163 }
164 }
165}
166
167impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> i2c::I2CClient for SHT3x<'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 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 self.temperature_client.map(|cb| cb.callback(Ok(stemp)));
183 } else {
184 self.temperature_client
185 .map(|cb| cb.callback(Err(ErrorCode::FAIL)));
186 }
187 }
188 if self.read_hum.get() {
189 self.read_hum.set(false);
190 if crc8(&buffer[3..5]) == buffer[5] {
191 let mut shum = buffer[3] as u32;
192 shum <<= 8;
193 shum |= buffer[4] as u32;
194 shum = (625 * shum) >> 12;
195 self.humidity_client.map(|cb| cb.callback(shum as usize));
196 } else {
197 self.humidity_client.map(|cb| cb.callback(usize::MAX));
198 }
199 }
200 self.buffer.replace(buffer);
201 self.state.set(State::Idle);
202 }
203 State::Read => {
204 self.buffer.replace(buffer);
205 let interval = self.alarm.ticks_from_ms(20);
206 self.alarm.set_alarm(self.alarm.now(), interval);
207 }
208 _ => {}
209 }
210 }
211 Err(i2c_err) => {
212 self.buffer.replace(buffer);
213 self.i2c.disable();
214 if self.read_temp.get() {
215 self.read_temp.set(false);
216 self.temperature_client
217 .map(|cb| cb.callback(Err(i2c_err.into())));
218 }
219 if self.read_hum.get() {
220 self.read_hum.set(false);
221 self.humidity_client.map(|cb| cb.callback(usize::MAX));
222 }
223 }
224 }
225 }
226}
227
228impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::HumidityDriver<'a>
229 for SHT3x<'a, A, I>
230{
231 fn set_client(&self, client: &'a dyn kernel::hil::sensors::HumidityClient) {
232 self.humidity_client.set(client);
233 }
234
235 fn read_humidity(&self) -> Result<(), ErrorCode> {
236 self.read_humidity()
237 }
238}
239
240impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::TemperatureDriver<'a>
241 for SHT3x<'a, A, I>
242{
243 fn set_client(&self, client: &'a dyn kernel::hil::sensors::TemperatureClient) {
244 self.temperature_client.set(client);
245 }
246
247 fn read_temperature(&self) -> Result<(), ErrorCode> {
248 self.read_temperature()
249 }
250}