单片机控制蜂鸣器发出和弦音(硬件+软件) 您所在的位置:网站首页 按键控制蜂鸣器播放不同音乐 单片机控制蜂鸣器发出和弦音(硬件+软件)

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

2024-03-31 18:26| 来源: 网络整理| 查看: 265

文章目录 前言硬件设计电路分析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)2); #define PWM_DUTY_SET(x) PWMDTYA &= ~Bin(00001100); PWMDTYA |= (((x)&0x03)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_H typedef 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分支,这其实还是蛮麻烦的,目前笔者水平有限,暂时想不出如何优化,虽然这种方式不算完美,但是封装的还可以,可以直接拿来使用,希望大家能学到新知识~ 如果本文对你有用,请点个赞吧,这是对我莫大的支持,当然也更欢迎评论区留言讨论以及收藏哦~ 祝:变得更强!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有