#![no_std]
#![deny(warnings)]
use core::marker::PhantomData;
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::digital::v2::OutputPin;
#[derive(Debug)]
pub enum Error {
CommError,
NoInterfaceDefined,
EnableLine,
}
#[derive(Clone, Copy)]
pub enum Model {
LP5009,
LP5012,
}
impl Model {
fn get_pin_count(&self) -> u8 {
match *self {
Model::LP5009 => 9,
Model::LP5012 => 12,
}
}
}
#[derive(Clone, Copy)]
pub enum Address {
Broadcast,
Independent(u8),
}
impl Address {
pub fn into_u8(self) -> u8 {
match self {
Address::Independent(address) => {
if address > 3 {
panic!("LP50XX only supports 4 dedicated addresses, 0b00, 0b01, 0b10 or 0b11")
}
return 0b00010100 | address;
}
Address::Broadcast => 0b0001100,
}
}
}
pub struct DefaultMode {}
pub struct ColorMode {}
impl ColorMode {
pub fn new() -> Self {
Self {}
}
}
pub struct MonochromaticMode {}
impl MonochromaticMode {
pub fn new() -> Self {
Self {}
}
}
pub struct LP50xx<MODE, I2C, EN> {
interface: Option<I2C>,
enable: EN,
transfer_callback: Option<fn(addr: Address, data: &[u8])>,
continuous_addressing: bool,
active_address: Address,
mode: PhantomData<MODE>,
model: Model,
brightness_factor: f32,
}
impl<I2C, EN> LP50xx<DefaultMode, I2C, EN>
where
EN: OutputPin,
{
pub fn init_with_i2c(model: Model, i2c: I2C, mut en: EN) -> Self {
en.set_low().ok();
Self {
interface: Some(i2c),
enable: en,
transfer_callback: None,
model,
active_address: Address::Broadcast,
continuous_addressing: true,
mode: PhantomData,
brightness_factor: 1.0,
}
}
pub fn init_with_callback(
model: Model,
mut en: EN,
callback: fn(addr: Address, data: &[u8]),
) -> Self {
en.set_low().ok();
Self {
interface: None,
enable: en,
transfer_callback: Some(callback),
model,
active_address: Address::Broadcast,
continuous_addressing: true,
mode: PhantomData,
brightness_factor: 1.0,
}
}
pub fn set_continuous_addressing(&mut self, state: bool) {
self.continuous_addressing = state;
}
pub fn set_active_address(&mut self, address: Address) {
self.active_address = address;
}
pub fn release(self) -> (Option<I2C>, EN) {
(self.interface, self.enable)
}
}
impl<MODE, I2C, EN> LP50xx<MODE, I2C, EN>
where
I2C: embedded_hal::blocking::i2c::Write<u8>,
EN: OutputPin,
{
pub fn into_color_mode(self) -> LP50xx<ColorMode, I2C, EN> {
self.into_mode::<ColorMode>()
}
pub fn into_monochromatic_mode(self) -> LP50xx<MonochromaticMode, I2C, EN> {
self.into_mode::<MonochromaticMode>()
}
fn into_mode<MODE2>(self) -> LP50xx<MODE2, I2C, EN> {
LP50xx {
interface: self.interface,
enable: self.enable,
transfer_callback: self.transfer_callback,
active_address: self.active_address,
model: self.model,
continuous_addressing: self.continuous_addressing,
mode: PhantomData,
brightness_factor: 1.0,
}
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
if self.interface.is_some() {
self.interface
.as_mut()
.unwrap()
.write(addr.into_u8(), data)
.map_err(|_| Error::CommError)?;
return Ok({});
}
if self.transfer_callback.is_some() {
self.transfer_callback.unwrap()(addr, data);
return Ok({});
}
return Err(Error::NoInterfaceDefined);
}
pub fn reset<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), Error>
where
DELAY: DelayMs<u8>,
{
self.write(Address::Broadcast, &[0x17, 0xff])?;
delay.delay_ms(1);
self.enable.set_low().map_err(|_| Error::EnableLine)?;
delay.delay_ms(10);
self.enable.set_high().map_err(|_| Error::EnableLine)?;
delay.delay_ms(10);
Ok(())
}
pub fn enable<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), Error>
where
DELAY: DelayMs<u8>,
{
self.enable.set_low().map_err(|_| Error::EnableLine)?;
delay.delay_ms(1);
self.enable.set_high().map_err(|_| Error::EnableLine)?;
delay.delay_ms(10);
self.write(Address::Broadcast, &[0x00, 0b01000000])
}
pub fn configure(
&mut self,
log_scale: bool,
power_save: bool,
auto_incr: bool,
pwm_dithering: bool,
max_current_option: bool,
global_off: bool,
) -> Result<(), Error> {
let value: u8 = 0x00
| (log_scale as u8) << 5
| (power_save as u8) << 4
| (auto_incr as u8) << 3
| (pwm_dithering as u8) << 2
| (max_current_option as u8) << 1
| (global_off as u8) << 0;
self.write(Address::Broadcast, &[0x01, value])
}
}
impl<I2C, EN> LP50xx<ColorMode, I2C, EN>
where
I2C: embedded_hal::blocking::i2c::Write,
EN: OutputPin,
{
pub fn set(
&mut self,
mut channel: u8,
(brightness, [r, g, b]): (u8, [u8; 3]),
) -> Result<(), Error> {
if channel < 1 {
panic!("Specified Channel index must be greater than 0");
}
channel = channel - 1;
let bright_addr = 0x07 + channel as u8;
let color_addr = 0x0b + (channel as u8) * 3;
self.write(self.active_address, &[bright_addr, brightness])?;
self.write(self.active_address, &[color_addr, r, g, b])?;
Ok(())
}
}
impl<I2C, EN> LP50xx<MonochromaticMode, I2C, EN>
where
I2C: embedded_hal::blocking::i2c::Write,
EN: OutputPin,
{
pub fn set_brightness_factor(&mut self, mut factor: f32) {
if factor < 0.01 {
factor = 0.01;
} else if factor > 1.0 {
factor = 1.0;
}
self.brightness_factor = factor;
}
pub fn brightness_factor(&self) -> f32 {
self.brightness_factor
}
pub fn set(&mut self, led: u8, value: u8) -> Result<(), Error> {
if led == 0 {
panic!("Specified LED index must be greater than 0");
}
if !self.continuous_addressing && led > self.model.get_pin_count() {
panic!("Specified LED is not supported");
}
let led_base_address = 0x0B;
let (address, pin_offset) = if self.continuous_addressing {
let addr_offset = get_led_address_offset(led, self.model);
let addr = Address::Independent(addr_offset);
let pin_offset = led - (addr_offset * self.model.get_pin_count());
(addr, pin_offset)
} else {
(self.active_address, led)
};
let result = (value as f32 * self.brightness_factor) as u8;
self.write(address, &[led_base_address + (pin_offset - 1), result])?;
Ok(())
}
}
fn get_led_address_offset(led_index: u8, model: Model) -> u8 {
let length = model.get_pin_count();
if led_index <= length {
return 0x00;
}
if led_index > length && led_index <= (length * 2) {
return 0x01;
}
return 0x02;
}
#[cfg(test)]
mod tests {
#[test]
fn correct_led_address_offset() {
let offset = super::get_led_address_offset(1, super::Model::LP5012);
assert_eq!(offset, 0x00);
let offset = super::get_led_address_offset(12, super::Model::LP5012);
assert_eq!(offset, 0x00);
let offset = super::get_led_address_offset(13, super::Model::LP5012);
assert_eq!(offset, 0x01);
let offset = super::get_led_address_offset(24, super::Model::LP5012);
assert_eq!(offset, 0x01);
let offset = super::get_led_address_offset(25, super::Model::LP5012);
assert_eq!(offset, 0x02);
}
}