⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 keyboard.asm

📁 AVR开发例程 有多种设计样例 帮你快速掌控AVR
💻 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 + -