#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
use bitfield_struct::bitfield;
use defmt::Format;
use embedded_hal::blocking::i2c;
const DEVICE_ADDR_DEFAULT: u8 = 0x48;
const REG_CONTROL: u8 = 0x06;
pub const REG_CONTROL_DEFAULT: u8 = 0x02;
#[cfg(not(feature = "external_mic"))]
#[bitfield(u8)]
#[derive(PartialEq, Eq, Format)]
pub struct ControlRegister {
power_down: bool,
#[bits(2)]
filter_setting: FilterSetting,
interrupt_enable: bool,
interrupt_type: bool,
#[bits(3)]
__: u8,
}
#[cfg(feature = "external_mic")]
#[bitfield(u8)]
#[derive(PartialEq, Eq, Format)]
pub struct ControlRegister {
power_down: bool,
#[bits(2)]
filter_setting: FilterSetting,
interrupt_enable: bool,
interrupt_type: bool,
enable_line_out: bool,
#[bits(2)]
__: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterSetting {
None = 0b00,
AWeighting = 0b01,
CWeighting = 0b10,
}
impl FilterSetting {
const fn from_bits(bits: u8) -> Self {
match bits {
0b00 => Self::None,
0b01 => Self::AWeighting,
_ => Self::CWeighting,
}
}
const fn into_bits(self) -> u8 {
self as _
}
}
impl ControlRegister {
pub fn set_filter(&mut self, filter_setting: FilterSetting) {
self.set_filter_setting(filter_setting);
}
}
const REG_RESET: u8 = 0x09;
pub const REG_RESET_DEFAULT: u8 = 0x00;
#[bitfield(u8)]
#[derive(PartialEq, Eq, Format)]
pub struct ResetRegister {
clear_interrupt: bool,
clear_min_max: bool,
clear_history: bool,
system_reset: bool,
#[bits(4)]
__: u8,
}
const REG_VERSION: u8 = 0x00;
const REG_DECIBEL: u8 = 0x0a;
const REGS_DEVICE_ID: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
const REG_MAX: u8 = 0x0c;
const REG_MIN: u8 = 0x0d;
const REG_SCRATCH: u8 = 0x05;
const REG_TAVG_HIGH: u8 = 0x07;
pub const REG_AVERAGING_TIME_DEFAULT_MS: u16 = 1000;
#[cfg(feature = "external_mic")]
const REG_GAIN: u8 = 0x0f;
pub struct PaSpl<I2C>
where
I2C: i2c::Read + i2c::Write + i2c::WriteRead,
{
i2c: Option<I2C>,
device_addr: u8,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
I2c(E),
NoI2cInstance,
BufferOverflow,
}
impl<E, I2C> PaSpl<I2C>
where
I2C: i2c::Read<Error = E> + i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
{
pub fn new(i2c: I2C) -> Self {
Self {
i2c: Some(i2c),
device_addr: DEVICE_ADDR_DEFAULT,
}
}
pub fn set_device_addr(&mut self, addr: u8) {
self.device_addr = addr;
}
pub fn get_avg_time(&mut self) -> Result<u16, Error<E>> {
let mut buffer: [u8; 2] = [0; 2];
self.read_bytes(REG_TAVG_HIGH, &mut buffer)?;
let avg_time_ms = ((buffer[0] as u16) << 8) | (buffer[1] as u16);
Ok(avg_time_ms)
}
pub fn get_control_register(&mut self) -> Result<ControlRegister, Error<E>> {
let control_reg_raw = self.read_byte(REG_CONTROL)?;
Ok(ControlRegister::from_bits(control_reg_raw))
}
pub fn get_device_id(&mut self) -> Result<u32, Error<E>> {
let mut buffer: [u8; 4] = [0; 4];
self.read_bytes(REGS_DEVICE_ID[0], &mut buffer)?;
let device_id: u32 = ((buffer[0] as u32) << 24)
| ((buffer[1] as u32) << 16)
| ((buffer[2] as u32) << 8)
| (buffer[3] as u32);
Ok(device_id)
}
pub fn get_firmware_version(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_VERSION)
}
#[cfg(feature = "external_mic")]
pub fn get_gain(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_GAIN)
}
pub fn get_latest_decibel(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_DECIBEL)
}
pub fn get_max_decibel(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_MAX)
}
pub fn get_min_decibel(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_MIN)
}
pub fn get_scratch(&mut self) -> Result<u8, Error<E>> {
self.read_byte(REG_SCRATCH)
}
pub fn reset(&mut self) -> Result<(), Error<E>> {
let reg_reset = ResetRegister::new().with_system_reset(true);
self.write_byte(REG_RESET, reg_reset.into_bits())
}
pub fn set_avg_time(&mut self, ms: u16) -> Result<(), Error<E>> {
let tavg_high_byte: u8 = (ms >> 8) as u8;
let tavg_low_byte: u8 = (ms & 0xFF) as u8;
let buffer = [tavg_high_byte, tavg_low_byte];
self.write_two_bytes(REG_TAVG_HIGH, &buffer)
}
pub fn set_control_register(&mut self, reg: ControlRegister) -> Result<(), Error<E>> {
self.write_byte(REG_CONTROL, reg.into_bits())
}
#[cfg(feature = "external_mic")]
pub fn set_gain(&mut self, value: u8) -> Result<(), Error<E>> {
self.write_byte(REG_GAIN, value)
}
pub fn set_scratch(&mut self, value: u8) -> Result<(), Error<E>> {
self.write_byte(REG_SCRATCH, value)
}
pub fn destroy(&mut self) -> I2C {
self.i2c
.take()
.expect("I2C instance has already been taken")
}
fn read_byte(&mut self, reg: u8) -> Result<u8, Error<E>> {
let mut buffer = [0; 1];
self.i2c
.as_mut()
.ok_or(Error::NoI2cInstance)?
.write_read(self.device_addr, &[reg], &mut buffer)
.map_err(Error::I2c)?;
Ok(buffer[0])
}
fn read_bytes(&mut self, start_reg: u8, buffer: &mut [u8]) -> Result<(), Error<E>> {
self.i2c
.as_mut()
.ok_or(Error::NoI2cInstance)?
.write_read(self.device_addr, &[start_reg], buffer)
.map_err(Error::I2c)?;
Ok(())
}
fn write_byte(&mut self, reg: u8, value: u8) -> Result<(), Error<E>> {
self.i2c
.as_mut()
.ok_or(Error::NoI2cInstance)?
.write(self.device_addr, &[reg, value])
.map_err(Error::I2c)
}
fn write_two_bytes(&mut self, reg: u8, buffer: &[u8]) -> Result<(), Error<E>> {
if buffer.len() > 2 {
return Err(Error::BufferOverflow);
}
self.i2c
.as_mut()
.ok_or(Error::NoI2cInstance)?
.write(self.device_addr, &[reg, buffer[0], buffer[1]])
.map_err(Error::I2c)
}
}
#[cfg(test)]
mod tests {
use crate::{
ControlRegister, FilterSetting, REG_AVERAGING_TIME_DEFAULT_MS, REG_CONTROL,
REG_CONTROL_DEFAULT, REG_RESET, REG_TAVG_HIGH,
};
use super::*;
use embedded_hal_mock::eh0::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
const DEVICE_VER_MEMS_LTS_ASA: u8 = 0x32;
const REG_TAVG_HIGH_DEFAULT_BYTE: u8 = 0x03;
const REG_TAVG_LOW_DEFAULT_BYTE: u8 = 0xE8;
#[test]
fn confirm_set_device_addr() {
let expectations = vec![];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let new_device_addr: u8 = 0x99;
pa_spl.set_device_addr(new_device_addr);
assert_eq!(new_device_addr, pa_spl.device_addr);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_device_id() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REGS_DEVICE_ID[0]],
vec![0x01, 0x02, 0x03, 0x04],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let device_id = pa_spl.get_device_id().unwrap();
assert_eq!(0x01020304, device_id);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_firmware_version() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_VERSION],
vec![DEVICE_VER_MEMS_LTS_ASA],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let version = pa_spl.get_firmware_version().unwrap();
assert_eq!(DEVICE_VER_MEMS_LTS_ASA, version);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_avg_time() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_TAVG_HIGH],
vec![REG_TAVG_HIGH_DEFAULT_BYTE, REG_TAVG_LOW_DEFAULT_BYTE],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let averaging_time_ms = pa_spl.get_avg_time().unwrap();
assert_eq!(REG_AVERAGING_TIME_DEFAULT_MS, averaging_time_ms);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_control_register() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_CONTROL],
vec![REG_CONTROL_DEFAULT], )];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let reg_control = pa_spl.get_control_register().unwrap();
let control_register_default_bits = ControlRegister::from_bits(REG_CONTROL_DEFAULT);
assert_eq!(control_register_default_bits, reg_control);
let mut mock = pa_spl.destroy();
mock.done();
}
#[cfg(feature = "external_mic")]
#[test]
fn confirm_get_gain() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_GAIN],
vec![18],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let expected_gain: u8 = 18;
let gain_val = pa_spl.get_gain().unwrap();
assert_eq!(expected_gain, gain_val);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_latest_decibel() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_DECIBEL],
vec![0x12],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let latest_decibel_val = pa_spl.get_latest_decibel().unwrap();
assert_eq!(0x12, latest_decibel_val);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_max_decibel() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_MAX],
vec![0x12],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.get_max_decibel();
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_min_decibel() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_MIN],
vec![0x12],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.get_min_decibel();
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_get_scratch() {
let expectations = vec![I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_SCRATCH],
vec![0x99],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let scratch_write_val: u8 = 0x99;
let scratch_read_val = pa_spl.get_scratch().unwrap();
assert_eq!(scratch_write_val, scratch_read_val);
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_reset() {
let expectations = vec![I2cTransaction::write(
DEVICE_ADDR_DEFAULT,
vec![REG_RESET, 0b0000_1000],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.reset();
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_set_avg_time() {
let new_avg_time_ms: u16 = 125;
let tavg_high_expected_byte: u8 = 0x00;
let tavg_low_expected_byte: u8 = 0x7D;
let expectations = vec![I2cTransaction::write(
DEVICE_ADDR_DEFAULT,
vec![
REG_TAVG_HIGH,
tavg_high_expected_byte,
tavg_low_expected_byte,
],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.set_avg_time(new_avg_time_ms);
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_set_control_register() {
let expectations = vec![
I2cTransaction::write_read(
DEVICE_ADDR_DEFAULT,
vec![REG_CONTROL],
vec![REG_CONTROL_DEFAULT], ),
I2cTransaction::write(
DEVICE_ADDR_DEFAULT,
vec![REG_CONTROL, 0b0000_0100], ),
];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let mut reg_control = pa_spl.get_control_register().unwrap();
reg_control.set_filter_setting(FilterSetting::CWeighting);
let result = pa_spl.set_control_register(reg_control);
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[cfg(feature = "external_mic")]
#[test]
fn confirm_set_gain() {
let new_gain_val: u8 = 43;
let expectations = vec![I2cTransaction::write(
DEVICE_ADDR_DEFAULT,
vec![REG_GAIN, new_gain_val],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.set_gain(new_gain_val);
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
#[test]
fn confirm_set_scratch() {
let scratch_write_val: u8 = 0x99;
let expectations = vec![I2cTransaction::write(
DEVICE_ADDR_DEFAULT,
vec![REG_SCRATCH, scratch_write_val],
)];
let i2c_mock = I2cMock::new(&expectations);
let mut pa_spl = PaSpl::new(i2c_mock);
let result = pa_spl.set_scratch(scratch_write_val);
assert!(result.is_ok());
let mut mock = pa_spl.destroy();
mock.done();
}
}