Steps to replicate
-
Generate fresh project for Arduino Micro as per the README
-
Add avr-device = "0.8.1" as a dependency in Cargo.toml (the same version depended on by arduino-hal)
-
Put this code in main.rs (adapted almost verbatim from the millis() example but simplified to just toggle the LED each second)
Code
/*!
* A basic implementation of the `millis()` function from Arduino:
*
* https://www.arduino.cc/reference/en/language/functions/time/millis/
*
* Uses timer TC0 and one of its interrupts to update a global millisecond
* counter. A walkthough of this code is available*9e:
*
* https://blog.rahix.de/005-avr-hal-millis/
*/
#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use core::cell;
use panic_halt as _;
// Possible Values:
//
// ╔═══════════╦══════════════╦═══════════════════╗
// ║ PRESCALER ║ TIMER_COUNTS ║ Overflow Interval ║
// ╠═══════════╬══════════════╬═══════════════════╣
// ║ 64 ║ 250 ║ 1 ms ║
// ║ 256 ║ 125 ║ 2 ms ║
// ║ 256 ║ 250 ║ 4 ms ║
// ║ 1024 ║ 125 ║ 8 ms ║
// ║ 1024 ║ 250 ║ 16 ms ║
// ╚═══════════╩══════════════╩═══════════════════╝
const PRESCALER: u32 = 1024;
const TIMER_COUNTS: u32 = 125;
const MILLIS_INCREMENT: u32 = PRESCALER * TIMER_COUNTS / 16000;
const TOGGLE_INTERVAL: u32 = 1000;
static MILLIS_COUNTER: avr_device::interrupt::Mutex<cell::Cell<u32>> =
avr_device::interrupt::Mutex::new(cell::Cell::new(0));
fn millis_init(tc0: arduino_hal::pac::TC0) {
// Configure the timer for the above interval (in CTC mode)
// and enable its interrupt.
tc0.tccr0a().write(|w| w.wgm0().ctc());
tc0.ocr0a().write(|w| w.set(TIMER_COUNTS as u8));
tc0.tccr0b().write(|w| match PRESCALER {
8 => w.cs0().prescale_8(),
64 => w.cs0().prescale_64(),
256 => w.cs0().prescale_256(),
1024 => w.cs0().prescale_1024(),
_ => panic!(),
});
tc0.timsk0().write(|w| w.ocie0a().set_bit());
// Reset the global millisecond counter
avr_device::interrupt::free(|cs| {
MILLIS_COUNTER.borrow(cs).set(0);
});
}
#[avr_device::interrupt(atmega32u4)]
fn TIMER0_COMPA() {
avr_device::interrupt::free(|cs| {
let counter_cell = MILLIS_COUNTER.borrow(cs);
let counter = counter_cell.get();
counter_cell.set(counter + MILLIS_INCREMENT);
})
}
fn millis() -> u32 {
avr_device::interrupt::free(|cs| MILLIS_COUNTER.borrow(cs).get())
}
// ----------------------------------------------------------------------------
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
millis_init(dp.TC0);
let mut led = pins.d13.into_output();
// Enable interrupts globally
unsafe { avr_device::interrupt::enable() };
let mut next_toggle = 0;
loop {
let time = millis();
if time >= next_toggle {
led.toggle();
next_toggle += TOGGLE_INTERVAL;
}
}
}
-
Upload to genuine Arduino Micro
-
Observe that LED fails to blink
What I know and why
- My installation of avr-hal/ravedude/avrdude/etc is okay because the unmodified example works correctly when uploaded to my genuine Arduino Uno
- My edits are okay because the modified code also works as intended on my Uno with
#[avr_device::interrupt(atmega328p)]
- My Micro is okay because the blink example works correctly and all code written and uploaded using the Arduino IDE works as well
- Porting this code is theoretically sound because the names and layouts of relevant registers on the
atmega328p and atmega32u4 are identical according to their datasheets
- The memory locations of relevant registers are correct in
avr-device because they match the locations used in the original C implementation of millis()
At this point, I am at a loss as to why this doesn't work and I have done my very best in ruling out the possibility that this is a problem on my end
Steps to replicate
Generate fresh project for Arduino Micro as per the README
Add
avr-device = "0.8.1"as a dependency inCargo.toml(the same version depended on byarduino-hal)Put this code in
main.rs(adapted almost verbatim from themillis()example but simplified to just toggle the LED each second)Code
Upload to genuine Arduino Micro
Observe that LED fails to blink
What I know and why
#[avr_device::interrupt(atmega328p)]atmega328pandatmega32u4are identical according to their datasheetsavr-devicebecause they match the locations used in the original C implementation ofmillis()At this point, I am at a loss as to why this doesn't work and I have done my very best in ruling out the possibility that this is a problem on my end