打印
[应用相关]

STM32利用蜂鸣器演奏音乐

[复制链接]
208|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-11-8 08:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前述
        本文在实现蜂鸣器演奏音乐时只使用了基于SysTick定时器的延时函数,最后做出来的音色效果并不是很好,目前推测的原因有很多,有可能是电路设计的问题也有可能是蜂鸣器的问题,也有可能是延时函数并不能及时翻转引脚电平的问题。有兴趣的同志可以试一试利用定时器中断实现蜂鸣器引脚电平翻转,看看效果会不会有所提升。

        本文只为大家提供算法思想。

乐理知识
音阶
        本人对乐理这方面不是很了解,在此只讲述一下我的理解,大家也可以去网上搜索。

        通俗一点,音阶就是Do、Re、Mi、Fa、So、La、Xi即1、2、3、4、5、6、7(C、D、E、F、G、A、B),越往后音调越高即频率越高,所以我们在后续的代码中只需要将乐谱中对应的音阶转换成蜂鸣器所需要的震荡频率就可以发出我们需要的音调了。

音符
        音符就是每一个音调持续的时间,如果假设正常的一个拍子为1s,那么全音符就是1s,二分音符就是0.5s,四分音符就是0.25s以此类推

        OK,现在我们已经可以编程了。

代码实现
延时函数
        首先初始化SysTick定时器,将SysTick定时器的时钟源设置为主频的八分频,如果主频为168那么定时器的时钟频率为,那么定时器计数1次的时间为,计时1us就需要21次,所以fac_us=定时器时钟的频率=21

        因为SysTick定时器是24位的倒计时器,所以计数最大值为2^24,所以计时的最大值为

如果主频超频到240MHz那么计时的最大值就为559240us,这里我们取540000us方便计算


void SysTick_Init(uint8_t SYSCLK)
{
    //将SysTick的时钟频率设置为主频的八分频,如果主频为168MHZ那么将SysTick的时钟频率为21MHZ
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
       
        fac_us=SYSCLK/8;    //周期=频率的倒数,频率为21MHz,那么1us就需要震荡21次
        fac_ms=fac_us*1000;  //ms=us*1000
}

void Delay_Nus(uint32_t nus)
{
        uint32_t temp;
       
        SysTick->LOAD=fac_us*nus;    //装载计数值
        SysTick->VAL=0x00;            //当前计数值清零
        SysTick->CTRL|=0x01;        //启动定时器
        do
        {
                temp=SysTick->CTRL;    //获取当前计数值
        }
        while((temp&0x01)&&!(temp&(1<<16)));    //当计时器启动且溢出标志位置1退出循环
       
        SysTick->CTRL&=~0x01;    //关闭计时器
        SysTick->VAL=0x00;        //当前计数值清零
}

/*
SysTick定时器为24位的倒计时器,所以最大计数值为2^24
如果主频超频到240MHz,那么可以计时的最大时间为(2^24)/(240/8)=559240us
这里取540000us方便计算
*/
void Delay_Us(uint32_t us)
{
        uint32_t repeat;
        uint32_t remain;
       
        repeat=(unsigned int)(us/540000);    //如果计时值为1088000=540000*2+8000us
        remain=(unsigned int)(us%540000);   
       
        while(repeat--)
        {
                Delay_Nus(540000);        //540000*2
        }
        if(remain!=0)
        {
                Delay_Nus(remain);        //+8000=1088000us
        }
}

GPIO初始化

#define BUZZER_BUS                GPIOF
#define BUZZER_Pin                GPIO_Pin_8

/*
@brief:Configure the GPIO which is connected to Buzzer.
@param:None.
@retern:None.
*/
void Buzzer_GPIO_Init(void)
{
        GPIO_InitTypeDef        Buzzer_GPIO_Init;
       
        Buzzer_GPIO_Init.GPIO_Mode=GPIO_Mode_OUT;
        Buzzer_GPIO_Init.GPIO_OType=GPIO_OType_PP;
        Buzzer_GPIO_Init.GPIO_Pin=BUZZER_Pin;
        Buzzer_GPIO_Init.GPIO_PuPd=GPIO_PuPd_DOWN;
        Buzzer_GPIO_Init.GPIO_Speed=GPIO_Speed_100MHz;
       
        GPIO_Init(BUZZER_BUS,&Buzzer_GPIO_Init);
}

