#led-controller #i2c-driver #led #i2c #hal #lp55231

ti-lp55231

适用于德州仪器LP55231 LED控制器的Linux I2C驱动程序

1个稳定版本

1.0.0 2023年10月6日

#1519 in 嵌入式开发

MIT 许可证

55KB
1K SLoC

LP55231 Linux Rust驱动程序

Open in Dev Containers

适用于德州仪器LP55231的Linux驱动程序,这是一个具有内部程序存储和集成电荷泵的9通道RGB/白LED控制器。

特性

  • 完整实现数据手册中的I2C控制接口
  • 优化的API以利用编程引擎
  • 具有可选功能的易于调试

初始化、设置和准备动画

本示例涵盖了驱动程序的典型初始化,为动画效果准备三个LED(红、绿、蓝)。

use ti_lp55231::{
  Channel,
  ChargePumpMode,
  ClockSelection,
  Direction,
  Engine,
  EngineExec,
  EngineMode,
  Instruction,
  LP55231,
  PreScale,
}

// Create the driver
let path = "/dev/i2c-2";
let i2c_addr = 0x32;
let ic = LP55231::create(path, i2c_addr)?;

// Power and configure the driver.
ic.set_enabled(true)?;
ic.set_misc_settings(Misc {
  auto_increment_enabled: true,
  powersave_enabled: true,
  charge_pump_mode: ChargePumpMode::Auto,
  pwm_powersave_enabled: true,
  clock_selection: ClockSelection::ForceInternal,
})?;

// Channel assignment.
let (r, g, b) = (Channel::D7, Channel::D1, Channel::D2);

// Enable logarithmic brightness for a smoother ramp up effect.
ic.set_log_brightness(r, true)?;
ic.set_log_brightness(g, true)?;
ic.set_log_brightness(b, true)?;

// Enable ratiometric dimming to preserve the ratio between the
// RGB components of all mapped channels during animations
ic.set_ratiometric_dimming(r, true)?;
ic.set_ratiometric_dimming(g, true)?;
ic.set_ratiometric_dimming(b, true)?;

// Set color to orange
ic.set_channel_pwm(r, 255)?;
ic.set_channel_pwm(g, 128)?;
ic.set_channel_pwm(b, 0)?;

// Program the IC (see other example for implementations of `create_program`)
let instructions = create_program(&[r, g, b])?;
ic.load_program(&instructions)?;

// Wait for the ENGINE_BUSY bit to clear,
// indicating that all instructions have been loaded.
ic.wait_while_engine_busy(Duration::from_millis(10))?;

// Set up one of the programming engines to Halt & Hold (ready to execute).
let engine = Engine::E1;
ic.set_engine_exec(engine, EngineExec::Hold)?;
ic.set_engine_mode(engine, EngineMode::Halt)?;

// Run the effect
ic.set_engine_exec(engine, EngineExec::Free)?;
ic.set_engine_mode(engine, EngineMode::RunProgram)?;

示例效果:闪烁

此示例实现了create_program,它准备了一个无限循环运行的闪烁效果。

fn create_program(channels_to_control: &[Channel]) -> [Instruction; 8] {
  [
    // ----- LED-to-Engine mapping table
    // 00. Map all target output channels to the programming engine for control.
    Instruction::map_channels(channels_to_control),

    // ----- blink effect start
    // 01-02. Set LED mapping table start/end index + activation.
    Instruction::mux_map_start(0),
    Instruction::mux_ld_end(0),
    // 03. Power all mapped LEDs off.
    Instruction::set_pwm(0),
    // 04. Wait ~0.5 seconds (15.625ms * 30).
    Instruction::wait(PreScale::CT15_625, 30),
    // 05. Set all LEDs to max brightness.
    Instruction::set_pwm(255),
    // 06. Wait ~0.5 seconds (15.625ms * 30).
    Instruction::wait(PreScale::CT15_625, 30),
    // 07. Loop back to beginning of blink effect index.
    Instruction::branch(1, 0),
  ]
}

