atsamd-rs / atsamd

Target atsamd microcontrollers using Rust
https://matrix.to/#/#atsamd-rs:matrix.org
Apache License 2.0
562 stars 200 forks source link

EIC accepts any pin, even if it's not the correct EXTINT pad #781

Open supersimple33 opened 2 days ago

supersimple33 commented 2 days ago

I have been trying to implement a external interrupt controller on the grand_central_m4. I believe the following code is correct based on what I saw in a few examples on here however the led does not turn on when pressing the connected switch.

//! Uses an external interrupt to blink an LED.
//!
//! You need to connect a button between D46 and ground. Each time the button
//! is pressed, the LED will count the total number of button presses so far.
#![no_std]
#![no_main]

use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use grand_central_m4::hal::eic;
use grand_central_m4::hal::eic::pin::ExternalInterrupt;
use grand_central_m4::pin_alias;
#[cfg(not(feature = "use_semihosting"))]
use panic_halt as _;
#[cfg(feature = "use_semihosting")]
use panic_semihosting as _;

use bsp::hal;
use bsp::pac;
use grand_central_m4 as bsp;

use bsp::entry;
use hal::clock::GenericClockController;
use hal::delay::Delay;
use hal::eic::pin::{ExtInt3, Sense};
use hal::gpio::{Pin, PullUpInterrupt};
use hal::prelude::*;
use pac::{interrupt, CorePeripherals, Peripherals};

use core::cell::RefCell;
use core::sync::atomic::{AtomicUsize, Ordering};

use cortex_m::peripheral::NVIC;

static RED_LED: Mutex<RefCell<Option<bsp::RedLed>>> = Mutex::new(RefCell::new(None));

fn toggle_led() {
    free(|cs| {
        RED_LED.borrow(cs).borrow_mut().as_mut().map(|l| l.toggle());
    });
}

#[entry]
fn main() -> ! {
    let mut peripherals = Peripherals::take().unwrap();
    let mut core = CorePeripherals::take().unwrap();
    let mut clocks = GenericClockController::with_internal_32kosc(
        peripherals.GCLK,
        &mut peripherals.MCLK,
        &mut peripherals.OSC32KCTRL,
        &mut peripherals.OSCCTRL,
        &mut peripherals.NVMCTRL,
    );
    let pins = bsp::Pins::new(peripherals.PORT);
    free(|cs| {
        RED_LED
            .borrow(cs)
            .replace(Some(pin_alias!(pins.red_led).into()))
    });

    let mut delay = Delay::new(core.SYST, &mut clocks);

    let gclk1 = clocks.gclk1();
    let eic_clock = clocks.eic(&gclk1).unwrap();
    let mut eic = eic::init_with_ulp32k(&mut peripherals.MCLK, eic_clock, peripherals.EIC);
    let button: Pin<_, PullUpInterrupt> = pins.d46.into();
    eic.button_debounce_pins(&[button.id()]);
    let mut extint_button = ExtInt3::new(button);
    extint_button.sense(&mut eic, Sense::BOTH);
    extint_button.enable_interrupt(&mut eic);
    let config_eic = eic.finalize();

    // Enable EIC interrupt in the NVIC

    free(|_| unsafe {
        core.NVIC.set_priority(interrupt::EIC_EXTINT_3, 1);
        NVIC::unmask(interrupt::EIC_EXTINT_3);
    });

    // Blink the LED once to show that we have started up.

    toggle_led();
    delay.delay_ms(200u8);
    toggle_led();
    delay.delay_ms(200u8);

    loop {
        delay.delay_ms(200u8);
        // toggle_led();
        for _ in 0..0xffff {
            cortex_m::asm::nop();
        }
    }
}

#[interrupt]
fn EIC_EXTINT_3() {
    // Increase the counter and clear the interrupt.
    toggle_led();
    unsafe {
        // Accessing registers from interrupts context is safe
        let eic = &*pac::EIC::ptr();
        let set_flags = eic.intflag.read().extint().bits();
        eic.intflag
            .modify(|_, w| w.extint().bits(0b1111111111110111 & set_flags));
    }
}
sajattack commented 2 days ago

GCLK1 is the 32KHz osciallator. You probably want gclk0.

supersimple33 commented 2 days ago

@sajattack Ohh yeah I forgot that. But after switching to GCLK0 the code still does not work.

jbeaurivage commented 9 hours ago

@supersimple33, according to the datasheet, looks like you should be using the EXTINT[6] line for the PC06 (d46) pin. Can you try modifying your code to see if that works?

From a quick glance at the eic module, it looks like an ExtInt* will accept any pin without emitting a compile error, not just the pins that belong to its interrupt line. This is clearly a bug with the HAL and I think it should be addressed.

supersimple33 commented 7 hours ago

@jbeaurivage Yeah that was totally it I forgot to double check the interrupts lines. You relieved so much pain, thank you!

jbeaurivage commented 7 hours ago

No problem! I'll keep this open, because the eic module needs some work to avoid this kind of mistake happening in the future.