找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

巢课
电巢直播8月计划
查看: 1|回复: 0
打印 上一主题 下一主题

[硬件] 吴坚鸿单片机程序风格赏析——(二)独立按键扫描与蜂鸣器

[复制链接]

551

主题

1470

帖子

3万

积分

EDA365管理团队

Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

积分
39487
跳转到指定楼层
1#
发表于 2019-9-27 15:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您!

您需要 登录 才可以下载或查看,没有帐号?注册

x
第二节:独立按键扫描与蜂鸣器" P, K, b' H" S: \3 ^) q+ f
(1)学习目标:利用上一节的程序框架,把按键行列扫描方式改成独立按键扫描方式。通过这节的练习,加深熟悉吴坚鸿的程序框架,以及独立按键扫描的编程方式。
- }" K: v. d7 T; C4 j0 o6 G (2)功能需求:每按一个按键,蜂鸣器就响一次。
8 t) A" B* }/ b (3)硬件原理:5 R: L& I) f9 l
(a)用4个IO来做独立按键扫描, 4个IO口都必须接上拉电阻20K左右。
7 G: L8 `4 D9 K1 D3 L9 |# f$ M& w (b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。2 h. D  u5 P9 g$ w2 _) v! \+ K" v, r
(4)源码适合的单片机IC18F4620,晶振为22.1184MHz
" K5 n! f* k: w (5)源代码讲解如下:- Q5 X9 F! d5 B" M) e) K: O. C& M6 G
#include<pic18.h>         //包含芯片相关头文件
% X5 x& w# o9 l3 M  I* r
4 [& w3 p2 N/ f$ ?- m- K$ C //补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr% H' x$ e7 i  P& g" o

* N+ f" q" D% Y: I# X9 I. V #define  beep_dr  LATA1  //蜂鸣器输出
# c# }9 w* J: P- g  J4 N$ H+ W2 b8 ?% M" F7 s6 R/ Q8 f4 s' f* a
7 {% L0 l2 K: o3 B2 m; G8 E/ ^
#define key_sr1 RB6    //独立按键输入
; [3 y# O( M" `! M0 k) L0 i #define key_sr2 RB7   //独立按键输入% i* y6 h- M# k! X) Q! `0 W1 h
$ X( j" y& k- G4 V
#define key_sr3 RB3  //独立按键输入0 U" p% M) E  W
#define key_sr4 RB4 //独立按键输入
) H( N3 Z5 m1 b3 u7 m) L: Y) j6 Z8 _' A; i* \

