How to enable interrupts on Arduinio Uno's INT0/D2 #326
-
There's a serie of 4 leds that blink in sequence and with the touch of a button an interrupt should be fired via pin D2 to reverse the sequence of the blinking. Following your extensive help in this issue and your blogpost I came up with the solution below. Except that it doesn't work. The leds blink in sequence, as usual, but the interrupts don't seem to fire on the pin. The button is working. It feels like I'm close. Any ideas what I'm missing? #![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use panic_halt as _;
use core::ops::Range;
use core::sync::atomic::{AtomicBool, Ordering};
use arduino_hal::port::{mode, Pin};
use either::*;
static REVERSED: AtomicBool = AtomicBool::new(false);
fn is_reversed() -> bool {
return avr_device::interrupt::free(|_| { REVERSED.load(Ordering::SeqCst) });
}
#[avr_device::interrupt(atmega328p)]
fn INT0() {
avr_device::interrupt::free(|_| {
let current = REVERSED.load(Ordering::SeqCst);
REVERSED.store(!current, Ordering::SeqCst);
});
}
fn blink_for_range(range : Range<u16>, leds : &mut[Pin<mode::Output>]) {
range.map(|i| i * 100).for_each(|ms| {
let iter = if is_reversed() {
Left(leds.iter_mut().rev())
} else {
Right(leds.iter_mut())
};
iter.for_each(|led| {
led.toggle();
arduino_hal::delay_ms(ms as u16);
})
});
}
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
pins.d2.into_pull_up_input(); // is this necessary?
let mut leds: [Pin<mode::Output>; 4] = [
pins.d3.into_output().downgrade(),
pins.d4.into_output().downgrade(),
pins.d5.into_output().downgrade(),
pins.d6.into_output().downgrade(),
];
unsafe { avr_device::interrupt::enable() };
loop {
blink_for_range(0..10, &mut leds);
blink_for_range(10..0, &mut leds);
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments
-
I have a PR opened with an example for this that might help you. Check out #238 |
Beta Was this translation helpful? Give feedback.
-
You have to actually enable the external interrupt/pin change interrupt machinery. Pin PD2 can be either INT0 as an "external interrupt" or PCINT18 as a "pin change interrupt". For "external interrupts" you can control on what edge the interrupt should trigger while for "pin change interrupts", any edge will trigger it. The example @tsemczyszyn linked shows how to set up the PCINT18 "pin change interrupt". The INT0 ISR in your code would be for the "external interrupt". To enable it, for example on a falling edge, this piece of setup code would be needed (untested): // Configure INT0 for falling edge. 0x03 would be rising edge.
dp.EXINT.eicra.modify(|_, w| w.isc0().bits(0x02));
// Enable the INT0 interrupt source.
dp.EXINT.eimsk.modify(|_, w| w.int0().set_bit());
If you externally pull the pin to a defined level (high or low) using a resistor, this is not needed. Otherwise this will enable an internal pull-up resistor to VCC. |
Beta Was this translation helpful? Give feedback.
-
It works! Thank you so much. Have a great weekend :) #![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use panic_halt as _;
use core::ops::Range;
use core::sync::atomic::{AtomicBool, Ordering};
use arduino_hal::port::{mode, Pin};
use either::*;
static REVERSED: AtomicBool = AtomicBool::new(false);
fn is_reversed() -> bool {
return avr_device::interrupt::free(|_| { REVERSED.load(Ordering::SeqCst) });
}
#[avr_device::interrupt(atmega328p)]
fn INT0() {
let current = REVERSED.load(Ordering::SeqCst);
REVERSED.store(!current, Ordering::SeqCst);
}
fn blink_for_range(range : Range<u16>, leds : &mut[Pin<mode::Output>]) {
range.map(|i| i * 100).for_each(|ms| {
let iter = if is_reversed() {
Left(leds.iter_mut().rev())
} else {
Right(leds.iter_mut())
};
iter.for_each(|led| {
led.toggle();
arduino_hal::delay_ms(ms as u16);
})
});
}
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
// thanks to tsemczyszyn and Rahix: https://github.com/Rahix/avr-hal/issues/240
// Configure INT0 for falling edge. 0x03 would be rising edge.
dp.EXINT.eicra.modify(|_, w| w.isc0().bits(0x02));
// Enable the INT0 interrupt source.
dp.EXINT.eimsk.modify(|_, w| w.int0().set_bit());
let mut leds: [Pin<mode::Output>; 4] = [
pins.d3.into_output().downgrade(),
pins.d4.into_output().downgrade(),
pins.d5.into_output().downgrade(),
pins.d6.into_output().downgrade(),
];
unsafe { avr_device::interrupt::enable() };
loop {
blink_for_range(0..10, &mut leds);
blink_for_range(10..0, &mut leds);
}
} |
Beta Was this translation helpful? Give feedback.
-
On Sat, Jan 29, 2022 at 01:16:58AM -0800, Jeroen Vlek wrote:
Closed #240.
|
Beta Was this translation helpful? Give feedback.
-
@stappersg You're absolutely right. Let me create a PR. |
Beta Was this translation helpful? Give feedback.
-
Also did a write up: https://www.perceptivebits.com/interrupted-by-embedded-rust/ |
Beta Was this translation helpful? Give feedback.
-
PR was merged, so closing this |
Beta Was this translation helpful? Give feedback.
You have to actually enable the external interrupt/pin change interrupt machinery. Pin PD2 can be either INT0 as an "external interrupt" or PCINT18 as a "pin change interrupt". For "external interrupts" you can control on what edge the interrupt should trigger while for "pin change interrupts", any edge will trigger it.
The example @tsemczyszyn linked shows how to set up the PCINT18 "pin change interrupt".
The INT0 ISR in your code would be for the "external interrupt". To enable it, for example on a falling edge, this piece of setup code would be needed (untested):