📄 main.asm
字号:
;********** 单片机电子琴 *********************
include "define.asm"
;*************** 复位向量和中断向量 *****************
org 000h
nop
goto main
;org 0004h
;goto serv
;**************** main ****************
main
bsf status,rp0;bank1
movlw 07h
movwf adcon1 ;设置lcd
bcf status,rp0;bank0
call lcdinitiate ;lcd 初始化
call functlist ;功能显示界面
movlw b'00000001';清屏
call lcddownwrite;下降触发并写入
bsf status,rp0;bank1
movlw 00h;将端口C设置为输出
movwf trisc;
movlw 0ffh;将端口B设置为输入
movwf trisb;
movlw 02h;设置选项寄存器:上拉电阻启用;INT下降沿触发
movwf option_reg;分频器给TMR0;分频比值设为1:8
bcf status,rp0;恢复到文件寄存器的体0
;movlw h'b0'
;movwf intcon ;开放INT和全局中断使能位
;**************************
movlw 0dbh;"0"键的行列码
movwf 40h;0的地址
movlw 77h;"1"
movwf 41h;
movlw 7bh;"2"
movwf 42h;
movlw 7dh;“3”
movwf 43h;
movlw 7eh;“4”
movwf 44h;
movlw 0b7h;“5”
movwf 45h;
movlw 0bbh;“6”
movwf 46h;
movlw 0bdh;“7”
movwf 47h;
movlw 0beh;“8”
movwf 48h;
movlw 0d7h;“9”
movwf 49h;
movlw 0ddh;“A”
movwf 4ah;
movlw 0deh;“B”
movwf 4bh;
movlw 0e7h;“C”
movwf 4ch;
movlw 0ebh;“D”
movwf 4dh;
movlw 0edh;“E”
movwf 4eh;
movlw 0eeh;“F”
movwf 4fh;
;********************************
;******** 判断与功能选择 *********
loopmain
call check
clrf special
movlw 0ddh;"A"高音
subwf value,0
btfsc status,z
call fenpin4
movlw 0deh
subwf value,0
btfsc status,z
call fenpin8
movlw 0e7h
subwf value,0
btfsc status,z
call fenpin16
movlw 0ebh;"D"存储
subwf value,0
btfsc status,z
call writein
movlw 0edh;"E"播放
subwf value,0
btfsc status,z
call song
movlw 0h;;送0h到W--------由特殊操作判断寄存器是否为0判断是否为特殊操作(因为进入特殊操作时都会给PANDUAN加1)
subwf special,0;special-W值送W
btfss status,z;看Z位是否为1,是!跳一步
goto loopmain
call dzq;默认模式电子琴
goto loopmain
;******************* serv ************************
;*************中断服务子程序************
serv
;*********保护现场**********
movwf w_temp;保护W
swapf status,w;保护STATUS
clrf status;选择体0
movwf status_temp;将STATUS存入体0的备份寄存器
;**********调查中断源**********
btfsc intcon,intf;检查是不是INT中断
goto intserv;是!跳转到INT中断处理部分
goto retfie0;其他情况均中断返回
;*************INT中断处理部分************
intserv;外部中断处理程序片段入口
clrf portc;令全部LED熄灭
bsf portc,6;点亮第六只LED,表示就绪
loopint
bsf status,rp0;bank1
movlw 02h;设置选项寄存器:上拉电阻启用;INT下降沿触发
movwf option_reg;分频器给TMR0;分频比值设为1:8
bcf status,rp0
call check;调用键盘程序
movlw 77h;是不是1键按下?
subwf value,0;
btfsc status,z;
call song1;是!播放歌曲1
movlw 7bh;否!是不是2键按下?
subwf value,0;
btfsc status,z;
call song2;是!播放歌曲2
bcf intcon,intf;清除INTF中断标志位
goto retfie0;中断返回
;**********恢复现场***********
retfie0
swapf status_temp,w;恢复STATUS
movwf status;
swapf w_temp,f;恢复W
swapf w_temp,w;
;movlw b'10010000';重新开放全局和INT中断
;movwf intcon
retfie;中断返回
;******************** 电子琴 ************
;*******************************************
dzq
movf fsr,0;将翻译键值送到W
call checkyinch;;查表得对应音长
movwf count;;通过W传给参数COUNT
movf fsr,0;;将翻译键值送到W
call checkyingao;;查表得对应音调(频率决定高低)
movwf temp0;;通过W传给参数TEMP0
call fangbo;;调用FANGBO子程序
return;;发一个单音完毕,返回到loopmain
;********************************
checkyinch;;对应音长
addwf pcl,1 ;地址偏移量加当前PC值
nop
retlw .100;单音‘1’
retlw .112;单音‘2’
retlw .126;单音‘3’
retlw .134;单音‘4’
retlw .150;单音‘5’
retlw .168;单音‘6’
retlw .189;单音‘7’
retlw .200;单音高音‘1’
;*************************************************
checklcd
addwf pcl,1 ;地址偏移量加当前PC值
retlw b'01001111' ;'0'的LCD显示码
retlw b'00110001' ;'1'的LCD显示码
retlw b'00110010' ;'2'的LCD显示码
retlw b'00110011' ;'3'的LCD显示码
retlw b'00110100' ;'4'的LCD显示码
retlw b'00110101' ;'5'的LCD显示码
retlw b'00110110' ;'6'的LCD显示码
retlw b'00110111' ;'7'的LCD显示码
retlw b'00111000' ;'8'的LCD显示码
retlw b'00111001' ;'9'的LCD显示码
retlw b'01000001' ;'A'的LCD显示码
retlw b'01000010' ;'B'的LCD显示码
retlw b'01000011' ;'C'的LCD显示码
retlw b'01000100' ;'D'的LCD显示码
retlw b'01000101' ;'E'的LCD显示码
retlw b'01000110' ;'F'的LCD显示码
checkyingao;;对应音调
addwf pcl,1 ;地址偏移量加当前PC值
nop
retlw .100;;单音‘1’
retlw .117;;单音‘2’
retlw .132;;单音‘3’
retlw .139;;单音‘4’
retlw .152;;单音‘5’
retlw .163;;单音‘6’
retlw .173;;单音‘7’
retlw .178;;单音高音‘1’
;**********************************************
;***********键盘部分***********************
check
call keyscan;调用键盘扫描子程序
comf value,0;位置码取反送W
btfsc status,z;测试有键按下否?有!跳过下条指令
goto check;无!则循环检测
movlw .13
movwf data1
call delaytemp;调用延时子程序,消除接通抖动的影响
call keyscan;再次调用键盘扫描子程序
comf value,0;位置码取反送W
btfsc status,z;测试有键按下否?有!跳过下条指令
goto check;无!则循环检测
call translate;调用键值翻译子程序
call lcdshow
return
;**************************************************
lcdshow
bsf status,rp0
movf option_reg,w
bcf status,rp0;恢复到文件寄存器的体0
movwf option_temp
bsf status,rp0;设置文件寄存器的体1
movlw 7h
movwf option_reg;设置定时器tmr0分频比为1:256
bcf status,rp0;恢复到文件寄存器的体0
call lcdinitiate
movlw b'00000001';清屏
call lcddownwrite;下降触发并写入
movf fsr,w
call checklcd
call enableport
bsf status,rp0
movlw 0ffh;将端口D设置为输入
movwf trisd;
bcf status,rp0
movf option_temp,w
bsf status,rp0
movwf option_reg
bcf status,rp0
return
;*****************************************************
;*****************************************
;***出口参数:有按键按下时寄存器value=按键行列码;
;***无键按下时value=ffh
;**************************************
keyscan
;bcf intcon,7;屏蔽全局中断
bcf status,rp1;设置文件寄存器的体1
bsf status,rp0;
bcf option_reg,rbup;启用B口上拉电阻
movlw 0fh;将端口B的方向控制码0FH先送W
movwf trisb;将其设为高四位出低四位入
movlw 0fh;将端口D的方向控制码0FH先送W
movwf trisd;将其设为高四位出低四位入
bcf status,rp0;恢复到文件寄存器的体0
movlw 00h;四条行线全部输出0
movwf portd;
nop;等待输出状态稳定下来
nop;
movf portb,0;读取列线状态
andlw 0fh;屏蔽掉高四位
movwf value;得到的列码暂存到VALUE寄存器中
xorlw 0fh;与0FH值对比
btfsc status,z;列码全为1吗?否!有键按下
goto nokey;是,无键按下
bsf status,rp0;设置文件寄存器的体
movlw 0f0h;将端口B的方向控制码0F0H先送W
movwf trisb;将其设为高四位入低四位出
movlw 0f0h;将端口D的方向控制码0F0H先送W
movwf trisd;将其设为高四位入低四位出
bcf status,rp0;恢复到文件寄存器的体0
movf value,0;读取列码
movwf portb;再经过列线输出
nop;
nop;
movf portd,0;读取行线状态
andlw 0f0h;屏蔽掉低四位
iorwf value,1;将行码和列码组合起来并放入VALUE
return;子程序返回
nokey
movlw 0ffh;建立一个标志值,表明无键按下
movwf value;
bsf status,rp0
clrf trisd
bcf status,rp0
;bsf intcon,7
return
;**********************************************************
;******* 出口参数:正常时寄存器fsr=键值(在table中的偏移量
;********** 失常时寄存器fsr=aah
;***********************************************************
translate
movlw 40h;地址指针FSR设置表头地址
movwf fsr;也就是给文件选择寄存器FSR设置初始值
loopt1
movf 0,0;以间接寻址方式读表,并放入W
xorwf value,0;与位置码比较
btfsc status,z;相等吗?否!跳一步
goto loopt2;是!跳转
incf fsr,1;地址指针FSR递增
btfss fsr,4;查到末尾了吗,即够16次了吗?(BIT4=1?)
goto loopt1;否!跳回继续查表
movlw 0aah;是!位置码超限(可能多键同时按下)
movwf fsr;返回一个“AAH=10101010B”值作标志
return;子程序返回
loopt2
bcf fsr,6;等效于FSR—40H(即01000000B)—>FSR
return; 在FSR中获得键值,返回
;*************方波**************
;出口参数:rc4输出方波
;**********************************
fangbo
l movlw .2
movwf count10
l1 bcf intcon,t0if ;将tmr0中断标志位清0
movf temp0,0 ;在tmr0中装入初值,开始计时
movwf tmr0 ;启动定时器
l2 btfss intcon,t0if ;判断中断溢出否
goto l2 ;否,循环
movlw b'00010000'
xorwf portc,1 ;是,rc4引脚输出电平翻转
decfsz count,1
goto l1
decfsz count10,1
goto l1
return
;*************分频比为8的子程序(中音用)***************************
fenpin8
incf special,1
incf special2,1
bsf status,rp0;设置文件寄存器的体1
movlw b'00000010';;设置分频比为1:8
movwf option_reg
bcf status,rp0;;恢复到文件寄存器的体0
return;子程序返回
;*************分频比为16的子程序(低音用)**********************
fenpin16
incf special,1
incf special2,1
bsf status,rp0;设置文件寄存器的体1
movlw b'00000011';;设置分频比为1:16
movwf option_reg
bcf status,rp0;;恢复到文件寄存器的体0
return;子程序返回
;*************分频比为4的子程序(高音用)**********************
fenpin4
incf special,1
incf special2,1
bsf status,rp0;设置文件寄存器的体1
movlw b'00000001';;设置分频比为1:4
movwf option_reg
bcf status,rp0;;恢复到文件寄存器的体0
return;子程序返回
;***********************************************************
;*************** writein ***********************
writein
incf special,1
clrf addr;将地址变量清0,按照书上的做法
call write;真正进入存储1,寻址2,存储
;************ e2pcom 存储子程序********************
;入口参数;yinfu寄存器中的数据写到addr寄存器指定的单元
;出口参数;是把读出的音符保存在yinfu 中
;***************************************************
write
clrf special2
bcf status,rp1;
bcf status,rp0;bank0
call keyscan;调用键盘扫描子程序
comf value,0;位置码取反送W
btfsc status,z;测试有键按下否?有!跳过下条指令
goto write;无!循环检测
movlw .13;预置外循环变量
movwf data1;以便产生10ms延时
call delaytemp;调用延时子程序,消除接通抖动的影响
call keyscan;再次调用键盘扫描子程序
comf value,0;位置码取反送W
btfsc status,z;测试有键按下否?有!跳过下条指令
goto write;无!循环检测
call translate;调用键值翻译子程序
movf fsr,w;键值为“D"时,不发音
xorlw 0dh
btfsc status,z
goto write
movf fsr,w;;送键值到W-若按键为‘9’,进入读功能(间接寻址
xorlw 09h;;与09h异或,影响状态位
btfsc status,z;;看Z位是否为0,是!跳一步
goto readin;;否!则调用读子程序
movf fsr,w;“0”返回主程序
xorlw 0h
btfsc status,z
goto loopmain
movf fsr,w;高低音转换
xorlw 0ah
btfsc status,z
goto fenpin4
movf fsr,w;高低音转换
xorlw 0bh
btfsc status,z
goto fenpin8
movf fsr,w;高低音转换
xorlw 0ch
btfsc status,z
goto fenpin16
movlw 0h;;送0h到W--------由特殊操作判断寄存器是否为0判断是否为特殊操作(因为进入特殊操作时都会给special2加1)
subwf special2,0;special2-W值送W
btfss status,z;看Z位是否为1,是!跳一步
goto write
call lcdshow
movf fsr,w;;将翻译键值送到W
movwf yinfu;;保存在定义的寄存器中
call checkyinch;;查表得对应音长
movwf count;;通过W传给参数COUNT设置音长的延时
movf yinfu,0;;将翻译键值送到W
call checkyingao;;查表得对应音调
movwf temp0;;通过W传给参数TEMP0设置频率
call fangbo;;调用FANGBO子程序
;************存储标准格式
bsf status,rp0
bsf status,rp1 ;bank3
btfsc eecon1,wr ;上一次写操作是否完成
goto $-1 ;否,返回继续检测 $表示本条指令
bcf status,rp0 ;bank2
movf addr,w ;取地址
movwf eeadr ;送地址寄存器
movf fsr,w ;取数据
movwf eedata ;送数据寄存器
bsf status,rp0 ;bank3
bcf eecon1,eepgd ;选定e2prom为访问对象
bsf eecon1,wren ;开放写操作使能控制
; bcf intcon,gie ;禁止所有中断
movlw 55h ;一下是固定的
movwf eecon2
movlw 0aah
movwf eecon2
bsf eecon1,wr ;启动写操作
bcf eecon1,wren ;禁止写操作发生
bcf status,rp1
bcf status,rp0 ;bank0
incf addr,f;地址递增
writecheck ;检查write中按下的按键是否已经松开
movlw .117;;预置外循环变量
movwf data1;;以便产生100ms延时
call delaytemp;;调用延时子程序,消除接通抖动的影响
movlw 0f0h;;将C口高4位输出状态反转,使D7-D3闪烁
xorwf portc,1;;以提示及时释放按键
;;或指明所显键值对应按键出现了粘连故障
call keyscan;;调用键盘扫描子程序
comf value,0;;位置码取反送W
btfss status,z;;测试键全部释放否?是!跳过下条指令
goto writecheck;;否,回到writecheck,循环检测
goto write;;是,大循环
;************** readin *********************
;入口参数:w中的键值
;**********************************
readin
movf addr,w;;将地址变量送W
xorlw 00h;与0比较,这一点设计要从write与read的整个循环来想
;如果read完成之后,没有新的write,则只能重复read
btfss status,z
goto read;不为0!则证明有新一次的输入,调用read
goto read1;为0!则功能为重复播放上一次内容,调用read1,不改
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -