单片机音乐演奏程序设计
——单字节编码方法
1、音乐演奏原理
利用单片机的定时器定时,定时到后对单片机的某个引脚取反产生方波信号,初值不同,方波信号的频率不同,方波信号经过放大后送喇叭发出不同音调的声音。选某个乐谱,将每一个音的音调和音长按照一定的规则进行编码,组成编码数表。然后编写程序,从编码数表中连续读出各个音的编码,根据音调编码获得其频率,再由频率计算出定时器的定时初值,从某个引脚产生出对应频率的方波信号,在喇叭上发出对应频率的声音;再根据音长编码产生延时,从而连续播放出音乐。
2、乐谱单字节编码方法
(1)定时器0/1产生各种音频的方法
表1 为C调的发音频率表,给出了各个音的频率f,可以用定时器/计数器,定时产生对应频率的方波信号,将方波信号送给喇叭,便可使喇叭发出同频率的声音。定时初值x与发声频率f的关系如下:
由公式 t=(2 n - x)×Tmc
式中 t=T/2、 T=1/f、 Tmc=12/fosc
n取16表示用模式1定时,Tmc为机器周期,fosc为晶振频率,因此初值x为:
x=65536-(1/f/2)×fosc/12
如果fosc=12Hz,则 x=65536-500000/f
表1 C音调发音频率表
C 调 |
5 |
6 |
7 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
ⅰ |
ⅱ |
ⅲ |
频率f(Hz) |
196 |
220 |
247 |
262 |
294 |
330 |
349 |
392 |
440 |
494 |
523 |
588 |
660 |
其它各音的发声频率如图1所示。
(2)乐谱单字节编码方法
字节的高4位表示谱的音名,低4位表示音的长短(单字节法最多只能表示15个音)。
音名表示方法:一个乐谱中的最低音用1表示,次低音用2表示……,依次类推,休止符用0表示。
音长短的表示方法:一个乐谱中的最短音用1表示,其它音的长度都是它的整数倍,是它的几倍用几表示。
(3)乐谱单字节编码举例
乐谱: 3 5 6 i 6 5 | 2 · 3 6 5 | 1 — | 1 0 ||
音名编码:该段乐谱的最低音5编码1,其它音6、7……依次编码为2、3……,休止符用0表示。其编码对应关系如表2所示。
表2 编码与音名对应关系
音 名 |
0 |
5 |
6 |
7 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
ⅰ |
ⅱ |
ⅲ |
编 码 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
a |
b |
c |
d |
编码与音名的换算:
音长编码:由于该段乐谱的最短音是1/4拍,因此将1/4拍编码为1,其音长编码如下:
1表示1/4拍、 2表示2/4拍、
3表示3/4拍、 4表示1拍、
6表示1.5拍、 8表示2拍、
c表示3拍……
上面一段乐谱的编码为: 0x62,0x82,0x91,0xb1,0x91,0x81,0x53,0x61,0x22,0x12,0x4c,0x04,0。(最后的0表示乐谱结束)
节拍时间计算:
设某歌曲的演奏速度为p拍/分钟,则每拍的时间间隔为:
分钟/p=60000毫秒/p
若某歌曲的最短音为1/4拍,对应的延时为15000毫秒/p,则15000毫秒/p是该歌曲的基本延时单位。
如p=100,即每分钟100拍,则一个基本延时单位(1/4拍)的时间长度为150ms。
3、程序设计方法
需要编写以下4个函数;建立歌曲编码表和频率表。
(1)建立歌曲编码表和频率表
乐谱编码数组mus[ ];
各音频率数组freq[ ]。
(2)主函数
主函数主要是对定时器、中断做初始化设置。
(3)音乐演奏函数
顺序从歌谱编码数组mus[ ]中读取数据,分离出高4位和低4位。用高4位读取数组freq[ ]的频率值,然后根据定时器初值与频率的关系,计算出定时初值x;用低4位控制发声延时,以上面所述的“15000毫秒/m”为基本延时单位,以低4位为倍数控制发声时间。
(4)延时函数
参考教材P16,其中添加1行:
x=x*150;660
(5)定时器中断函数
4、定时器2产生各种音频的方法
对于定时器2,发声频率f与单片机振荡频率fosc的关系为:
f=fosc/(2 16 - x)/4 2 16 -x= fosc/f/4 65536-x= fosc/f/4
因此初值x为:
x=65536-fosc/f/4
如果fosc=12Hz,则 x=65536-3000000/f
音乐演奏程序流程
读取第i个音mus[i++]编码
mus[i]为0结束
分离出高低4位给high和low
读取freq[high]得到发声频率f
由f计算出定时初值x
x的高低8位分别送给全局变量xh和xl及计数器THx和TLx
开定时器Tx(TRx=1)
调用延时函数delay(low)
关定时器Tx(TRx=0)
源代码:
music.h
/*
* Copyrite (c) 2018, 单片机与嵌入式系统工作室
* All Rights Reserved
*
* File Name :music.h(举例)
* File Description :music .h file
*
* Current Version: 1.1
* Author : 徐子航
* Finish Time : 2018.5.13
*
* Earlier Version :
* Author :
* Finish Time :
*/
#ifndef _MUSIC_H
#define _MUSIC_H
#include <reg51.h>
/*------------------------------------------------------------------*-
Custom type
-*------------------------------------------------------------------*/
typedef unsigned char tByte;
typedef unsigned int tWord;
typedef unsigned long tlong;
/*------------------------------------------------------------------*-
Global variable declaration
-*------------------------------------------------------------------*/
extern tByte high ; //高4位
extern tByte low ;
extern tWord pos_flag ;//位置指向
extern tWord x; //定时器的初值
/*------------------------------------------------------------------*-
speaker pin
-*------------------------------------------------------------------*/
sbit speaker = P1^6;
/*------------------------------------------------------------------*-
Global function declaration
-*------------------------------------------------------------------*/
void Delay_Ms(tWord x);
void TIME0_Init();
void Play_Music();
#endif
music.c
/*
* Copyrite (c) 2018, 单片机与嵌入式系统工作室
* All Rights Reserved
*
* File Name :music.c(举例)
* File Description :小白杨
*
* Current Version: 1.1
* Author : 徐子航
* Finish Time : 2018.5.13
*
* Earlier Version :
* Author :
* Finish Time :
*/
#include <music.h>
/*------------------------------------------------------------------*-
Music simple spectral coding table. code type not change
-*------------------------------------------------------------------*/
tByte code mus[] = {0x83,0x61,0x52,0x52,0x61, 0x51,0x31,0x21,0x14,0x33
,0x51,0x81,0x91,0x61,0x51, 0x54,0x54,0x81,0x91,0x61
,0x51,0x63,0x81,0x62,0x51, 0x61,0x34,0x52,0x61,0x61
,0x51,0x31,0x31,0x21,0x24, 0x24,0x33,0x51,0x51,0x61
,0x51,0x31,0x22,0x14,0x11, 0x21,0x33,0x51,0x81,0x91
,0x61,0x51,0x62,0x61,0x51, 0x34,0x81,0x91,0x61,0x51
,0x63,0x81,0x62,0x51,0x31, 0x24,0x00}; //小白杨乐谱编码表
tWord code freq[] = {0,362,294,330,349,392,440,494,523,588,660}; //频率编码表
/*------------------------------------------------------------------*-
Global variable definition
-*------------------------------------------------------------------*/
tByte high = 0;
tByte low = 0;
tWord pos_flag = 0;
tWord x = 0; // X should be defined as tWord type, and tByte will overflow.
//150*xms
void Delay_Ms(tWord x)
{
tByte i = 0;
x = x*150;
while(x--)
for(i = 0; i< 123; i++);
}
/*------------------------------------------------------------------*-
TIME0 Init to Mode 1
-*------------------------------------------------------------------*/
void TIME0_Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x44; //设置定时初值
TH0 = 0xFC; //设置定时初值
ET0 = 1;
EA = 1 ;
}
/*------------------------------------------------------------------*-
Play_Music function
Describtion: The encoding value is resolved to the corresponding frequency (high 4 bits) and the beat (low 4 bits).
Start again when the mus array is over.
-*------------------------------------------------------------------*/
void Play_Music(void)
{
low = mus[pos_flag] & 0x0f;
high = mus[pos_flag] >> 4;
x = 65536 - 500000/freq[high];
++pos_flag;
if(pos_flag >= 67)
pos_flag = 0;
}
/*------------------------------------------------------------------*-
TIME0 Interrupt handler
Describtion: There will be many interrupts in the corresponding beat of a syllable,
so the initial value should be reset at the middle.
-*------------------------------------------------------------------*/
void TIME0_IRQHander(void) interrupt 1
{
TL0 = x%256; //设置定时初值
TH0 = x/256; //设置定时初值
speaker = ~speaker;
}
main.c
/*
* Copyrite (c) 2018, 单片机与嵌入式系统工作室
* All Rights Reserved
*
* File Name :main.c(举例)
* File Description :
*
* Current Version: 1.1
* Author : 徐子航
* Finish Time : 2018.5.13
*
* Earlier Version :
* Author :
* Finish Time :
*/
#include <music.h>
int main()
{
TIME0_Init();
while(1)
{
Play_Music();
TR0 = 1; //定时器0开始计时
Delay_Ms(low);
TR0 = 0;
}
return 0;
}
附录1: