单片机蜂鸣器c语言《兰花草》音乐代码

Python09

单片机蜂鸣器c语言《兰花草》音乐代码,第1张

程序名: MCS51U实验板配套程序-歌曲:兰花草 电路介绍:蜂鸣器接P2.0;实验前要连接J12 *********************************************************************** OUT BIT P2.0 ORG 0 AJMP START ORG 0BH AJMP TIM0 START: MOV TMOD,#1 MOV IE,#10000010B START0: MOV 30H,#0 NEXT: MOV A,30H MOV DPTR,#TABLE MOVC A,@A+DPTR MOV R2,A JZ END0 ANL A,#0FH MOV R5,A MOV A,R2 SWAP A ANL A,#0FH JNZ SING CLR TR0 AJMP D1 SING: DEC A MOV 22H,A RL A MOV DPTR,#TABLE1 MOVC A,@A+DPTR MOV TH0,A MOV 21H,A MOV A,22H RL A INC A MOVC A,@A+DPTR MOV TL0,A MOV 20H,A SETB TR0 D1: CALL DELAY INC 30H AJMP NEXT END0: CLR TR0 AJMP START0 TIM0: PUSH ACC PUSH PSW MOV TL0,20H MOV TH0,21H CPL OUT POP PSW POP ACC RETI DELAY: MOV R7,#2 D2: MOV R4,#125 D3: MOV R3,#248 DJNZ R3,$ DJNZ R4,D3 DJNZ R7,D2 DJNZ R5,DELAY RET ==================================== TABLE1: DW 64021,64103,64260,64400 DW 64524,64580,64684,64777 DW 64820,64898,64968,65030 DW 64934 TABLE: 1 DB 42H,82H,82H,82H,84H,02H,72H DB 62H,72H,62H,52H,48H DB 0B2H,0B2H,0B2H,0B2H,0B4H,02H,0A2H 2 DB 12H,0A2H,0D2H,92H,88H DB 82H,0B2H,0B2H,0A2H,84H,02H,72H DB 62H,72H,62H,52H,44H,02H,12H 3 DB 12H,62H,62H,52H,44H,02H,82H DB 72H,62H,52H,32H,48H DB 00H END

其实原理很简单,就是控制单片机的某个引脚,输出一定频率的方波信号,而输出方波信号的方法,是最基础的,最简单的编程了,用定时器定时,根据信号频率算出信号周期,然后计算出定时的时间。那单片机演奏歌曲的程序,也是同样的原理,只是事先根据歌曲的简谱查出每个音阶的信号频率,再根据各音阶频率计算出定时器的初值。演奏时,按简谱的各音阶顺序输出不同的频率的信号就行了。

下表是音阶与频率对应关系表,给出常用音阶对应的定时常数。

//绝对调试通过,AVR-GCC,mega16,带数码显示,单键开关多功能控制

#include <avr/io.h>

#include <avr/interrupt.h>

#include <tone.h>

#include <avr/pgmspace.h>

#define uchar unsigned char

#define uint unsigned int

#define key_input PINA

#define pgm16(A) ((typeof(A))pgm_read_word(&(A)))

const unsigned char seg_code[] ={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0x7f,0x3f}

