📄 keyboard.asm
字号:
;除法需要用一定的算法来实现,暂时没有涉及
.include "m16def.inc" ;文件头,其中包含ATmega16芯片的硬件资源定义,针对ATmega16的
;程序一般需要有此指令。
.org $0000 ;org是一条伪指令,表示随后程序的起始位置。即表示下条指令
;"rjmp RESET"位于程序存储器的$0000地址
rjmp RESET ;单片机复位后从$0000开始执行程序,在这里放一条跳转指令,
;跳转到主程序开始的地址
.def cnt1d = r17
.org $018 ;UDRE为空的溢出中断向量。中断向量表见ATmega16的数据手册42页。
rjmp UDRE_EPT
.org $0030
.db 1,2,3,'+',4,5,6,'-',7,8,9,'*','.',0,'=','/';
.org $0040
UDRE_EPT:
out UDR,r10 ;发送数据
USART_WAIT:
in r26,UCSRA
sbrs r26,6
rjmp USART_WAIT
sbi UCSRA,6 ;清除txc位
ldi r26,0b00011000 ;将2进制数0b00011000送入寄存器r26;
out UCSRB,r26 ;关闭UDRE中断允许
reti
.org $0060 ;$0000地址之后是中断向量区,跳过中断区。中断与中断向量将在
;以后解释,可以参考光盘中ATmega16的数据手册。
RESET:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND) ;设置堆栈,一般将堆栈设置到RAM的结尾。
out SPL,r16 ;在自己编写程序时,可以使用以上代码
ldi r16,0b11111111 ;
out DDRA,r16
out DDRD,r16 ;设置PA,PD端口全部为输出
out PORTA,r16 ;PA端口全部输出高电平,LED熄灭
out PORTD,r16 ;PD端口输出高电平
ldi r16,0b11111110;
out DDRB,r16 ;设置PB0为输入,PB0管脚连接按键
out PORTB,r16 ;PB1~PB7输出高电平,PB0为输入,关闭内部上拉
ldi r16,0b00001111;
out DDRC,r16 ;设置PC0~PC3为输出,进行行线扫描;PC4~PC7为输入,获取列线电平状态
ldi r16,0b11111110;
out PORTC,r16 ;PC0输出低电平,PC4~PC7打开内部上拉 ; 端口的初始化完成。
;USART的初始化,采用中断方式发送数据
ldi r16,95 ;将10进制数95送入寄存器r16。晶体14.7456MHZ,波特率9600,可以算出UBRR应为95
ldi r17,0
out UBRRH,r17 ;设置波特率高位。由于UBRRH的复位默认值为0,所以此句可以省略
out UBRRL,r16 ;设置波特率低位。 波特率设置完成。
ldi r16,0b00011000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16 ;RXEN,TXEN被写为1,允许USART进行接收和发送
ldi r16,0b10000110 ;
out UCSRC,r16 ;选择8位数据
sei ;USART初始化完成
ldi r23,0
MAIN:
call SC_KEYBOARD ;
call CAL ;
ldi r16,0x0d ;下面部分为回车换行
mov r10,r16
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16 ;RXEN,TXEN被写为1,允许USART进行接收和发送
ldi r16,0x0a
mov r10,r16
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16 ;RXEN,TXEN被写为1,允许USART进行接收和发送
rjmp MAIN
SC_KEYBOARD:
ldi r17,0XFE
SC_MAIN:
ldi r24,0
mov r16,r17
ldi r21,4
cpi r16,0XFF
breq SC_KEYBOARD ;4根行线扫描完毕,重新扫描。
out PORTC,r16 ;对应行线送低电平,
call DLUS ;等待低电平状态稳定
in r16,PINC
andi r16,0xf0
ldi r18,0xf0
cpse r16,r18
rjmp SC_ACT
rjmp SC_NEXT_LINE ;没有按键按下
SC_ACT:
call DLMS ;有按键按下,按键消抖
in r16,PINC ;再读键盘列线状态
com r16 ;r16取反
andi r16,0xf0
SC1:
cpi r16,0x10
breq SC2 ;第4根列线为0
dec r21
lsr r16 ;
rjmp SC1
SC2:
mov r16,r17 ;R17中为当前循环中pinc的状态,得知行线
com r16
SC3:
cpi r16,1
breq SC4
adiw r24,4
lsr r16
rjmp SC3
SC4:
add r24,r21
adiw r24,0x30
adiw r24,0x2f ;常数数组在程序区的偏移量
ldi zh,0
mov zl,r24
lpm
mov r10,r0
ldi r16,6
cpse r23,r16 ;判断是第几个输入字符,是第六个吗?
rjmp SC5
ldi r23,0 ;是第六个,则次数记录清0
SC5:
inc r23
cpi r23,3
breq SC6
cpi r23,6
breq SC6
mov r21,r0
cpi r23,5
breq SC7
cpi r23,2
breq SC7
mov r15,r21
rjmp SC8 ;第一次或者第四次输入的数据,送r15保存即可
SC7:
mov r16,r15
lsl r15
lsl r15
lsl r15
add r15,r16
add r15,r16 ; r15*10
add r15,r21
ldi r16,5
cpse r16,r23
mov r14,r15
SC8:
mov r10,r0
ldi r16,0x30
add r10,r16
SC6:
ldi r16,3
cpse r23,r16
rjmp SC9
mov r22,r0 ;保存操作符
SC9:
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
SC10:
in r16,PINC
andi r16,0xf0
ldi r18,0xf0
cpse r16,r18 ;wait until key up
rjmp SC10
ldi r16,6
cpse r23,R16
rjmp SC_NEXT_LINE ;不是等号,继续
ret ;是等号,计算结果并且输出;给个寄存器,然后退出
SC_NEXT_LINE:
mov r16,r17
com r16
lsl r16
com r16
ori r16,0b11110000 ;保证高4位为f。
mov r17,r16
rjmp SC_MAIN ;
CAL:
cpi r22,'+' ;
breq CAL_ADD
cpi r22,'-'
breq CAL_SUB
cpi r22,'*'
breq CAL_MUL
rjmp CAL_WRONG
CAL_ADD:
adc r14,r15
mov r16,r14 ;针对如果加法和大于100的情况
cpi r16,100
brlo CAL_ADD1
subi r16,100 ;减去100
mov r14,r16
ldi r18,0x31 ;大于等于100,则先送出去1,
mov r10,r18
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
CAL_ADD1: ;相加和小于100,准备转换,传输
rjmp CAL_TRAN;
CAL_SUB:
cp r14,r15
brsh CAL_SUB_PLUS
ldi r16,'-'
mov r10,r16 ;显示负号
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
mov r18,r14
mov r14,r15
mov r15,r18
CAL_SUB_PLUS:
sub r14,r15
rjmp CAL_TRAN;
CAL_MUL:
mul r14,r15
ldi r16,'H'
mov r10,r16 ;显示H
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ldi r16,'E'
mov r10,r16 ;显示E
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ldi r16,'X'
mov r10,r16 ;显示X
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ldi r16,':'
mov r10,r16 ;显示:
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ldi r18,0x30 ;将乘法的计算结果以十六进制的形式发送出去
ldi r19,0x37
mov r16,r1 ;转换成ascii码
swap r16
andi r16,0x0f
cpi r16,10
brlo CAL_MUL1
add r16,r19
rjmp CAL_MUL2
CAL_MUL1:
add r16,r18
CAL_MUL2:
mov r10,r16 ;显示
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
mov r16,r1
andi r16,0x0f
cpi r16,10
brlo CAL_MUL3
add r16,r19
rjmp CAL_MUL4
CAL_MUL3:
add r16,r18
CAL_MUL4:
mov r10,r16 ;显示
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
mov r16,r0
swap r16
andi r16,0x0f
cpi r16,10
brlo CAL_MUL5
add r16,r19
rjmp CAL_MUL6
CAL_MUL5:
add r16,r18
CAL_MUL6:
mov r10,r16 ;显示
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
mov r16,r0
andi r16,0x0f
cpi r16,10
brlo CAL_MUL7
add r16,r19
rjmp CAL_MUL8
CAL_MUL7:
add r16,r18
CAL_MUL8:
mov r10,r16 ;显示
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ret
CAL_WRONG:
ldi r16,'?'
mov r10,r16
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ret
CAL_TRAN:
ldi r19,6 ;这部分内容是将二进制转换成十进制,然后转成ascii码
mov r16,r14 ;例如,hex:23--->dec:35
mov r15,r14 ;copy r14,因为如果各位加6,会有进位
andi r16,0x0f
cpi r16,10
brlo CAL_TRAN3
add r15,r19
CAL_TRAN3:
mov r16,r14
swap r16
andi r16,0x0f
mov r14,r15 ;r16中个位进位前的十位数;r14中是加过6之后的十位数,这才对
CAL_TRAN2:
cpi r16,0
breq CAL_TRAN1
dec r16
add r14,r19
mov r18,r14
andi r18,0x0f
cpi r18,10 ;R18只是用来跟那个10作比较
brlo CAL_TRAN2
add r14,r19
rjmp CAL_TRAN2 ;二进制到十进制转换完成
CAL_TRAN1:
ldi r18,0x30 ;二进制转换成ascii码。
mov r16,r14
swap r16
andi r16,0x0f
add r16,r18
mov r10,r16
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
mov r16,r14
andi r16,0x0f
add r16,r18
mov r10,r16
ldi r16,0b00111000 ;将2进制数0b00011000送入寄存器r16;0b00111000,是允许udre为空中断
out UCSRB,r16
ret
DLUS:
ldi r19,0xFF
DLUS1:
dec r19 ;r19减去1
brne DLUS1 ;不为0转DELAY1;为0按顺序执行
ret
;延时子程序
DLMS:
ldi r20,0x02 ;设置延时子程序参数
ldi r18,0xff
ldi r19,0x90
DLMS1:
dec r18 ;r18减去1
brne DLMS1 ;不为0转DELAY1;为0按顺序执行
dec r19 ;r19减去1
brne DLMS1 ;不为0转DELAY1;为0按顺序执行
dec r20 ;r17减去1
brne DLMS1 ;不为0转DELAY1;为0按顺序执行,延时程序结束,返回主程序
ret
rjmp RESET
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -