diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 98625c611b4..42fed381919 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -63,7 +63,7 @@ ufmt-write = { version = "0.1.0", optional = true } # corresponding feature. esp32 = { version = "0.28.0", features = ["critical-section"], optional = true } esp32c2 = { version = "0.17.0", features = ["critical-section"], optional = true } -esp32c3 = { version = "0.20.0", features = ["critical-section"], optional = true } +esp32c3 = { path = "/projects/esp/esp-pacs/esp32c3", version = "0.20.0", features = ["critical-section"], optional = true } esp32c6 = { version = "0.11.0", features = ["critical-section"], optional = true } esp32h2 = { version = "0.7.0", features = ["critical-section"], optional = true } esp32p4 = { git = "https://github.com/esp-rs/esp-pacs", rev = "c801d10", optional = true } diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index a49b14487ec..20193308ba3 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -27,10 +27,15 @@ use core::{convert::Infallible, marker::PhantomData}; #[cfg(any(adc, dac))] pub(crate) use crate::analog; pub(crate) use crate::gpio; -use crate::peripherals::{GPIO, IO_MUX}; #[cfg(any(xtensa, esp32c3))] pub(crate) use crate::rtc_pins; pub use crate::soc::gpio::*; +#[cfg(feature = "async")] +use crate::AsyncMode; +use crate::{ + peripherals::{GPIO, IO_MUX}, + BlockingMode, +}; /// Convenience type-alias for a no-pin / don't care - pin pub type NoPinType = Gpio0; @@ -1485,19 +1490,38 @@ impl embedded_hal_async::digital::Wait for AnyPin> { } /// General Purpose Input/Output driver -pub struct IO { +pub struct IO { _io_mux: IO_MUX, pub pins: Pins, + _mode: PhantomData, } -impl IO { - pub fn new(gpio: GPIO, io_mux: IO_MUX) -> Self { +#[cfg(feature = "async")] +impl IO { + pub fn new_async(mut gpio: GPIO, io_mux: IO_MUX) -> IO { + crate::peripherals::bind_gpio_interrupt(&mut gpio, asynch::handle_gpio_interrupt); let pins = gpio.split(); - let io = IO { + IO { _io_mux: io_mux, pins, - }; - io + _mode: PhantomData::default(), + } + } +} + +impl IO { + pub fn new(gpio: GPIO, io_mux: IO_MUX) -> IO { + let pins = gpio.split(); + IO { + _io_mux: io_mux, + pins, + _mode: PhantomData::default(), + } + } + + pub fn set_interrupt_handler(&mut self, handler: unsafe extern "C" fn() -> ()) { + let mut gpio = unsafe { crate::peripherals::GPIO::steal() }; + crate::peripherals::bind_gpio_interrupt(&mut gpio, handler); } } @@ -2734,19 +2758,8 @@ mod asynch { }); } - #[cfg(not(any(esp32p4)))] - #[interrupt] - unsafe fn GPIO() { - handle_gpio_interrupt(); - } - - #[cfg(any(esp32p4))] - #[interrupt] - unsafe fn GPIO_INT0() { - handle_gpio_interrupt(); - } - - fn handle_gpio_interrupt() { + #[handler] + pub(super) fn handle_gpio_interrupt() { let intrs_bank0 = InterruptStatusRegisterAccessBank0::interrupt_status_read(); #[cfg(any(esp32, esp32s2, esp32s3, esp32p4))] diff --git a/esp-hal-common/src/interrupt/riscv.rs b/esp-hal-common/src/interrupt/riscv.rs index 8586a9c520c..0df13b73a9c 100644 --- a/esp-hal-common/src/interrupt/riscv.rs +++ b/esp-hal-common/src/interrupt/riscv.rs @@ -196,6 +196,13 @@ mod vectored { Ok(()) } + /// Bind the given interrupt to the given handler + pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) { + let ptr = &peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _ + as *mut unsafe extern "C" fn() -> (); + ptr.write_volatile(handler); + } + /// Enables an interrupt at a given priority, maps it to the given CPU /// interrupt and assigns the given priority. /// diff --git a/esp-hal-common/src/interrupt/xtensa.rs b/esp-hal-common/src/interrupt/xtensa.rs index 2f041a5c99a..ae653488ea5 100644 --- a/esp-hal-common/src/interrupt/xtensa.rs +++ b/esp-hal-common/src/interrupt/xtensa.rs @@ -308,6 +308,13 @@ mod vectored { Ok(()) } + /// Bind the given interrupt to the given handler + pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) { + let ptr = &peripherals::__INTERRUPTS[interrupt as usize]._handler as *const _ + as *mut unsafe extern "C" fn() -> (); + ptr.write_volatile(handler); + } + fn interrupt_level_to_cpu_interrupt( level: Priority, is_edge: bool, diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 28c3996a5a2..fcefe29a3a4 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -450,3 +450,11 @@ impl FlashSafeDma { self.inner } } + +/// Driver mode async +#[non_exhaustive] +pub struct AsyncMode; + +/// Driver mode non-async +#[non_exhaustive] +pub struct BlockingMode; diff --git a/esp-hal-common/src/peripheral.rs b/esp-hal-common/src/peripheral.rs index 53e85c38575..45109ebe2f3 100644 --- a/esp-hal-common/src/peripheral.rs +++ b/esp-hal-common/src/peripheral.rs @@ -225,7 +225,7 @@ mod peripheral_macros { #[doc(hidden)] #[macro_export] macro_rules! peripherals { - ($($(#[$cfg:meta])? $name:ident <= $from_pac:tt),*$(,)?) => { + ($($(#[$cfg:meta])? $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? ),*$(,)?) => { /// Contains the generated peripherals which implement [`Peripheral`] mod peripherals { @@ -282,6 +282,18 @@ mod peripheral_macros { $( pub use peripherals::$name; )* + + $( + $( + $( + paste::paste!{ + pub fn [](_peripheral: &mut peripherals::$name, handler: unsafe extern "C" fn() -> ()) { + unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); } + } + } + )* + )* + )* } } diff --git a/esp-hal-common/src/soc/esp32c3/peripherals.rs b/esp-hal-common/src/soc/esp32c3/peripherals.rs index 0a744d6aec4..5d1c9e229aa 100644 --- a/esp-hal-common/src/soc/esp32c3/peripherals.rs +++ b/esp-hal-common/src/soc/esp32c3/peripherals.rs @@ -30,7 +30,7 @@ crate::peripherals! { DS <= DS, EFUSE <= EFUSE, EXTMEM <= EXTMEM, - GPIO <= GPIO, + GPIO <= GPIO (GPIO,GPIO_NMI), GPIO_SD <= GPIO_SD, HMAC <= HMAC, I2C0 <= I2C0, diff --git a/esp-hal-common/src/soc/esp32s3/peripherals.rs b/esp-hal-common/src/soc/esp32s3/peripherals.rs index 123f65708f8..aa93b173659 100644 --- a/esp-hal-common/src/soc/esp32s3/peripherals.rs +++ b/esp-hal-common/src/soc/esp32s3/peripherals.rs @@ -30,7 +30,7 @@ crate::peripherals! { DS <= DS, EFUSE <= EFUSE, EXTMEM <= EXTMEM, - GPIO <= GPIO, + GPIO <= GPIO (GPIO,GPIO_NMI), GPIO_SD <= GPIO_SD, HMAC <= HMAC, I2C0 <= I2C0, diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 8c01b4caa1b..aa148283290 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -437,6 +437,66 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .into() } +#[cfg(feature = "interrupt")] +#[proc_macro_attribute] +pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { + use proc_macro::Span; + use proc_macro_error::abort; + use syn::{parse::Error as ParseError, spanned::Spanned, ItemFn, ReturnType, Type}; + + use self::interrupt::{check_attr_whitelist, WhiteListCaller}; + + let mut f: ItemFn = syn::parse(input).expect("`#[handler]` must be applied to a function"); + + let attr_args = match NestedMeta::parse_meta_list(args.into()) { + Ok(v) => v, + Err(e) => { + return TokenStream::from(darling::Error::from(e).write_errors()); + } + }; + + if attr_args.len() > 0 { + abort!(Span::call_site(), "This attribute accepts no arguments") + } + + // XXX should we blacklist other attributes? + + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { + return error; + } + + let valid_signature = f.sig.constness.is_none() + && f.sig.abi.is_none() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, + }, + } + && f.sig.inputs.len() <= 1; + + if !valid_signature { + return ParseError::new( + f.span(), + "`#[handler]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`", + ) + .to_compile_error() + .into(); + } + + f.sig.abi = syn::parse_quote!(extern "C"); + + quote!( + #f + ) + .into() +} + /// Create an enum for erased GPIO pins, using the enum-dispatch pattern /// /// Only used internally diff --git a/esp32c3-hal/examples/embassy_wait.rs b/esp32c3-hal/examples/embassy_wait.rs index fb00a937b57..8be7346968e 100644 --- a/esp32c3-hal/examples/embassy_wait.rs +++ b/esp32c3-hal/examples/embassy_wait.rs @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks), ); - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let io = IO::new_async(peripherals.GPIO, peripherals.IO_MUX); // GPIO 9 as input let mut input = io.pins.gpio9.into_pull_down_input(); diff --git a/esp32c3-hal/examples/gpio_interrupt.rs b/esp32c3-hal/examples/gpio_interrupt.rs index bb0a1bed045..755d499b124 100644 --- a/esp32c3-hal/examples/gpio_interrupt.rs +++ b/esp32c3-hal/examples/gpio_interrupt.rs @@ -28,7 +28,9 @@ fn main() -> ! { let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Set GPIO5 as an output - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + io.set_interrupt_handler(handler); + let mut led = io.pins.gpio5.into_push_pull_output(); // Set GPIO9 as an input @@ -46,8 +48,8 @@ fn main() -> ! { } } -#[interrupt] -fn GPIO() { +#[handler] +fn handler() { critical_section::with(|cs| { esp_println::println!("GPIO interrupt"); BUTTON diff --git a/esp32s3-hal/examples/embassy_wait.rs b/esp32s3-hal/examples/embassy_wait.rs index 5e26a449dd4..da90684d006 100644 --- a/esp32s3-hal/examples/embassy_wait.rs +++ b/esp32s3-hal/examples/embassy_wait.rs @@ -37,7 +37,7 @@ async fn main(_spawner: Spawner) { embassy::init(&clocks, timer_group0); } - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let io = IO::new_async(peripherals.GPIO, peripherals.IO_MUX); // GPIO 0 as input let mut input = io.pins.gpio0.into_pull_down_input(); diff --git a/esp32s3-hal/examples/gpio_interrupt.rs b/esp32s3-hal/examples/gpio_interrupt.rs index fd40f43cd1e..6b63211d8db 100644 --- a/esp32s3-hal/examples/gpio_interrupt.rs +++ b/esp32s3-hal/examples/gpio_interrupt.rs @@ -13,7 +13,6 @@ use esp32s3_hal::{ clock::ClockControl, gpio::{Event, Gpio0, Input, PullDown, IO}, interrupt, - macros::ram, peripherals::{self, Peripherals}, prelude::*, xtensa_lx, @@ -30,7 +29,9 @@ fn main() -> ! { let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Set GPIO15 as an output, and set its state high initially. - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + io.set_interrupt_handler(handler); + let mut led = io.pins.gpio15.into_push_pull_output(); let mut button = io.pins.gpio0.into_pull_down_input(); button.listen(Event::FallingEdge); @@ -51,9 +52,8 @@ fn main() -> ! { } } -#[ram] -#[interrupt] -fn GPIO() { +#[handler] +fn handler() { esp_println::println!( "GPIO Interrupt with priority {}", xtensa_lx::interrupt::get_level()