use core::assert; use embedded_hal::blocking::spi::Transfer; use crate::urukul::Error; pub struct Attenuator { spi: SPI, data: [u8; 4], } impl Attenuator where SPI: Transfer, { pub fn new(spi: SPI) -> Self { Attenuator { spi, // data[y] refers to the yth byte for SPI communication data: [0, 0, 0, 0], } } /* * Set attenuations of all attenuators * att[x] refers to the attenuation for channel x */ pub fn set_attenuation(&mut self, att: [f32; 4]) -> Result<(), Error> { for i in 0..4 { let mut atten = att[i]; if att[i] > 31.5 { atten = 31.5; } if att[i] < 0.0 { atten = 0.0; } // Set data as attenuation * 2 // Flip data using bitwise XOR, active low data // Data is most signifant attenuator first self.data[3 - i] = (((atten * 2.0) as u8) ^ 0xFF) << 2 } let mut clone = self.data.clone(); // Transmit SPI once to set attenuation self.spi .transfer(&mut clone) .map(|_| ()) .map_err(|_| Error::AttenuatorError) } pub fn set_channel_attenuation( &mut self, channel: u8, attenuation: f32, ) -> Result<(), Error> { assert!(channel < 4); let mut arr: [f32; 4] = self.get_attenuation()?; arr[channel as usize] = attenuation; self.set_attenuation(arr).map(|_| ()) } pub fn get_channel_attenuation(&mut self, channel: u8) -> Result> { assert!(channel < 4); match self.get_attenuation() { Ok(arr) => Ok(arr[channel as usize]), Err(e) => Err(e), } } pub fn get_attenuation(&mut self) -> Result<[f32; 4], Error> { let mut clone = self.data.clone(); match self.spi.transfer(&mut clone).map_err(Error::SPI) { Ok(arr) => { let mut ret: [f32; 4] = [0.0; 4]; for index in 0..4 { ret[index] = ((arr[3 - index] ^ 0xFC) as f32) / 8.0; } Ok(ret) } Err(e) => Err(e), } } /* * Test method for Attenuators. * Return the number of test failed. */ pub fn test(&mut self) -> Result> { // Test attenuators by getting back the attenuation let mut error_count = 0; // Convert cached SPI data into attenuation floats let att_floats: [f32; 4] = [ ((self.data[3] ^ 0xFC) as f32) / 8.0, ((self.data[2] ^ 0xFC) as f32) / 8.0, ((self.data[1] ^ 0xFC) as f32) / 8.0, ((self.data[0] ^ 0xFC) as f32) / 8.0, ]; // Set the attenuation to an arbitrary value, then read the attenuation self.set_attenuation([3.5, 9.5, 20.0, 28.5])?; match self.get_attenuation() { Ok(arr) => { if arr[0] != 3.5 { error_count += 1; } if arr[1] != 9.5 { error_count += 1; } if arr[2] != 20.0 { error_count += 1; } if arr[3] != 28.5 { error_count += 1; } } Err(_) => return Err(Error::AttenuatorError), }; self.set_attenuation(att_floats)?; Ok(error_count) } } impl Transfer for Attenuator where SPI: Transfer, { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { self.spi.transfer(words).map_err(Error::SPI) } }