|
EDA365欢迎您!
您需要 登录 才可以下载或查看,没有帐号?注册
x
第二节:独立按键扫描与蜂鸣器+ q1 ]6 @3 h2 Y$ K* Z
(1)学习目标:利用上一节的程序框架,把按键行列扫描方式改成独立按键扫描方式。通过这节的练习,加深熟悉吴坚鸿的程序框架,以及独立按键扫描的编程方式。
3 d1 U5 g4 v3 i4 ]9 i- r8 B (2)功能需求:每按一个按键,蜂鸣器就响一次。- V* A* j% {. v' r# E7 J3 _* C
(3)硬件原理:( A6 J- Q4 H' I- N" |6 B
(a)用4个IO来做独立按键扫描, 4个IO口都必须接上拉电阻20K左右。
4 r6 ^$ p1 W& u- M R) C (b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。
3 E+ Y6 d6 s5 W- |+ `- P( D (4)源码适合的单片机 IC18F4620,晶振为22.1184MHz
; y2 D/ s" q8 f+ w (5)源代码讲解如下:
" [8 M# M! d, M0 r #include<pic18.h> //包含芯片相关头文件
3 i: w" h% f0 h' A$ Q
$ v( U% B6 I6 U) _, F. j' f //补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr1 l' _9 T" n0 A* c# p
) K+ P9 `2 c, G# y- \ #define beep_dr LATA1 //蜂鸣器输出
0 D% d! q/ J8 O" j: ]+ N' r: Y
3 g$ Q# e7 V2 `% y- M' e( [; E* Y6 j, `- v- Y* T' O
#define key_sr1 RB6 //独立按键输入
9 }' S- c9 x. ]3 U( i, f #define key_sr2 RB7 //独立按键输入. [# |; m( W8 Q/ B: Y6 Q" L1 D, r4 D! c
. |6 S9 B) o2 t6 ] c, G4 _1 u #define key_sr3 RB3 //独立按键输入* n. }1 B7 f/ O) T
#define key_sr4 RB4 //独立按键输入
5 U# n! X: R/ A8 f
7 z. R2 ~. F$ p5 }( Q
0 L; y6 z, v! a' d //补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
, d6 k# K- D, d3 F" J5 W% I //前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值( o5 o# X* n! L* i7 G; d$ g
#define cnt_delay_cnt1 25 //按键去抖动延时阀值
+ B2 B( [) x; w4 Z" X# u4 N- z #define cnt_voice_time 60 //蜂鸣器响的声音长短的延时阀值8 }" A8 X9 C4 F$ @- }4 k
5 [3 D; E: G, ~1 m2 g# S0 M //补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
" p9 _% r) A* ? //断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
5 m- r& C3 Q" `( n9 @ void key_scan(); //按键扫描函数,放在定时中断里
B- _. ~2 ~: V2 n e& x void key_service(); //按键服务函数,放在main函数循环里
5 R1 u, G8 p' K7 ?2 [6 z3 _- p) _4 \3 ?) Q) m& ?0 C
3 g# n. E K( @% _' u( R( n
//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名# z: b( Z6 z$ r( O: ]( p. V" s
//后缀都用_step表示。5 c R1 Q9 o9 c$ _- B9 a4 W, w
6 V j* \8 g( A4 w. q, `( u2 b1 I unsigned char key_step=1; //按键扫描步骤变量,在switch()语句的括号里
6 B6 ^: R+ r5 p8 z$ I+ g$ K6 A //补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名% g8 R! {4 b: m" G3 a
//后缀都用_lock表示。
1 z# I" N m) a0 N; q6 O unsigned char key_lock1=0; //按键自锁标志
) c. b+ e3 Q5 Z" _1 F unsigned char key_lock2=0; //按键自锁标志
+ j& ~' q7 P5 `2 S6 t5 \ unsigned char key_lock3=0; //按键自锁标志
' \' _5 A: y1 v( z6 n unsigned char key_lock4=0; //按键自锁标志
0 c y/ }7 m! B4 M' s* [0 C
* e: F0 Z1 U) q3 F //补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
- ]: r. S. N6 y* F2 C1 ` //后缀都用_cnt表示。
5 x) i# g& R/ y. D unsigned int delay_cnt1=0; //延时计数器的变量
# K; |- I6 N+ y r unsigned int delay_cnt2=0; //延时计数器的变量& K% _: {5 m7 e5 E# t# g! x! x
unsigned int delay_cnt3=0; //延时计数器的变量9 ^5 n1 K9 [- Q7 E% G% J4 m' O" K Y
unsigned int delay_cnt4=0; //延时计数器的变量 ~( u% Z+ C; L6 M; H; I
unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时0 f5 ^) ]# A2 d' n
% J% v+ c% T- E/ l, q# \2 R
//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类
# t+ ^ e8 i; E+ `9 Z- j //后缀都用_sec表示。
* q4 t3 J! }- [" ]. c7 i Unsigned char key_sec=0; //哪个按键被触发
( R, q& Z! d3 `" A% g* p& O2 f6 v1 O) T
//主程序( ]% }7 m5 C6 C" p: ^ v5 ~
main()* b" G# C* X7 {2 [- t, U
{
6 ?8 ]4 }& p" ?9 [" e* e$ x ADCON0=0x00;
7 J! ?0 `0 ~8 a, Y) P ADCON1=0x0f; //全部为数字信号/ O E3 T6 |* ^! n* ]' p/ ]/ F
ADCON2=0xa1; //右对齐- l9 F& @7 M( [1 T% j" }# U+ ?' b
RBPU=0; //上拉电阻
" r# L. q% Y2 N: C SSPEN=0; //决定RA5不作为串口7 w2 a, W- V* A
) p7 u3 c4 z9 ^' [+ k7 q& O; _
TRISB3=1; //配置独立按键IO为输入
4 k8 X- _7 F2 D TRISB4=1; //配置独立按键IO为输入( C; Y, m; p5 J
TRISB6=1; //配置独立按键IO为输入
6 H. e7 R2 {9 D2 ]2 X* Y TRISB7=1; //配置独立按键IO为输入
) z1 Q+ p' S4 c. f) h! A
( M$ z' P/ N- z. X: L' Z
9 W, k6 y7 c# u1 `" W: d: D T1CON=0x24; //定时器中断配置; H( _3 J. Z! u/ n6 x, l4 [& y
TMR1H=0xF5; p7 H; y; s6 V6 e
TMR1L=0x5F;5 y$ k# ?0 k* O
TMR1IF=0;4 Y' q, q! Z7 w) u) T* q4 M
TMR1IE=1;, W) s7 \. o) p% s5 B* r2 C
TMR1ON=1;
( u2 t. P; z/ p( s# ~ TMR1IE=1;
3 y. t: @+ X8 O //补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
3 A) s* x& d6 { m$ a" ~2 Q //大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可
$ i) g$ D( P: e
. E- F6 e9 d7 S beep_dr=0; //关蜂鸣器,上电初始化IO
' Z9 ~& p5 M+ F1 ]/ [7 W( K
n( V% N% U J while(1)
1 H. T" q* P9 K3 W* g$ x1 Q {; K* x6 M; h. o* _* j5 n
CLRWDT(); //喂看门狗,大家不用过度关注此行7 h2 k% y& L, S2 Z4 Z
key_service(); //按键服务
, [+ L# V" Y- U* U }
5 V" |( ]/ \ [' E9 N$ |& |/ m6 S& i( i, o9 L
}
/ V( k* e; h/ h & v# X$ R3 }# f
4 ^: i. J3 D6 Q8 D. c" ]& U$ Y
void key_scan() //按键扫描函数
; X) S$ P6 g, u0 c) K/ \ {
# U1 Z q! ~# V0 Y% I //补充说明:因为独立按键扫描没有了行列按键扫描的delay1(40)延时,所以速度很6 Y, y* w3 c; \' C" p9 `
//快,可以一次中断扫描完所有的按键,不用switch(key_step)语句来分段
1 ` W8 E% x. n" V# | if(key_sr1==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位- {: c) K$ O! \& p4 `3 a
{' U% \; ~$ @5 e# N
key_lock1=0; //按键自锁标志清零. Z: n1 O. N/ u$ u
delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙 # s) P# k: D2 m
}6 h7 ^( M% Z+ l( p( I
else if(key_lock1==0) //有按键按下,且是第一次被按下
2 T; d% M" o8 A3 v4 S5 } {+ n& D6 s( O( X- |
++delay_cnt1; //延时计数器 d( ]/ I7 `5 F! f8 J
if(delay_cnt1>cnt_delay_cnt1)* N1 b2 l/ V7 w+ v
{
- ~3 H8 D: T. m: d9 Y delay_cnt1=0;. q5 \; ]& X3 r2 V, T
key_lock1=1; //自锁按键置位,避免一直触发
$ Z8 c$ U$ u: |( _6 P7 V+ z key_sec=1; //触发1号键
2 \1 R$ [* C: G2 a2 i6 W5 Z. r }
5 i; V7 j. ?9 r2 J }$ k5 s1 ]. E5 [8 z
( F, T" @" b, n- u# P6 R4 K if(key_sr2==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位/ X |% C4 h' O4 N
{0 |: m9 a$ v- P U0 A$ E! O" f3 r8 ]
key_lock2=0; //按键自锁标志清零
; ~8 y: j, G. ], b8 a5 m5 Y delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙 7 H+ r% I9 S' N" K) Y7 G" U3 B
}2 E( ^$ w. o. B5 W& P( }1 p
else if(key_lock2==0) //有按键按下,且是第一次被按下% S6 A5 h! v) R
{, ^1 p2 a' J. b/ u
++delay_cnt2; //延时计数器
4 b$ k" n2 h$ ] if(delay_cnt2>cnt_delay_cnt1)' S" h! q+ G: E+ L& u
{
' _( f) ~1 d8 \* m: R- e delay_cnt2=0;
* H- Z+ R4 T3 j( j9 N' \ key_lock2=1; //自锁按键置位,避免一直触发
0 b0 ?3 n9 r0 g/ ?0 E2 Q9 m$ {* K key_sec=2; //触发2号键
/ _5 D8 z' c6 S7 V) S }
6 V- G' M' @* { r$ q, t }
+ }( ~; M4 r) |+ f" _0 v6 ^
/ c! b) t6 g3 l' v
. T1 D/ W5 ^- W& l if(key_sr3==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位% K* Q% v5 y- x3 c8 M5 s- W0 f
{2 c0 O) t+ o* `2 y; w+ a( d* i
key_lock3=0; //按键自锁标志清零
; q1 j; Z+ o7 ~, j0 d/ b delay_cnt3=0; //按键去抖动延时计数器清零,此行非常巧妙 4 u: ~" O, i% F0 x F- @+ ?
}
. I: ?! O s$ ]8 m: l6 [, [ else if(key_lock3==0) //有按键按下,且是第一次被按下
7 o% D+ I8 g& G {
r! D8 C. a$ ?; z* i5 { ++delay_cnt3; //延时计数器. |( Z5 C2 C5 {
if(delay_cnt3>cnt_delay_cnt1)1 v5 o/ o; v$ a
{/ x% Q: e4 w' d* U& Z( Y
delay_cnt3=0;" ?) ^: v1 ~9 y( N0 ^8 R @6 e! ?
key_lock3=1; //自锁按键置位,避免一直触发
5 s8 k. b# J L2 O9 l key_sec=3; //触发3号键( z% ? f4 W/ d9 V
}& G+ X7 ]1 J; @# [0 v6 p
}% |7 _3 S/ D# m# `; W
/ \' `5 i* u' n% U- ~ x2 V
) |: Y, m( Q0 g8 w if(key_sr4==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
6 k5 y( j, k9 P: D0 q5 l" Y {$ R4 v- ^7 s+ u" O5 t0 G9 f% p
key_lock4=0; //按键自锁标志清零
7 S+ z6 G; W4 G3 t& g" L( { delay_cnt4=0; //按键去抖动延时计数器清零,此行非常巧妙
9 z( G$ V* d5 R; ?) l0 } }: D8 H/ `5 G4 K' ^# ?8 H
else if(key_lock4==0) //有按键按下,且是第一次被按下; ]$ L6 P+ a7 W: H6 I s) L! e
{
3 S6 T, B% W+ y0 S; I$ m: K ++delay_cnt4; //延时计数器
% N/ h+ j# J/ T5 n, t, F if(delay_cnt4>cnt_delay_cnt1)" D- d$ w( X X& }7 f
{
3 m7 p% V1 T% }& e% L delay_cnt4=0;# j( z/ N1 ?# \$ @0 k R( ]$ o) Z2 B
key_lock4=1; //自锁按键置位,避免一直触发8 `! @0 U- L8 P& m& o+ n
key_sec=4; //触发4号键: J! |4 a5 Q9 T* N* S+ P
}3 o5 ?# H* F# s. N& x
}9 j+ T. h' X- ~. A! i1 N) B
3 }) _% c& u- x9 ?
}
- [( G$ I% e1 }# w1 M; ^# U
* u* M9 I: m9 ~ void key_service() //按键服务函数* F* e# D$ b/ V$ E" u+ S
{+ ~. z9 m. V: v; y6 }2 J
switch(key_sec) //按键服务状态切换4 J6 W+ O. R. H; [ Q r
{
8 c5 Y* V. O6 A; G9 z* \( { case 1:// 1号键- J; t; z$ [2 _ [
2 m2 F, l' {$ M, |+ h! g! r$ p0 H4 t2 ~. \! O" n9 s# Z" \
// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0
1 G. j9 Y. I+ x1 X //时,会不断自减,一直到它为0时,自动把蜂鸣器关闭 D" ]+ c" ]0 l2 h
voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停* D, l6 k( l$ ?4 n9 ?
3 V/ g9 X6 y9 ^5 z& _8 e2 }: L; D3 C
key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,. \" E- m) W- d, M1 P( ]% R; F
//避免一直触发7 f1 n+ A" A+ d2 X
break; & Y3 `9 U* @3 t" d/ f# a
case 2:// 2号键
! Z. L* k) @$ S- u7 H5 o; l voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停
& B1 i T3 h6 e5 \+ f1 f+ b key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,- \! i! t& f. b1 @1 p/ k
//避免一直触发! _) d" F/ W, L! s
break; " U4 ]7 E7 a; [" L" t, F. p/ O
case 3://3号键
0 h+ Q6 C6 m8 S8 s; U
7 ]. m e( V4 V. a h. O voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停
) x) [4 P3 e+ L$ D4 ~- [: H key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,1 B7 c, K: E% [* L$ N
//避免一直触发4 V4 x8 a) c3 g# E; [" k3 R
break;
1 v. h/ F2 L3 D" n; Q case 4://4号键* [* w: P5 o; h; Y% X
: I' |1 \, u( \- k0 ? voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停
, l( F9 C1 o5 j, K0 a: D1 F key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,, f9 p' Y2 i f! t: |
//避免一直触发: v. Z9 b+ C" C' D! h
break; 4 Z0 ?! n% }9 }. h7 U- s
h4 y) L. O/ F- l5 d6 q6 y* q3 e" E2 }
; |$ | s/ p! _3 `2 U+ l$ b7 B- y
% B9 b& q& i( o+ k: C/ m+ Z" Q- \
9 o* \" b' J; X } 9 u* t0 A. i* H3 [
}
G6 p2 v- B% p, f" i. |) N8 R/ a, v6 ?1 }5 s8 N" x L1 R/ o, f: D
) K* m4 v$ H- \( g' T. ~/ ~1 @2 ?
) i$ B2 b O' Q. Z& l, v7 L) T/ ?4 i, U @
//中断$ T3 q% W: d) `9 t5 w3 \5 G
void interrupt timer1rbint(void)
, f; `" N c$ B. j& B {* k6 I$ B% c& |/ ]
if(TMR1IE==1&&TMR1IF==1) //定时中断
2 p% G) r5 Q5 v% n, ~6 u; g$ ~ {
1 K+ N' @5 Q2 s) `& q 2 g+ X" B2 u& S& M; c9 B8 q4 f
TMR1IF=0; //定时中断标志位关闭
3 E/ X! e' \5 P TMR1ON=0; //定时中断开关关闭
( Z) O5 X, n/ t) ^' s
' u- A+ H& o6 l! }& [ key_scan(); //按键扫描函数. I5 @. `' ~$ e4 C2 x6 q
if(voice_time_cnt) //控制蜂鸣器声音的长短8 u; {7 j6 b: I! \0 s
{
4 q& M" |- W; f2 \: A0 h8 m beep_dr=1; //蜂鸣器响& _, G* J& m, ~" r; R1 K
--voice_time_cnt; //蜂鸣器响的声音长短的计数延时& a q/ l! C/ c* v$ R2 r4 N
}
4 q# Y9 u, B4 r0 S2 e1 P else
( l4 v5 L; H2 Q {8 `) S4 f$ |# `: r2 V
beep_dr=0; //蜂鸣器停止
) D% V0 R+ b" N$ u9 W }
; l' \' Z3 f/ @% `) h TMR1H=0xF5; //重新设置定时时间间隔' L( s: O( I2 {/ O" i
TMR1L=0x5F;1 l [$ ~2 f3 B4 Z& V% z! w
TMR1ON=1; //定时中断开关打开
L& `# ^7 a7 Y1 N O }
) I5 I" ~' ^% e0 m }
/ x& ~ p( x, x8 R* }
5 Y4 J, }9 B2 Z' g* C8 r7 D: r' }9 i. x4 } \7 t- _/ f
(6)小结:' { k N- V0 F2 x& y0 u
利用上一节的程序框架,把行列扫描改成独立按键扫描的速度非常快,体现了吴坚鸿程序框架的优越性。通过此节的学习,让大家加深熟悉吴坚鸿程序框架,同时也掌握了独立按键的编程方式。(未完待续下一节) |
|