文章目录

  • 前言
  • 硬件设计
    • 电路分析
    • io口分析
  • 软件设计
    • 准备工作
    • 设计思路
    • 实际代码
      • H文件
      • C文件
      • 说明
  • 总结

前言

刚学习单片机时,用的都是开发板上的蜂鸣器,硬件电路是非常简单的,只需调好管脚的PWM波的频率或者用定时器来翻转IO口就好了,原理图如下:

这样子的电路会导致蜂鸣器在发声时只是单纯的哔哔叫,最多变一变音调,给人的感觉会很生硬,我们想要的效果是和弦音(其实我也不太懂什么叫和弦音,只知道前辈们都这么叫,在我看来,好像只是加了个延音的效果,声音结束的不会那么生硬),嘛,总之是比较好听的声音~

硬件设计

看下和弦音的电路吧~

这个电路在设计时,电解电容值标注错了,实际使用时,一般在10uf~47uf效果都还可以。

电路分析

简单分析下这个电路,这个电路相比上一个电路来说,从功能上来说,主要就两个区别:

1.多了一个连接io的网络;
2.多了一个电解电容。

io口分析

原理图上能看到有两个io口,其中:

PA11是要发出PWM波的,PWM的频率决定了蜂鸣器的音调高低。就叫它PWM口好了。
PA12是输出开关量的,输出高电平时,三极管导通,同时电容会被充电,输出低电平时,电容放电,支持三极管的导通,电放完后,蜂鸣器变不会再鸣叫了,可以把这个管脚叫做蜂鸣器的激活管脚。

软件设计

准备工作

我今天想分享的是和弦音蜂鸣器的模块化编程,由于蜂鸣器可能会被用在各种平台上,但io口的配置以及pwm波的配置都不会是通用的,所以这些地方需要读者自己搞定,我可以po出我的相关设置,作为参考:

#define PWM_PERIOD_SET(x)    PWMDTYA &= ~Bin(11000000); PWMDTYA |= ((x&0x03)<<6); PWMPRD = ((x&0x03FC)>>2);
#define PWM_DUTY_SET(x)     PWMDTYA &= ~Bin(00001100); PWMDTYA |= (((x)&0x03)<<2); PWMDTY1 =(((x)&0x03FC)>>2);
#define BUZZER_BEEP_SET(x)  PWM_PERIOD_SET(x);PWM_DUTY_SET(x/2);#define BUZZER_ENABLE       P26 = 1
#define BUZZER_DISABLE      P26 = 0
#define PWM_START           PWMCON |= 0x82
#define PWM_STOP            PWMCON &= ~0x82

我用的是赛元的单片机,根据寄存器描述将PWM设置以及IO口操作用宏定义封装了起来,后面代码只会用到这几个命令:

#define BUZZER_BEEP_SET(x)   PWM_PERIOD_SET(x);PWM_DUTY_SET(x/2);
#define BUZZER_ENABLE       P26 = 1                                    //蜂鸣器使能
#define BUZZER_DISABLE      P26 = 0                                    //蜂鸣器失能
#define PWM_START           PWMCON |= 0x82                         //启动PWM输出
#define PWM_STOP            PWMCON &= ~0x82                            //停止PWM输出

大家可以根据自己的平台来进行封装,一开始宏定义使用起来困难的话,可以用函数来搞定,我们的目的只是设置PWM的频率和占空比而已,方法是多样的~

设计思路


首先看上图,这个其实是控制连接电容的那个IO口的时序图,当时间间隔合适时,它发出的声音是叮(延音)叮(延音)叮(延音),请先脑补一下。在发声期间,PWM是一直在输出的。
我在写蜂鸣器功能时,是把它当做了一个独立的任务,每10ms会运行一次,这个时间间隔当然可以变化,时间间隔越短,你就能组合出越多的蜂鸣效果。
我设计了这么一个数据结构,用数组来存储每种声音的关键信息(频率信息+时间节点信息),代码如下:

code u16 beep_node_table[4][9] =
{//频率, 节点1, 节点2, 频率, 节点3, 节点4, 频率, 节点5, 节点6
/*KEY*/ { 187,    17,    50,     0,      0,      0,     0,      0,      0}, //4khz, 500ms
/*ON*/  { 412,    20,    30,     326,    70,     270,   0,      0,      0}, //1.8khz, 500ms,2.3khz, 2400ms
/*END*/ { 187,    50,    100,    0,      0,      0,     0,      0,      0}, //(4khz, 330ms)*3
/*ERR*/ { 187,    33,    66,     0,      0,      0,     0,      0,      0}, //4khz, 660ms
};

1.频率信息很好理解,就是PWM的频率,用于设置蜂鸣器的音调;
2.节点信息,这个是我自己起的名字,是时间节点的意思,以KEY(按键音)这一行代码为栗子,我解释一下,应该也很容易明白。蜂鸣器任务每10ms运行一次,当按键音刚刚被触发时,时间节点是0,运行到蜂鸣器任务后,会首先将频率设置一下,然后启动PWM输出,使能蜂鸣器的电容口,内部计时的变量会累加。当运行了17次(170ms)蜂鸣器任务后,失能蜂鸣器的电容口,PWM输出不做处理,即它依然在输出。当运行了50次(500ms)蜂鸣器任务后,会失能蜂鸣器的电容口,停止PWM端口的输出,这是蜂鸣器就不响了。从出发到结束,蜂鸣器的声音就是–170ms的叮+330ms的延音。

实际代码

直接po上我的蜂鸣器任务代码吧,这样可以对它有更全面的认知。

H文件

#ifndef __TASK_BUZZER_H
#define __TASK_BUZZER_Htypedef enum
{NO_BEEP            = 0    ,   BEEP_KEY        = 1    , BEEP_POWER_ON         = 2    ,   BEEP_PROGRAM_START  = 3    , BEEP_PROGRAM_END  = 4    ,BEEP_ERROR_ALARM   = 5    ,
}BEEP_MODE;extern BEEP_MODE now_beep_mode;extern void task_buzzer(void);
extern void buzzer_beep_set(u8 mode);#endif

C文件

#include "includes.h"typedef enum
{WAIT_FOR_BEEP          = 0    ,IS_BEEPING         = 1    ,HAS_BEEPED         = 2    ,}BEEP_STATE;BEEP_MODE now_beep_mode = BEEP_POWER_ON;
BEEP_STATE beep_state = WAIT_FOR_BEEP;//   BEEP_KEY        = 1    ,
//  BEEP_POWER_ON       = 2    ,
//  BEEP_PROGRAM_START  = 3    ,
//  BEEP_ERROR_ALARM    = 4    ,
//  NO_BEEP         = 0    ,code u16 beep_node_table[4][9] =
{//频率, 节点1, 节点2, 频率, 节点3, 节点4, 频率, 节点5, 节点6
/*KEY*/ { 187,    17,    50,     0,      0,      0,     0,  0,  0}, //4khz, 500ms
/*ON*/  { 412,    20,    30,     326,    70,     270,   0,  0,  0}, //1.8khz, 500ms,2.3khz, 2400ms
/*END*/ { 187,    50,    100,    0,      0,      0,     0,  0,  0}, //(4khz, 330ms)*3
/*ERR*/ { 187,    33,    66,     0,      0,      0,     0,  0,  0}, //4khz, 660ms
};static void beep_control(u8 *now_mode);/*************************************************************/
/*函数名:task_buzzer
/*输  入:无
/*输  出:无
/*描  述:任务3,蜂鸣器任务,每10ms运行一次,选择发声函数
/*************************************************************/
void task_buzzer(void)
{       if((set_menu.beep_state==FUNCTION_OFF)&&(now_beep_mode!=BEEP_ERROR_ALARM)){return;}switch(now_beep_mode){case BEEP_KEY       :   beep_control(&now_beep_mode);   break;case BEEP_POWER_ON    :   beep_control(&now_beep_mode);   break;case BEEP_PROGRAM_START   :   beep_control(&now_beep_mode);   break;case BEEP_PROGRAM_END   : beep_control(&now_beep_mode);   break;case BEEP_ERROR_ALARM :   beep_control(&now_beep_mode);   break;case NO_BEEP      :   beep_control(&now_beep_mode);   break;default: BUZZER_DISABLE;PWM_STOP; break;}
}/*************************************************************/
/*函数名:beep_control
/*输  入:无
/*输  出:无
/*描  述:控制发声,函数太长了,还不够通用,提升空间还很大
/*************************************************************/
static void beep_control(u8 *now_mode)
{static u16 node = 0;static u8  repeat_count = 0;if(beep_state == WAIT_FOR_BEEP){node = 0;repeat_count = 0;beep_state = IS_BEEPING;}if(beep_state == IS_BEEPING){switch(*now_mode){case BEEP_KEY:if(node == 0){BUZZER_BEEP_SET(beep_node_table[0][0]);BUZZER_ENABLE;PWM_START;}if(node == beep_node_table[0][1]){BUZZER_DISABLE;}if(node >= beep_node_table[0][2]){BUZZER_DISABLE;PWM_STOP;*now_mode = NO_BEEP;beep_state = HAS_BEEPED;}             node++;break;case BEEP_POWER_ON:  if(node == 0){BUZZER_BEEP_SET(beep_node_table[1][0]);BUZZER_ENABLE;PWM_START;}if(node == beep_node_table[1][1]){BUZZER_DISABLE;}if(node == beep_node_table[1][2]){BUZZER_BEEP_SET(beep_node_table[1][3]);BUZZER_ENABLE;}if(node == beep_node_table[1][4]){BUZZER_DISABLE;}  if(node >= beep_node_table[1][5]){BUZZER_DISABLE;PWM_STOP;*now_mode = NO_BEEP;beep_state = HAS_BEEPED;}node++;break;case BEEP_PROGRAM_END :if(node == 0){BUZZER_BEEP_SET(beep_node_table[2][0]);BUZZER_ENABLE;PWM_START;}if(node == beep_node_table[2][1]){BUZZER_DISABLE;}if(node >= beep_node_table[2][2]){BUZZER_DISABLE;PWM_STOP;repeat_count++;if(repeat_count>=3){*now_mode = NO_BEEP;                     beep_state = HAS_BEEPED;}else{BUZZER_BEEP_SET(beep_node_table[2][0]);BUZZER_ENABLE;PWM_START;node = 0;}                                               }               node++;break;case BEEP_ERROR_ALARM:if(node == 0){BUZZER_BEEP_SET(beep_node_table[3][0]);BUZZER_ENABLE;PWM_START;}if(node == beep_node_table[3][1]){BUZZER_DISABLE;}if(node >= beep_node_table[3][2]){BUZZER_DISABLE;PWM_STOP;*now_mode = NO_BEEP;beep_state = HAS_BEEPED;}              node++;break;default :beep_state = HAS_BEEPED;BUZZER_DISABLE;PWM_STOP;                   *now_mode = NO_BEEP;break;}}
}/*************************************************************/
/*函数名:buzzer_beep_init
/*输  入:发声模式
/*输  出:无
/*描  述:初始化发声状态
/*************************************************************/
void buzzer_beep_set(u8 mode)
{now_beep_mode = mode;beep_state = WAIT_FOR_BEEP;
}
/*****************************************end*******************************************/

