capsules_extra/
adc_microphone.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5use core::cell::Cell;
6
7use kernel::hil::adc;
8use kernel::hil::gpio;
9use kernel::hil::sensors::{SoundPressure, SoundPressureClient};
10use kernel::utilities::cells::{OptionalCell, TakeCell};
11use kernel::utilities::math;
12use kernel::ErrorCode;
13
14#[derive(Copy, Clone, PartialEq)]
15enum State {
16    Idle,
17    ReadingSPL,
18}
19
20pub struct AdcMicrophone<'a, P: gpio::Pin> {
21    adc: &'a dyn adc::AdcChannel<'a>,
22    enable_pin: Option<&'a P>,
23    spl_client: OptionalCell<&'a dyn SoundPressureClient>,
24    spl_buffer: TakeCell<'a, [u16]>,
25    spl_pos: Cell<usize>,
26    state: Cell<State>,
27}
28
29impl<'a, P: gpio::Pin> AdcMicrophone<'a, P> {
30    pub fn new(
31        adc: &'a dyn adc::AdcChannel<'a>,
32        enable_pin: Option<&'a P>,
33        spl_buffer: &'a mut [u16],
34    ) -> AdcMicrophone<'a, P> {
35        enable_pin.map(|pin| pin.make_output());
36        AdcMicrophone {
37            adc,
38            enable_pin,
39            spl_client: OptionalCell::empty(),
40            spl_buffer: TakeCell::new(spl_buffer),
41            spl_pos: Cell::new(0),
42            state: Cell::new(State::Idle),
43        }
44    }
45
46    fn compute_spl(&self) -> u8 {
47        let max = self.spl_buffer.map_or(0, |buffer| {
48            let avg = (buffer.iter().fold(0usize, |a, v| a + *v as usize) / buffer.len()) as u16;
49            let max = buffer
50                .iter()
51                .map(|v| if *v > avg { v - avg } else { 0 })
52                .fold(0, |a, v| if a > v { a } else { v });
53            let mut conv = (max as f32) / (((1 << 15) - 1) as f32) * 9_f32;
54            conv = 20f32 * math::log10(conv / 0.00002f32);
55            conv as u8
56        });
57        max
58    }
59}
60
61impl<'a, P: gpio::Pin> SoundPressure<'a> for AdcMicrophone<'a, P> {
62    fn read_sound_pressure(&self) -> Result<(), ErrorCode> {
63        if self.state.get() == State::Idle {
64            // self.enable_pin.map (|pin| pin.set ());
65            self.state.set(State::ReadingSPL);
66            self.spl_pos.set(0);
67            let _ = self.adc.sample();
68            Ok(())
69        } else {
70            Err(ErrorCode::BUSY)
71        }
72    }
73
74    fn set_client(&self, client: &'a dyn SoundPressureClient) {
75        self.spl_client.set(client);
76    }
77
78    fn enable(&self) -> Result<(), ErrorCode> {
79        self.enable_pin.map(|pin| pin.set());
80        Ok(())
81    }
82
83    fn disable(&self) -> Result<(), ErrorCode> {
84        self.enable_pin.map(|pin| pin.clear());
85        Ok(())
86    }
87}
88
89impl<P: gpio::Pin> adc::Client for AdcMicrophone<'_, P> {
90    fn sample_ready(&self, sample: u16) {
91        if self.state.get() == State::ReadingSPL {
92            if self.spl_buffer.map_or(false, |buffer| {
93                if self.spl_pos.get() < buffer.len() {
94                    buffer[self.spl_pos.get()] = sample;
95                    self.spl_pos.set(self.spl_pos.get() + 1);
96                }
97                if self.spl_pos.get() < buffer.len() {
98                    let _ = self.adc.sample();
99                    false
100                } else {
101                    self.state.set(State::Idle);
102                    true
103                }
104            }) {
105                // self.enable_pin.map (|pin| pin.clear ());
106                let spl = self.compute_spl();
107                self.spl_client.map(|client| client.callback(Ok(()), spl));
108            }
109        }
110    }
111}