Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interrupts Examples | Sleep Wakeups (Esp32-c3) #2080

Closed
Syphixs opened this issue Oct 17, 2023 · 1 comment
Closed

Interrupts Examples | Sleep Wakeups (Esp32-c3) #2080

Syphixs opened this issue Oct 17, 2023 · 1 comment

Comments

@Syphixs
Copy link

Syphixs commented Oct 17, 2023

HI! I am new to the rust embedded world, or general rust I would say so I am looking for some guidance from more experienced folks out there! :D Sadly I can not find any examples nor issues for my problem and all the examples either don't work or are outdated as it seems.

I have an Esp32-c3 devboard which supports most of the embassy functionalities as I found out via #745 .
Furthermore I had a look at the embassy examples from their repo here .

My Task: I have a button and a led connected to the chip. I want that the led is blinking when I either press the button OR a timer runs for 10 seconds. IF either the button is pressed or the timer ends I want the timer to restart from the beginning. I want the chip as power efficient as possible so I thought I use some interrupts.

I managed to get it working in standard esp-hal which looks like this:

ESP HAL VERSION

#![no_std]
#![no_main]

use core::cell::RefCell;
use core::time::Duration;
use critical_section::Mutex;
use esp_backtrace as _;
use esp_println::println;
use hal::{
    clock::ClockControl,
    gpio::{Event, Gpio4, Input, PullDown, PullUp, RTCPinWithResistors, IO},
    interrupt,
    peripherals::{self, Peripherals},
    prelude::*,
    riscv,
    rtc_cntl::{
        get_reset_reason, get_wakeup_cause,
        sleep::{RtcioWakeupSource, TimerWakeupSource, WakeSource, WakeupLevel},
        SocResetReason,
    },
    Delay, Rtc, Timer,
};
#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    let mut rtc = Rtc::new(peripherals.RTC_CNTL);

    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    // Set GPIO3 as an output
    let mut led = io.pins.gpio3.into_push_pull_output();

    // Set GPIO4 as an input
    let mut pin4 = io.pins.gpio4;
    pin4.rtcio_pullup(true);

    println!("up and runnning!");
    let reason = get_reset_reason(hal::Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn);
    println!("reset reason: {:?}", reason);
    let wake_reason = get_wakeup_cause();
    println!("wake reason: {:?}", wake_reason);

    let mut delay = Delay::new(&clocks);

    match wake_reason {
        hal::reset::SleepSource::Timer => {
            println!("timer wakeup");
            for _ in 0..3 {
                led.set_high().unwrap();
                delay.delay_ms(200u32);
                led.set_low().unwrap();
                delay.delay_ms(200u32);
            }
        }
        hal::reset::SleepSource::Gpio => {
            println!("gpio wakeup");
            for _ in 0..5 {
                led.set_high().unwrap();
                delay.delay_ms(500u32);
                led.set_low().unwrap();
                delay.delay_ms(200u32);
            }
        }
        _ => {
            println!("other wakeup")
        }
    }

    let wakeup_pins: &mut [(&mut dyn RTCPinWithResistors, WakeupLevel)] =
        &mut [(&mut pin4, WakeupLevel::Low)];
    let rtcio = RtcioWakeupSource::new(wakeup_pins);
    let timer = TimerWakeupSource::new(Duration::from_secs(10));

    println!("Enter Deep Sleep!");
    delay.delay_ms(100u32);
    rtc.sleep_deep(&[&timer, &rtcio], &mut delay);
}

Now I wanted to achieve the same in embassy which ended up in something like this.

Embassy Version

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use embassy_executor::{Executor, Spawner};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
use embassy_sync::mutex::Mutex;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Timer};
use esp_backtrace as _;
use esp_println::println;
use hal::gpio::{GpioPin, Input, PullUp, PushPull};
use hal::interrupt::Priority;
use hal::peripherals::Interrupt;
use hal::prelude::main;
use hal::riscv;
use hal::IO;
use hal::{clock::ClockControl, embassy, peripherals::Peripherals, prelude::*};
use static_cell::make_static;

enum InterruptType {
    Time,
    Button,
}
static CHANNEL: Channel<CriticalSectionRawMutex, InterruptType, 1> = Channel::new();

#[embassy_executor::task]
async fn waiting_task() {
    println!("WT begins");
    Timer::after(Duration::from_millis(10_000)).await;
    CHANNEL.send(InterruptType::Time).await;
    println!("WT ends");
}

#[embassy_executor::task]
async fn button_task(mut button: GpioPin<Input<PullUp>, 4>) {
    button.wait_for_falling_edge().await.unwrap();
    CHANNEL.send(InterruptType::Button).await;
    println!("Button Task ends");
}

#[main]
async fn main(spawner: Spawner) -> ! {
    esp_println::println!("Init!");
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    embassy::init(
        &clocks,
        hal::systimer::SystemTimer::new(peripherals.SYSTIMER),
    );

    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    // GPIO 3 as output
    let mut led = io.pins.gpio3.into_push_pull_output();
    // GPIO 4 as input but this seems wrong to do here as well
    let button = io.pins.gpio4.into_pull_up_input();

    // Async requires the GPIO interrupt to wake futures
    // hal::interrupt::enable(Interrupt::GPIO, hal::interrupt::Priority::Priority1).unwrap();
    // enable all interrupts
    unsafe {
        riscv::interrupt::enable();
    }

    // This should be inside the main task but can not make it a static lifetime for some reason
    spawner.spawn(button_task(button)).ok();
    loop {
        // Spawn interrupt tasks? this seems kinda weird and wrong
        spawner.spawn(waiting_task()).ok();
        println!("Waiting... for input - sleeping");
        match CHANNEL.receive().await {
            InterruptType::Time => {
                println!("Time Interrupt");
                for _ in 0..3 {
                    led.set_high().unwrap();
                    Timer::after(Duration::from_millis(200)).await;
                    led.set_low().unwrap();
                    Timer::after(Duration::from_millis(200)).await;
                }
            }
            InterruptType::Button => {
                println!("Button Interrupt");
                for _ in 0..5 {
                    led.set_high().unwrap();
                    Timer::after(Duration::from_millis(500)).await;
                    led.set_low().unwrap();
                    Timer::after(Duration::from_millis(200)).await;
                }
            }
        }
    }
}

Even I can tell their are mayor problems with this code.

  • First of all how do I end a started waiting_task if the button interrupt comes before that and remove it from the task queue without finishing its timer?
  • I thought a channel might be a good idea as I have read its basically a signal so i thought if i use channel.await in the main loop i can combine multiple interrupts that way
  • Another hurdle seems to provide the mut button to an async function. Should I use a mutex here as it seems i cannot make a lifetime annotation

Any help would be really greatly appreciated.

I have attached all of the files to hopefully recreate my issue.

embassy_button_blinks.zip

button-blinks-task.zip

@Murmele
Copy link
Contributor

Murmele commented Mar 4, 2024

I have right know the same problem to implement a button to recognize short and long press. I find it ugly needing 3 tasks per button to be able to evaluate a button press

@Syphixs Syphixs closed this as not planned Won't fix, can't repro, duplicate, stale Jul 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants