页面配置
Rust嵌入式开发-蜂鸣器
这几天突然对嵌入式有了兴趣,但是又不想接触C语言和C++的大坑(主要是arduino的引导坏了,程序无法烧录进去,不会修),后面发现rust正在逐渐支持嵌入式开发,所以来尝试一手。
提示
主要器件以及依赖: 器件:
- 芯片:stm32f103c8t6
- 无源蜂鸣器
- led二极管
- 1K电阻
软件依赖:
- rust 1.85.0-nightly
- cortex-m =
- cortex-m-rt = "0.7.5"
- cortex-m-semihosting = "0.5"
- panic-halt = "1.0.0"
- stm32f1xx-hal =
- openocd 0.12.0+dev-01557-gdd1758272-dirty
- arm-none-eabi-addr2line 2.36.1
软件具体的环境可以参照视频:rust嵌入式编程
之所以选择stm32f103c8t6,是因为这个板子便宜,并且支持的依赖库也多。
电路图
大致的电路图是这样的,实际上的电路是使用面包板连接的。这里并联了一个二极管,作用是判断蜂鸣器是否在工作,在代码中有体现。(因为电路图是一晚上现学现画的,所以可能存在器件或者连线画错的情况,但大体上的电路逻辑就是如此了)

有源蜂鸣器和无源蜂鸣器之间的区别(这里使用无源蜂鸣器是因为手头上就只有这个): GPT的解释: 有源蜂鸣器和无源蜂鸣器的区别主要体现在工作原理和驱动方式上:
- 工作原理:
- 有源蜂鸣器:内部自带振荡电路,当电流通过时,蜂鸣器会自动发出声音。只需要提供直流电源或特定的电压信号即可工作。
- 无源蜂鸣器:没有内建振荡电路,需要外部驱动信号(如方波信号)才能发出声音。它的工作依赖于输入的交流信号。
- 驱动方式:
- 有源蜂鸣器:只需接通电源,蜂鸣器会根据电压大小或频率自动发声,无需其他外部电路。
- 无源蜂鸣器:需要通过控制器(如单片机)产生方波或脉冲信号来驱动蜂鸣器工作。蜂鸣器的频率和音调由控制信号的频率决定。
- 声音特点:
- 有源蜂鸣器:声音通常比较稳定,适合产生固定频率的声音,例如警报、提示音等。
- 无源蜂鸣器:可以通过调节外部驱动信号的频率生成不同的音调,因此可以实现更丰富的音效。
- 电路复杂性:
- 有源蜂鸣器:电路简单,使用时只需要连接电源,适合需要简单音响输出的应用。
- 无源蜂鸣器:需要额外的驱动电路或控制器,电路相对复杂,但可以实现更灵活的声音控制。
总结来说,有源蜂鸣器适合简单的警示功能,而无源蜂鸣器则更适用于需要变化音调的场景。
因为这里使用了无源蜂鸣器,所以需要给无源蜂鸣器输出一个频率以驱动其工作
总体逻辑
无源蜂鸣器的使用需要模拟频率输出,那么就要寻找支持频率输出的引脚,而频率输出是由 TIM 定时器 产生的,也就是说要寻找支持 TIM 定时器 的引脚,然后将其与蜂鸣器连接。一般情况下,STM32 微控制器的 TIM 定时器可以配置为输出 PWM 信号,PWM 信号的频率决定了蜂鸣器发出的声音频率,而占空比则控制声音的响度。具体步骤如下:
- 选择支持 TIM 定时器的引脚:
- 查找 STM32 微控制器的引脚映射表,确定哪些引脚支持 TIM 定时器的输出功能。每个定时器(如 TIM1、TIM2 等)通常都有多个通道(如 TIM1_CH1、TIM1_CH2 等),并且每个通道都对应一个引脚。
- 使用 STM32 的工具,如 STM32CubeMX,可以帮助你查看每个定时器通道和 GPIO 引脚之间的映射关系。
- 配置 TIM 定时器:
- 选择合适的 TIM 定时器通道,设置其频率和占空比。频率决定了蜂鸣器的音调(Hz),占空比则影响声音的强弱。
- 配置定时器为 PWM 输出模式,并选择合适的频率来驱动蜂鸣器。例如,要产生 1 kHz 的频率,可以将定时器的频率设置为 1 kHz。
- 连接蜂鸣器:
- 将选择的引脚连接到蜂鸣器的正极端,蜂鸣器的负极端可以连接到地(GND)。
- 通过定时器输出的 PWM 信号驱动蜂鸣器。
- 调整频率与占空比:
- 根据需要调整定时器的频率来控制蜂鸣器发出的声音频率(如 1 kHz、2 kHz 等),也可以调整占空比来改变声音的音质或响度。
代码
src/main.rs
#![no_std]
#![no_main]
// pick a panicking behavior
use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics
// use panic_abort as _; // requires nightly
// use panic_itm as _; // logs messages over ITM; requires ITM support
// use panic_semihosting as _; // logs messages to the host stderr; requires a debugger
use cortex_m::asm;
use cortex_m_rt::entry;
use stm32f1xx_hal::{pac,prelude::*};
use stm32f1xx_hal::time::{ms, Hertz};
use stm32f1xx_hal::timer::{Channel, Tim2NoRemap};
#[entry]
fn main() -> ! {
asm::nop(); // To not have main optimize to abort in release mode, remove when you add code
let cp = cortex_m::Peripherals::take().unwrap();
let mut gpioc = dp.GPIOC.split();
let mut pc13 = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
let mut flash = dp.FLASH.constrain();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let mut delay = cp.SYST.delay(&clocks);
let mut afio = dp.AFIO.constrain();
let mut gpioa = dp.GPIOA.split();
let mut pa0 = gpioa.pa0.into_push_pull_output(&mut gpioa.crl);
let pwm_pin = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl);
let mut pwm = dp
.TIM2
.pwm_hz::<Tim2NoRemap, _, _>(pwm_pin, &mut afio.mapr, 1.kHz(), &clocks);
pwm.enable(Channel::C2);
pwm.set_period(1.kHz());
let max = pwm.get_max_duty();
loop {
pc13.toggle();
pa0.set_high();
pwm.set_duty(Channel::C2, max);
delay.delay_ms(3000_u16);
pwm.set_duty(Channel::C2, max/16);
delay.delay_ms(1000_u16);
pa0.set_low();
delay.delay_ms(1000_u16);
}
}
代码详解:
- 交替功能
let pwm_pin = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl);
这一段功能是将PA1引脚设置为交替功能,这个是用于引脚支持频率输出。因为我们需要输出时钟频率,所有要使用支持TIM2_CH的引脚
- 时钟频率输出:
let mut pwm = dp
.TIM2
.pwm_hz::<Tim2NoRemap, _, _>(pwm_pin, &mut afio.mapr, 1.kHz(), &clocks); //配置 PWM 输出(脉宽调制)信号
pwm.enable(Channel::C2); // 启用通道C2 对应TIM2_CH2
pwm.set_period(1.kHz()); //设置 PWM 的频率为 1 kHz,对应的周期是 1 毫秒
let max = pwm.get_max_duty(); //获取 PWM 输出信号 的最大 占空比
pwm.set_duty(Channel::C2, max); //通道输出
这里需要注意一点就是如果没通过pwm.enable启用通道,但是在下面调用通道其他通道如:Channel::C3的话,会有板载报错,从而中断程序(编译会通过,并且会成功上传)