( e- G. w: f5 ?) U6 _ //补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
6 ~. M4 T+ d/ f7 u8 D" u9 f. g //前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值
9 F$ f* B( j7 n! z$ w% o$ q #define cnt_delay_cnt1   25   //按键去抖动延时阀值$ W1 W: W2 O' L
#define cnt_voice_time   60  //蜂鸣器响的声音长短的延时阀值9 {0 p% R/ d' f' _+ y9 u

% r: S0 q  O+ y //补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
3 C( j* {# |( \3 Z //断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。' g+ Q6 g/ M1 V: p  r9 A
void key_scan();                         //按键扫描函数,放在定时中断里$ ]5 P0 W) d! X
void key_service();                            //按键服务函数,放在main函数循环里! f( \- C( _! N/ @; `- u+ A

& N7 K% l% B- d' h: W
$ Y! i# @$ M  T5 [1 w8 E //补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
- s0 p1 C  I- ?1 k0 K# z$ t //后缀都用_step表示。) e( F3 F) M  q' d, I; E

2 \9 M) R) E; I unsigned char key_step=1;      //按键扫描步骤变量,在switch()语句的括号里
5 ]* D6 {- [; G- l7 {1 I/ U //补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
/ L9 E2 Y" Q/ L! g" m //后缀都用_lock表示。5 M) m4 E' {! }: B  n5 C9 z7 A# ?
unsigned char key_lock1=0;   //按键自锁标志
. T$ t8 u. y$ I' Y8 a unsigned char key_lock2=0;   //按键自锁标志
! s0 ^: J) H' `- s+ z6 A unsigned char key_lock3=0;   //按键自锁标志
0 U! `# g7 k' M5 @& P& L unsigned char key_lock4=0;   //按键自锁标志
1 R# i4 x$ r, C; o6 k7 H$ ?7 x6 s5 K4 q/ I* I1 S* A1 y" Y- a: g% u
//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
! A1 f6 g0 Q3 C* C //后缀都用_cnt表示。
; Z  }8 U0 b! G6 z/ P unsigned int  delay_cnt1=0;     //延时计数器的变量4 |0 g/ D8 [" F) q  U2 D+ s  {
unsigned int  delay_cnt2=0;     //延时计数器的变量& \* ^. _2 r' [: H
unsigned int  delay_cnt3=0;     //延时计数器的变量
! E# ]* J6 g+ h5 J8 l/ U unsigned int  delay_cnt4=0;     //延时计数器的变量# I9 X  n2 ?5 j
unsigned int voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
& q( y2 `: R8 {! a# d8 t- _; l( w# g( U: J" w, X
//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类0 E- I) Z# f! S$ O! n* P- E
//后缀都用_sec表示。8 e3 b! a* L3 Q8 n% f
Unsigned char key_sec=0;  //哪个按键被触发7 l/ ~. p* T  z! n* i! g
0 [0 T# J' k" d2 o3 @5 d
//主程序
' j1 L. t8 [3 }) U. l! T" Y main()
/ o( w- F. ?! Q6 D5 K; Q% n, Z! u {
; h+ X3 o8 G/ n( G2 A ADCON0=0x00;  
/ P% O5 H# l% g" f( d ADCON1=0x0f;                               //全部为数字信号: b3 Z6 o) m; O9 \4 j. c
     ADCON2=0xa1;                               //右对齐8 \' G. `( `/ {
     RBPU=0;                                      //上拉电阻
0 {! I% X1 e8 o" @# S0 P     SSPEN=0;                                    //决定RA5不作为串口
  m+ \# C* \  R- @/ l1 Q
: {' d) f* J/ O3 P0 U     TRISB3=1;            //配置独立按键IO为输入, K5 Y1 v  \, j+ ^' ]% V" v$ w: r( K2 r
     TRISB4=1;           //配置独立按键IO为输入
9 h+ E( Y3 A9 z& f' k- i     TRISB6=1;           //配置独立按键IO为输入7 _2 F7 g* p% o2 G7 |0 w; K+ n  @
     TRISB7=1;           //配置独立按键IO为输入- X" \" b, r* ^8 V8 ?# t
/ j1 |4 A$ e+ N

3 p2 ~* S4 g$ E$ K9 r     T1CON=0x24;     //定时器中断配置: O2 H, a7 l! b
     TMR1H=0xF5;
$ ?# `) C* y6 P% i     TMR1L=0x5F;2 }7 x0 d2 m  w, T
     TMR1IF=0;4 c1 n! L  s& ?) {& E
     TMR1IE=1;3 O, [7 n6 f' F1 X5 g* z7 {
     TMR1ON=1;2 B/ a# [  L1 l5 T2 D* M* X
     TMR1IE=1;) {- o6 K) V5 ]! Y7 O" M
//补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
/ [6 w9 F% ]9 ]% x& q7 p //大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可4 F, q# v; u6 Q/ ~9 Q! h$ v

3 V# n; q) Y7 H) B7 @+ X     beep_dr=0;                               //关蜂鸣器,上电初始化IO
% M8 O! n4 ~$ @2 X0 j. x# a. K( }, A3 @1 Q
    while(1)    ! T5 N3 c; @. }  ]- B, A  X
    {% ^& }! v, ?9 P  J: D8 [' Y8 `
                      CLRWDT(); //喂看门狗,大家不用过度关注此行+ U4 m/ `  P% S' q
                 key_service();        //按键服务
& a. j9 L1 \5 C( _- I }; M: P& D" o: s6 p8 I
. E5 S" U2 C+ Y8 u: q' q, `& X
}
% c; ~; @$ {& r1 p# Z  `( n        
& k+ ^( D8 F+ i0 b- L9 b
3 ~7 V# S+ E3 U+ @. b0 X. J4 j void key_scan()                                //按键扫描函数( p) N8 `) V. X+ }% {  @4 ?- U6 a" Z
{  
6 c  s; a2 _0 b% s7 u" ^' R3 U# X     //补充说明:因为独立按键扫描没有了行列按键扫描的delay1(40)延时,所以速度很# b+ _) a# M, ]' Q5 h/ C. v8 o
//快,可以一次中断扫描完所有的按键,不用switch(key_step)语句来分段
9 Y5 z) H7 a# @   if(key_sr1==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位0 K7 p& z* q( t; R9 f7 R
   {
- B$ M- h' Z8 _5 b) E+ ?* O key_lock1=0;  //按键自锁标志清零
% \% M/ P: F3 o+ b+ C delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        
$ T1 |$ s+ T7 \, A }+ b+ P. ^! t2 @- d9 y5 c3 Q& I( a& H
else if(key_lock1==0)  //有按键按下,且是第一次被按下. o; {- U3 m6 J% b4 L; {9 U
{- w2 X5 @& W# ^, i( w
++delay_cnt1;  //延时计数器7 f7 q: _+ P8 C1 n* z) U) v* r0 i
if(delay_cnt1>cnt_delay_cnt1)  q: G0 S9 V# m  l" ~- G, P" m$ ?
{
: y$ Z* S2 x* M1 |. E" j7 S delay_cnt1=0;* e5 k5 l7 x1 S3 U3 Q( M6 v
key_lock1=1;  //自锁按键置位,避免一直触发! S9 Q) [) `7 {& q( h
                   key_sec=1;         //触发1号键- M( a( \& r6 ^2 w% F
}! D0 `" a# M( E# M3 L
}
7 P" b, y) [$ W+ F/ E6 Q- y  l& ~1 x' k  i# V. }% H
   if(key_sr2==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位3 P7 i4 S) f0 v( `7 E
   {
! B  `+ S0 w7 g9 ~! e5 I3 K1 L4 Z key_lock2=0;  //按键自锁标志清零
0 Z2 v" C: ?- | delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙        7 X* Y  k  U4 Q2 \9 W3 t
}) k! e; k# d- F
else if(key_lock2==0)  //有按键按下,且是第一次被按下) n! y% ^0 [* N
{' g4 }; w3 V; O7 _, o" ]
++delay_cnt2;  //延时计数器
( t# l3 H; F1 D' B if(delay_cnt2>cnt_delay_cnt1)* K" Y9 O: }9 A; |9 Y
{
+ |# n. W+ b# V* P" \# n delay_cnt2=0;
& q3 w, ^4 e* r( l  k key_lock2=1;  //自锁按键置位,避免一直触发" R. W0 y# B* Z* v9 _4 D2 G6 K
                   key_sec=2;         //触发2号键* k( f6 v! C0 v  x7 T3 F
}5 r8 G, ~, w& c" a% }+ S
}
# j7 W6 E; i) H
- v8 R8 W' b% A% U) G8 F/ p# [
0 F/ W7 C' X7 w2 Q   if(key_sr3==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
5 ~& X% J& v! A- S( l% Q( Q4 A% p   {
" @, L9 ?! h1 C6 x  C key_lock3=0;  //按键自锁标志清零
. f7 P; ?$ O% w- D delay_cnt3=0; //按键去抖动延时计数器清零,此行非常巧妙        ) ^0 ?: `& _2 d% Q: W! `6 R
}/ h5 Q" n! ?2 A; a" a. T7 P' D
else if(key_lock3==0)  //有按键按下,且是第一次被按下7 `+ U  x6 }* Y& J" B2 ^- \
{
4 t0 ?5 l* M2 ^( l+ S$ F; @ ++delay_cnt3;  //延时计数器6 e3 ^6 K: R/ {5 C2 E7 }- a
if(delay_cnt3>cnt_delay_cnt1)& m( N: I% e; W/ ?
{
+ F" ?) w3 X6 c+ N delay_cnt3=0;- V* I7 u4 G, X
key_lock3=1;  //自锁按键置位,避免一直触发0 P; j0 [3 I; o; Y) k
                   key_sec=3;         //触发3号键8 m" E4 `4 l6 ^% E2 T$ S2 V7 n
}# B) I& @9 l$ S
}
# \& X0 B) ~. ~' x4 m, m" p5 G  }# m  s5 G: C+ Q7 k" }
: M2 y1 s# |1 {' |4 Y/ \  |
   if(key_sr4==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
( y" A; Z  s5 Q: O# a: o) r# I7 \4 l   {
8 L2 [: P! X: p9 _8 M3 e2 s key_lock4=0;  //按键自锁标志清零
+ I, U2 P$ Q# G; T1 B delay_cnt4=0; //按键去抖动延时计数器清零,此行非常巧妙        " n# N) q+ Y' E
}
) u: _$ {  m1 O' |* R  h5 a else if(key_lock4==0)  //有按键按下,且是第一次被按下- W- x+ y" b' P, T& I! h4 C' k
{# l, C+ m# Q6 ~2 f
++delay_cnt4;  //延时计数器
3 w4 e$ N  n5 l$ H  Q9 D if(delay_cnt4>cnt_delay_cnt1)
& c+ N, M% e# ~  i. i4 X {
2 g$ c3 P% f( _9 o+ J delay_cnt4=0;$ Z# f; }6 r: y; T8 S; M
key_lock4=1;  //自锁按键置位,避免一直触发- l+ }0 v8 {6 q2 b# L/ \( ]3 C# k
                   key_sec=4;         //触发4号键" M+ G4 j+ p1 r) O+ [
}
( G+ ^; X' T+ }. h) H: f) d }6 u* U" i0 A$ o- Y; ]

$ {6 L0 y1 W2 I) S6 A }7 ~) X3 a" T, ]2 L2 T

; W$ P0 _9 S1 O( s0 w3 F- B void key_service()                //按键服务函数
7 e1 [: B' q4 d' ~! q- B" ? {& N6 m1 a- J- ]2 e1 f
         switch(key_sec)                //按键服务状态切换
+ d1 y/ \" p" w( J% a         {) j. X4 m( a/ J" z2 G
                 case 1:// 1号键
' k, l/ Y/ O# B" v) x; h3 R
" y1 f# W2 _  d$ L6 v" y, f% N4 r' k. ~5 k+ S
// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0
- p  d* g; }1 U //时,会不断自减,一直到它为0时,自动把蜂鸣器关闭
* h/ p, O/ d+ O5 E+ g                                   voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
8 R: j+ H+ e0 F; P. I                                                                                           $ N* b  `8 r9 |; A5 J$ B
                         key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,6 G' {" P- S6 w. c3 D% C
//避免一直触发
6 w  |) Y7 ^! o& m5 l" K% i; i                         break;        / `& \: G8 P8 n& l: m
                 case 2:// 2号键
' Z% i1 N2 K7 x1 ~' s. P! v                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
3 c" j4 V& X0 p5 j' H9 P                         key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,/ M# b% ^. F! C$ H8 W. |) Z( {
//避免一直触发
2 e4 s; t, W2 y: l& a- B                         break;        
) c& g# l. @6 k* L                 case 3://3号键
+ y( m. m" D4 ?% m
/ f# g, j. B: \* [: U( b4 W1 [                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停' ^- B5 F3 P' [8 `  N$ r8 B% h" Z% M
                         key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
% D: _" _1 d4 Q9 ^* y, l //避免一直触发
8 _/ S8 P2 x2 f& M                         break;        
8 }7 G6 B6 Z, u& Y, _* ?5 N/ `                 case 4://4号键
6 N7 p: D+ @' b; f8 `- j
  \& d+ g8 ^  H% n) e                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
- t/ B. Y  }* R0 H, r: y9 W! p/ f                         key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
- P" N  M7 m2 G) z+ |9 I1 `3 @; V //避免一直触发
' N, Q+ @$ m1 }8 L1 W. R) }1 x                         break;        
. G8 j4 C; C, t: T# ?* W/ G( N' e5 u& Z- Z2 z& _- @
% A8 N. ]+ n( H& E1 T+ @+ Q

! Q% O8 B! \1 x9 Y, i& ^, c                        
' S( x7 e1 y5 ]  ?2 z! v0 c( [         }               
% b7 j2 H- @0 `" d3 O0 i* Q  p }
! G/ D: Y, V' e% h5 z9 d8 h7 r+ b3 y: f
8 N$ X. }+ K- ^
* O$ V/ |( A8 y2 \

8 m- o! J4 F+ l. I* r1 l) X //中断* S" f2 s2 _* |7 W% O' \
void interrupt timer1rbint(void)
) d( f' M$ B8 T; _, n {: w: w+ {6 U% S( G% ^
     if(TMR1IE==1&&TMR1IF==1)    //定时中断
) F8 |0 n  ?: s" k# o: C' g         {6 ~& J0 @( g9 r2 b- }8 P- A# E& s
         
% w7 m* g+ y7 A0 A' X                TMR1IF=0;     //定时中断标志位关闭
; k- [' N. Z1 @' X                 TMR1ON=0;    //定时中断开关关闭
6 ?. y: ^# H! ?6 A+ E$ c/ J; L
                  key_scan();                    //按键扫描函数
) W8 A0 q) n- z# u) O+ {9 g5 Y       if(voice_time_cnt)                       //控制蜂鸣器声音的长短! l' P: w0 @9 r7 r
                  {: {4 `1 e# D% K& n+ R
                         beep_dr=1;         //蜂鸣器响
# M/ z9 Y  R! _* P) [( w! I7 \2 w. U                       --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
4 _; e' _. P6 }                  }
& d: L8 P! \% D+ U' ?8 B, o# y# |                 else
  ~9 U! V4 I. p' X5 }! B                 {* ?8 @: t) {6 o5 M9 ~1 i5 [3 g
beep_dr=0;      //蜂鸣器停止7 _/ m5 B* H. m0 y! k
                 }
: e/ o2 ^- B  d& S0 z       TMR1H=0xF5;     //重新设置定时时间间隔
' p/ p2 g; \5 M! f       TMR1L=0x5F;* s1 _0 b! r$ l! w4 {9 V! D3 _
       TMR1ON=1;        //定时中断开关打开- T3 y3 l$ k2 L/ f
     }  [& S* q* \# g4 |
}* V' X% o2 P- f& q: b" W% w: I
# a0 t4 d+ M2 i
: z# H; O1 P2 I. X: F# V- c" o
(6)小结:
2 @6 K; l' w7 M. C- r" p' a4 k 利用上一节的程序框架,把行列扫描改成独立按键扫描的速度非常快,体现了吴坚鸿程序框架的优越性。通过此节的学习,让大家加深熟悉吴坚鸿程序框架,同时也掌握了独立按键的编程方式。(未完待续下一节)
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 支持!支持! 反对!反对!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

巢课

技术风云榜

关于我们|手机版|EDA365 ( 粤ICP备18020198号 )

GMT+8, 2025-4-5 07:35 , Processed in 0.055572 second(s), 32 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表