说明

其中用到的宏定义都已经说明过了,重点看下面这一段代码就好了,结合我上面的代码思路章节,应该就可以很好的理解和弦音蜂鸣器的控制了。

         case BEEP_KEY:if(node == 0){BUZZER_BEEP_SET(beep_node_table[0][0]);BUZZER_ENABLE;PWM_START;}if(node == beep_node_table[0][1]){BUZZER_DISABLE;}if(node >= beep_node_table[0][2]){BUZZER_DISABLE;PWM_STOP;*now_mode = NO_BEEP;beep_state = HAS_BEEPED;}             node++;break;

总结

其实这个模块的代码还并不能达到我想要的效果,还有优化的空间,就比如说每一种声音模式要对应一个case分支,这其实还是蛮麻烦的,目前笔者水平有限,暂时想不出如何优化,虽然这种方式不算完美,但是封装的还可以,可以直接拿来使用,希望大家能学到新知识~
如果本文对你有用,请点个赞吧,这是对我莫大的支持,当然也更欢迎评论区留言讨论以及收藏哦~
祝:变得更强!

单片机控制蜂鸣器发出和弦音(硬件+软件)相关推荐

  1. 51单片机控制蜂鸣器发SOS国际求救信号声音实验

    说明:本文是51单片机控制蜂鸣器发声实验的高级版,和上个实验差不多,上个实验是控制蜂鸣器发声,本实验可以理解为控制蜂鸣器间断发出不同长度的声音,本文同样重在夯实基础,如果是大神,请自觉飘过. 好了我们 ...

  2. 51单片机蜂鸣器演奏《小苹果》C语言程序,单片机控制蜂鸣器演奏音阶实例

    单片机可以控制蜂鸣器发声音 蜂鸣器是非常常见的发音元器,音乐卡.报警装置.电子琴.各种小家电等都会用到.单片机的PWM功能可以设置输出不同频率的信号,所以我们可以利用单片机的PWM控制三极管的通断来推 ...

  3. 单片机控制蜂鸣器唱生日快乐歌曲 PROTEUS 和51单片机教程(附仿真文件+源代码)

    功能:按一下开关后,单片机控制蜂鸣器唱生日快乐歌曲 PROTEUS 和51单片机教程 单片机控制蜂鸣器唱生日快乐歌曲 程序源代码如下: /* 生日歌 */ #include <reg51.h&g ...

  4. Verilog实现4位按键分别控制蜂鸣器发出不同音阶(未完成,请指导)

    实验任务: 使用板载4位独立按键,进行4位按键消抖检测,当没有按键按下时不响,按下则发出声响,分别为 DO RE MI FA. 实验思路 还是有两个part,一个part用来检测按键是否按下,输入ke ...

  5. 单片机控制蜂鸣器和弦音发音程序

    //文件buz.h#ifndef _BUZ_H_ #define _BUZ_H_//SetupBzhx(M_BZ_KEY) ; //=====buz 驱动频率定义============== //声音 ...

  6. c语言写按键控制蜂鸣器,51单片机用按键控制蜂鸣器发出do re mi fa...的声音,...

    满意答案 0fhk9 2017.12.30 采纳率:53%    等级:7 已帮助:1961人 T0HEQU 30H T0L EQU 31H ORG 0000H LJMP MAIN ORG 000BH ...

  7. 基于android开发手机控制空调的程序(硬件+软件)

    本文适用于在没有红外发射器的手机上,利用音频接口连接相关发射器进行信号发射.效果展示:http://t.cn/RLhOuCf 2016年2月28日更新: 使用音频还是要给手机插音频线,所以具有相当大的 ...

  8. STM32驱动压电式蜂鸣器发出和弦音原理图加程序

    一.原理图 一.驱动代码 #include "beep.h"void beep_init(void) {GPIO_InitTypeDef GPIO_InitStructure;RC ...

  9. 蜂鸣器播放爱你歌曲c语言程序设计,51单片机控制蜂鸣器播放5首歌曲汇编程序...

    欢乐颂 DB 32,32,32,32,32,32,32,32,32,32,32,32,48,16,64 DB 32,32,32,32,32,16,16,32,32,32,16,16,32,32,32, ...

