Skip to content

Unable to port millis() implementation to Arduino Micro #701

Description

@PlesleronTepryos

Steps to replicate

  1. Generate fresh project for Arduino Micro as per the README

  2. Add avr-device = "0.8.1" as a dependency in Cargo.toml (the same version depended on by arduino-hal)

  3. 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;
        }
    }
}
  1. Upload to genuine Arduino Micro

  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions