📄 sldiy16-11.asm
字号:
;********************************************
;SL-DIY-08-16 实验十一
;I/O端口的又一应用-声音输入处理。
;请修改以上程序,如何用声控来启动。
;PB2的声控开关控制发音
;********************************************
;*ATmega16 8MHz
;*双龙电子 www.sl.com.cn
;* teny 2005.11.12
;********* teny@sl.com.cn **************
;********************************************
.include "m16def.inc"
.org $000
rjmp reset11
.def TEMPDH =r2
.def TEMPDL =r3
.def CNT =r10
.def SCNN =r11
.def KEYN =r12
.def SCNK =r13
.def SCNDP =r14
.def KSNI =r15
.def TEMP =r16
.def TEMP1 =r17
.def TEMP2 =r18
.def TEMP3 =r19
.def SCNTT =r26 ;
.def MUSN =r22
.def TONL =r21
.def TONH =r20
.def PLYTON =r25
.def TONSET =r24
.def TONLNG =r23
.cseg
.org OVF0addr ;TIM1_OVF定时器1溢出中断处理入口地址
RJMP OUTPM ;转定时器1溢出中断处理,发音周期到,
;则跳转到发音输出态
.cseg ;跳过中断区
.org 0x010 ;定时器1溢出中断处理程序。
;发音周期到,重新装入计数值,并由PC0输出
OUTPM: OUT TCNT1H,TONH ;重新将TONH新计时值载入TCNT1H内
OUT TCNT1L,TONL ;重新将TONH新计时值载入TCNL1H内
SBIS PORTC,0 ;先检测PC0是否为1而跳转
RJMP SETOP1 ;若PC0口为0,则跳转到SETOP1,令PC0转为1
SETOP0: CBI PORTC,0 ;若PC0为1,则令PC0转为1
LDI MUSN,$00 ;同时令MUSN为00值
RETI ;回中断前主程序,并令可再次中断返回
SETOP1: SBI PORTC,0 ;若PC0为1,则令PC0转为1
LDI MUSN,$01 ;同时令MUSN为01值
RETI ;回中断前主程序,并令可再次中断返回
.cseg
.org 0x020 ;
reset11:
ldi r17,$02 ;栈指针置为$025f
ldi r16,$5f
out sph,r17
out spl,r16
wdr ;在使用看门狗计时器前需重设看门狗计时器
ldi temp,$0F ;WDTCR地址$21设定以TEMP缓冲,令WDE=D3=1
out WDTCR,temp ;并令预除为2048ms,设定WDE=D3=1,输出到WDTCR内
LDI MUSN,$00 ;令MUSN为00值
ldi temp,$00 ;令TEMP暂存器放入00
OUT TCCR1A,TEMP ;将TEMP=00输出到TCCR1A内,禁止比较器及PWM动作
OUT TCCR1B,TEMP ;将TEMP=00输出到TCCR1A内,停止TC1计时及捕捉 ?
LDI TEMP,(1<<CS11) ;将02值预存入SRAM的$100内,作TC1的TCCR1B控制
STS $0100,TEMP ;令为计时预除8
CLI ;关中断
ldi r16,(1<<TOIE1) ;
out timsk,r16 ;令TOIE1=1触发中断
ldi temp,$00 ;PB口输入,带上拉电阻
out ddrb,temp
LDI TEMP,$FF ;
OUT DDRC,TEMP ;PA口输出
out portc,temp
out portB,temp
CLR TEMP2 ;暂存器清零
CLR TEMP1 ;暂存器清零
CLR KSNI ;暂存器清零
LDI SCNTT,$02
CLR TONLNG ;暂存器清零
start11:
in r16,pinb ;读取PB口管脚状态,看是否有开关合上
sbrs r16,2 ;若PB2的开关合上,启动演奏乐曲
rcall yin
rjmp start11 ;若无键按下循环检测
yin:
push r16
WDR ;关看门狗
LDI ZH,HIGH(yinyue*2) ;启动演奏则令数据装入Z指针
LDI ZL,LOW(yinyue*2) ;音乐演奏乐谱存放在YINYUE1*2的起始地址
NEXMUT1: LPM ;将Z指针所指程序存储器乐曲依次取音符码及节拍码置于R0
MOV PLYTON,R0 ;将取出的第一个音符码装入PLYTON作周期控制
LD R0,Z+ ;Z指针加1
LPM ;将Z所指程序存储器乐曲,依次取节拍码置于R0
MOV TONLNG,R0 ;将取出的第一个节拍码装入PLYTON作节拍控制
OR R0,PLYTON ;将此R0节拍码与音符码PLYTON作OR运算
LD R0,Z+ ;Z指针加1
BRNE PLAYM1 ;若音符码及节拍码非全为00值,则跳到PLAYM1演奏
LDI TEMP,$00 ;若音符码及节拍码全为零(0000),则为乐曲结束标记
OUT TCCR1B,TEMP ;将TEMP=00输出到TCCR1B内,停止TC1计时及捕捉
CLI ;关中断?
SBI PORTC,0 ;令PC0=1,将喇叭输出OFF
pop r16
ret ;乐曲演奏完,调用返回
PLAYM1: PUSH ZH ;进栈保存数据
PUSH ZL ;进栈保存数据
TST PLYTON ;检测PLYTON是否为0
BREQ MUSTD1 ;若为0,则跳至MUSTD1作节拍等待
LDI ZH,HIGH(mustab*2) ;乐曲码装入Z地址
LDI ZL,LOW(mustab*2) ;计时器值存放于MUSTAB*2起始地址
MOV TEMP,PLYTON ;将乐曲码PLYTON装入TEMP寄存器内
DEC TEMP ;寄存器TEMP减1
LSL TEMP ;寄存器TEMP左移即*2
ADD ZL,TEMP ;将正确的计时器控制乐曲码的存表位移,且使TEMP加入ZL
LDI TEMP,$00 ;令TEMP=00,以便让ZH与进位标志位相加
ADC ZH,TEMP ;将ZH与TEMP=00以及进位标志位C相加,得到真正的Z地址
LPM ;将Z所指的PROM的预存乐曲码计时长度低位值装入R0
MOV TONL,R0 ;将乐曲码计时长度低位值R0装入TONL内
OUT TCNT1L,R0 ;将乐曲码计时长度低位值R0装入TCNT1L内
LD R0,Z+ ;Z指针加1
LPM ;将Z所指PROM的乐曲码的计时节拍码置于R0
MOV TONH,R0 ;将节拍高位值R0装入TONH和TCNT1H内
OUT TCNT1H,R0 ;
POP ZL ;出栈将ZL,ZH由堆栈指针依次取回
POP ZH
LDS TEMP,$0100 ;将SRAM地址$0100 的内容装入TEMP内
OUT TCCR1B,TEMP ;将原$0100的SRAM内容输出到TCCR1B,控制TC1
SEI ;内容02令TC1为计时预除8,开中断
MUSTD1: RCALL PLYDEL ;调用延时子程序,延时0.2S
DEC TONLNG ;将节拍码TONLNG减1
BRNE MUSTD1 ;若节拍码TONLNG不为0则转回,再发音;为0则顺执
RJMP NEXMUT1 ;继续到NEXMUT1取乐曲码和节拍码
PLYDEL: LDI TEMP,185 ;延时子程序,185*1ms,即PLYDEL=185ms为十进制时间常数
DT3: LDI TEMP1,04 ;送时间常数4*250us=1ms,DT3=1ms
DT2: LDI TEMP2,250 ;250为十进制时间常数,250*8*125ns=250ms,DT2=250us
DT1: WDR ;1T
WDR ;2T
WDR ;3T
WDR ;4T
WDR ;5T
DEC TEMP2 ;6T,TEMP-1
BRNE DT1 ;8T,TEMP2不为0(则共执行250*8*T=250us)转
DEC TEMP1 ;为0按顺序执行
BRNE DT2 ;TEMP1-1
DEC TEMP ;TEMP1不为0(则共执行250*4us=1ms)转,为0按顺序执行
BRNE DT3 ;TEMP-1
RET ;TEMP不为0(则共执行185*1ms=185ms)转,为0按顺序执行
;子程序返回
yinyue: ;生日快乐歌
.DB 20,02,00,01,20,01,22,04,20,04
.DB 25,04,24,04,00,04
.DB 20,02,00,01,20,01,22,04,20,04
.DB 27,04,25,04,00,04
.DB 20,02,00,01,20,01,32,04,29,04
.DB 25,04,24,04,22,04
.DB 30,02,00,01,30,01,29,04,25,04
.DB 27,04,25,04,00,04
.DB 20,02,00,01,20,01,22,04,20,04
.DB 25,04,24,04,00,04
.DB 20,02,00,01,20,01,22,04,20,04
.DB 27,04,25,04,00,04
.DB 20,02,00,01,20,01,32,04,29,04
.DB 25,04,24,04,22,04
.DB 30,02,00,01,30,01,29,04,25,04
.DB 27,04,25,04,00,04
;万水千山总是情
.db 17,04,18,04,20,06,20,02,22,04,20,04,17,12,15,04 ;
.db 13,06,17,02,15,04,13,04,10,12,10,04,8,8,13,04 ;
.db 15,04,17,04,20,04,22,04,17,04,15,15,15,04,00,04 ;
.db 17,04,18,04,20,06,20,02,22,04,20,04,17,12,15,04 ;
.db 13,06,17,02,15,04,13,04,10,12,10,04,8,8,13,06 ;
.db 17,02,15,06,13,02,13,04,10,04,13,15,13,8,17,04 ;
.db 20,04,22,12,25,10,22,04,18,04,20,06,22,02,20,12 ;
.db 17,04,20,8,17,04,20,04,22,12,25,04,25,04,22,04 ;
.db 22,04,20,04,17,12,15,04,13,06,17,02,15,04,13,04 ;
.db 12,04,13,15,13,15 ;
.db 00,00
.cseg ;
mustab: ;音符表
;约定:低音为数字后一点表示,高音为数字前一点表示,
; 半音为#号,'为隔开音符
;1 2 3 4 5 6 7 8 9
;1. '#1. '2. '#2. '3. '4. '#4. '5. '#5.
;10 11 12 13 14 15 16 17 18
;6. '#6. '7. '1 '#1 '2 '#2 '3 '4
.DW 63627,63730,63835,63927
.DW 64020,64102,64185,64259
.DW 64330,64399,64463,64523
.DW 64579,64632,64683,64731
.DW 64776,64819
;19 20 21 22 23 24 25 26 27
;#4 '5 '#5 '6 '#6 '7 '.1 '#.1 '.2
; 28 29 30 31 32 33 34 35 36
;'#.2 '.3 '.4 '#.4 '.5 '#.5 '.6 '#.6 '.7
.DW 64859,64897
.DW 64933,64967,64999,65029
.DW 65057,65084,65109,65133
.DW 65156,65177,65197,65216
.DW 65243,65251,65267,65282
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -