I'm trying to translate the following code from the Arduino IDE Into Rust using the avr_hal
crate to make a passive buzzer play notes:
#include "pitches.h"
// notes in the melody:
int melody[] = {
NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_C6};
int duration = 500; // 500 miliseconds
void setup() {
}
void loop() {
for (int thisNote = 0; thisNote < 8; thisNote++) {
// pin8 output the voice, every scale is 0.5 sencond
tone(8, melody[thisNote], duration);
// Output the voice after several minutes
delay(1000);
}
// restart after two seconds
delay(2000);
}
I can't figure out how to use a Pwm pin to set the duty and frequency as it only exposes methods to set the duty.
#![no_std]
#![no_main]
mod pitches;
use arduino_hal::simple_pwm::{IntoPwmPin, Prescaler, Timer4Pwm};
use panic_halt as _;
use pitches::{NOTE_A5, NOTE_B5, NOTE_C5, NOTE_C6, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5};
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let timer = Timer4Pwm::new(dp.TC4, Prescaler::Prescale8);
let mut buzzer = pins.d8.into_output().into_pwm(&timer);
// notes in the melody:
let melody: [isize; 8] = [
NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_C6,
];
loop {
melody.iter().for_each(|note| {
// TODO: How do I use the PWM buzzer output here???
arduino_hal::delay_ms(1000);
});
arduino_hal::delay_ms(2000);
}
}
I'm just starting to learn arduino and electronics in general and honestly I don't understand 100% how the tone
function works under the hood.
I would appreciate to get an answer that explains to me how that function works as well as helping me understand the core concepts :D
I achieved the functionality you are looking for. It's not a very elegant solution, and you can no longer use TIMER1, but it's something.
#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use arduino_hal::{
hal::port::Dynamic,
port::{mode::Output, Pin},
};
use panic_halt as _;
use core::cell;
static BUZZER: avr_device::interrupt::Mutex<cell::Cell<Option<Pin<Output, Dynamic>>>> =
avr_device::interrupt::Mutex::new(cell::Cell::new(None));
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
//let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
// Timer Configuration:
// - WGM = 4: CTC mode (Clear Timer on Compare Match)
// - Prescaler 256
// - OCR1A = 31249
//
// => F = 16 MHz / (256 * (1 + 237.89)) = 261.626 Hz
// (^ this formula I deduced from reading the datasheet)
//
let tmr1 = dp.TC1;
tmr1.tccr1a.write(|w| w.wgm1().bits(0b00));
tmr1.tccr1b
.write(|w| w.cs1().prescale_256().wgm1().bits(0b01));
// Enable the timer interrupt
tmr1.timsk1.write(|w| w.ocie1a().set_bit());
let mut buzzer = pins.d2.into_output().downgrade();
buzzer.set_low();
unsafe {
avr_device::interrupt::enable();
}
avr_device::interrupt::free(|cs| BUZZER.borrow(cs).replace(Some(buzzer)));
loop {
tmr1.ocr1a.write(|w| w.bits(238));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(212));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(189));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(178));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(159));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(141));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
tmr1.ocr1a.write(|w| w.bits(126));
tmr1.tcnt1.write(|w|{ w.bits(0) });
arduino_hal::delay_ms(1000);
}
}
#[avr_device::interrupt(atmega328p)]
fn TIMER1_COMPA() {
avr_device::interrupt::free(|cs| {
if let Some(mut x) = BUZZER.borrow(cs).take() {
x.toggle();
BUZZER.borrow(cs).replace(Some(x));
}
})
}
Explaining a little bit, after setting up the timer, the tone change is done setting the frecuence of the timer-interruptions. The next line is a timer reset, be cause if not done, the program will halt for a moment at the 10th tone (no idea why)
tmr1.ocr1a.write(|w| w.bits(238));
tmr1.tcnt1.write(|w|{ w.bits(0) });
If you want to stop the buzzer, just set the frecuency to an impossible to hear one.