按键和PIC单片机
一、按键
按键是嵌入式系统基本的人机交互手段,其使用步骤包括:检测连通、消抖、行为判断

二、按键的物理连接与检测
简单按键

缺点:占用端口引脚太多,一个按键就占一个引脚。
行列扫描按键

-
行为input,列为output
-
每个列依次接地,同时其他列高阻态(建议不用逻辑1,防止短路),观察哪一/些行为0,就能锁定到被按下的按键
-
有多个按键同时被按下时,程序可能会错误判断按键,不可信
-
上面这种对比简单按键并没有优势,还是太占用引脚
行列扫描的扩展

- 二极管限制了电流的流向,通过控制两个引脚的输入输出方向来判断不同按键。
- 但是因为每个按键都加入了一个二极管,成本增加且占用面积。
全扫描

行列之间两两一个,行行、列列间也是两两一个。
简单按键+全扫描

要求7~10与1~6不能同时检测,因为两组之间彼此干扰。因此必须先检测7~中有没有按下,没有的话再检测1~6。检测到有一个按下后就结束,不能检测多个同时按下的情况,不准确。
三、PIC16F18854单片机
按键检测及显示实验
按键排布
简单按键+全扫描,四根线对应连接到PIC单片机的4个引脚,最后一根接地。

预期效果
4位数码管显示按键编号、行为、按动次数
总体流程
数码管片选与段选各自维护一个查找表,在主程序中循环对数码管进行片选、段选、延时,其中段选时按照显示缓冲区内容执行。定时器中断周期设置为5ms,中断服务程序提供:连通检测、消抖、状态转换、显示驱动服务。其中关键部分是中断服务程序的设计。


中断服务程序
中断服务程序分为4个模块,模块间充分解耦,其间只有个别变量在整个流程中进行传递,其他大部分变量都是各个模块自身的局部变量。

1、连通检测
step1:单片机4个引脚弱上拉,依次检查4个引脚有无0电平,即7~10号按键有无按下,若无则进入step2。
step2:4个引脚两两间有6个按键,依次检测一遍,若只有1对为连通则说明仅一个按键按下,若有多对连通则为多按键被按下。
最后将检测到的连通状态cur_key传入消抖模块。
代码流程图:
2、消抖模块
检测连通检测模块传入的cur_key与上次传入的是否相同,若连着两次相同则认为不是抖动,继续传入状态转换模块;若不同,则认为是抖动造成的,把上次的key传入状态转换模块。本质是要求同一按键连按10ms才认为是按下而不是抖动。
代码流程图:

3、状态转换
状态转换模块功能是,根据消抖模块传入的cur_key来改变当前状态tatus,并且把相对应事件event和cur_key一起传入给后面的显示驱动模块。“输入——状态改变——输出”的运行逻辑正是一个状态机。
state\action | MULTIKEY | UP | TIMEOUT | SAME | OTHER |
---|---|---|---|---|---|
CLICK | init(single) | wait | press(press) | click | init(single) |
WAIT | init(single) | wait | init(single) | double(double) | click(single) |
PRESS | init(press_up) | init(press_up) | - | same(press) | init(press_up) |
INIT | init | init | - | click | click |
DOUBLE | init | init | - | double | init |
代码流程图:

4、显示驱动
根据状态转换模块传来的event和cur_key调制主程序段选时使用的显示缓存区,从而在数码管显示按键状态和相应信息。
代码流程图:

PIC汇编程序
#include <xc.inc>
psect intentry, class=CODE, delta=2
psect reset_vec, class=CODE, delta=2
psect eeprom_data,class=EEDATA,delta=2,space=3,noexec
psect powerup, class=CODE, delta=2
psect cinit,class=CODE,delta=2
psect functab,class=ENTRY,delta=2
psect idloc,class=IDLOC,delta=2,noexec
global _main, reset_vec
psect config, class=CONFIG, delta=2
#ifndef BOOTLOADER
dw 0xDFEC
dw 0xF7FF
dw 0xFFBF
dw 0xEFFE
dw 0xFFFF
#endif
psect reset_vec
reset_vec:
ljmp _main
;GLOBAL VARIABLES
psect CommonVar, class=COMMON, space=1, delta=1
;main
dis_cache: ds 4
delay1: ds 1
delay2: ds 1
;debounce
last_result: ds 1
count: ds 1
;status_trans
action: ds 1
state: ds 1
event: ds 1
wait_count: ds 1
click_count: ds 1
last_key: ds 1
;connection_check
index: ds 1
;global
cur_key: ds 1
psect udata
i: ds 1
j: ds 1
led_select: ds 1
workplace2: ds 1
dis_last_key: ds 1
dis_count: ds 2
swap: ds 1
press_count:ds 1
;STATIC TABLE
psect Table, class=CODE, delta=2
event_table:
MOVWF PCL
IRP event,1,0,3,0,1,1,0,1,2,1,4,4,0,3,4
RETLW event
ENDM
action_table:
MOVWF PCL
IRP row_click,0b1111,0b101,0b1010,0b0,0b1111
RETLW row_click
ENDM
IRP row_wait,0b1111,0b101,0b1111,0b10100,0b0
RETLW row_wait
ENDM
IRP row_press,0b1111,0b1111,0xff,0b1010,0b1111
RETLW row_press
ENDM
IRP row_init,0b1111,0b1111,0xff,0b0,0b0
RETLW row_init
ENDM
IRP row_double,0b1111,0b1111,0xff,0b10100,0b1111
RETLW row_double
ENDM
seq_table:
ADDWF PCL,f
IRP seq_num,0b00111111,0b00000110,0b01011011,0b01001111,0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01101111,0b01110111
RETLW seq_num
ENDM
code_table:
MOVWF PCL
IRP code_num,0b00000111,0b00001011,0b00001101,0b00001110
RETLW code_num
ENDM
key_table:
ADDWF PCL,F
IRP key_num,5,1,2,3,4,6
RETLW key_num
ENDM
;INTERRUPT SERVICE
psect intentry
intentry:
;clear the signal bit
BANKSEL PIR0
BCF PIR0,5
;init the TMR0 offset
BANKSEL TMR0L
;MOCLW 0xfec0c
MOVLW 0xec
MOVWF TMR0H
MOVLW 0x78
MOVWF TMR0L
connection_check:
CLRF cur_key
BANKSEL i
MOVLW 1
MOVWF i
CLRF index
BANKSEL TRISB
MOVLW 0x0f
MOVWF TRISB
BANKSEL WPUB
MOVLW 0x0f
MOVWF WPUB
jedge_i_16:
BANKSEL i
BTFSS i,4
GOTO i_unequ_16
GOTO i_equ_16
i_unequ_16:
BANKSEL i
MOVF i, W
BANKSEL PORTB
ANDWF PORTB, W
BANKSEL workplace2
MOVWF workplace2
MOVF workplace2,f
BTFSS STATUS,2
GOTO branch1_portb_unequ_0
GOTO branch1_portb_equ_0
i_equ_16:
MOVF cur_key, f
BTFSS STATUS,2
GOTO trunk_curkey_unequ_0
GOTO trunk_curkey_equ_0
branch1_portb_equ_0:
MOVF cur_key,f
BTFSS STATUS,2
GOTO branch1_curkey_unequ_0
GOTO branch1_curkey_equ_0
branch1_portb_unequ_0:
BANKSEL i
LSLF i, f
INCF index,f
GOTO jedge_i_16
branch1_curkey_unequ_0:
MOVLW 255
MOVWF cur_key
GOTO trunk_curkey_unequ_0
branch1_curkey_equ_0:
MOVLW 7
ADDWF index, W
MOVWF cur_key
GOTO branch1_portb_unequ_0
trunk_curkey_unequ_0:
GOTO debounce
trunk_curkey_equ_0:
BANKSEL i
MOVLW 1
MOVWF i
MOVLW 2
MOVWF j
CLRF index
jedge_i_8:
BTFSS i, 3
GOTO i_unequ_8
GOTO i_equ_8
i_unequ_8:
MOVF j, w
BTFSS j, 4
GOTO j_unequ_16
GOTO j_equ_16
i_equ_8:
GOTO trunk_curkey_unequ_0
j_unequ_16:
BANKSEL j
COMF j, W
BANKSEL TRISB
MOVWF TRISB
BANKSEL WPUB
MOVWF WPUB
BANKSEL PORTB
MOVWF PORTB
BANKSEL i
MOVF i, W
BANKSEL PORTB
ANDWF PORTB, W
BANKSEL workplace2
MOVWF workplace2
MOVF workplace2,F
BTFSS STATUS,2
GOTO branch2_portb_unequ_0
GOTO branch2_portb_equ_0
j_equ_16:
BANKSEL i
LSLF i, f
LSLF i, W
MOVWF j
GOTO jedge_i_8
branch2_portb_equ_0:
MOVF cur_key,F
BTFSS STATUS,2
GOTO branch2_curkey_unequ_0
GOTO branch2_curkey_equ_0
branch2_portb_unequ_0:
LSLF j, f
INCF index, f
GOTO i_unequ_8
branch2_curkey_equ_0:
;cur_key = key_table[index]
MOVLW HIGH(key_table)
MOVWF PCLATH
MOVF index,W
CALL key_table
MOVWF cur_key
GOTO j_equ_16
branch2_curkey_unequ_0:
MOVLW 255
MOVWF cur_key
GOTO i_equ_8
debounce:
MOVF last_result, W
XORWF cur_key, W
BTFSC STATUS, 2
GOTO deb_continue ;no shake
INCF count ;shake
MOVLW 2
XORWF count, W
BTFSS STATUS, 2
GOTO deb_ignore ;not equ
MOVF cur_key, W;equ
MOVWF last_result
GOTO deb_continue
deb_ignore:
MOVF last_result, W
MOVWF cur_key
GOTO transfer
deb_continue:
CLRF count
GOTO transfer
transfer:
;key value
MULTI EQU 0xFF
NOKEY EQU 0
;action index
MULTIKEY EQU 0
UP EQU 1
TIMEOUT EQU 2
SAME EQU 3
OTHER EQU 4
;state value
CLICK EQU 0
WAIT EQU 5
PRESS EQU 10
INIT EQU 15
DOUBLE EQU 20
;const
UPCOUNT EQU 60
PRESSCOUNT EQU 100
PRESSADD EQU 100
trans_judge_action:
MOVLW MULTI
XORWF cur_key, w
BTFSC STATUS, 2
GOTO action_multi ;multi
MOVLW NOKEY ;continue
XORWF cur_key, w
BTFSC STATUS, 2
GOTO action_up_timeout ;up
MOVF last_key, w ;down
XORWF cur_key, w
BTFSC STATUS, 2
GOTO action_same_timeout ;same
GOTO action_other ;other
;
action_multi:
MOVLW MULTIKEY
GOTO do_transfer
action_up_timeout:
MOVLW WAIT
XORWF state, w
BTFSS STATUS, 2
GOTO action_up ;not wait
INCF wait_count ;waiting
MOVLW UPCOUNT
XORWF wait_count, w
BTFSS STATUS, 2
GOTO action_up
GOTO action_timeout
action_up:
MOVLW UP
GOTO do_transfer
action_same_timeout:
MOVLW CLICK
XORWF state, w
BTFSS STATUS, 2
GOTO action_same
INCF click_count
MOVLW PRESSCOUNT
XORWF click_count, w
BTFSS STATUS, 2
GOTO action_same
GOTO action_timeout
action_same:
MOVLW SAME
GOTO do_transfer
action_timeout:
MOVLW TIMEOUT
GOTO do_transfer
action_other:
MOVLW OTHER
GOTO do_transfer
do_transfer:
MOVWF action
CLRF event
MOVF state, w
addlw 1
ADDWF action, f
MOVLW INIT+1
SUBWF action, w
BTFSC STATUS, 0
GOTO change_state ;no carry -> state+action+1>=INIT+1
movlp high(event_table)
MOVLW low(event_table)
ADDWF action, w
BTFSC STATUS, 0
INCF PCLATH
CALL event_table ;get next state
MOVWF event
change_state:
movlp high(action_table)
MOVLW low(action_table)
ADDWF action, w
BTFSC STATUS, 0
INCF PCLATH
CALL action_table ;get next state
MOVWF state
clear_count_judge:
MOVF event, f
BTFSC STATUS, 2
GOTO transfer_final ;event == 0
clear_count:
CLRF wait_count
CLRF click_count
transfer_final:
banksel swap
MOVF last_key, w
MOVWF swap
MOVWF cur_key, w
BTFSS STATUS, 2
MOVWF last_key
MOVF swap, w
MOVWF cur_key
display:
MOVF event, f
BTFSC STATUS, 2
RETFIE
banksel dis_last_key
MOVF dis_last_key, W
XORWF cur_key, W
BTFSC STATUS, 2
GOTO display_judge_event
display_change:
banksel dis_count
CLRF dis_count
CLRF dis_count+1
MOVF cur_key, W
banksel dis_last_key
MOVWF dis_last_key
display_judge_event:
banksel dis_count
MOVLW 3
SUBWF event, W
BTFSS STATUS, 0
GOTO event_1_2 ;if carry -> event<3
MOVLW 3
XORWF event, W
BTFSC STATUS, 2
GOTO event_3
GOTO display_final
event_1_2:
MOVF event,W
ADDWF dis_count, F
GOTO display_final
event_3:
INCF press_count
MOVLW PRESSADD
XORWF press_count, W
BTFSC STATUS, 2
GOTO event_3_add_count
GOTO display_final
event_3_add_count:
INCF dis_count
CLRF press_count
display_final:
MOVF cur_key, W
MOVWF dis_cache
MOVF event,W
MOVWF dis_cache+1
MOVLW 10
SUBWF dis_count, W
BTFSS STATUS, 0
GOTO display_count ;if carry
display_count_carry:
INCF dis_count+1
MOVLW 10
SUBWF dis_count, F
SUBWF dis_count+1, W
BTFSC STATUS, 0
CLRF dis_count+1 ;if no carry
display_count:
MOVF dis_count, W
MOVWF dis_cache+3
MOVF dis_count+1, W
MOVWF dis_cache+2
RETFIE
;MAIN FUNCTION
psect main,class=CODE,delta=2 ; PIC10/12/16
global _main
_main:
;Timer0 configuration
BANKSEL T0CON0
MOVLW 0b00010000;set Timer0 in 16bit mode,post:1:8
MOVWF T0CON0
BANKSEL T0CON1
MOVLW 01000000;FOCS/4,synchronized to FOSC/4,pre:1:1
MOVWF T0CON1
BANKSEL TMR0L
MOVLW 0xfe
MOVWF TMR0H
MOVLW 0x0c
MOVWF TMR0L;init the TMR0 offset
;interrupt configuration
BANKSEL PIR0
BCF PIR0,5;clear the timer signal bit
banksel INTCON
BSF INTCON,0;INT enable
BSF INTCON,7;global enable
banksel PIE0
BSF PIE0,5;timer0_interrupt enable
;start timing
BANKSEL T0CON0
BSF T0CON0,7; enable Timer0
;init PORTA / PORTB / PORTC
BANKSEL PORTA
CLRF PORTA ;Init PORTA
BANKSEL LATA ;Data Latch
CLRF LATA
BANKSEL ANSELA
CLRF ANSELA ;digital I/O
BANKSEL TRISA
CLRF TRISA
BANKSEL LATB ;Data Latch
CLRF LATB
BANKSEL ANSELB
CLRF ANSELB ;digital I/O
BANKSEL PORTC
BANKSEL PORTC
CLRF PORTC ;Init PORTC
BANKSEL LATC ;Data Latch
CLRF LATC
BANKSEL ANSELC
CLRF ANSELC ;digital I/O
BANKSEL TRISC
CLRF TRISC
;init variables
CLRF dis_cache
CLRF dis_cache + 1
CLRF dis_cache + 2
CLRF dis_cache + 3
CLRF last_result
CLRF count
CLRF action
MOVLW 15
MOVWF state
CLRF wait_count
CLRF click_count
CLRF last_key
CLRF index
CLRF cur_key
BANKSEL led_select
CLRF led_select
CLRF i
CLRF j
CLRF dis_last_key
CLRF dis_count
CLRF swap
CLRF press_count
CLRF workplace2
;MAIN
LOOP:
;code_sel
MOVLW HIGH(code_table)
MOVWF PCLATH
MOVLW LOW(code_table)
BANKSEL led_select
ADDWF led_select, W
BTFSS STATUS, 0
GOTO CALL_TABLE
INCF PCLATH
CALL_TABLE:
ADDLW 1
CALL code_table
BANKSEL PORTA
MOVWF PORTA
;seq_sel
MOVLW HIGH(seq_table)
MOVWF PCLATH
;w = dis_cache + offset
MOVLW high(dis_cache)
MOVWF FSR0H
MOVLW low(dis_cache)
BANKSEL led_select
ADDWF led_select, w
MOVWF FSR0L
MOVF INDF0, w
;seq_table[dis_cache + offset]
CALL seq_table
MOVWF PORTC
;led_select = (led_select + 1) mod 4
BANKSEL led_select
INCF led_select,f
MOVLW 0b11
ANDWF led_select,f
;delay
MOVLW 2
MOVWF delay2
DELAY1:
MOVLW 0xff
MOVWF delay1
DECFSZ delay1,f
GOTO $-1
DECFSZ delay2,f
GOTO DELAY1
;end seq_select
CLRF PORTC
GOTO LOOP
end reset_vec