有源蜂鸣器内含振荡源,只要一通电就发声,但发生频率固定,音色单一;无源蜂鸣器内部不含振荡源,内部结构相当于电磁场扬声器,可以通过给他输出一定频率的信号才能发声。
人耳能听到的频率范围在20Hz--20kHz之间,通过STM32的GPIO引脚快速切换高低电平输出就能实现无源蜂鸣器的发声,切换的频率不同,发出的音调就不一样。
二、音乐播放的实现
一段音乐就是不同频率的声音按一定的时间节拍转换发出。所以音乐包含音调和节拍信息。
C调各音符频率如下:
音符 | 频率 Hz | 音符 | 频率 Hz |
低1 Do | 262 | 中1 Do | 523 |
低2 Re | 294 | 中2 Re | 587 |
低3 Mi | 330 | 中3 Mi | 659 |
低4 Fa | 349 | 中4 Fa | 698 |
低5 So | 392 | 中5 So | 784 |
低6 La | 440 | 中6 La | 880 |
低7 Si | 523 | 中7 Si | 988 |
如果要实现歌曲“红尘情歌”,要准备相应的数据。
歌谱如下:
程序中首先准备音频数据表:
// 低Si Do Re Mi Fa So La Si ¸高Do¸高Re¸高Mi¸高Fa¸高So 无
uc16 tone[] ={247,262,294,330,349,392,440,294,523, 587, 659, 698, 784, 1000};
u8 music[]={ 5,5,6,8,7,6,5,6,13,13,……};//音调
u8 time[] = { 2,4,2,2,2,2,2,8,4, 4, ……}; //节拍时间
依次从音调数组中取music[i],然后根据music[i]的值在tone数组中得到该音的发声频率(tone[music[i]]),调用sound函数控制蜂鸣器发声,声音的发声时间有time数组控制。
三、项目创建与配置
1、创建项目文件夹(设为pMusic)
2、通过Keil5创建新项目,保存在所创建的文件夹中(设项目名为pMusic),选择MCU芯片为"STM32F103ZE"(本程序使用的硬件为:STM32-PZ6806L开发板)
3、在pMusic项目文件夹中新建"CMSIS"、"Device"、"Public"、"Startup"、"User"和"Lib"文件夹。
① 在"CMSIS"文件夹中复制"core_cm3.h"和"core_cm3.c"文件;
② 在" Device "文件夹中复制"stm32f10x.h"、"system_stm32f10x.h"和"system_stm32f10x.c"文件;
③ 在" Startup "文件夹中复制"startup_stm32f10x_hd.s"文件;
④在"Lib"文件夹中新建"inc"和"src"两个子文件夹,在"inc"文件夹中复制"misc.h"、"stm32f10x_gpio.h"和"stm32f10x_rcc.h"文件;在"src"文件夹中复制"misc.c"、"stm32f10x_gpio.c"和"stm32f10x_rcc.c"文件;
4、为项目添加"CMSIS"、"Device"、"Public"、"Startup"、"User"和"Lib"组,并将上述C程序文件和"startup_stm32f10x_hd.s"启动文件加入到相应组中。展开项目树如下:
5、打开“项目配置”对话框,在"Output"选项卡中选择"Create HEX File",在"C/C++"选项卡中的"Include Paths"中添加如下包含路径:".CMSIS;", ".Device;", ".Libinc;",".Public;"。
(以上步骤可以参看:使用STM32固件库操作控制LED灯(CMSIS) 使用STM32固件库函数操作控制LED灯
6、在"stm32f10x.h"中添加函数参数检查宏
(参看:使用STM32固件库函数操作控制LED灯)
#ifdef USE_FULL_ASSERT /** * @brief 这个assert_param宏用于函数参数检查 * @param expr:如果expr是 false,就调用 assert_failed函数报告源文件名和 * 失败的行号,如果expr是 true ,就返回一个空值 * @retval None */ #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */ void assert_failed(uint8_t* file, uint32_t line); #else #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ |
7、新建一个文件(system.h),保存到"Public"文件夹中,内容为:
#ifndef __SYSTEM__H #define __SYSTEM__H #include "stm32f10x.h" //定义位带地址宏 #define BITBAND(addr,bitnum) ((addr&0xF0000000) + 0x02000000 + ((addr&0x000FFFFF)<<5) + (bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitnum)) //IO口地址映射 //数据输出寄存器地址 #define GPIOA_ODR_Addr (GPIOA_BASE + 12) #define GPIOB_ODR_Addr (GPIOB_BASE + 12) #define GPIOC_ODR_Addr (GPIOC_BASE + 12) #define GPIOD_ODR_Addr (GPIOD_BASE + 12) #define GPIOE_ODR_Addr (GPIOE_BASE + 12) #define GPIOF_ODR_Addr (GPIOF_BASE + 12) #define GPIOG_ODR_Addr (GPIOG_BASE + 12) //数据输入寄存器地址 #define GPIOA_IDR_Addr (GPIOA_BASE + 12) #define GPIOB_IDR_Addr (GPIOB_BASE + 12) #define GPIOC_IDR_Addr (GPIOC_BASE + 12) #define GPIOD_IDR_Addr (GPIOD_BASE + 12) #define GPIOE_IDR_Addr (GPIOE_BASE + 12) #define GPIOF_IDR_Addr (GPIOF_BASE + 12) #define GPIOG_IDR_Addr (GPIOG_BASE + 12) #endif |
该文件定义了GPIO端口位带操作的宏。
(位带操作请参看:通过位带地址操作GPIO在数码管显示数字)
8、新建文件"SysTick.h",保存到"Public"文件夹中,内容为:
#ifndef __SysTick__H #define __SysTick__H #include "stm32f10x.h" void SysTick_Init(u8 SYSCLK); void delay_us(u32 nus); void delay_ms(u16 nms); #endif |
新建文件"SysTick.c",保存到"Public"文件夹中,内容为:
#include "SysTick.h" #include "misc.h" u8 fac_us = 0; u16 fac_ms = 0; void SysTick_Init(u8 SYSCLK) {undefined SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us = SYSCLK / 8; fac_ms = (u16)fac_us*1000; }
void delay_us(u32 nus) {undefined u32 temp; SysTick->LOAD = nus * fac_us; SysTick->VAL = 0; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; do{undefined temp = SysTick->CTRL; }while((temp&0x01)&&(!(temp&(1<<16)))); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0; } void delay_ms(u16 nms) {undefined u32 temp; SysTick->LOAD = nms * fac_ms; SysTick->VAL = 0; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; do{undefined temp = SysTick->CTRL; }while((temp&0x01)&&(!(temp&(1<<16)))); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0; } |
这两个文件实现了通过SysTick精准延时的函数,提供给后续的音频频率产生程序使用。
(关于SysTick,请参看:在STM32项目中使用SysTick实现延时)
将"SysTick.c"文件添加到项目的"Public"组中。
9、实现发声
①开发板无源蜂鸣器的电路连接如下:
从电路连接可以看出通过MCU的PB5(GPIOB_5)控制蜂鸣器的发声。
②在项目文件夹的"User"文件夹下新建"Beep"文件夹,在项目中新建"beep.h"文件,保存在"User/Beep"文件夹中,文件内容为:
#ifndef __BEEP__H #define __BEEP__H #include "system.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" //定义GPIOB的位地址变量宏 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) #define PBeep PBout(5) #define BEEP_PORT GPIOB #define BEEP_PIN GPIO_Pin_5 #define BEEP_PORT_RCC RCC_APB2Periph_GPIOB void BEEP_Init(void); void Sound(u16 frq); void play(void); #endif |
③在项目中新建"beep.c"文件,保存在"User/Beep"文件夹中,文件内容为:
#include "beep.h" #include "systick.h" void BEEP_Init(void) {undefined GPIO_InitTypeDef GPIO_mode; RCC_APB2PeriphClockCmd( BEEP_PORT_RCC, ENABLE ); //使能GPIOB时钟 GPIO_mode.GPIO_Pin = BEEP_PIN; GPIO_mode.GPIO_Speed = GPIO_Speed_50MHz; GPIO_mode.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(BEEP_PORT, &GPIO_mode); //设置GPIOB_5为推挽输出,50MHz速度 }
void Sound(u16 frq) {undefined u32 n; if(frq != 1000) //如果频率不为1000则按频率输出,否则只延时 {undefined n = 500000/((u32)frq); PBeep = 0; delay_us(n); PBeep = 1; delay_us(n); }else delay_us(1000); }
void play(void) {undefined // 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 不发音 uc16 tone[] = {247,262,294,330,349,392,440,294,523,587,659,698,784,1000};//音频数据表 //红尘情歌 u8 music[]={ 5,5,6,8,7,6,5,6,13,13,//音调 5,5,6,8,7,6,5,3,13,13, 2,2,3,5,3,5,6,3,2,1, 关键字:STM32 无源蜂鸣器 播放音乐 引用地址:教你如何使用STM32控制无源蜂鸣器发声播放音乐
上一篇:4.3、使用寄存器版本点亮LED灯(内附代码) 推荐阅读最新更新时间:2024-11-13 05:08
stm32所有I/O和控制引脚的电流能力
疑问 :所有I/O和控制引脚上的总注入电流(4) ±25 照这句话 理解如果点个 10mA的光耦 那只能点 2只半了??那这个电流也太小了点吧? 答案:在注2中给出了注入电流的定义:当VIN VDD时,有一个正向注入电流;当VIN VSS时,有一个反向注入电流。 从这个定义可以看出,注入电流与正常的输出电流和正常的入地的吸收(Sink)电流是不同的。注入电流是当VIN VDD时或当VIN VSS时才会出现,而正常的时候是不会有注入电流的。 正常的输出电流和正常的入地的吸收(Sink)电流的极限值分别是:I VDD 和I VSS ,它们分别有150mA。 请注意,这个表里的参数是极限值,即如果任何一个参数超过了这里列
[单片机]
关于STM32中的assert_param()
在STM32的固件库和提供的例程中,到处都可以见到assert_param()的使用。如果打开任何一个例程中的stm32f10x_conf.h文件,就可以看到实际上assert_param是一个宏定义; 在固件库中,它的作用就是检测传递给函数的参数是否是有效的参数。 所谓有效的参数是指满足规定范围的参数,比如某个参数的取值范围只能是小于3的正整数,如果给出的参数大于3,则这个assert_param()可以在运行的程序调用到这个函数时报告错误,使程序员可以及时发现错误,而不必等到程序运行结果的错误而大费周折。 这是一种常见的软件技术,可以在调试阶段帮助程序员快速地排除那些明显的错误。 它确实在程序的运行上牺牲了效率(但
[单片机]
stm32 中怎样把变量定义到flash中
目的:将变量定义到flash中 其实写这个是相当于一个记录而已,突然在做字模显示问题,想到如果将字模数据放到内存中的话,难免多了很容易出问题,不如就放在flash中,这样就可以不去改变他 int a;a在内存中 const int a;a在flash中,这个可以通过打印地址来看
[单片机]
使用Ulink2的JTAG与SWD 调试stm32 区别
一、硬件连接 我调试stm32F407使用的ULINK2,Ulink2内部实物图如下。 1.1 JTAG interface 标准20pin JTAG引脚中,JTAG调试只使用8pin,定义如下: 1)NTRST ---------------Test ReSeT/ pin — Use 100K Ohm pull-up resistor to VCC. 必须有该引脚 2)TDI -----------------Test Data In pin — Use 100K Ohm pull-up resistor to VCC.数据输入 必须有该引脚 3)TMS/SW
[单片机]
Cartesiam发布优化STM32开发板的新版NanoEdge™ AI Studio
嵌入式系统人工智能(AI)软件开发公司Cartesiam发布了针对STM32开发板优化的新版NanoEdge™AI Studio软件工具。STM32是横跨多重电子应用领域的全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST;纽约证券交易所代码:STM)开发的市场领先的微控制器。Cartesiam的NanoEdge AI Studio开发环境消除了传统的AI开发障碍,面向缺乏机器学习专业资源的企业。新版本让任何嵌入式开发者都可以用STM32微控制器快速开发强大的AI解决方案。在其诸多优化功能中,它还可直接通过STM32的串行/ USB端口和Cartesiam增强版自动数据符合性检查和质量验证工具在Nan
[单片机]
STM32按键的短按/长按/双击
上一篇文章已经写好了定时器,并且实现了按键长按。 现在狭隘的用一下: https://mp.weixin.qq.com/s?__biz=MzUyOTM0NDkyMA==&mid=2247484014&idx=1&sn=b07bde8a1d5b15467b2c80dfd7ae3547&chksm=fa633299cd14bb8f8e9334282059930bafabf44dfcf502b0a39b7f30326268f8574acc0b0ccf&mpshare=1&scene=23&srcid=0606fxjOEkyOf9zu21GHV78G#rd //⑥外部中断 2 服务程序 按键效果!!! void EXTI2_I
[单片机]
基于ZigBee和STM32的智能家居控制系统的设计与实现(五)--终结
##基于ZigBee和STM32的智能家居控制系统的设计与实现(五)–终结篇 ###说明 首先祝贺自己顺利的完成了毕业答辩工作,想起整个过程还是挺让自己感动的。最后还被评为优秀毕业设计,虽然并没有什么luan用,但是,马上毕业了,还是挺让人怀念的。整个资料从第一篇博客说起就说会全部开源的,期间承蒙各位朋友的支持,给与资助,在此感谢了。虽然资料中私人信息删除了一些,但是不免有疏漏,所以有关个人信息还挺各位删除掉,小弟在此谢谢了! ###总结 下面简单介绍下系统使用到的一些资料和系统实现的功能。 开发系统:Windows10 64位 Ubuntu 14.04 32 位 软件版本:Qt 4.8.6 IAR .0 Altium Des
[单片机]
STM32之八定时器中断
STM32的通用定时器是一个通过可编程预分频器(PSC)驱动的16 位自动装载计数器(CNT)构成。STM32的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32的每个通用定时器都是完全独立的,没有互相共享的任何资源。 STM3的通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括: 1)16位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。 2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535之间
[单片机]
推荐帖子
小广播
推荐内容
热门活动
换一批 更多
设计资源 培训 开发板 精华推荐
最新单片机文章
更多开源项目推荐
更多精选电路图
更多热门文章
更多每日新闻
更多往期活动
11月13日历史上的今天
厂商技术中心
随便看看
|