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!
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. #[arduino_hal::entry]
fn main() -> ! {
#[arduino_hal::entry]
marks this as the program’s entry point.!
, 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();
Button and LED Setup
let mut button_pin = pins.d2.into_pull_up_input();
let mut led = pins.d13.into_output();
D2
as a pull-up input for button detection.D13
as an output for the onboard LED. dp.EXINT.pcicr.write(|w| unsafe { w.bits(0b100) });
dp.EXINT.pcmsk2.write(|w| w.bits(0b100));
PCICR
) for pin D2.PCMSK2
) on pin D2.Timer1 Initialization
let tmr1: TC1 = dp.TC1;
init_timer(&tmr1, &mut serial);
init_timer()
to configure Timer1.Enabling Global Interrupts
unsafe { avr_device::interrupt::enable(); }
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();
}
}
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;
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());
fn rotate(flag: &AtomicBool) -> bool {
avr_device::interrupt::free(|_cs| {
if flag.load(Ordering::SeqCst) {
true
} else {
false
}
})
}
#[avr_device::interrupt(atmega328p)]
fn TIMER1_COMPA() {
ALARM_ON.store(true, Ordering::SeqCst);
}
#[avr_device::interrupt(atmega328p)]
fn PCINT2() {
PIN_CHANGED.store(true, Ordering::SeqCst);
}