254 lines
7.1 KiB
Rust
254 lines
7.1 KiB
Rust
//! This example demonstrates the usage of async GPIO operations on VA108xx.
|
|
//!
|
|
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally tie the PB22 to PB23 pins well
|
|
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
|
|
#![no_std]
|
|
#![no_main]
|
|
// This imports the logger and the panic handler.
|
|
use embassy_example as _;
|
|
|
|
use embassy_executor::Spawner;
|
|
use embassy_sync::channel::{Receiver, Sender};
|
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
|
use embassy_time::{Duration, Instant, Timer};
|
|
use embedded_hal_async::digital::Wait;
|
|
use va108xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync};
|
|
use va108xx_hal::gpio::{Input, Output, PinState, Port};
|
|
use va108xx_hal::pins::{PinsA, PinsB};
|
|
use va108xx_hal::{
|
|
pac::{self, interrupt},
|
|
prelude::*,
|
|
};
|
|
|
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
|
|
|
const CHECK_PA0_TO_PA1: bool = true;
|
|
const CHECK_PB22_TO_PB23: bool = false;
|
|
|
|
// Can also be set to OC10 and works as well.
|
|
const PB22_TO_PB23_IRQ: pac::Interrupt = pac::Interrupt::OC11;
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct GpioCmd {
|
|
cmd_type: GpioCmdType,
|
|
after_delay: u32,
|
|
}
|
|
|
|
impl GpioCmd {
|
|
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
|
|
Self {
|
|
cmd_type,
|
|
after_delay,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub enum GpioCmdType {
|
|
SetHigh,
|
|
SetLow,
|
|
RisingEdge,
|
|
FallingEdge,
|
|
}
|
|
|
|
// Declare a bounded channel of 3 u32s.
|
|
static CHANNEL_PA0_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
|
static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
|
|
|
#[embassy_executor::main]
|
|
async fn main(spawner: Spawner) {
|
|
defmt::println!("-- VA108xx Async GPIO Demo --");
|
|
|
|
let mut dp = pac::Peripherals::take().unwrap();
|
|
|
|
// Safety: Only called once here.
|
|
va108xx_embassy::init(
|
|
&mut dp.sysconfig,
|
|
&dp.irqsel,
|
|
SYSCLK_FREQ,
|
|
dp.tim23,
|
|
dp.tim22,
|
|
);
|
|
|
|
let porta = PinsA::new(dp.porta);
|
|
let portb = PinsB::new(dp.portb);
|
|
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
|
let out_pa0 = Output::new(porta.pa0, PinState::Low);
|
|
let in_pa1 = Input::new_floating(porta.pa1);
|
|
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
|
let in_pb23 = Input::new_floating(portb.pb23);
|
|
|
|
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
|
let in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
|
|
|
spawner
|
|
.spawn(output_task(
|
|
"PA0 to PA1",
|
|
out_pa0,
|
|
CHANNEL_PA0_PA1.receiver(),
|
|
))
|
|
.unwrap();
|
|
spawner
|
|
.spawn(output_task(
|
|
"PB22 to PB23",
|
|
out_pb22,
|
|
CHANNEL_PB22_TO_PB23.receiver(),
|
|
))
|
|
.unwrap();
|
|
|
|
if CHECK_PA0_TO_PA1 {
|
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
|
|
defmt::info!("Example PA0 to PA1 done");
|
|
}
|
|
if CHECK_PB22_TO_PB23 {
|
|
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
|
|
.await;
|
|
defmt::info!("Example PB22 to PB23 done");
|
|
}
|
|
|
|
defmt::info!("Example done, toggling LED0");
|
|
loop {
|
|
led0.toggle();
|
|
Timer::after(Duration::from_millis(500)).await;
|
|
}
|
|
}
|
|
|
|
async fn check_pin_to_pin_async_ops(
|
|
ctx: &'static str,
|
|
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
|
mut async_input: impl Wait,
|
|
) {
|
|
defmt::info!(
|
|
"{}: sending SetHigh command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
|
async_input.wait_for_high().await.unwrap();
|
|
defmt::info!(
|
|
"{}: Input pin is high now ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
|
|
defmt::info!(
|
|
"{}: sending SetLow command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
|
async_input.wait_for_low().await.unwrap();
|
|
defmt::info!(
|
|
"{}: Input pin is low now ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
|
|
defmt::info!(
|
|
"{}: sending RisingEdge command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
|
async_input.wait_for_rising_edge().await.unwrap();
|
|
defmt::info!(
|
|
"{}: input pin had rising edge ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
|
|
defmt::info!(
|
|
"{}: sending Falling command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender
|
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
|
.await;
|
|
async_input.wait_for_falling_edge().await.unwrap();
|
|
defmt::info!(
|
|
"{}: input pin had a falling edge ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
|
|
defmt::info!(
|
|
"{}: sending Falling command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender
|
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
|
.await;
|
|
async_input.wait_for_any_edge().await.unwrap();
|
|
defmt::info!(
|
|
"{}: input pin had a falling (any) edge ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
|
|
defmt::info!(
|
|
"{}: sending Falling command ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
|
async_input.wait_for_any_edge().await.unwrap();
|
|
defmt::info!(
|
|
"{}: input pin had a rising (any) edge ({} ms)",
|
|
ctx,
|
|
Instant::now().as_millis()
|
|
);
|
|
}
|
|
|
|
#[embassy_executor::task(pool_size = 2)]
|
|
async fn output_task(
|
|
ctx: &'static str,
|
|
mut out: Output,
|
|
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
|
) {
|
|
loop {
|
|
let next_cmd = receiver.receive().await;
|
|
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
|
match next_cmd.cmd_type {
|
|
GpioCmdType::SetHigh => {
|
|
defmt::info!("{}: Set output high", ctx);
|
|
out.set_high();
|
|
}
|
|
GpioCmdType::SetLow => {
|
|
defmt::info!("{}: Set output low", ctx);
|
|
out.set_low();
|
|
}
|
|
GpioCmdType::RisingEdge => {
|
|
defmt::info!("{}: Rising edge", ctx);
|
|
if !out.is_set_low() {
|
|
out.set_low();
|
|
}
|
|
out.set_high();
|
|
}
|
|
GpioCmdType::FallingEdge => {
|
|
defmt::info!("{}: Falling edge", ctx);
|
|
if !out.is_set_high() {
|
|
out.set_high();
|
|
}
|
|
out.set_low();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
|
|
#[interrupt]
|
|
#[allow(non_snake_case)]
|
|
fn OC10() {
|
|
on_interrupt_for_async_gpio_for_port(Port::A);
|
|
on_interrupt_for_async_gpio_for_port(Port::B);
|
|
}
|
|
|
|
// This interrupt only handles PORT B interrupts.
|
|
#[interrupt]
|
|
#[allow(non_snake_case)]
|
|
fn OC11() {
|
|
on_interrupt_for_async_gpio_for_port(Port::B);
|
|
}
|