打印
[产品应用]

CW32L031 PWM演奏一首歌曲

[复制链接]
759|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lulugl|  楼主 | 2023-9-24 15:38 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 lulugl 于 2023-9-24 15:39 编辑


有源蜂鸣器与无源蜂鸣器的驱动方式:
 注意:这里的“源”不是指电源,而是指震荡源。
 也就是说,有源蜂鸣器内部带震荡源,所以只要一通电就会叫;而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫,必须用2K-5K的方波去驱动它。
有源蜂鸣器往往比无源的贵,就是因为里面多个震荡电路。
无源蜂鸣器的优点是:
1. 便宜
2. 声音频率可控,可以做出“多来米发索拉西”的效果
3. 在一些特例中,可以和LED复用一个控制口
单片机驱动他激蜂鸣器的方式有两种:一种是PWM输出口直接驱动,另一种是利用I/O 定时翻转电平产生驱动波形对蜂鸣器进行驱动。
PWM 输出口直接驱动是利用PWM输出口本身可以输出一定的方波来直接驱动蜂鸣器。在单片机的软件设置中有几个系统寄存器是用来设置PWM口的输出的,可以设置占空比、周期等等,通过设置这些寄存器产生符合蜂鸣器要求的频率的波形之后,只要打开PWM输出,PWM输出口就能输出该频率的方波,这个时候利用这个波形就可以驱动蜂鸣器了。比如频率为2000Hz的蜂鸣器的驱动,可以知道周期为500μs,这样只需要把PWM的周期设置为500μs,占空比电平设置为250μs,就能产生一个频率为2000Hz的方波,通过这个方波再利用三极管就可以去驱动这个蜂鸣器了。
而利用I/O定时翻转电平来产生驱动波形的方式会比较麻烦一点,必须利用定时器来做定时,通过定时翻转电平产生符合蜂鸣器要求的频率的波形,这个波形就可以用来驱动蜂鸣器了。
比如为2500Hz的蜂鸣器的驱动,可以知道周期为400μs,这样只需要驱动蜂鸣器的I/O口每200μs翻转一次电平就可以产生一个频率为2500Hz,占空比为1/2duty的方波,再通过三极管放大就可以驱动这个蜂鸣器了。
驱动的原理图

音阶频率转换表