蜂鸣器播放
        下面函数的形参用的都是float型变量,这是因为形参的单位是Hz和Ms,函数内部处理时会将频率转换为周期并放大一百万倍进行处理,也就是以Us为单位进行处理。假设频率转换为周期后为5.123456……S

在函数内部处理时将转换成512 3456Us进行处理,这样产生的频率更加精准

/*
@brief:Play music with buzzer.
@param:
        scale->音阶.Unit->Hz
        note ->音符. Unit->ms
@retern:None.
*/
void Music_Play(float scale,float note)    //这里用float型是因为形参是以Hz和Ms为单位,但我们
{                                          //后续用的是MHz和Us为单位,也就是将其放大了                                                                        
        uint32_t temp;                         //一百万倍,这样就产生的频率和时长就更加精确
        uint32_t T;
        uint32_t repeat;
       
        T=(unsigned int)((1/scale)*1000000);    //将频率转换为周期,单位为us
        repeat=(unsigned int)(note*(scale/1000));    //一个周期的时间为T,那么只需要重复
                                                   //T*repeat=note即repeat=note/T*1000,单位Ms
        if(scale!=0)
        {
                while(repeat--)
                {
                        GPIO_SetBits(BUZZER_BUS,BUZZER_Pin);
                        Delay_Us(T/2);
                        GPIO_ResetBits(BUZZER_BUS,BUZZER_Pin);
                        Delay_Us(T/2);
                }
        }
        else
        {
                Delay_Us(note*1000);
        }
}

/*
float(*)[2]表明这个指针指向的对象是一个包含两个float类型元素的数组,MusicNotation是一个指针变量名。也就是将二维数组转换成了一位数组传递,这个一位数组每个单元有两个float型元素
*/
void Music_Player(float(* MusicNotation)[2])   
{
        uint32_t i=0;
       
        while((MusicNotation[0])||(MusicNotation[1]))
        {
                Music_Play(MusicNotation[0],MusicNotation[1]);
                i++;
        }
}

音阶转换
        接下来就是网上搜索,将音阶转换成对应的频率,这不是什么技术活,大家可以直接copy

/*
Quicker search :
        @music Scale
                Low-pitched         Range->C1        C2         C3
                Middle-pitched         Range->C4
                High-pitched        Range->C5        C6         C7          C8
        @Music Note
*/


/************************* Music Scale ****************************/
/*        Unit->Hz   */
       
//Low-pitched Range
#define        C1        33                //Dou
#define        D1        37                //Ri
#define E1        41                //Mi
#define        F1        44                //Fa
#define        G1        49                //Sou
#define A1        55                //La
#define        B1        62                //Xi
       
#define        C2        65
#define        C_2        69
#define        D2        73
#define D_2        78
#define E2        82
#define F2        87
#define        F_2        93
#define        G2        98
#define        G_2        104
#define A2        110
#define A_2        117
#define B2        123

#define        C3        131
#define        C_3        139
#define        D3        147
#define D_3        156
#define E3        165
#define F3        175
#define        F_3        185
#define        G3        196
#define        G_3        208
#define A3        220
#define A_3        233
#define B3        247

//Middle-pitched Range
#define        C4        262
#define        C_4        277
#define        D4        294
#define D_4        311
#define E4        330
#define F4        349
#define        F_4        370
#define        G4        392
#define        G_4        415
#define A4        440
#define A_4        466
#define B4        494

//High-pitched Range
#define        C5        535
#define        C_5        554
#define        D5        587
#define D_5        622
#define E5        659
#define F5        698
#define        F_5        740
#define        G5        784
#define        G_5        831
#define A5        880
#define A_5        932
#define B5        988
       
