158 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! This example showcases channels and timing utilities of embassy.
 | |
| //!
 | |
| //! This example works best on STM32F3DISCOVERY board. It flashes a single LED, then if the USER
 | |
| //! button is pressed the next LED is flashed (in clockwise fashion). If the USER button is double
 | |
| //! clicked, then the direction changes. If the USER button is pressed for 1 second, then all the
 | |
| //! LEDS flash 3 times.
 | |
| //!
 | |
| 
 | |
| #![no_std]
 | |
| #![no_main]
 | |
| #![feature(type_alias_impl_trait)]
 | |
| 
 | |
| use defmt::*;
 | |
| use embassy_executor::Spawner;
 | |
| use embassy_stm32::exti::ExtiInput;
 | |
| use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed};
 | |
| use embassy_stm32::peripherals::PA0;
 | |
| use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
 | |
| use embassy_sync::channel::Channel;
 | |
| use embassy_time::{with_timeout, Duration, Timer};
 | |
| use {defmt_rtt as _, panic_probe as _};
 | |
| 
 | |
| struct Leds<'a> {
 | |
|     leds: [Output<'a, AnyPin>; 8],
 | |
|     direction: i8,
 | |
|     current_led: usize,
 | |
| }
 | |
| 
 | |
| impl<'a> Leds<'a> {
 | |
|     fn new(pins: [Output<'a, AnyPin>; 8]) -> Self {
 | |
|         Self {
 | |
|             leds: pins,
 | |
|             direction: 1,
 | |
|             current_led: 0,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn change_direction(&mut self) {
 | |
|         self.direction *= -1;
 | |
|     }
 | |
| 
 | |
|     fn move_next(&mut self) {
 | |
|         if self.direction > 0 {
 | |
|             self.current_led = (self.current_led + 1) & 7;
 | |
|         } else {
 | |
|             self.current_led = (8 + self.current_led - 1) & 7;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async fn show(&mut self) {
 | |
|         self.leds[self.current_led].set_high();
 | |
|         if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.recv()).await {
 | |
|             self.leds[self.current_led].set_low();
 | |
|             self.process_event(new_message).await;
 | |
|         } else {
 | |
|             self.leds[self.current_led].set_low();
 | |
|             if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.recv()).await {
 | |
|                 self.process_event(new_message).await;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async fn flash(&mut self) {
 | |
|         for _ in 0..3 {
 | |
|             for led in &mut self.leds {
 | |
|                 led.set_high();
 | |
|             }
 | |
|             Timer::after(Duration::from_millis(500)).await;
 | |
|             for led in &mut self.leds {
 | |
|                 led.set_low();
 | |
|             }
 | |
|             Timer::after(Duration::from_millis(200)).await;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async fn process_event(&mut self, event: ButtonEvent) {
 | |
|         match event {
 | |
|             ButtonEvent::SingleClick => {
 | |
|                 self.move_next();
 | |
|             }
 | |
|             ButtonEvent::DoubleClick => {
 | |
|                 self.change_direction();
 | |
|                 self.move_next();
 | |
|             }
 | |
|             ButtonEvent::Hold => {
 | |
|                 self.flash().await;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Format)]
 | |
| enum ButtonEvent {
 | |
|     SingleClick,
 | |
|     DoubleClick,
 | |
|     Hold,
 | |
| }
 | |
| 
 | |
| static CHANNEL: Channel<ThreadModeRawMutex, ButtonEvent, 4> = Channel::new();
 | |
| 
 | |
| #[embassy_executor::main]
 | |
| async fn main(spawner: Spawner) {
 | |
|     let p = embassy_stm32::init(Default::default());
 | |
|     let button = Input::new(p.PA0, Pull::Down);
 | |
|     let button = ExtiInput::new(button, p.EXTI0);
 | |
|     info!("Press the USER button...");
 | |
|     let leds = [
 | |
|         Output::new(p.PE9.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE10.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE11.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE12.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE13.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE14.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE15.degrade(), Level::Low, Speed::Low),
 | |
|         Output::new(p.PE8.degrade(), Level::Low, Speed::Low),
 | |
|     ];
 | |
|     let leds = Leds::new(leds);
 | |
| 
 | |
|     spawner.spawn(button_waiter(button)).unwrap();
 | |
|     spawner.spawn(led_blinker(leds)).unwrap();
 | |
| }
 | |
| 
 | |
| #[embassy_executor::task]
 | |
| async fn led_blinker(mut leds: Leds<'static>) {
 | |
|     loop {
 | |
|         leds.show().await;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[embassy_executor::task]
 | |
| async fn button_waiter(mut button: ExtiInput<'static, PA0>) {
 | |
|     const DOUBLE_CLICK_DELAY: u64 = 250;
 | |
|     const HOLD_DELAY: u64 = 1000;
 | |
| 
 | |
|     button.wait_for_rising_edge().await;
 | |
|     loop {
 | |
|         if with_timeout(Duration::from_millis(HOLD_DELAY), button.wait_for_falling_edge())
 | |
|             .await
 | |
|             .is_err()
 | |
|         {
 | |
|             info!("Hold");
 | |
|             CHANNEL.send(ButtonEvent::Hold).await;
 | |
|             button.wait_for_falling_edge().await;
 | |
|         } else if with_timeout(Duration::from_millis(DOUBLE_CLICK_DELAY), button.wait_for_rising_edge())
 | |
|             .await
 | |
|             .is_err()
 | |
|         {
 | |
|             info!("Single click");
 | |
|             CHANNEL.send(ButtonEvent::SingleClick).await;
 | |
|         } else {
 | |
|             info!("Double click");
 | |
|             CHANNEL.send(ButtonEvent::DoubleClick).await;
 | |
|             button.wait_for_falling_edge().await;
 | |
|         }
 | |
|         button.wait_for_rising_edge().await;
 | |
|     }
 | |
| }
 |