const uint music[] PROGMEM={M3,F2,M5,B1,M3,F4,M2,F4,M1,B2,L6,F2,M1,F2,MM,M1,F2,L6,F2,L5,B2, L6,F2,M1,B1,MM,M1,F2,L6,F2,M6,F2,L5,F2,M3,F2,M2,F2,MM,M2,F4,M1,F4,L6,F2,M3,F2,M2,B2,

M3,F2,MM,M3,F2,MM,M3,F2,M2,F2,M1,B2,L6,F2,M1,F2,L7,F2,L5,F2,L6,B2, L5,F2,L6,F2,MM,L6,F2,L5,F2,M6,F2,M5,F2,MM,M5,F2,M3,F2,M2,F2,MM,M2,F4,M3,F4,M3,F2,M2,F2,M1,B2,

M6,F2,H1,B1,M6,F2,H2,F2,H1,F2,MM,H1,F2,M6,F2,MM,M6,F2,M5,F2,MM,M5,F2,M3,F2,M5,B2, M6,F2,H1,B1,M6,F2,H2,F2,H1,F2,MM,H1,F2,M6,F2,M5,F2,MM,M5,F2,MM,M5,F2,M3,F2,M2,B2,

M3,B1,MM,M3,F2,M5,F2,M6,F2,M5,F2,MM,M5,F2,M3,F2,MM,M3,F2,M2,F2,MM,M2,F2,M1,F2,L6,B2, L5,F2,M5,F4,MM,M5,F4,MM,M5,F2,M6,F2,M5,F2,M3,F2,M2,F2,M3,F2,MM,M3,F2,M2,B1,L6,F2,M1,B2,

M6,F2,H1,B1,M6,F2,H2,F2,H1,B1,M6,F2,MM,M6,F2,M5,F2,MM,M5,F2,M3,F2,M5,B2,M6,F2,H1,B1,M6,F2,H2,F2,H1,B1,M6,F2,

M5,F2,MM,M5,F2,MM,M5,F2,M3,F2,M2,B2,MM,M2,F2,MM,M2,F4,M1,F4,M2,F2,M3,F2,M5,F2,MM,M5,F2,MM,M5,F2,M3,F2,M2,F2,MM,M2,F4,M3,F4,M2,F2,M1,F2,L6,B2,

L5,F2,M5,F4,MM,M4,F4,MM,M5,F2,M6,F2,M5,F2,M3,F2,M2,F2,M2,F4,M3,F4,MM,M3,F2,M2,B1,L6,F2,M1,B3,0XFF,0XFF}

//因为两个相同的音符之间无停顿,人为插入了MM(极短暂的

volatile uint i=1

volatile uchar timeOK=0

uchar key_scan(void)

ISR(TIMER0_COMP_vect) //CTC模式,控制音调高低

{

OCR0=pgm16(music[i-1])

}

ISR(TIMER1_OVF_vect) //时间溢出模式,控制节拍长短

{

i+=2

TCNT1H=(65536-pgm16(music[i]))/256 //相当于(65536-beat[i])/256

TCNT1L=(65536-pgm16(music[i]))%256 //相当于(65536-beat[i])%256

if(pgm16(music[i])==0xff)i=1 //在音调数组的最后加一个0xff,作为终止信号

if(pgm16(music[i])==0x00)TIMSK&=(~(1<<OCIE0))

else TIMSK|=(1<<OCIE0)

}

ISR(TIMER2_OVF_vect) //时间溢出模式,用于检测按键,周期10ms

{

TCNT2=100

timeOK=1

}

void timer0_init(void)

{

TCCR0=0x00

TCNT0=0x00

OCR0=pgm16(music[i-1])

TCCR0=0x1a

//TIMSK|=(1<<OCIE0)

}

void timer1_init(void)

{

TCCR1B=0x00

TCNT1H=(65536-pgm16(music[i]))/256

TCNT1L=(65536-pgm16(music[i]))%256

TCCR1A=0x00

TCCR1B=0x05

//TIMSK|=(1<<TOIE1)

}

void timer2_init(void)

{

TCCR2=0x00

TCNT2=100

TCCR2=0x03

TIMSK|=(1<<TOIE2)

}

void MCU_init(void)

{

DDRA=0X00

DDRB=0X00

DDRC=0XFF

DDRD=0XFF

PORTB=0X00

PORTC=0xfe

PORTD=0xff

}

uchar key_scan(void) //按钮扫描函数

