Skip to main content

afe4400/
lib.rs

1//! # AFE4400 - an `embedded-hal` compatible driver
2//!
3//! The [AFE4400][afe4400] is a pulse oximetry analog front-end chip by TI. It controls LED switching, has
4//! ambient light cancellation, and other very nice features for pulse oximetry applications.
5//!
6//! The main communication interface for the AFE4400 is via SPI. However, there are also some
7//! digital pins that can be configured for smoother operation; two are required and four are for
8//! additional diagnostic value.
9//!
10//! ## Mandatory
11//! - `adc_pdn`: The powerdown pin; should be held high (not floating!) for the duration of operation.
12//! - `adc_rdy`: The "ADC Ready" pin, indicating a sample is ready for reading.
13//! It is useful to use `adc_rdy` as the driver for an edge-triggered interrupt.
14//!
15//! ## Optional
16//! - `daig_end`: Diagnostics complete
17//! - `adc_done`: ADC conversion complete
18//! - `led_err`: Connection issue with the LEDs
19//! - `sensor_err`: Connection issue with the photodiodes
20//!
21//! [afe4400]: http://www.ti.com/product/afe4400
22#![no_std]
23#![allow(dead_code)]
24
25extern crate embedded_hal as hal;
26#[macro_use]
27extern crate nb;
28
29use core::fmt::Debug;
30
31use hal::spi::FullDuplex;
32use hal::digital::v2::{InputPin, OutputPin};
33
34
35/* Useful commands */
36const SW_RST: u8 = 0x04;
37
38/// Registers
39///
40/// See p.49 of docs for full register map.
41mod registers {
42    pub const CONTROL0: u8 = 0x00;
43    pub const LED2STC: u8 = 0x01;
44    pub const LED2ENDC: u8 = 0x02;
45    pub const LED2LEDSTC: u8 = 0x03;
46    pub const LED2LEDENDC: u8 = 0x04;
47    pub const ALED2STC: u8 = 0x05;
48    pub const ALED2ENDC: u8 = 0x06;
49
50    pub const LED1STC: u8 = 0x07;
51    pub const LED1ENDC: u8 = 0x08;
52    pub const LED1LEDSTC: u8 = 0x09;
53    pub const LED1LEDENDC: u8 = 0x0A;
54    pub const ALED1STC: u8 = 0x0B;
55    pub const ALED1ENDC: u8 = 0x0C;
56
57    pub const LED2CONVST: u8 = 0x0D;
58    pub const LED2CONVEND: u8 = 0x0E;
59    pub const ALED2CONVST: u8 = 0x0F;
60    pub const ALED2CONVEND: u8 = 0x10;
61
62    pub const LED1CONVST: u8 = 0x11;
63    pub const LED1CONVEND: u8 = 0x12;
64    pub const ALED1CONVST: u8 = 0x13;
65    pub const ALED1CONVEND: u8 = 0x14;
66
67    pub const ADCRSTSTCT0: u8 = 0x15;
68    pub const ADCRSTENDCT0: u8 = 0x16;
69    pub const ADCRSTSTCT1: u8 = 0x17;
70    pub const ADCRSTENDCT1: u8 = 0x18;
71    pub const ADCRSTSTCT2: u8 = 0x19;
72    pub const ADCRSTENDCT2: u8 = 0x1A;
73    pub const ADCRSTSTCT3: u8 = 0x1B;
74    pub const ADCRSTENDCT3: u8 = 0x1C;
75
76    pub const PRPCOUNT: u8 = 0x1D;
77
78    pub const CONTROL1: u8 = 0x1E;
79    pub const TIAGAIN: u8 = 0x20;
80    pub const TIA_AMB_GAIN: u8 = 0x21;
81    pub const LEDCNTRL: u8 = 0x22;
82    pub const CONTROL2: u8 = 0x23;
83
84    pub const ALARM: u8 = 0x29;
85    pub const LED2VAL: u8 = 0x2A;
86    pub const ALED2VAL: u8 = 0x2B;
87    pub const LED1VAL: u8 = 0x2C;
88    pub const ALED1VAL: u8 = 0x2D;
89    pub const LED2_ALED2VAL: u8 = 0x2E;
90    pub const LED1_ALED1VAL: u8 = 0x2F;
91
92    pub const DIAG: u8 = 0x30;
93}
94
95/// Controller in charge of communication with AFE4400
96pub struct Afe4400<SPI: FullDuplex<u8>, IN: InputPin, OUT: OutputPin> {
97    pub spi: SPI,
98    pub diag_end: Option<IN>,
99    pub adc_done: Option<IN>,
100    pub led_err: Option<IN>,
101    pub sensor_err: Option<IN>,
102    pub afe_pdn: OUT,
103    pub adc_rdy: IN
104}
105
106pub enum LEDSource {
107    LED,
108    AMB,
109    LEDAMB,
110}
111
112/// Possible AFE4400 errors.
113///
114/// Currently, this is simply failing the self-check or passing forward a communication error.
115#[derive(Debug)]
116pub enum Error<E: Debug> {
117    SelfCheckFail,
118    InvalidSetting,
119    Other(E),
120}
121
122impl<E: Debug> From<E> for Error<E> {
123    fn from(error: E) -> Self {
124        Error::Other(error)
125    }
126}
127
128type SpiError<SPI> = <SPI as FullDuplex<u8>>::Error;
129type AfeError<SPI> = Error<SpiError<SPI>>;
130
131impl<SPI, IN, OUT> Afe4400<SPI, IN, OUT>
132    where SPI: FullDuplex<u8>,
133          IN: InputPin,
134          OUT: OutputPin,
135          <SPI as FullDuplex<u8>>::Error: Debug
136{
137
138    /// Send data to a specified register.
139    ///
140    /// `register` should be a constant defined in `afe4400::registers`.
141    ///
142    /// Can return an SPI error if communication failure occurs.
143    pub fn write_data(&mut self, register: u8, data: u32) -> Result<(), AfeError<SPI>> {
144        block!(self.spi.send(register))?;
145        let first_transfer = ((data >> 16) & 0xFF) as u8;
146        let second_transfer = ((data >> 8) & 0xFF) as u8;
147        let third_transfer = (data & 0xFF) as u8;
148        block!(self.spi.send(first_transfer))?;
149        block!(self.spi.send(second_transfer))?;
150        block!(self.spi.send(third_transfer))?;
151        Ok(())
152    }
153
154    /// Read data from a specified register
155    ///
156    /// `register` should be a constant defined in `afe4400::registers`.
157    ///
158    /// Can return an SPI error if communication failure occurs.
159    pub fn read_data(&mut self, register: u8) -> Result<u32, AfeError<SPI>> {
160        self.write_data(registers::CONTROL0, 0x01 as u32)?;
161        block!(self.spi.send(register))?;
162        let mut register_data: u32 = 0;
163        for _ in 0..3 {
164            register_data = register_data << 8;
165            register_data |= block!(self.spi.read())? as u32;
166        }
167        self.write_data(registers::CONTROL0, 0x00 as u32)?;
168        Ok(register_data)
169    }
170
171    #[allow(overflowing_literals)]
172    fn get_led_data(&mut self, led_register: u8) -> Result<i32, AfeError<SPI>> {
173        let mut value = self.read_data(led_register)? as i32;
174        if value & 0x00200000 != 0 {
175            value |= 0xFFC00000;
176        }
177        Ok(value)
178    }
179
180    /// Get data from LED1 (IR)
181    pub fn get_led1_data(&mut self, source: LEDSource) -> Result<i32, AfeError<SPI>> {
182        match source {
183            LEDSource::LED => self.get_led_data(registers::LED1VAL),
184            LEDSource::AMB => self.get_led_data(registers::ALED1VAL),
185            LEDSource::LEDAMB => self.get_led_data(registers::LED1_ALED1VAL),
186        }
187    }
188
189    /// Get data from LED1 (IR)
190    pub fn get_led2_data(&mut self, source: LEDSource) -> Result<i32, AfeError<SPI>> {
191        match source {
192            LEDSource::LED => self.get_led_data(registers::LED2VAL),
193            LEDSource::AMB => self.get_led_data(registers::ALED2VAL),
194            LEDSource::LEDAMB => self.get_led_data(registers::LED2_ALED2VAL),
195        }
196    }
197
198    pub fn set_cancellation_current(&mut self, current: u8) -> Result<(), AfeError<SPI>> {
199        if current > 0b1010 {
200            Err(Error::InvalidSetting)
201        } else {
202            let mask = 0b1111 << 16;
203            let mut tia_settings = self.read_data(registers::TIA_AMB_GAIN)?;
204            tia_settings &= !mask;
205            tia_settings |= (current as u32) << 16;
206            self.write_data(registers::TIA_AMB_GAIN, tia_settings)?;
207            Ok(())
208        }
209    }
210
211    pub fn set_second_stage_gain(&mut self, gain: u8, enabled: bool) -> Result<(), AfeError<SPI>> {
212        if gain > 0b0100 {
213            Err(Error::InvalidSetting)
214        } else {
215            let mask = (0b01 << 14) & (0b0111 << 8);
216            let mut tia_settings = self.read_data(registers::TIA_AMB_GAIN)?;
217            tia_settings &= !mask;
218            tia_settings |= (gain as u32) << 8;
219            tia_settings |= if enabled { 0b01 << 14 } else { 0b00 << 14 };
220            self.write_data(registers::TIA_AMB_GAIN, tia_settings)?;
221            Ok(())
222        }
223    }
224
225    pub fn set_first_stage_cap(&mut self, cap_value: u8) -> Result<(), AfeError<SPI>> {
226        if cap_value > 0b11111 {
227            Err(Error::InvalidSetting)
228        } else {
229            let mask = 0b011111 << 3;
230            let mut tia_settings = self.read_data(registers::TIA_AMB_GAIN)?;
231            tia_settings &= !mask;
232            tia_settings |= (cap_value as u32) << 3;
233            self.write_data(registers::TIA_AMB_GAIN, tia_settings)?;
234            Ok(())
235        }
236    }
237
238    pub fn set_first_stage_gain(&mut self, rf_value: u8) -> Result<(), AfeError<SPI>> {
239        if rf_value > 0b111 {
240            Err(Error::InvalidSetting)
241        } else {
242            let mask = 0b0111;
243            let mut tia_settings = self.read_data(registers::TIA_AMB_GAIN)?;
244            tia_settings &= !mask;
245            tia_settings |= rf_value as u32;
246            self.write_data(registers::TIA_AMB_GAIN, tia_settings)?;
247            Ok(())
248        }
249    }
250
251    /// Set current sent to LEDs.
252    ///
253    /// LED current and cancellation filter gain are used to compensate for changing ambient light
254    /// conditions.
255    ///
256    /// LEDCNTRL (0x22)
257    ///   LED1[15:8], LED2[7:0]
258    ///  Formula:
259    ///      LED_Register_value
260    ///      ------------------  *  50 mA = current
261    ///           256
262    ///
263    pub fn set_led_current(&mut self, value: u8) -> Result<u32, AfeError<SPI>> {
264        let both_leds = ((value as u32) << 8) + value as u32;
265        self.write_data(registers::LEDCNTRL, both_leds)?;
266        self.read_data(registers::LEDCNTRL)
267    }
268
269    pub fn get_led_current(&mut self) -> Result<u8, AfeError<SPI>> {
270        Ok((self.read_data(registers::LEDCNTRL)? & 0x00FF) as u8)
271    }
272
273    /// Recommended default pulse timings according to the AFE4400 data sheet.
274    pub fn default_pulse_timings(&mut self) -> Result<(), AfeError<SPI>> {
275        self.write_data(registers::CONTROL0, 0x000000)?;
276        self.write_data(registers::CONTROL1, 0x0102)?; // Enable timers
277        self.write_data(registers::CONTROL2, 0x000100)?;
278        self.write_data(registers::LED2STC, 6050)?;
279        self.write_data(registers::LED2ENDC, 7998)?;
280        self.write_data(registers::LED2LEDSTC, 6000)?;
281        self.write_data(registers::LED2LEDENDC, 7999)?;
282        self.write_data(registers::ALED2STC, 50)?;
283        self.write_data(registers::ALED2ENDC, 1998)?;
284        self.write_data(registers::LED1STC, 2050)?;
285        self.write_data(registers::LED1ENDC, 3998)?;
286        self.write_data(registers::LED1LEDSTC, 2000)?;
287        self.write_data(registers::LED1LEDENDC, 3999)?;
288        self.write_data(registers::ALED1STC, 4050)?;
289        self.write_data(registers::ALED1ENDC, 5998)?;
290        self.write_data(registers::LED2CONVST, 4)?;
291        self.write_data(registers::LED2CONVEND, 1999)?;
292        self.write_data(registers::ALED2CONVST, 2004)?;
293        self.write_data(registers::ALED2CONVEND, 3999)?;
294        self.write_data(registers::LED1CONVST, 4004)?;
295        self.write_data(registers::LED1CONVEND, 5999)?;
296        self.write_data(registers::ALED1CONVST, 6004)?;
297        self.write_data(registers::ALED1CONVEND, 7999)?;
298        self.write_data(registers::ADCRSTSTCT0, 0)?;
299        self.write_data(registers::ADCRSTENDCT0, 3)?;
300        self.write_data(registers::ADCRSTSTCT1, 2000)?;
301        self.write_data(registers::ADCRSTENDCT1, 2003)?;
302        self.write_data(registers::ADCRSTSTCT2, 4000)?;
303        self.write_data(registers::ADCRSTENDCT2, 4003)?;
304        self.write_data(registers::ADCRSTSTCT3, 6000)?;
305        self.write_data(registers::ADCRSTENDCT3, 6003)?;
306        self.write_data(registers::PRPCOUNT, 7999)?;
307        // self.set_cancellation_filters(0x06)?;
308        // self.set_led_current(0x1A)?;
309        Ok(())
310    }
311
312    /// Perform a self check to verify connections and chip clock integrity.
313    ///
314    /// Returns Ok(()) if the self-check passes
315    pub fn self_check(&mut self) -> Result<(), AfeError<SPI>> {
316        let original_value = self.read_data(registers::CONTROL1)?;
317        for _ in 0..5 {
318            if self.read_data(registers::CONTROL1)? != original_value {
319                return Err(Error::SelfCheckFail);
320            }
321        }
322        if self.read_data(registers::CONTROL1)? != 0 {
323            Ok(())
324        } else {
325            Err(Error::SelfCheckFail)
326        }
327    }
328
329}