Cw32L031 pwm波形频率计算:
F=PLCK/((PSC+1)*(ADDR+1))
根据以上面的计算公式,我们的PLCK是已知的,PSC在初始化PWM时也是已知的,我们就只需要改变ADDR的装载值就可以实现不同频率的PWM波,用于驱动无源蜂鸣器发出指定音调的声音了。
根据开发板的原理图,我们驱动蜂鸣器的IO为PB3,经查数据手册,为GTIMI2的通道2,为此初始化代码如下:
void RCC_Configuration(void)
{
    ///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
    __RCC_FLASH_CLK_ENABLE();
    FLASH_SetLatency(FLASH_Latency_2);
    /* 0. HSI使能并校准 */
    RCC_HSI_Enable(RCC_HSIOSC_DIV1);
    /* 1. 设置HCLK和PCLK的分频系数 */
    RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
    RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
    RCC_SystemCoreClockUpdate(48000000);
}
void GPIO_Configuration(void)
{
    /* PB3作为GTIM2的CH2 PWM 输出 */
    __RCC_GPIOB_CLK_ENABLE();
    PB03_AFx_GTIM2CH2();//复用为GTIM2-2
    PB03_DIGTAL_ENABLE();
    PB03_DIR_OUTPUT();
    PB03_PUSHPULL_ENABLE();
}
void PWM_OutputConfig(void)
{
    GTIM_InitTypeDef GTIM_InitStruct = {0};
    __RCC_GTIM2_CLK_ENABLE();//启用GTIM2时钟
    GTIM_InitStruct.Mode = GTIM_MODE_TIME; //设置为定时器模式
    GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;  //为连续计时模式
    GTIM_InitStruct.Prescaler = 11;    // 计数时钟TCLKD = TCLK/(PSC+1)
    GTIM_InitStruct.ReloadValue = 0xffff; //GTIM的重载值
    GTIM_InitStruct.ToggleOutState = DISABLE; //GTIM的翻转输出使能选择
    GTIM_TimeBaseInit(CW_GTIM2, &GTIM_InitStruct);
    GTIM_OCInit(CW_GTIM2, GTIM_CHANNEL2, GTIM_OC_OUTPUT_PWM_HIGH);//CHy 通道输出高电平
    GTIM_SetCompare2(CW_GTIM2, 0xffff); //设置占空比为100%,PB3输出低电平,蜂鸣器停止
GTIM_Cmd(CW_GTIM2, ENABLE);
}
根据上面的公式,我们确定ARR的计算公式为
ADRR= (48000000/((11+1)* 音频频率))-1
简化为ADRR= (4000000/音频频率)-1
乐谱的计算,我们先用宏定义指定声音的频率值:
//定义低音音名E
#define L1 330
#define L2 370
#define L3 415
#define L4 440
#define L5 494
#define L6 554
#define L65 587
#define L7 622
//定义中音音名E
#define M1 659
#define M2 740
#define M3 831
#define M4 880
#define M5 988
#define M6 1109
#define M7 1245
//定义高音音名E
#define H1 1319
#define H2 1480
#define H3 1661
#define H4 1760
#define H5 1976
声明一个结构体用于存放对应的音阶与节拍时长
typedef struct
{
short mName; //音名
short mTime; //时值,全音符,二分音符,四分音符
}tNote;
一个节拍定义时间长度为2S
//定义时值单位,决定演奏的速度 ms为单位 2000为佳
#define TT 2000  
在网上找的一个现成的曲谱为:
const tNote YourScore[]=
{
{M6,TT/4},{H3,TT/8+TT/16},{H2,TT/16},{H2,TT/4},{H1,TT/8},{M6,TT/8},
{M5,TT/4},{H1,TT/8+TT/16},{H2,TT/16},{M6,TT/4},{0,TT/4},
{M6,TT/4},{H2,TT/4},{H1,TT/4},{M6,TT/8},{M5,TT/8},
{M2,TT/4},{M5,TT/8},{M6,TT/8},{M5,TT/8},{M3,TT/4},{0,TT/8},
//好运来祝你好运来,好运带来了喜和爱
{M3,TT/4},{M6,TT/8+TT/16},{M5,TT/16},{M6,TT/4},{M6,TT/8},{M5,TT/8},
{M6,TT/4},{H2,TT/8+TT/16},{H1,TT/16},{H2,TT/4},{0,TT/4},
{H1,TT/8+TT/16},{H1,TT/16},{H1,TT/8},{H2,TT/8},{H3,TT/8},{H3,TT/8},{H2,TT/8},{H1,TT/8},
{M5,TT/4},{H1,TT/8+TT/16},{M6,TT/16}, {M6,TT/2+TT/4},{0,TT/4},
//好运来我们好运来,迎着好运兴旺发达通四海
{M6,TT/8+TT/16},{M6,TT/16},{H1,TT/8},{H1,TT/8},{M6,TT/4},{0,TT/8},{M6,TT/8},
{M5,TT/8},{M3,TT/8},{M5,TT/8},{H1,TT/8},{M6,TT/4},{0,TT/4},
//叠个千纸鹤,再系个红腰带
{M6,TT/8},{H1,TT/8},{H1,TT/8+TT/16},{H1,TT/16},{H1,TT/8},{M6,TT/8},{M5,TT/4},
{M6,TT/8},{M5,TT/8},{M2,TT/8},{M5,TT/8},{M3,TT/4},{0,TT/8},{M2,TT/8},
//愿善良的人们天天好运来
{M3,TT/8},{M2,TT/8},{M1,TT/8},{M3,TT/8},{M2,TT/4},{0,TT/8},{M3,TT/8},
{M6,TT/8},{M5,TT/8},{M3,TT/8},{M6,TT/8},{M6,TT/8},{M5,TT/4},{0,TT/8},
//你勤劳生活美,你健康春常在
{M6,TT/8},{H1,TT/8},{H1,TT/8+TT/16},{M6,TT/16},{H2,TT/8},{H2,TT/8},{H2,TT/8},{H1,TT/8},
{M6,TT/4},{M5,TT/8},{H1,TT/8},{M6,TT/2+TT/4},{0,TT/4},
//你一生的忙碌为了笑逐颜开
{M6,TT/4},{H3,TT/8+TT/16},{H2,TT/16},{H2,TT/4},{H1,TT/8},{M6,TT/8},
{M5,TT/4},{H1,TT/8+TT/16},{H2,TT/16},{M6,TT/4},{0,TT/4},
{M6,TT/4},{H2,TT/4},{H1,TT/4},{M6,TT/8},{M5,TT/8},
{M2,TT/4},{M5,TT/8},{M6,TT/8},{M5,TT/8},{M3,TT/4},{0,TT/8},
//好运来祝你好运来,好运带来了喜和爱
{M3,TT/4},{M6,TT/8+TT/16},{M5,TT/16},{M6,TT/4},{M6,TT/8},{M5,TT/8},
{M6,TT/4},{H2,TT/8+TT/16},{H1,TT/16},{H2,TT/4},{0,TT/4},
{H1,TT/8+TT/16},{H1,TT/16},{H1,TT/8},{H2,TT/8},{H3,TT/8},{H3,TT/8},{H2,TT/8},{H1,TT/8},
{M5,TT/4},{H1,TT/8+TT/16},{M6,TT/16}, {M6,TT/2+TT/4},{0,TT/4},
//好运来我们好运来,迎着好运兴旺发达通四海
{H2,TT/4+TT/2},{0,TT/4},{H3,TT/4+TT/2},{0,TT/4},{M6,TT/4+TT/2},
{0,TT/4},{M5,TT/8},{M5,TT/8},{M6,TT/4},{0,TT/4},
//通四海,好运来
//Brave Heart
{L6,TT/2},{M3,TT/2},{M2,TT/2},{M5,TT/2},
{L7,TT/2},{0,TT/8},{M1,TT/8},{M2,TT/8},{M1,TT/8+TT/4}, //jump
{0,TT/2},{L5,TT/4},//A
{L6,TT/2},{M3,TT/2},{M2,TT/2},{M6,TT/2},
{M5,TT/2},{0,TT/8},{M2,TT/8},{M7,TT/8},{H1,TT/8},
{H1,TT/4},{0,TT/2},{H1,TT/8},{H2,TT/8},
{H3,TT/4},{H3,TT/8},{H2,TT/8+TT/2},{0,TT/2+TT/8},{M6,TT/8},{H1,TT/8},{H4,TT/8},
{H3,TT/4},{H3,TT/8},{H2,TT/8+TT},{0,TT/2+TT/4},//Jump
{L6,TT/8},{L5,TT/8},{L6,TT/8},{L7,TT/4},{M1,TT/8},
{0,TT/8},{M1,TT/8},{M1,TT/8},{M2,TT/8},{L7,TT/8},{L6,TT/8},{L5,TT/8},{L6,TT/8+TT/4},//Jump
{0,TT/8},{L6,TT/8},{L6,TT/8},{L5,TT/4},{L3,TT/8+TT},//B
{0,TT/4},{L6,TT/8},{L5,TT/8},{L6,TT/8},{L7,TT/4},{M1,TT/8},
{0,TT/8},{M1,TT/8},{M1,TT/8},{M2,TT/8},{L7,TT/8},{L6,TT/8},{L5,TT/8},{L6,TT/8+TT/4},//Jump
{0,TT/8},{L6,TT/8},{L6,TT/8},{L7,TT/4},{M1,TT/4},//Jump
{L7,TT/8},{M1,TT/8},{M2,TT/8},{M2,TT/4},{L5,TT/4},
{M4,TT/4+TT/8},{M3,TT/8},{M3,TT/4+TT/8},{M1,TT/8},
{M2,TT/4},{M2,TT/8},{M5,TT/8+TT/16},{0,TT/16},{M2,TT/8},{M3,TT/8},{M2,TT/8},//question
{M2,TT/4+TT/8},{M1,TT/8},{M1,TT/2},
{L7,TT/4},{L7,TT/8},{M3,TT/4+TT/8},{L7,TT/8},{L6,TT/8},
{L6,TT/2},{L6,TT/8},{L7,TT/4},{M1,TT/8},{M2,TT/4},{L6,TT/4},{L7,TT/4},{M1,TT/8},{L7,TT/8},
{L7,TT/2},{M1,TT/4+TT/8},{M2,TT/8},{M2,TT},//C
{0,TT/2},{0,TT/4+TT/8},{M1,TT/16},{M2,TT/16},
{M3,TT/4+TT/8},{M3,TT/8},{M5,TT/8+TT/16},{M4,TT/16+TT/8},{M3,TT/8},
{M2,TT/4+TT/8},{L5,TT/8},{L5,TT/4},{0,TT/8},{L7,TT/16},{M1,TT/16},
{M2,TT/4+TT/8},{M2,TT/8},{M4,TT/8+TT/16},{M3,TT/16+TT/8},{M2,TT/8},
{M2,TT/4+TT/8},{M1,TT/8},{M1,TT/2},//D
{0,TT/4},{L6,TT/4},{L7,TT/4},{M1,TT/4},
{M4,TT/4},{M3,TT/4},{M2,TT/4},{M1,TT/8},{M3,TT/4},{0,TT/8},//Jump //question
{0,TT/4},{M3,TT/8},{M2,TT/8},{M1,TT/8},{M2,TT/8+TT/2+TT/4},//Jump  E
{0,TT/8},{M1,TT/16},{M2,TT/16},
{M3,TT/4},{0,TT/8},{M3,TT/8},{M5,TT/8+TT/16},{M4,TT/16+TT/8},{M3,TT/8},
{M2,TT/4+TT/8},{L5,TT/8},{L5,TT/4},{0,TT/8},{L7,TT/16},{M1,TT/16},
{M2,TT/4},{0,TT/8},{M2,TT/8},{M4,TT/8+TT/16},{M3,TT/16+TT/8},{M2,TT/8},
{M2,TT/4+TT/8},{M1,TT/8},{M1,TT/2}, //F
{M4,TT/8+TT/16},{M3,TT/16+TT/8},{M2,TT/8},{M1,TT/8},{L7,TT/8},{0,TT/4},
{M5,TT/8+TT/16},{M4,TT/16+TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},{0,TT/4},
{M4,TT/8+TT/16},{M3,TT/16+TT/8},{M2,TT/8},{M1,TT/8},{L7,TT/4},
{M1,TT},{0,TT/8},{0,TT/4},{L65,TT/8},{L65,TT/8},{L65,TT/8},{L65,TT/8},{M1,TT/8},
{M1,TT},//G
};
以下函数实现发声功能:
//蜂鸣器发出声音
//usFreq即发声频率
void buzzerSound(unsigned short usFraq)   //usFraq是发声频率,即真实世界一个音调的频率。
                                        //取值是(系统时钟/65536)+1~20000,单位为Hz。即122~20000Hz。
{
unsigned long ulVal;
if((usFraq<=4000000/65536UL)||(usFraq>20000))
{
buzzerQuiet();
}
else
{
ulVal=(4000000/usFraq)-1;//系统时钟除以现实频率,装入ARR
GTIM_SetCompare2(CW_GTIM2, ulVal/2); //占空比,决定音量的大小
GTIM_SetReloadValue(CW_GTIM2,ulVal);     //重载值,频率的大小——》音调的高与低。
GTIM_Cmd(CW_GTIM2, ENABLE);
}
}
播放一整首哥的函数:
//Play Music
void musicPlay(void)
{
uint8_t i=0;
while(1)
{
if(YourScore.mTime==0)
break;
buzzerSound(YourScore.mName);
SysTickDelay(YourScore.mTime);
i++;
SysTickDelay(10);    //10ms,加不加差别不大
}
}
下载好后,就可以实现播放音乐了。