示例效果:发光

fn create_program(channels_to_control: &[Channel]) -> [Instruction; 9] {
  [
    // ----- LED-to-Engine mapping table
    // 00. Map all target output channels to the programming engine for control.
    Instruction::map_channels(channels_to_control),

    // ----- glow effect start
    // 01-02. Set LED mapping table start/end index + activation.
    Instruction::mux_map_start(0),
    Instruction::mux_ld_end(0),
    // 03. Quickly ramp up to max brightness.
    Instruction::ramp(PreScale::CT0_488, 4, Direction::Up, 255),
    // 04. Wait ~0.5 seconds (15.625ms * 30 = 468.75ms).
    Instruction::wait(PreScale::CT15_625, 30),
    // 05. Begin ramping brightness down to half (255 - 127 = 128).
    Instruction::ramp(PreScale::CT15_625, 4, Direction::Down, 127),
    // 06. Wait ~0.5 seconds (15.625ms * 30 = 468.75ms).
    Instruction::wait(PreScale::CT15_625, 30),
    // 07. Begin ramping brightness up to max (128 + 127 = 255).
    Instruction::ramp(PreScale::CT15_625, 4, Direction::Up, 127),
    // 08. Loop back to first step of effect.
    Instruction::branch(1, 0),
  ]
}

切换效果

编程引擎支持多达96条指令,这为您设置多个效果提供了充足的空间。要切换效果

  • 暂停编程引擎
  • 更新程序计数器到下一个效果的第一个索引
  • 恢复暂停编程引擎。

示例

// Pause engine execution.
ic.set_engine_exec(Engine::E1, EngineExec::Hold)?;
ic.wait_while_engine_busy(Duration::from_millis(1))?;

// 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(Engine::E1, 42)?;

// Unpause the engine and begin the new animation.
ic.set_engine_exec(Engine::E1, EngineExec::Free)?;
ic.set_engine_mode(Engine::E1, EngineMode::RunProgram)?;

调试

写入后读取验证

可以通过以下方式启用写入后读取验证

let ic = LP55231::create(...)?;
ic.verify_writes = true;

这将导致驱动程序在每次I2C写入指令之后执行读取操作,以比较寄存器中的值。如果读取的值与写入的值不匹配,它将引发异常。

[!注意] 这在开发期间非常有用,尤其是在使用编程引擎时,编程引擎必须处于正确的内部状态,才能允许更改。

调试输出

当通过debug_enabled属性启用时,驱动程序将发出有用的(但相当冗长)输出,以帮助您理解每个读取和写入操作的寄存器状态。示例

let ic = LP55231::create(...)?;
ic.debug_enabled = true;
ic.set_enabled(true)?;

将产生以下输出

set_enabled(true) {
  00000000 << 0x00 ENABLE_ENGINE_CNTRL1
  00100000 >> 0x00 ENABLE_ENGINE_CNTRL1
}

为多个I2C调用范围调试输出

多个调试调用的作用域可以与 debug::scope! 宏结合使用

fn multiple_i2c_calls(
  ic: &mut LP55231,
  value: bool,
) -> Result<(), LinuxI2CError> {
  debug::scope!(ic, "example({})", value);
  ic.set_enabled(value)?;
  ic.set_enabled(!value)?;
  Ok(())
}

multiple_i2_calls(true)?;

将产生以下输出

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
  }
}

有关更多详细信息,请参阅 debug.rs 文档。

开始开发

  1. 克隆项目并在 VS Code 中打开文件夹
  2. 接受插件建议(非 Linux 环境中需要开发容器)
  3. 在开发容器中重新打开

[!注意] 此项目使用 hermit 来管理此项目的 Rust 工具链。无需预先安装 Rust。

待办事项

  • 在块中读取/写入页面(在 read/write_program_page 中的 at_once 参数)

依赖关系

~2MB
~44K SLoC