Add led drivers

This commit is contained in:
Mona Mayrhofer 2025-06-26 16:05:13 +02:00
parent c1b98bb7d0
commit e023814626
No known key found for this signature in database
GPG key ID: 3E2BDA732A957188
4 changed files with 275 additions and 12 deletions

15
.zed/tasks.json Normal file
View file

@ -0,0 +1,15 @@
// Project tasks configuration. See https://zed.dev/docs/tasks for documentation.
//
// Example:
[
{
"label": "cargo run --release [roleplay-microcontroller]",
"command": "cargo",
"args": ["run", "--release"],
"use_new_terminal": false,
"allow_concurrent_runs": false,
"reveal": "always",
"hide": "never",
"shell": "system"
}
]

67
Cargo.lock generated
View file

@ -107,7 +107,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f377753756ec12e76b52d2dd657437be0448cc9736402ffadd0b8b8b9602c8a1"
dependencies = [
"embassy-sync",
"embassy-sync 0.6.2",
"embedded-io",
"embedded-io-async",
"futures-intrusive",
@ -215,7 +215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12"
dependencies = [
"embassy-futures",
"embassy-sync",
"embassy-sync 0.6.2",
"embassy-time",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
@ -262,7 +262,7 @@ checksum = "ed041cc19a603d657124fddefdcbe5ef8bd60e77d972793ebb57de93394f5949"
dependencies = [
"document-features",
"embassy-net-driver",
"embassy-sync",
"embassy-sync 0.6.2",
"embassy-time",
"embedded-io-async",
"embedded-nal-async",
@ -291,6 +291,20 @@ dependencies = [
"heapless",
]
[[package]]
name = "embassy-sync"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136"
dependencies = [
"cfg-if",
"critical-section",
"embedded-io-async",
"futures-sink",
"futures-util",
"heapless",
]
[[package]]
name = "embassy-time"
version = "0.4.0"
@ -499,7 +513,7 @@ dependencies = [
"document-features",
"embassy-embedded-hal",
"embassy-futures",
"embassy-sync",
"embassy-sync 0.6.2",
"embedded-can",
"embedded-hal 1.0.0",
"embedded-hal-async",
@ -537,7 +551,7 @@ dependencies = [
"critical-section",
"document-features",
"embassy-executor",
"embassy-sync",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time-driver",
"embassy-time-queue-utils",
@ -612,7 +626,7 @@ dependencies = [
"critical-section",
"document-features",
"embassy-net-driver",
"embassy-sync",
"embassy-sync 0.6.2",
"embedded-io",
"embedded-io-async",
"enumset",
@ -874,7 +888,9 @@ dependencies = [
"bleps",
"critical-section",
"embassy-executor",
"embassy-futures",
"embassy-net",
"embassy-sync 0.7.0",
"embassy-time",
"embedded-io",
"embedded-io-async",
@ -885,8 +901,10 @@ dependencies = [
"esp-wifi",
"heapless",
"log",
"smart-leds",
"smoltcp",
"static_cell",
"ws2812-spi",
]
[[package]]
@ -1081,6 +1099,15 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rgb"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
dependencies = [
"bytemuck",
]
[[package]]
name = "riscv"
version = "0.12.1"
@ -1163,6 +1190,24 @@ dependencies = [
"serde",
]
[[package]]
name = "smart-leds"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66df34e571fa9993fa6f99131a374d58ca3d694b75f9baac93458fe0d6057bf0"
dependencies = [
"smart-leds-trait",
]
[[package]]
name = "smart-leds-trait"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edeb89c73244414bb0568611690dd095b2358b3fda5bae65ad784806cca00157"
dependencies = [
"rgb",
]
[[package]]
name = "smoltcp"
version = "0.12.0"
@ -1502,6 +1547,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "ws2812-spi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fd98e2b649252eced2ec3aa8d5048e7d2ac294276b0567939bbf47741f9934"
dependencies = [
"embedded-hal 1.0.0",
"smart-leds-trait",
]
[[package]]
name = "xtensa-lx"
version = "0.10.0"

View file

@ -4,8 +4,18 @@ version = "0.1.0"
edition = "2024"
[[bin]]
name = "uwu"
name = "m5stamp_c3_ws2812"
path = "./src/bin/main.rs"
test = false
doctest = false
bench = false
[lib]
name = "m5stamp_c3_ws2812"
path = "./src/lib.rs"
test = false
doctest = false
bench = false
[dependencies]
embassy-net = { version = "0.6.0", features = [
@ -52,6 +62,10 @@ esp-wifi = { version = "0.13.0", features = [
] }
heapless = { version = "0.8.0", default-features = false }
static_cell = { version = "2.1.0", features = ["nightly"] }
smart-leds = "0.4.0"
ws2812-spi = "0.5.0"
embassy-futures = "0.1.1"
embassy-sync = "0.7.0"
[profile.dev]
# Rust debug is too slow.

View file

@ -1,12 +1,25 @@
#![no_std]
#![no_main]
use core::pin::Pin;
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use embassy_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::channel::{Channel, Sender};
use embassy_time::{Duration, Instant, Timer};
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::peripherals::GPIO;
use esp_hal::spi::master::{Config, Spi};
use esp_hal::time::Rate;
use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup;
use log::info;
use smart_leds::RGB8;
use smart_leds::SmartLedsWrite;
use static_cell::StaticCell;
use ws2812_spi::Ws2812;
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
@ -15,8 +28,11 @@ fn panic(_: &core::panic::PanicInfo) -> ! {
extern crate alloc;
static CHANNEL: StaticCell<Channel<NoopRawMutex, i32, 2>> = StaticCell::new();
#[esp_hal_embassy::main]
async fn main(spawner: Spawner) {
let channel = CHANNEL.init(Channel::new());
// generator version: 0.3.1
esp_println::logger::init_logger_from_env();
@ -39,13 +55,176 @@ async fn main(spawner: Spawner) {
)
.unwrap();
// TODO: Spawn some tasks
let _ = spawner;
spawner
.spawn(listen_rotary_encoder(
RotaryEncoderConfig {
clk: Input::new(
peripherals.GPIO8,
InputConfig::default().with_pull(Pull::Up),
),
ot: Input::new(
peripherals.GPIO10,
InputConfig::default().with_pull(Pull::Up),
),
},
channel.sender(),
))
.unwrap();
let mut led_strip = {
let spi = Spi::new(
peripherals.SPI2,
Config::default().with_frequency(Rate::from_mhz(3)),
)
.unwrap()
.with_mosi(peripherals.GPIO4);
Ws2812::new(spi)
};
let colors = [
RGB8 { r: 0, g: 255, b: 0 },
RGB8 { r: 255, g: 0, b: 0 },
RGB8 { r: 255, g: 0, b: 0 },
RGB8 {
r: 0,
g: 255,
b: 255,
},
RGB8 {
r: 255,
g: 0,
b: 255,
},
RGB8 {
r: 255,
g: 255,
b: 0,
},
];
let mut color_idx = 0;
loop {
info!("Hello world!");
Timer::after(Duration::from_secs(1)).await;
let delta = channel.receive().await;
color_idx += delta;
// led_strip
// .write([
// colors[color_idx % colors.len()],
// colors[(color_idx + 1) % colors.len()],
// colors[(color_idx + 2) % colors.len()],
// colors[(color_idx + 3) % colors.len()],
// ])
// .unwrap();
//
let cnt = 4;
led_strip
.write((0..cnt).map(|it| {
if it <= (color_idx % cnt) {
RGB8 {
r: 255,
g: 50,
b: 00,
}
} else {
RGB8 { r: 0, g: 0, b: 0 }
}
}))
.unwrap();
}
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin
}
pub struct Throttler<'a> {
input: Input<'a>,
throttle: Duration,
last_fire: Option<Instant>,
}
impl<'a> Throttler<'a> {
pub fn new(input: Input<'a>, throttle: Duration) -> Self {
Self {
input,
throttle,
last_fire: None,
}
}
pub async fn wait_for(&mut self) {
if let Some(allow_next_fire) = self
.last_fire
.and_then(|last_fire| last_fire.checked_add(self.throttle))
{
Timer::at(allow_next_fire).await;
}
self.input.wait_for_falling_edge().await;
self.last_fire = Some(Instant::now());
}
}
struct RotaryEncoderConfig<'a, 'b> {
clk: Input<'a>,
ot: Input<'b>,
}
#[derive(Clone, Copy, Debug)]
enum RotaryState {
Clk,
Ot,
}
#[embassy_executor::task]
async fn listen_rotary_encoder(
mut config: RotaryEncoderConfig<'static, 'static>,
channel: Sender<'static, NoopRawMutex, i32, 2>,
) {
info!("Waiting for rotary.");
let mut state: Option<RotaryState> = None;
let mut clk_input = Throttler::new(config.clk, Duration::from_millis(50));
let mut ot_input = Throttler::new(config.ot, Duration::from_millis(50));
loop {
let clk = async {
clk_input.wait_for().await;
};
let ot = async {
ot_input.wait_for().await;
};
let next_state = match select(clk, ot).await {
Either::First(_) => (RotaryState::Clk),
Either::Second(_) => (RotaryState::Ot),
};
//info!("State: {next_state:?}");
match (state, next_state) {
(None, RotaryState::Ot) => {
if clk_input.input.is_high() {
state = Some(RotaryState::Ot);
}
}
(None, RotaryState::Clk) => {
if ot_input.input.is_high() {
state = Some(RotaryState::Clk);
}
}
(Some(RotaryState::Clk), RotaryState::Ot) => {
info!("Clk -> Ot");
state = None;
channel.send(1).await;
}
(Some(RotaryState::Ot), RotaryState::Clk) => {
info!("Ot -> Clk");
state = None;
channel.send(-1).await;
}
(Some(RotaryState::Ot), RotaryState::Ot)
| (Some(RotaryState::Clk), RotaryState::Clk) => {}
}
}
}