LP55231 Linux Rust Driver
Linux driver for Texas Instruments LP55231, a 9 channel RGB/White LED controller with internal program memory and integrated charge Pump.
Features:
- Full implementation of I2C control interface in datasheet
- Ergonomic API to leverage the programming engine
- Easy to debug with optional features:
- read-after-write checks to validate register writes
- debug output to see the value in a register before and after a write
Initialization, setup, and preparing animations
This example covers a typical initialization of the driver, preparing three LEDs (R, G, B) for animated effects.
use
// Create the driver
let path = "/dev/i2c-2";
let i2c_addr = 0x32;
let ic = LP55231create?;
// Power and configure the driver.
ic.set_enabled?;
ic.set_misc_settings?;
// Channel assignment.
let = ;
// Enable logarithmic brightness for a smoother ramp up effect.
ic.set_log_brightness?;
ic.set_log_brightness?;
ic.set_log_brightness?;
// Enable ratiometric dimming to preserve the ratio between the
// RGB components of all mapped channels during animations
ic.set_ratiometric_dimming?;
ic.set_ratiometric_dimming?;
ic.set_ratiometric_dimming?;
// Set color to orange
ic.set_channel_pwm?;
ic.set_channel_pwm?;
ic.set_channel_pwm?;
// Program the IC (see other example for implementations of `create_program`)
let instructions = create_program?;
ic.load_program?;
// Wait for the ENGINE_BUSY bit to clear,
// indicating that all instructions have been loaded.
ic.wait_while_engine_busy?;
// Set up one of the programming engines to Halt & Hold (ready to execute).
let engine = E1;
ic.set_engine_exec?;
ic.set_engine_mode?;
// Run the effect
ic.set_engine_exec?;
ic.set_engine_mode?;
Example effect: blinking
This example is an implementation of create_program that prepares a blinking
effect to run in an endless loop.
Example effect: glow
Switching between effects
The programming engine supports up to 96 instructions, which gives you plenty of room to set up multiple effects. To switch between effects:
- pause the programming engine
- update the program counter to first index of next effect
- unpause the programming engine.
Example:
// Pause engine execution.
ic.set_engine_exec?;
ic.wait_while_engine_busy?;
// Update the program counter to the starting instruction of the desired effect
// This example assumes we're jumping to instruction 42, of the possible 96
// programming memory addresses.
ic.set_engine_program_counter?;
// Unpause the engine and begin the new animation.
ic.set_engine_exec?;
ic.set_engine_mode?;
Debugging
Read-after-write verifications
Read-after-write checks can be enabled with:
let ic = LP55231create?;
ic.verify_writes = true;
This will cause the driver to perform a read after every I2C write instruction to compare the value in the register. It will throw an exception if the read value does not match the written value.
[!NOTE] This is useful during development, especially around using the programming engines which must be in the correct internal state in order to allow changes.
Debug output
When enabled via debug_enabled property, the driver will emit useful (but
rather verbose) output to help you understand the state of registers with every
read and write operation. Example:
let ic = LP55231create?;
ic.debug_enabled = true;
ic.set_enabled?;
Will produce output:
set_enabled(true) {
00000000 << 0x00 ENABLE_ENGINE_CNTRL1
00100000 >> 0x00 ENABLE_ENGINE_CNTRL1
}
Scoping debug output for multiple I2C calls
Scope for multiple debug calls can be combined with the debug::scope! macro:
multiple_i2_calls?;
Would result in the following output:
example(true) {
set_enabled(true) {
00000000 << 0x00 ENABLE_ENGINE_CNTRL1
00100000 >> 0x00 ENABLE_ENGINE_CNTRL1
}
set_enabled(false) {
00100000 << 0x00 ENABLE_ENGINE_CNTRL1
00000000 >> 0x00 ENABLE_ENGINE_CNTRL1
}
}
See debug.rs docs for more details.
Getting started with development
- Clone the project and open the folder in VS Code
- Accept plugin suggestions (dev container required in non-linux envs)
- Re-open in dev container
[!NOTE] This project uses hermit to manage the Rust toolchain for this project. No prior installation of Rust required.
TODO
- Read/write pages in blocks (
at_onceparam inread/write_program_page)