{

static uchar key_state=0,key_time1=0,key_time0=0 //静态变量,调用以后,值被保留

uchar key_press,key_return=0

key_press=(~key_input)&0x01 //有键1,无键0

switch(key_state) //状态机

{

case 0: //状态0:无按钮按下状态

if(key_press)key_state=1

break

case 1: //状态1:检测到有按钮被按下

if(key_press) //按钮仍按下,转2

{

//TIMSK^=(1<<TOIE0)

key_state=2

key_time1=0 //按下时间初始

}

else //按钮已经释放,消抖

key_state=0

break

case 2: //状态2:等待释放或等待按下时间达到长按要求

if(!key_press) //按钮已经释放,转3,继续判断是双按还是单按

{

key_state=3

key_time0=0 //释放时间初始

}

else if (++key_time1>=100) //按下时间达到1000ms,返回值2,作长按判断,转7等待按钮释放

{

key_state=8

key_time1=0

key_return=2

}

break

case 8:

if(!key_press)

{

key_state=0

key_time1=0

}

else if (++key_time1>=50)

{

key_return=2

key_time1=0

}

break

case 3: //状态3:双按的第二次按钮输入前的等待间隙,设定100ms(经过测试,100ms为比较合适的时间间隙,即为人体普通可以双击的速度

if(key_press)key_state=4 //在此时间,如果有按钮被按下,将当作释放抖动,转4进一步作出判断

else if (++key_time0>4) //如果没有按钮被按下,则等待此100ms过去,然后转5

{

key_state=5

key_time0=0

}

break

case 4: //状态4:第二按的消抖处理

if(key_press)key_state=2 //快速连按(被认为人手指达不到要求的速率)将被返回到2,当作第一按处理

else key_state=3 //如果第二按只出现一次,不到10ms,消抖

break

case 5: //状态5:第二按前等待时间到达,进入正常的第二按等待输入状态,第二按只允许在此时间内输入方为有效

if(key_press)key_state=6 //此时间内有被按下,转6,判决抖动

else if(++key_time0>14) //无输入,则等待输入有效时间过去,时间满后,判定为单按,返回值1,状态位回到0

{

key_state=0

key_time0=0 //双击间隔时间一般为100ms,能够稳定做到第二击完成时间总共约180~200ms(因人而异)

key_return=1 //为增加采样成功率,可适当缩短第二按前的间隙时间,增加第二按等待输入时间,但总时间不可短于150ms

} //小于150ms,手指将很难达到双击的速度要求,如果时间太长则单按判断时间过长,按钮不灵敏,此处180ms经测试较为合适

break

case 6: //状态6:第二按的抖动判断

if(key_press) //按钮仍有效,按下有效,返回3,双按成立,转7等待按钮释放

{

key_state=7

key_return=3

}

else key_state=5 //抖动消除,退回5继续等待输入或等待输入有效时间过去

case 7: //按钮释放等待状态,所有已按下并被识别操作目的的按钮状态都将转到7等待按钮释放,并将所有计时初始

if(!key_press)key_state=0

key_time1=0

key_time0=0

break

}

return key_return

}

int main(void)

{

MCU_init()

timer0_init()

timer1_init()

timer2_init()

sei()

while(1)

{

if(timeOK)

{

cli()

uchar key

key=key_scan()

if(key>0)PORTD=seg_code[key]

switch(key)

{

case 1:

TIMSK^=((1<<OCIE0)|(1<<TOIE1)) DDRB^=(1<<DDB3) PORTC=0xfd

break

case 2:

//TIMSK|=((1<<OCIE0)&(1<<TOIE1))

if(i>1)i+=10

if (i>=(sizeof(music)-20))

{

i=1

TIMSK&=(~((1<<OCIE0)|(1<<TOIE1)))

DDRB&=~(1<<DDB3)

}

PORTC=0xf0

break

case 3:

TIMSK&=(~((1<<OCIE0)|(1<<TOIE1)))

i=1

TIMSK|=((1<<OCIE0)|(1<<TOIE1))

PORTC=0xfe

break

default:

break

}

timeOK=0

sei()

}

}

}