在avr_hal中等价于 “tone()” 的函数是什么?

huangapple go评论60阅读模式
英文:

Equivalent of "tone()" in avr_hal

问题

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 getting an answer that explains to me how that function works as well as helping me understand the core concepts 在avr_hal中等价于 “tone()” 的函数是什么?

我正在尝试将以下代码从Arduino IDE翻译成Rust,使用avr_hal crate来使被动蜂鸣器播放音符:

#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);
}

我无法弄清楚如何使用Pwm引脚设置占空比和频率,因为它只提供了设置占空比的方法。

#![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);
    }
}

我刚刚开始学习Arduino和电子知识,老实说,我不完全理解tone函数在内部是如何工作的。

我希望能够得到一个解释这个函数如何工作的答案,并帮助我理解核心概念:D

英文:

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 在avr_hal中等价于 “tone()” 的函数是什么?

答案1

得分: 0

我已经实现了你所需要的功能。这不是一个非常优雅的解决方案,而且你不能再使用 TIMER1,但这是一个解决办法。

#![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);

    // 定时器配置:
    // - WGM = 4: CTC 模式(在比较匹配时清除定时器)
    // - 分频器 256
    // - OCR1A = 31249
    //
    // => F = 16 MHz / (256 * (1 + 237.89)) = 261.626 Hz 
    //    (^ 这个公式我从数据手册中推导出来的)
    //
    let tmr1 = dp.TC1;
    tmr1.tccr1a.write(|w| w.wgm1().bits(0b00));
    tmr1.tccr1b
        .write(|w| w.cs1().prescale_256().wgm1().bits(0b01));

    // 启用定时器中断
    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));
        }
    })
}

简要解释一下,设置定时器后,音调的变化是通过设置定时器中断的频率来实现的。下一行是定时器复位,因为如果不这样做,程序会在第10个音调处短暂停顿(不知道为什么)。

tmr1.ocr1a.write(|w| w.bits(238));
tmr1.tcnt1.write(|w|{ w.bits(0) });

如果要停止蜂鸣器,请将频率设置为一个无法听到的值。

英文:

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&lt;cell::Cell&lt;Option&lt;Pin&lt;Output, Dynamic&gt;&gt;&gt;&gt; =
avr_device::interrupt::Mutex::new(cell::Cell::new(None));
#[arduino_hal::entry]
fn main() -&gt; ! {
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
//
// =&gt; 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.

huangapple
  • 本文由 发表于 2023年6月26日 03:28:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76552093.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定