diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96eef822..33bad179 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,4 +40,4 @@ jobs: with: key: ${{ matrix.board }} - run: cargo build --verbose - working-directory: boards/${{ matrix.board }} + working-directory: boards/${{ matrix.board }} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index f299079f..0fb3d2e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ exclude = [ "boards/stm32f767zi", "boards/stm32l432kc", ] -resolver = "2" +resolver = "2" \ No newline at end of file diff --git a/boards/stm32l476rg/src/tasks.rs b/boards/stm32l476rg/src/tasks.rs index a557c36c..3ab248d8 100644 --- a/boards/stm32l476rg/src/tasks.rs +++ b/boards/stm32l476rg/src/tasks.rs @@ -1,2 +1,3 @@ pub mod keyence; +pub mod led_driver; pub mod temperature; diff --git a/boards/stm32l476rg/src/tasks/led_driver.rs b/boards/stm32l476rg/src/tasks/led_driver.rs new file mode 100644 index 00000000..680d52e4 --- /dev/null +++ b/boards/stm32l476rg/src/tasks/led_driver.rs @@ -0,0 +1,45 @@ +use crate::io::i2c::Stm32l476rgI2c; +use defmt_rtt as _; +use embassy_stm32::i2c::I2c; +use embassy_stm32::time::Hertz; +use hyped_sensors::led_driver::{LedDriver, LedDriverAddresses, LedDriverConfigAddresses}; + +#[embassy_executor::task] +pub async fn write_led() -> ! { + let p = embassy_stm32::init(Default::default()); + let i2c = I2c::new_blocking(p.I2C1, p.PB8, p.PB9, Hertz(100_000), Default::default()); + let mut hyped_i2c = Stm32l476rgI2c::new(i2c); + + let mut led_driver_sensor = LedDriver::new(&mut hyped_i2c, LedDriverAddresses::Address30) + .expect("Failed to create LED driver instance. Check wiring and I2C address of component."); + + const DRIVER_ADDRESSES: [u8; 8] = [0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; + let mut index = 0; + + loop { + // flash each LED in sequence + if index == 8 { + // reset LED Driver after flashing 8th test LED + match led_driver_sensor.reset() { + Ok(_) => (), + Err(_) => { + defmt::error!("Failed to reset LED Driver"); + } + }; + } + match led_driver_sensor.set_led_colour( + LedDriverConfigAddresses::LedConfig0, + DRIVER_ADDRESSES[index], + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ) { + Ok(_) => (), + Err(_) => { + defmt::error!("Failed to set colour of LED."); + } + }; + index += 1; + } +} diff --git a/lib/core/Cargo.lock b/lib/core/Cargo.lock deleted file mode 100644 index ca54f965..00000000 --- a/lib/core/Cargo.lock +++ /dev/null @@ -1,234 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "defmt" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" -dependencies = [ - "bitflags", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" -dependencies = [ - "defmt-parser", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "defmt-parser" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" -dependencies = [ - "thiserror", -] - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - -[[package]] -name = "embedded-io-async" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" -dependencies = [ - "embedded-io", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "serde", - "stable_deref_trait", -] - -[[package]] -name = "hyped_core" -version = "0.1.0" -dependencies = [ - "defmt", - "embedded-io-async", - "heapless", - "rand_core", - "rust-mqtt", - "serde", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rust-mqtt" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f71160765f368fd9a84e0955e2ddb6d64ac9018fee1c5323354d6d08c816b40" -dependencies = [ - "defmt", - "embedded-io", - "embedded-io-async", - "heapless", - "rand_core", -] - -[[package]] -name = "serde" -version = "1.0.204" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.204" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/lib/sensors/src/led_driver.rs b/lib/sensors/src/led_driver.rs new file mode 100644 index 00000000..92aeee97 --- /dev/null +++ b/lib/sensors/src/led_driver.rs @@ -0,0 +1,280 @@ +use hyped_io::i2c::{HypedI2c, I2cError}; + +/// LedDriver implements logic and required registers and addresses from the LP503x LED driver + +/// LED driver is set to colour bank control mode, LED colour and brightness can only be set +/// for those in bank control mode. +/// All LEDs in bank control mode share the same colour and brightness changes unless set +/// off bank control mode + +/// When setting colours and brightness of LEDs, use the set_led_colours() function. +/// LEDs needing to be set are controlled by the addresses in LedDriverConfigAddresses, LedConfig0 and LedConfig1 +/// Pass these in as LedDriverConfigAddresses::LedConfigx (where x is either 0 or 1) + +/// reset() resets the LED driver, turning off all LEDs and removing them from bank control mode + +/// link to data sheet (https://www.ti.com/lit/ds/symlink/lp5036.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1698441544495&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F) + +pub struct LedDriver<'a, T: HypedI2c + 'a> { + i2c: &'a mut T, + device_address: u8, +} + +impl<'a, T: HypedI2c> LedDriver<'a, T> { + /// Create new instance of LED driver and attempt to configure + pub fn new(i2c: &'a mut T, device_address: LedDriverAddresses) -> Result { + let device_address = device_address as u8; + + // set up led driver enabling Chip_En in DEVICE_CONFIG1 + match i2c.write_byte_to_register(device_address, DEVICE_CONFIG0, CHIP_EN) { + Ok(_) => Ok(Self { + i2c, + device_address, + }), + Err(e) => Err(LedDriverError::I2cError(e)), + } + } + + /// Device reset LED driver + pub fn reset(&mut self) -> Result<(), LedDriverError> { + match self + .i2c + .write_byte_to_register(self.device_address, RESET, 0xFF) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + Ok(()) + } + + /// set bank colour and brightness + pub fn set_led_colour( + &mut self, + led_configx: LedDriverConfigAddresses, + ledx_bank_en: u8, + a_colour: u8, + b_colour: u8, + c_colour: u8, + brightness: u8, + ) -> Result<(), LedDriverError> { + let led_config: u8 = led_configx as u8; + // enable LEDs, x, to bank control mode - can be multiple (see documentation at top) + match self + .i2c + .write_byte_to_register(self.device_address, led_config, ledx_bank_en) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + // change bank A colour value + match self + .i2c + .write_byte_to_register(self.device_address, BANK_A_COLOUR, a_colour) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + // change bank B colour value + match self + .i2c + .write_byte_to_register(self.device_address, BANK_B_COLOUR, b_colour) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + // change bank C colour value + match self + .i2c + .write_byte_to_register(self.device_address, BANK_C_COLOUR, c_colour) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + // set bank brightness value + match self + .i2c + .write_byte_to_register(self.device_address, BANK_BRIGHTNESS, brightness) + { + Ok(_) => (), + Err(e) => return Err(LedDriverError::I2cError(e)), + } + + Ok(()) + } +} + +#[derive(Debug)] +pub enum LedDriverError { + I2cError(I2cError), +} + +pub enum LedDriverAddresses { + // independent addresses + Address30 = 0x30, // 011 0000 - ADDR1 and ADDR0 both GND + Address31 = 0x31, // 011 0001 + Address32 = 0x32, // 011 0010 + Address33 = 0x33, // 011 0011 + + // broadcast address + AddressBroadcast = 0x1c, // 001 1100 +} + +// device registers +const DEVICE_CONFIG0: u8 = 0x00; + +const RESET: u8 = 0x38; + +// LED config registers +pub enum LedDriverConfigAddresses { + LedConfig0 = 0x02, + LedConfig1 = 0x03, +} + +// 6th bit for DEVICE_CONFIG0, enables LP503x +const CHIP_EN: u8 = 0x20; + +// colour bank registers +const BANK_BRIGHTNESS: u8 = 0x04; +const BANK_A_COLOUR: u8 = 0x05; +const BANK_B_COLOUR: u8 = 0x06; +const BANK_C_COLOUR: u8 = 0x07; + +#[cfg(test)] +mod tests { + use super::*; + use heapless::FnvIndexMap; + use hyped_io::i2c::mock_i2c::MockI2c; + + #[test] + fn test_config() { + let i2c_values = FnvIndexMap::new(); + let mut i2c = MockI2c::new(i2c_values); + let _ = LedDriver::new(&mut i2c, LedDriverAddresses::Address30); + + // Verify values are written to required registers + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, DEVICE_CONFIG0)), + Some(&Some(CHIP_EN)) + ); + } + + #[test] + fn test_all_led_config0() { + let i2c_values = FnvIndexMap::new(); + let mut i2c = MockI2c::new(i2c_values); + let mut led_driver = LedDriver::new(&mut i2c, LedDriverAddresses::Address30) + .expect("could not create led_driver"); + let _ = led_driver.set_led_colour( + LedDriverConfigAddresses::LedConfig0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ); + + // Verify values are written to required registers + assert_eq!( + i2c.get_writes().get(&( + LedDriverAddresses::Address30 as u8, + LedDriverConfigAddresses::LedConfig0 as u8 + )), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_A_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_B_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_C_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_BRIGHTNESS)), + Some(&Some(0xFF)) + ); + } + + #[test] + fn test_all_led_config1() { + let i2c_values = FnvIndexMap::new(); + let mut i2c = MockI2c::new(i2c_values); + let mut led_driver = LedDriver::new(&mut i2c, LedDriverAddresses::Address30) + .expect("could not create led_driver"); + let _ = led_driver.set_led_colour( + LedDriverConfigAddresses::LedConfig1, + 0xF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ); + + // Verify values are written to required registers + assert_eq!( + i2c.get_writes().get(&( + LedDriverAddresses::Address30 as u8, + LedDriverConfigAddresses::LedConfig1 as u8 + )), + Some(&Some(0xF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_A_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_B_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_C_COLOUR)), + Some(&Some(0xFF)) + ); + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, BANK_BRIGHTNESS)), + Some(&Some(0xFF)) + ); + } + + #[test] + fn test_reset() { + let i2c_values = FnvIndexMap::new(); + let mut i2c = MockI2c::new(i2c_values); + let mut led_driver = LedDriver::new(&mut i2c, LedDriverAddresses::Address30) + .expect("could not create led_driver"); + let _pre = led_driver.set_led_colour( + LedDriverConfigAddresses::LedConfig0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ); + let _ = led_driver.reset(); + + // Verify values in LedConfig0 are reset to default values + assert_eq!( + i2c.get_writes() + .get(&(LedDriverAddresses::Address30 as u8, RESET)), + Some(&Some(0xFF)) + ); + } +} diff --git a/lib/sensors/src/lib.rs b/lib/sensors/src/lib.rs index bf4b49cf..f8aef69b 100644 --- a/lib/sensors/src/lib.rs +++ b/lib/sensors/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] pub mod keyence; +pub mod led_driver; pub mod temperature;