使用特权

评论回复
沙发
AdaMaYun| | 2023-11-21 15:59 | 只看该作者
有源的应该比无源的声音更清脆吧

使用特权

评论回复
板凳
星辰大海不退缩| | 2024-1-21 15:37 | 只看该作者
声音其实就是震动频率

使用特权

评论回复
地板
jf101| | 2024-1-27 14:24 | 只看该作者
楼主这个频率控制的精度怎样?

使用特权

评论回复
5
OKAKAKO| | 2024-1-27 16:39 | 只看该作者
控制蜂鸣器频率

使用特权

评论回复
6
LOVEEVER| | 2024-1-27 18:00 | 只看该作者
PWM 输出口直接驱动是利用PWM输出口本身可以输出一定的方波来直接驱动蜂鸣器。

使用特权

评论回复
7
小夏天的大西瓜| | 2024-1-27 20:45 | 只看该作者
这个蜂鸣器就是依靠震荡实现发生的

使用特权

评论回复
8
星辰大海不退缩| | 2024-2-21 23:17 | 只看该作者
2500Hz的蜂鸣器的驱动,可以知道周期为400μs,这样只需要驱动蜂鸣器的I/O口每200μs翻转一次电平就可以产生一个频率为2500Hz,占空比为1/2duty的方波,再通过三极管放大就可以驱动这个蜂鸣器了。

使用特权

评论回复
9
小小蚂蚁举千斤| | 2024-2-22 11:03 | 只看该作者
在单片机的软件设置中有几个系统寄存器是用来设置PWM口的输出的,可以设置占空比、周期等等,通过设置这些寄存器产生符合蜂鸣器要求的频率的波形之后,只要打开PWM输出,PWM输出口就能输出该频率的方波,这个时候利用这个波形就可以驱动蜂鸣器了。

使用特权

评论回复
10
szt1993| | 2024-2-25 16:21 | 只看该作者
有源蜂鸣器内部带震荡源,所以只要一通电就会叫;而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫,必须用2K-5K的方波去驱动它。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

151

主题

723

帖子

9

粉丝