#define        C6        1047
#define        C_6        1109
#define        D6        1175
#define D_6        1245
#define E6        1319
#define F6        1397
#define        F_6        1480
#define        G6        1568
#define        G_6        1661
#define A6        1760
#define A_6        1865
#define B6        1976

#define        C7        2093
#define        C_7        2217
#define        D7        2349
#define D_7        2489
#define E7        2580
#define F7        2890
#define        F_7        2960
#define        G7        3136
#define        G_7        3322
#define A7        3520
#define A_7        3729
#define B7        3951

#define        C8        4186.0
#define        C_8        4434.9
#define        D8        4698.6
#define D_8        4978.0
#define E8        5274.0
#define F8        5587.7
#define        F_8        5919.9
#define        G8        6271.9
#define        G_8        6644.9
#define A8        7040.0
#define A_8        7458.6
#define B8        7902.1
       
/************************* Music Note ****************************/       
/*        Unit->ms   */
#define FullNote        1500
#define BinaryNote        750
#define QuarterNote        325
#define        EighthNote        162.5
#define SixteenNote        81.25

乐谱转换
        网上搜索相关音乐的乐谱,抄上去就行了,二维数组中第一个元素存放音阶即频率,第二个元素存放音符即时长

/*
Music name:两只老虎
Author          :未知
*/
float TwoTigers[][2]=
{
        {C4,QuarterNote},       
        {D4,QuarterNote},       
        {E4,QuarterNote},
        {C4,QuarterNote},
       
        {C4,QuarterNote},
        {D4,QuarterNote},
        {E4,QuarterNote},
        {C4,QuarterNote},
       
        {E4,QuarterNote},
        {F4,QuarterNote},
        {G4,QuarterNote},
        {G4,QuarterNote},
       
        {E4,QuarterNote},
        {F4,QuarterNote},
        {G4,QuarterNote},
        {G4,QuarterNote},
       
        {G4,QuarterNote},
        {A4,QuarterNote},
        {G4,QuarterNote},
        {F4,QuarterNote},
       
        {E4,QuarterNote},
        {E4,QuarterNote},
        {C4,QuarterNote},
        {C4,QuarterNote},
       
        {G4,QuarterNote},
        {A4,QuarterNote},
        {G4,QuarterNote},
        {F4,QuarterNote},
       
        {E4,QuarterNote},
        {E4,QuarterNote},
        {C4,QuarterNote},
        {C4,QuarterNote},
       
        {D4,QuarterNote},
        {D4,QuarterNote},
        {G3,QuarterNote},
        {G3,QuarterNote},
       
        {C4,QuarterNote},
        {C4,QuarterNote},
        {0,QuarterNote},
        {0,QuarterNote},
       
        {D4,QuarterNote},
        {D4,QuarterNote},
        {G3,QuarterNote},
        {G3,QuarterNote},
       
        {C4,QuarterNote},
        {C4,QuarterNote},
        {0,QuarterNote},
        {0,QuarterNote},
       
        {0,0}                        //Stop signal.
};

        如果是变量定义在外部文件,别忘记在头文件声明变量。

extern float TwoTigers[][2];
音乐演奏
        在主函数里调用之前写的那些代码。

#define TARGET_GLOBAL
#define UseHappyBirthday
#define UseTwoTigers
#include "main.h"

int main(void)
{
        System_Init();
        SysTick_Init(168);
        Buzzer_GPIO_Init();
       
        Music_Player(TwoTigers);
        while(1)
        {

        }
}

源码链接

链接:https://pan.quark.cn/s/e0a803203bbb
提取码:gGDr

效果演示
        可能是延时函数的原因最后得到的声音效果并不是很好,大家也可以用定时器中断试一试。

当然也有可能是其他原因




STM32蜂鸣器演奏音乐
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/2402_83561731/article/details/143451210

使用特权

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

本版积分规则

1931

主题

15611

帖子

11

粉丝