diff --git a/Cargo.toml b/Cargo.toml index f099c99..1b2fad5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vorago-reb1" -version = "0.1.1" +version = "0.2.0" authors = ["Robin Mueller "] edition = "2021" description = "Board Support Crate for the Vorago REB1 development board" diff --git a/examples/blinky-button-irq.rs b/examples/blinky-button-irq.rs index bf01274..c46b057 100644 --- a/examples/blinky-button-irq.rs +++ b/examples/blinky-button-irq.rs @@ -2,17 +2,124 @@ #![no_main] #![no_std] +use core::cell::{Cell, RefCell}; + +use cortex_m::interrupt::Mutex; use cortex_m_rt::entry; use panic_halt as _; use rtt_target::{rprintln, rtt_init_print}; -use va108xx_hal::pac::interrupt; +use va108xx_hal::{ + clock::{set_clk_div_register, FilterClkSel}, + gpio::{FilterType, InterruptEdge, PinsA}, + pac::{self, interrupt}, + prelude::*, + time::Hertz, + timer::set_up_ms_timer, +}; +use vorago_reb1::button::Button; +use vorago_reb1::leds::Leds; + +static LEDS: Mutex>> = Mutex::new(RefCell::new(None)); +static MS_COUNTER: Mutex> = Mutex::new(Cell::new(0)); +static BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); + +#[derive(Debug, PartialEq)] +pub enum PressMode { + Toggle, + Keep, +} + +// You can change the press mode here +const PRESS_MODE: PressMode = PressMode::Keep; #[entry] fn main() -> ! { rtt_init_print!(); - rprintln!("-- Vorago Button Blinky --"); + rprintln!("-- Vorago Button IRQ Example --"); + let mut dp = pac::Peripherals::take().unwrap(); + let pinsa = PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA); + let edge_irq = match PRESS_MODE { + PressMode::Toggle => InterruptEdge::HighToLow, + PressMode::Keep => InterruptEdge::BothEdges, + }; + + // Configure an edge interrupt on the button and route it to interrupt vector 15 + let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq( + edge_irq, + Some(&mut dp.SYSCONFIG), + &mut dp.IRQSEL, + pac::interrupt::OC15, + ); + + if PRESS_MODE == PressMode::Toggle { + // This filter debounces the switch for edge based interrupts + button = button.filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); + set_clk_div_register( + &mut dp.SYSCONFIG, + FilterClkSel::Clk1, + Hertz::from(50.khz()).0, + ); + } + + set_up_ms_timer( + &mut dp.SYSCONFIG, + &mut dp.IRQSEL, + 50.mhz().into(), + dp.TIM0, + interrupt::OC0, + ); + let mut leds = Leds::new( + pinsa.pa10.into_push_pull_output(), + pinsa.pa7.into_push_pull_output(), + pinsa.pa6.into_push_pull_output(), + ); + for led in leds.iter_mut() { + led.off(); + } + // Activate the IRQs so the processors sees them as well + unmask_irqs(); + // Make both button and LEDs accessible from the IRQ handler as well + cortex_m::interrupt::free(|cs| { + LEDS.borrow(cs).replace(Some(leds)); + BUTTON.borrow(cs).replace(Some(button)); + }); loop {} } +fn unmask_irqs() { + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC15); + } +} + #[interrupt] -fn OC15() {} +fn OC0() { + cortex_m::interrupt::free(|cs| { + let mut ms = MS_COUNTER.borrow(cs).get(); + ms += 1; + MS_COUNTER.borrow(cs).set(ms); + }); +} + +#[interrupt] +fn OC15() { + cortex_m::interrupt::free(|cs| { + if PRESS_MODE == PressMode::Toggle { + if let Some(ref mut leds) = LEDS.borrow(cs).borrow_mut().as_deref_mut() { + leds[0].toggle(); + } + } else { + if let (Some(ref mut leds), Some(ref button)) = ( + LEDS.borrow(cs).borrow_mut().as_deref_mut(), + BUTTON.borrow(cs).borrow().as_ref(), + ) { + if button.released() { + leds[0].off(); + } else { + leds[0].on(); + } + } + } + }); +} diff --git a/examples/blinky-leds.rs b/examples/blinky-leds.rs index add25fe..7df72fa 100644 --- a/examples/blinky-leds.rs +++ b/examples/blinky-leds.rs @@ -84,7 +84,12 @@ fn main() -> ! { } } LibType::Bsp => { - let mut leds = Leds::new(PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA)); + let pinsa = PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA); + let mut leds = Leds::new( + pinsa.pa10.into_push_pull_output(), + pinsa.pa7.into_push_pull_output(), + pinsa.pa6.into_push_pull_output(), + ); loop { for _ in 0..10 { // Blink all LEDs quickly diff --git a/src/button.rs b/src/button.rs new file mode 100644 index 0000000..dab5f44 --- /dev/null +++ b/src/button.rs @@ -0,0 +1,65 @@ +//! # API for the REB1 button +//! +//! ## Examples +//! +//! - [Button Blinky with Interrupts](https://github.com/robamu-org/vorago-reb1-rs/blob/main/examples/blinky-button-irq.rs) +use va108xx_hal::{ + gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, + pac, + prelude::*, +}; + +pub struct Button { + button: Pin, +} + +impl Button { + pub fn new(pin: Pin) -> Button { + Button { button: pin } + } + + pub fn pressed(&self) -> bool { + self.button.is_low().ok().unwrap() + } + + pub fn released(&self) -> bool { + self.button.is_high().ok().unwrap() + } + + /// Configures an IRQ on edge. + /// + /// Please note that you still have to unpend the Cortex-M interrupt yourself + pub fn edge_irq( + mut self, + edge_type: InterruptEdge, + syscfg: Option<&mut pac::SYSCONFIG>, + irqsel: &mut pac::IRQSEL, + irq: pac::interrupt, + ) -> Self { + self.button = self.button.interrupt_edge(edge_type, syscfg, irqsel, irq); + self + } + + /// Configures an IRQ on level. + /// + /// Please note that you still have to unpend the Cortex-M interrupt yourself + pub fn level_irq( + mut self, + level: InterruptLevel, + syscfg: Option<&mut pac::SYSCONFIG>, + irqsel: &mut pac::IRQSEL, + irq: pac::interrupt, + ) -> Self { + self.button = self.button.interrupt_level(level, syscfg, irqsel, irq); + self + } + + /// Configures a filter on the button. This can be useful for debouncing the switch. + /// + /// Please note that you still have to set a clock divisor yourself using the + /// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work. + pub fn filter_type(mut self, filter: FilterType, clksel: FilterClkSel) -> Self { + self.button = self.button.filter_type(filter, clksel); + self + } +} diff --git a/src/leds.rs b/src/leds.rs index fe04e2b..46a68b7 100644 --- a/src/leds.rs +++ b/src/leds.rs @@ -1,6 +1,11 @@ +//! # API for using the REB1 LEDs +//! +//! ## Examples +//! +//! - [LED example](https://github.com/robamu-org/vorago-reb1-rs/blob/main/examples/blinky-leds.rs) use va108xx_hal::{ gpio::dynpins::DynPin, - gpio::pins::{Pin, PinsA, PushPullOutput, PA10, PA6, PA7}, + gpio::pins::{Pin, PushPullOutput, PA10, PA6, PA7}, prelude::*, }; @@ -13,12 +18,9 @@ pub struct Leds { } impl Leds { - pub fn new(led_parts: PinsA) -> Self { - let led2 = led_parts.pa10.into_push_pull_output(); - let led3 = led_parts.pa7.into_push_pull_output(); - let led4 = led_parts.pa6.into_push_pull_output(); + pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds { Leds { - leds: [led2.into(), led3.into(), led4.into()], + leds: [led_pin1.into(), led_pin2.into(), led_pin3.into()], } } } @@ -72,14 +74,14 @@ macro_rules! ctor { ctor!(LD2, LD3, LD4); impl Led { - /// Turns the LED off + /// Turns the LED off. Setting the pin high actually turns the LED off pub fn off(&mut self) { - self.pin.set_low().ok(); + self.pin.set_high().ok(); } - /// Turns the LED on + /// Turns the LED on. Setting the pin low actually turns the LED on pub fn on(&mut self) { - self.pin.set_high().ok(); + self.pin.set_low().ok(); } /// Toggles the LED diff --git a/src/lib.rs b/src/lib.rs index 8ec1ae9..4ccc9a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ #![no_std] +pub mod button; pub mod leds;