最新文章

  1. 深入剖析SolrCloud(一)
  2. 25种用户十秒离开你网站的原因!
  3. Dubbo自定义日志拦截器
  4. mysql 列数据显示转成行数据显示_Mysql的列修改成行并显示数据的简单实现
  5. 20150303+JQuery选择器-02
  6. Linux下Elasticsearch-2.4.0的安装与简单配置(单节点)Head插件安装(已测试)
  7. Python envoy 模块源码剖析
  8. linux中mtools工具_Linux中mtools命令起什么作用呢?
  9. python kafka消费实时数据,python生产和消费kafka数据
  10. openstack部署过程中问题记录
  11. node url模块
  12. [2019.04.16] 由Python写成的自动解压脚本
  13. Java-图书管理系统(控制台程序)
  14. xposed微信模块源代码
  15. IBM X3650M4服务器拆机风扇 69Y5611 94Y6620 GFC0812DS 线序
  16. 怎么更改计算机网上邻居,如何更换登入网上邻居帐号
  17. kali linux虚拟机镜像的安装(详细手把手教你)
  18. 计算机课学生段密码,启课程学生端电脑版
  19. 再先进的在线教学,也要回归这个本质
  20. Scaling Vision Transformers

热门文章

  1. Linux—虚拟机VMware最详细安装教程
  2. win10 ad用户 计算机名,更改 AD 用户的显示名称 - Windows Server | Microsoft Docs
  3. 豆瓣上备受好评的21本Python书籍大集合【墙裂推荐收藏】
  4. android 开发 华为手机型号,华为手机用户可以体验Android P了!9款华为机型开放EMUI 9.0升级...
  5. iOS之---优化应用
  6. python 阶乘和
  7. Excel中mid函数的用法
  8. wps插入图片后使图片完整显示
  9. 国道219线阿里段已基本畅通 农牧民生产生活秩序井然
  10. WAF绕过“小迪安全课堂笔记”信息收集,漏洞发现,漏洞利用,权限控制