Author
Topic: Alarm Clock (Arduino Uno R3) (Read 62 Times)
Role
Posts: 7
Points: 0

This simple program turns an "alarm" (speaker) on after approximately 7s. The user can snooze the alarm for 7s by pressing the button.

This code was derived using multiple examples from this repository -- thanks!

Tutorial

To open the Alarm Clock example, click on the navigation menu button on the top left side of the screen. Then, click on 'open example' as shown below.

Once the 'Pick Example Code To Load' window opens, select 'Alarm Clock(Arduino)' as shown below.

Now, the Alarm Clock project will open up. To flash the code onto your board, follow the instructions in Section 3 of the Making Your First Project tutorial.

 

Breakdown of main.rs:

1. Attribute Macros and Imports

    #![no_std]
    #![no_main]
    #![feature(abi_avr_interrupt)]
  • #![no_std] removes the standard library to make the code suitable for bare-metal (embedded) programming.
  • #![no_main] tells Rust that we are defining our own entry point instead of using the standard main() function.
  • #![feature(abi_avr_interrupt)] enables AVR-specific interrupt handling.

Imports

    use arduino_hal::port::mode::{Output, PwmOutput};
    use arduino_hal::port::Pin;
    use arduino_hal::prelude::*;
    use arduino_hal::simple_pwm::*;
    use avr_device::atmega328p::tc1::tccr1b::CS1_A;
    use avr_device::atmega328p::TC1;
    use core::mem;
    use core::sync::atomic::{AtomicBool, Ordering};
    use core::sync::atomic::Ordering::SeqCst;
    use arduino_hal::delay_ms;
    use panic_halt as _;
    use ufmt::{uWrite, uwriteln};

This section imports necessary Rust core libraries and traits:

  • arduino_hal::port::mode::*: Used for GPIO configuration.
  • avr_device::atmega328p::TC1: Provides access to Timer1 registers.
  • core::sync::atomic::*: Used for atomic flag operations.
  • panic_halt: Provides a simple panic handler that stops execution.
  • ufmt: Lightweight formatting library for embedded Rust.

2. Defining Global State and Interrupt Handling

    struct InterruptState {
        blinker: Pin<Output>,
    }

    static ALARM_ON: AtomicBool = AtomicBool::new(false);
    static PIN_CHANGED: AtomicBool = AtomicBool::new(false);

    static mut INTERRUPT_STATE: mem::MaybeUninit<InterruptState> = mem::MaybeUninit::uninit();
  • ALARM_ON and PIN_CHANGED are atomic flags for handling interrupts safely.
  • InterruptState stores the state of the LED pin for use inside an interrupt handler.

3. Main Function (Entry Point)

    #[arduino_hal::entry]
    fn main() -> ! {
  • #[arduino_hal::entry] marks this as the program’s entry point.
  • The function returns !, indicating it never exits.

Peripheral Initialization

    let dp = arduino_hal::Peripherals::take().unwrap();
    let pins = arduino_hal::pins!(dp);
    let timer0 = Timer0Pwm::new(dp.TC0, Prescaler::Prescale64);
    let mut pwm_led = pins.d5.into_output().into_pwm(&timer0);
  • arduino_hal::Peripherals::take() grants access to the device’s peripherals.
  • Timer0Pwm configures Timer0 for PWM control on pin D5.

Serial Communication Setup

    let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
    ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").unwrap_infallible();
  • Sets up serial communication at 57,600 baud for debugging.

Button and LED Setup

    let mut button_pin = pins.d2.into_pull_up_input();
    let mut led = pins.d13.into_output();
  • Configures pin D2 as a pull-up input for button detection.
  • Configures pin D13 as an output for the onboard LED.

4. Configuring External Interrupts

    dp.EXINT.pcicr.write(|w| unsafe { w.bits(0b100) });
    dp.EXINT.pcmsk2.write(|w| w.bits(0b100));
  • Enables pin change interrupt control register (PCICR) for pin D2.
  • Enables mask for pin change interrupt (PCMSK2) on pin D2.

Timer1 Initialization

    let tmr1: TC1 = dp.TC1;
    init_timer(&tmr1, &mut serial);
  • Calls init_timer() to configure Timer1.

Enabling Global Interrupts

    unsafe { avr_device::interrupt::enable(); }
  • Enables global interrupts to allow the MCU to respond to external events.

5. Main loop

    loop {
        if(rotate(&PIN_CHANGED)){
            ALARM_ON.store(false, SeqCst);
            tmr1.tcnt1.reset();
            tmr1.timsk1.write(|w| w.ocie1a().set_bit());
            led.toggle();
            PIN_CHANGED.store(false, SeqCst);
        }
        if(rotate(&ALARM_ON)){
            tmr1.timsk1.write(|w| w.ocie1a().clear_bit());
            pwm_led.enable();
            pwm_led.set_duty(150);
            pwm_led.disable();
        }
    }
  • Checks if PIN_CHANGED was triggered by an interrupt.
  • If PIN_CHANGED is set, it resets the alarm, restarts Timer1, and toggles the LED.
  • If ALARM_ON is set, it disables Timer1 and pulses the PWM LED.

6. Timer Initialization Function

    pub fn init_timer<W: uWrite<Error = ::core::convert::Infallible>>(tmr1: &TC1, serial: &mut W) {

Timer1 Configuration

    const CLOCK_SOURCE: CS1_A = CS1_A::PRESCALE_1024;
    let ticks :u16 = 65535;
  • Uses a prescaler of 1024 to slow down the timer.
  • Configures the timer compare register (OCR1A).
    tmr1.tccr1a.write(|w| w.wgm1().bits(0b00));
    tmr1.tccr1b.write(|w| {
        w.cs1().variant(CLOCK_SOURCE).wgm1().bits(0b01)
    });
    tmr1.ocr1a.write(|w| w.bits(ticks));
    tmr1.timsk1.write(|w| w.ocie1a().set_bit());
  • Configures Timer1 in Clear Timer on Compare Match (CTC) mode.
  • Writes the compare match value and enables interrupts.

7. Helper Function for Atomic Flag Rotation

    fn rotate(flag: &AtomicBool) -> bool {
        avr_device::interrupt::free(|_cs| {
            if flag.load(Ordering::SeqCst) {
                true
            } else {
                false
            }
        })
    }
  • Ensures safe access to atomic flags inside interrupt handlers.

8. Interrupt Handlers Helper Function for Atomic Flag Rotation

    #[avr_device::interrupt(atmega328p)]
    fn TIMER1_COMPA() {
        ALARM_ON.store(true, Ordering::SeqCst);
    }
  • Timer1 Compare Match Interrupt sets ALARM_ON to true.
    #[avr_device::interrupt(atmega328p)]
    fn PCINT2() {
        PIN_CHANGED.store(true, Ordering::SeqCst);
    }
  • Pin Change Interrupt on D2 sets PIN_CHANGED to true.

This Topic Is Closed!