• 蜂鸣器


    学习单片机有将近20天了,有一点感触,刚开始用proteus仿真,总是出现各种错误,无奈之下买了一块板子.要真正学习单片机必须有开发板,这句话一点没错.另外如果之前有数模和C语言的基础,学习起来是很快的, 单片机是座桥,ARM近在咫尺拉!

     .

    一、根据简谱写程序数据

    二、程序实现

    三、其他

    四、附录(五线谱对应表)

    .

    一、根据简谱写程序数据 (必须有简单的简谱知识,为此我也专门去网上找了几个视频学了下 )

    1、音符变量对应表(F大调)

     

    2、根据乐谱确定变量

    这里简单说一点,<兰花花>是2/4拍,也就是以四分音符为一拍,每小结两拍, "T"代表一拍,"T/2"代表半拍,"T/4"代表四分之一拍  

    3、程序数组:

    //《兰花花》

    code CNote MusicTab[] =

    { // {音名,时值}

    {d2,T/2},{e2,T/2},{d2,T/2},{d2,T/4},{c2,T/4},{d2,T/2},{e2,T/2},{d2,T},

    {c2,T/2},{d2,T/2},{a1,T/2},{g1,T/4},{g1,T/4},{f1,T/2},{d1,T+T/2},

    {g1,T/2},{a1,T/2},{c2,T/2},{c2,T/2},{a1,T/2},{d2,T/2},{c2,T/2},{a1,T/2},

    {g1,T/4},{a1,T/4},{g1,T/4},{f1,T/4},{d1,T/2},{c1,T/2},{d1,T*2},

    {0,T}, {0,T},

    {0,0} //结束标志

    };


     二、程序实现

    beep.h

    //#define          0xF922     //  262Hz  低1
    //#define 0xF9E2 // 294Hz 低2
    //#define 0xFA8C // 330Hz 低3
    #define as 0xFAD9 // 349Hz 低4
    #define c1 0xFB69 // 392Hz 低5
    #define d1 0xFBE9 // 440Hz 低6
    #define e1 0xFC5C // 494Hz 低7
    #define f1 0xFC8F // 523Hz 中1
    #define g1 0xFCF0 // 587Hz 中2
    #define a1 0xFD46 // 659Hz 中3
    #define a1s 0xFD6D // 698Hz 中4
    #define c2 0xFDB5 // 784Hz 中5
    #define d2 0xFDF5 // 880Hz 中6
    #define e2 0xFE2E // 988Hz 中7
    #define f2 0xFE48 // 1046Hz 高1
    #define g2 0xFE79 // 1175Hz 高2
    #define a2 0xFEA3 // 1318Hz 高3
    //#define 0xFEB7 // 1397Hz 高4
    //#define 0xFEDB // 1568Hz 高5
    //#define 0xFEFB // 1760Hz 高6
    //#define 0xFF16 // 1967Hz 高7


    //定义发声时值
    //T*4全音符,T*2二分音符,T四分音符,T/2八分音符,T/4十六分音符
    #define T 1000

    //《兰花花》
    code CNote MusicTab[] =
    { // {音名,时值}
    {d2,T/2},{e2,T/2},{d2,T/2},{d2,T/4},{c2,T/4},{d2,T/2},{e2,T/2},{d2,T},
    {c2,T/2},{d2,T/2},{a1,T/2},{g1,T/4},{g1,T/4},{f1,T/2},{d1,T+T/2},
    {g1,T/2},{a1,T/2},{c2,T/2},{c2,T/2},{a1,T/2},{d2,T/2},{c2,T/2},{a1,T/2},
    {g1,T/4},{a1,T/4},{g1,T/4},{f1,T/4},{d1,T/2},{c1,T/2},{d1,T*2},
    {0,T}, {0,T},
    {0,0} //结束标志
    };

    beep.c

    #include <REG52.H>   //包含所选单片机的定义头文件

    sbit SPEAKER = P2^3; //定义音乐输出口

    typedef struct //定义音符结构
    { unsigned int mFreq; //发声频率(对应的定时器初值)
    unsigned int mDelay; //发声时值
    }CNote;
    unsigned char ReloadH; //定义定时器T1重装值
    unsigned char ReloadL;
    /************************************************************
    函数:T1_ISR()
    功能:定时器T1中断服务函数,产生音乐振荡频率
    ************************************************************
    */
    void T1_ISR() interrupt 3
    { TR1 = 0;
    TH1 = ReloadH;
    TL1 = ReloadL;
    TR1 = 1;
    SPEAKER = !SPEAKER; //音乐声频的半个波
    }
    /*************************************************************
    函数:Delay()
    功能:延时0.001~65.536s
    参数:t>0时,延时(t*0.001)s
    t=0时,延时65.536s
    ************************************************************
    */
    void Delay(unsigned int t)
    { do
    { TH0 = 0xFC; //定时器赋初值,定时1ms
    TL0 = 0x66+17;
    TR0 = 1; //启动定时器
    while ( !TF0 ); //等待定时器溢出
    TR0 = 0; //关闭定时器
    TF0 = 0; //清除溢出标志
    } while ( --t != 0 ); //循环t次
    }
    /***********************************************************
    函数:Sound()
    功能:演奏一个音符
    参数:*note,音符指针,指向要演奏的音符
    **********************************************************
    */
    void Sound(CNote *note)
    { //利用定时器T1发出音符的频率
    if ( note->mFreq != 0 )
    { ReloadH = (unsigned char)(note->mFreq >> 8);
    ReloadL = (unsigned char)(note->mFreq);
    TH1 = 0xFF;
    TL1 = 0xF0;
    TR1 = 1;
    }
    Delay(note->mDelay); //发声延时
    TR1 = 0; //停止发声
    TF1 = 0;
    SPEAKER = 1;
    Delay(10);
    }
    /************************************************************
    函数:Play()
    功能:演奏一段乐曲
    参数:music[],要演奏的乐曲
    ************************************************************
    */
    void Play(CNote music[])
    { unsigned int n = 0;
    for (;;)
    { if ( music[n].mDelay == 0 ) break;
    Sound(&(music[n]));
    n++;
    }
    }

    #include "beep.h" //包含乐曲头文件
    void main() //主程序
    {
    TMOD = 0x11; //设定时器
    ET1 = 1;
    EA = 1;
    for (;;)
    {
    Play(MusicTab); //演奏第一首乐曲
    Delay(500); //等待数秒
    }
    }

    我测试代码后,觉得有那么点意思了,但是还不是很理想,个别音很颤... 


    三 、其他

    1、计时器初值的计算 

    细心的朋友注意到了beep.h中的代码有点乱,这里解释一下,这个头文件里定义的是每个音符所对应的计时器初值,在串口通信里通常用T1作为波特率发生器,这里我们用T1作为音符频率发生器,踏实这样算出来的:

    假设音乐频率为 X,晶振为11.0592MHz
    一、先求出一个定时周期的时间
       1.求机器周期:1/11.0592*12=1.085 us (一个记时周期为12个晶振周期即1.085微秒)  
       2.音乐频率周期 1/X  1/(2X) 一个音频脉冲为二个周期,
    二、计算所需定时周期数
        记时周期数=音乐频率周期/记时周期
    三、得到定时器初值
        定时器初值=65536-记时周期数
    四、例子
        如440HZ标准音
        音乐频率周期=1/(2X)=1/(2*440)=1136.36 us
        记时周期数=1136.36us/1.085=1047.34个
        定时器初值=65536-1047.34~=64489  十六进制为(0xFBE9)

    在beep.h中已经有了21个音符的频率,下面就要根据公式把计时器初值算出来,如果手算的话工作量太大,直接用程序代替吧 

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    using namespace std;

    typedef struct
    {
    //char tone[2]; //音调
    int audio_fre; //音符频率
    int audio_cycle; //频率周期
    int time_num; //记时数
    int init; //定时器初值
    }ress;
    ress res[21];
    int hz[] = {262,294,330,349,392,440,494,
    523,587,659,698,784,880,988,
    1046,1175,1318,1397,1568,1760,1967};
    int t = sizeof(hz) / sizeof(hz[0]);
    int main()
    {
    int i;
    for (i = 0;i < 21; i++)
    {
    res[i].audio_fre = hz[i];
    res[i].audio_cycle = (((double)(1000000) / (2*res[i].audio_fre)));
    res[i].time_num = (res[i].audio_cycle) / (1.085);
    res[i].init = 65536 - res[i].time_num;
    }
    printf("音符频率 频率周期 记时数 初值\n");
    for (i = 0;i < 21; i++)
    {
    printf("%6d %6d %8d %#7X\n",res[i].audio_fre,res[i].audio_cycle,res[i].time_num,res[i].init);
    }

    system("pause");
    }


    2、蜂鸣器如何发声

    无源蜂鸣器根据输入方波的频率来决定发声音调, 周期是指一个波形完整变化的过程,对于方波包括一半低电平和一半高电平,所以前面计算的时候,才出现 1/(2X),我们让它在一个周期内 (1/X)变化一次(由高到低或由低到高变动,这才是方波嘛) 

    四、附录(五线谱对应表)

  • 相关阅读:
    hdu 1074
    hdu 4091
    hdu 4422
    hdu 3940
    hdu 2831
    hdu 1172
    hdu 3732
    hdu 1250
    hud 2073
    IOS socket基于tcp/udp的通信
  • 原文地址:https://www.cnblogs.com/hicjiajia/p/2426339.html
Copyright © 2020-2023  润新知