I am new to rust and learning to using it for embedded.
I am having some trouble trying to drive a ws2812 led strip using timer interrupt request on pi pico:
- How could I use the
timer
twice: one for setting up theTIMER_IRQ_1
, one for ws2812count_down
. rust won't let me due totimer
was moved when I use it the second time for thecount_down
- The
timer
does not live long enough when I implements it as below. How do I keep thetimer
live longer?
Any help is appreciated.
The code:
//! # interrupt request demo for rp-pico
#![no_std]
#![no_main]
use core::cell::RefCell;
use critical_section::Mutex;
use embedded_hal::digital::v2::OutputPin;
use fugit::ExtU32;
use panic_halt as _;
use rp_pico::{
entry,
hal::{
self,
gpio::{
self, bank0::Gpio27, FunctionPio0, FunctionSioInput, FunctionSioOutput,
Interrupt::EdgeLow, Pin, PullDown, PullUp,
},
pac::{self, interrupt},
pio::{PIOExt, SM0},
prelude::*,
timer::{Alarm, Alarm1, CountDown, Timer},
Watchdog,
},
pac::PIO0,
};
use smart_leds::{
// brightness,
colors,
// SmartLedsWrite,
RGB8,
};
use ws2812_pio::Ws2812;
const STRIP_LEN: usize = 16;
type ActionBtn1 = gpio::Pin<gpio::bank0::Gpio20, FunctionSioInput, PullUp>;
type ActionBtn2 = gpio::Pin<gpio::bank0::Gpio21, FunctionSioInput, PullUp>;
type LedPin = gpio::Pin<gpio::bank0::Gpio25, FunctionSioOutput, PullDown>;
type ActionBtns = (ActionBtn1, ActionBtn2);
type NeoStripCtl<'timer> = (Alarm1, LedPin, Ws<'timer>);
type LedStrip = [RGB8; STRIP_LEN];
type Ws<'timer> = Ws2812<PIO0, SM0, CountDown<'timer>, Pin<Gpio27, FunctionPio0, PullDown>>;
static G_BTNS: Mutex<RefCell<Option<ActionBtns>>> = Mutex::new(RefCell::new(None));
Mutex::new(RefCell::new(None));
// Global variables for controlling neo led strip
static G_NEO_CTL: Mutex<RefCell<Option<NeoStripCtl>>> = Mutex::new(RefCell::new(None));
static G_LED_STRIP: Mutex<RefCell<Option<LedStrip>>> = Mutex::new(RefCell::new(None));
const FRAME_DELAY: u32 = 32; // ~30FPS
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
// Configure the clocks
// The default is to generate a 125 MHz system clock
let clocks = hal::clocks::init_clocks_and_plls(
rp_pico::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);
// Set the pins up according to their function on this particular board
let pins = rp_pico::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let led_pin = pins.led.reconfigure();
// Reconfigure action buttons and enable IRQ on high-to-low
let action_btn_1 = pins.gpio20.reconfigure();
action_btn_1.set_interrupt_enabled(EdgeLow, true);
let action_btn_2 = pins.gpio21.reconfigure();
action_btn_2.set_interrupt_enabled(EdgeLow, true);
let mut timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
// --------- binding `timer` declared here
let mut alarm1 = timer.alarm_1().unwrap();
alarm1.schedule(FRAME_DELAY.micros()).unwrap();
alarm1.enable_interrupt();
// LED strip initialized
let leds: [RGB8; STRIP_LEN] = [colors::BLACK; STRIP_LEN];
// Split the PIO state machine 0 into individual objects, so that
// Ws2812 can use it:
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let ws = Ws2812::new(
pins.gpio27.into_function(),
&mut pio,
sm0,
clocks.peripheral_clock.freq(),
timer.count_down(),
);
// Move variables to global
critical_section::with(|cs| {
G_BTNS
.borrow(cs)
.replace(Some((action_btn_1, action_btn_2)));
G_NEO_CTL.borrow(cs).replace(Some((alarm1, led_pin, ws)));
// -------------------- argument requires that `timer` is borrowed for `'static`
G_LED_STRIP.borrow(cs).replace(Some(leds));
});
// unmask interrupt controllers
unsafe {
pac::NVIC::unmask(pac::Interrupt::IO_IRQ_BANK0);
pac::NVIC::unmask(pac::Interrupt::TIMER_IRQ_1);
}
loop {
cortex_m::asm::wfi();
}
}
// - `timer` dropped here while still borrowed
#[interrupt]
fn IO_IRQ_BANK0() {
// Do something when btn is pressed
}
#[interrupt]
fn TIMER_IRQ_1() {
static mut NEO_STRIP_CTL: Option<NeoStripCtl> = None;
static mut LED_STRIP: Option<LedStrip> = None;
// lazy load global variables
if LED_STRIP.is_none() {
critical_section::with(|cs| {
*LED_STRIP = G_LED_STRIP.borrow(cs).take();
})
}
if NEO_STRIP_CTL.is_none() {
critical_section::with(|cs| {
*NEO_STRIP_CTL = G_NEO_CTL.borrow(cs).take();
})
}
if let Some((alarm1, led_pin, ws)) = NEO_STRIP_CTL {
// lit up statup led before doing some work
led_pin.set_high().unwrap();
// Do something with ws
// reschedule alarm1
alarm1.clear_interrupt();
alarm1.schedule(FRAME_DELAY.micros()).unwrap();
// turm off status led when done
led_pin.set_low().unwrap();
}
}