您可以添加到网摘 让更多人关注此文章:
// 单片机音乐编程实验 在很多儿童玩具或一些需要音乐提示(如报警仪、定时闹钟、电子宠物等)的场合,需要用到单片机音乐编程的功能。原理就是利用单片机的引脚发出某一定频率的信号驱动外部发声设备发出声音,或者放出美妙的音乐。 在这个例程中,音乐曲调的原始编码已做好,只要按照曲谱给定参数即可实现播放不同的乐曲,使实验生动有趣。参数两个:前一个按照曲谱对照编码表给定,第二个,为节拍数,8->1拍,4->半拍。 musich equ 40h ;音乐定时高位数据 musicl equ 41h ;音乐定时低位数据 musicstart equ 42h ;音乐开始首地址
; ************************ 预定义结束 *******************
org 00h ;程序开始 jmp main ;跳转到主程序
org 1bh ;定时器T1中断入口地址 jmp timer1 ;定时器服务程序
org 030h ;主程序代码开始 main:mov sp,#30h ;设置堆栈 lcall music ;调用音乐程序 jmp main ;重新开始
musdelay:mov r0,#20 ;短时间延时 d:djnz r0,d ;延时时间 20uS ret ;返回 ; ************ 定时器 服务程序 ******************* timer1:clr tr1 ;停止定时器工作 mov th1,musich ;重新设置定时初值 mov tl1,musicl ; cpl beep ;从蜂鸣器输出声音 setb tr1 ;开始定时工作 reti ;中断返回 ;**************************************************** ;************ 音乐播放程序 ********************* music:mov tmod,#10h ;设置定时器T1工作方式1 setb ea ;总的中断 setb et1 ;定时器T1中断允许 mov musicstart,#00h ;音乐开始地址 mov musich,#0ffh ;音乐定时常数高位 mov musicl,#0ffh ;音乐定时常数低位 loopm:mov dptr,#mu_tab ;得到音符表头 mov a,musicstart ;开始查表 movc a,@a+dptr ;获得音乐数据 jz musend ;结束符 00H,返回 rl a ;A*2 mov b,a ;暂存地址 mov dptr,#mustab ;获得音符表头 movc a,@a+dptr ;根据音乐数据查得定时常数 mov musich,a ;保存定时常数高位 mov th1,a ;设置定时器高位常数 mov a,b ;开始找低位数据 inc a ;指向下一个数据 mov dptr,#mustab ;得到音符表头 movc a,@a+dptr ;查得低位数据 mov musicl,a ;保存数据 mov tl1,a ;设置定时低位常数 setb tr1 ;定时器T1开始工作 mov dptr,#mu_tab ;得到音乐表 inc musicstart ;指向下一个单元(音符节拍) mov a,musicstart ;开始查找 movc a,@a+dptr ;找到数据 lcall delay1 ;根据参数延时,发出持续音乐 inc musicstart ;指向下一个音 clr tr1 ;停止发声 lcall delay jmp loopm ;重复,直到遇到结束符号 00H. musend:clr tr1 ;乐曲播放完毕,关闭定时器 clr ea ;禁止中断 clr et1 ;关闭定时器T1中断 clr beep ;禁止蜂鸣器 mov a,#20 lcall delay1 ret ;********************* ;************ 节拍发生器 ,产生音乐节拍 ********************************* delay1:mov r0,#00h ;节拍发生器,用来发生节拍 mov r1,#00h ; mov r2,a ;这是节拍发生器的参数,当它为8 dlay1:djnz r0,dlay1 ;表示1拍 mov r0,#00h djnz r1,dlay1 mov r0,#00h mov r1,#00h djnz r2,dlay1 ret ; ———————————————————— ; ******** 延时 产生休止符 *****************
delay:mov r0,#00h ;延时 10ms mov r1,#100 dlay:djnz r0,dlay mov r0,#100 djnz r1,dlay ret ; ************************ ; ************************曲谱编码,这是定义的曲谱表,对应如下 ************************************************** ; 1 低音 DO 2 低音 RE 3 低音ME 4 低音 FA 5 低音 SO 6 低音LA 7 低音SI mustab: db 'h','j', 0f8h,8bh, 0f9h,5bh, 0fah,14h, 0fah,0ch, 0fbh,03h, 0fbh,8fh, 0fch,0bh ; 8 中音 DO 9 中音 RE 0ah 中音ME 0bh 中音 FA 0ch 中音 SO 0dh 中音LA 0eh 中音SI db 0fch,43h, 0fch,0abh, 0fdh,08h, 0fdh,33h, 0fdh,81h, 0fdh,0c7h, 0feh,05h ; 0fh 高音 DO 10h 高音 RE 11h 高音ME 12h 高音 FA 13h 高音 SO 14h 高音LA 15h 高音SI db 0feh,21h, 0feh,55h, 0feh,84h, 0fdh,99h, 0feh,0c0h, 0feh,0e3h, 0ffh,02h
;以下为音乐乐谱代码,前两位表示曲谱,后一位表示节拍 ;8为 1 拍,4 ->半拍
mu_tab: db 6,4,8,4,10,4,1,4, 9,8,8,4,7,4,10,8,9,8,6,16 db 8,4,10,4,12,4,12,4, 13,8,12,4,11,4,10,16 db 00h end
;c语言 ;******************************************* // 音乐编程 //通过蜂鸣器发出讯响 //只要通过修改 后面的音乐程序里面的码 //表就可以自己谱曲唱歌! //前一个参数为按照编码表给定,第二个参数为节拍数
#include <80d51.h>
#define uchar unsigned char
#define key1 P3_4 //键盘定义 #define key2 P3_5 #define key3 P3_6
#define beep P3_7 //蜂鸣器定义
uchar timer_h; uchar timer_l;
//**************************************************************************** void reset(); void delay(uchar times); void dems(); //休止符延时 void display(uchar disseg,uchar disdata); uchar keyb();
// 这里定义的是数码管对应的字符字根 code uchar disbuf_u[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78, 0x00,0x10,0x08,0x03,0x46,0x21,0x06,0x0e}; // 这里定义音乐音符表 code uchar musictabl[]={0xf0,0xf0, 0xf8,0x8b,0xf9,0x5b,0xfa,0x14,0xfa,0x0c,0xfb,0x03,0xfb,0x8f,0xfc,0x0b,
0xfc,0x43,0xfc,0xab,0xfd,0x08,0xfd,0x33,0xfd,0x81,0xfd,0xc7,0xfe,0x05,
0xfe,0x32,0xfe,0x55,0xfe,0x84,0xfe,0x99,0xfe,0xc0,0xfe,0xe3,0xff,0x02};
//这里定义音乐曲谱表格 code uchar musicdata[]={6,4, 8,4,10,4, 1,4, 9,8, 8,4, 7,4,10,8,9,8,6,16, 8,4,10,4,12,4,12,4,13,8,12,4,11,4,10,16,0};
// ***************************************************************************
void ClockIrqHandler (void) interrupt 1 using 3; //定义定时器T0中断服务程序
//下面是定时器T0的中断服务程序 void ClockIrqHandler (void) interrupt 1 using 3 { TR0 = 0; //关闭定时器 TL0 = timer_l; TH0 = timer_h; beep = ~beep; TR0 = 1; //打开定时器 } // ***************** 定时器程序 **********************************
uchar keybuf;
void main() { uchar counts,mustmp,tmp,keym = 0; //键盘返回结果的缓冲区 reset(); //调用初始化程序
while(1) //设置一个无限制循环 { for(counts=0;musicdata[counts]!=0;counts+=2) { mustmp = musicdata[counts]; TR0 = 0;
tmp = musictabl[mustmp]; timer_h = tmp;
tmp = musictabl[mustmp+1]; timer_l = tmp;
TR0 = 1; display(0,musicdata[counts+1]); delay(musicdata[counts+1]); TR0 = 0; dems(); } // display(250,0); // delay(255); // delay(255); // display(keybuf-1,keybuf); //在相应的位置显示返回的结果 } }
void reset() {
keybuf = 0; P1 = 255; //关闭LED显示 P0 = 0; //关闭数码显示 P2 = 255; // //********* 上面是一些基础定义 **************
TL0 = 0xff; //初值 TH0 = 0xff; //初值 ET0=1; // 允许定时器T0中断 EA =1; // 中断总允许 } //***************************************************************************** //下面是延时程序。具体的延时时间不能通过表面程序看出,(为什么?) //如果我们需要一个精密的延时程序,那么我们可以采用内嵌汇编代码的方式 void delay(uchar times) { int t=24000; //延时倍数 uchar i=times; for(;i!=0;i--) {for(;t!=0;t--){}} }
void dems() //休止符延时 { _asm mov R0,#0 llp: mov R1,#100 llk: djnz R1,llk djnz R0,llp _endasm; }
//这里是显示子程序,入口参数为 // disseg -> 位选 可选范围 0-7 一共8个数码管 // disdata -> 段选 可选范围 0-31 一个32个字符 // 段选 0-15 16个字符 为 "0"->"F" // 段选 16-31 16个字符 为 "0."->"F."
void display(uchar disseg,uchar disdata) { uchar dataf; if(disseg < 8) //只有当要显示的位数有效,才显示.否则,不显示 { dataf = 1; while(disseg) { dataf <<= 1; disseg--; } P0 = dataf; P2 = disbuf_u[disdata]; } else{P0=0,P2=255;} //关闭数码管显示 }
uchar keyb() { uchar key,keytmp;
key1 = 1; //将输出线拉高 key2 = 1; key3 = 1;
key = P3 ; //读回来 key = key & 0xf0; //获得键盘结果 if(key == 112 ) return 0; //如果用户没有按键返回 0 else { keytmp = key; delay(1); //判断是不是干扰 key = P3 & 0xf0; if (key != keytmp ) return 0; //是干扰,返回 0 else //不是干扰,等待用户释放按键 { do{ key1 = 1; //输出拉高 key2 = 1; key3 = 1; key = P3 & 0xf0; //读回来 P1_0 = ~P1_0; //如果用户不释放,闪烁 p1.0 }while(key != 112 ); //等待用户释放 P1_0 = 1; //用户释放以后,清除p1.0指示灯 switch(keytmp) { case 96: return 3;//返回用户按键结果 case 80: return 2; case 48: return 1; } }
} } //***************************** [1]
|