|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
通过上节课的实验,大家会发现,我们逐渐进入比较实质性的学习了,需要记住的内容也更多了,个别地方可能会感觉吃力。但是大家不要担心,要有信心。这个跟小孩学走路一样,刚开始走路不太稳,没关系,多走几步多练练。看视频的时候要注意专心,一遍看不懂,思考一下,再回头看第二遍和第三遍,没准一下就明白了。如果三遍还看不明白,那就把不懂的问题放一放,继续往下学两课再回头看一次,多咨询一下其他的同学,讨论一下,可能就会茅塞顿开。 1.1 基本数字逻辑门电路8 h8 L+ V( K1 g4 q' l( J# j
不管是数字电路,还是C语言,我们都会经常遇到逻辑运算和逻辑电路,在这里我介绍一下,大家先简单了解一下,知道有这么回事,回头遇到了,再详细研究。; f+ B7 O8 w$ K: S7 v: y
首先,在“逻辑”这个概念范畴内,存在真和假这两个逻辑值,而将其对应到数字电路或C语言中,就变成了“非0值”和“0值”这两个值,即逻辑上的“假”就是数字电路或C语言中的“0”这个值,而逻辑“真”就是其它一切“非0值”。- F$ f' {4 z8 I, ^ `; ~
然后,我们来具体分析一下几个主要的逻辑运算符。我们假定有2个字节变量:A和B,二者进行某种逻辑运算后的结果为F。
: c( W j# D, M+ v9 a* y, L5 @以下逻辑运算符都是按照变量整体值进行运算的,通常就叫做逻辑运算符:7 Q2 ~. E0 N2 \; @6 n
&&:逻辑与,F = A && B,当A、B的值都为真(即非0值,下同)时,其运算结果F为真(具体数值为1,下同);当A、B值任意一个为假(即0,下同)时,结果F为假(具体数值为0,下同)。( w8 i5 x7 ^- G2 C" p
||:逻辑或,F = A || B,当A、B值任意一个为真时,其运算结果F为真;当A、B值都为假时,结果F为假。. [0 z' T( i+ p7 Z9 o g9 ^
! :逻辑非,F = !A,当A值为假时,其运算结果F为真;当A值为真时,结果F为假。
' Y3 m7 I9 D$ k$ s以下逻辑运算符都是按照变量内的每一个位来进行运算的,通常就叫做位运算符:4 E, G2 J9 h- G
& :按位与,F = A & B,将A、B两个字节中的每一位都进行与运算,再将得到的每一位结果组合为总结果F,例如A = 0b11001100,B = 0b11110000,则结果F就等于0b11000000。
. N" j n* ?! v1 U0 L| :按位或,F = A | B,将A、B两个字节中的每一位都进行或运算,再将得到的每一位结果组合为总结果F,例如A = 0b11001100,B = 0b11110000,则结果F就等于0b11111100。6 w% o h9 \! s1 \
~ :按位取反,F = ~A,将A字节内的每一位进行非运算(就是取反),再将得到的每一位结果组合为总结果F,例如,A = 0b11001100,则结果F就等于0b00110011;这个运算符我们在前面的流水灯实验里已经用过了,现在再回头看一眼,是不是清楚多了。
! r4 Q Z9 d8 I1 p^ :按位异或,异或的意思是,如果运算双方的值不同(即相异)则结果为真,双方值相同则结果为假。在C语言里没有按变量整体值进行的异或运算,所以我们仅以按位异或为例,F = A ^ B,A = 0b11001100,B = 0b11110000,则结果F就等于0b00111100。' a! I% e" }: P; ` t
我们今后要看资料或芯片手册的时候,会经常遇到一些电路符号,表5-1就是数字电路中的常用符号,知道这些符号有利于我们理解器件的逻辑结构,尤其重点认识以下表中的“国外流行图形符号”。在这里我们先简单看一下,后边遇到了知道到这里查阅就可以了。
% F+ k7 h: q" n4 Y0 t! o" O: t! r表5-1 数字逻辑门电路* R2 q# K% J- W0 q
![]()
# ] d# u3 \) c: I3 a4 V% b' l1.2 定时器的学习3 n$ `' I8 d% x5 @1 i
定时器是单片机的重点中的重点,但不是难点,大家一定要完全理解并且熟练掌握定时器的应用。
" v' n$ E" Q1 s, {6 |: d1.2.1 定时器的初步认识
( |, Y8 D! `, I* ], p* P时钟周期:时钟周期T是时序中最小的时间单位具体计算的方法就是1/时钟源,我们KST-51单片机开发板上用的晶振是11.0592M,那么对于我们这个单片机系统来说,时钟周期=1/11059200秒。
, z5 p- [# A+ j机器周期:我们的单片机完成一个操作的最短时间。机器周期主要针对汇编语言而言,在汇编语言下程序的每一条语句执行所使用的时间都是机器周期的整数倍,而且语句占用的时间是可以计算出来的,而C语言一条语句的时间是不可计算的。51单片机系列,在其标准架构下一个机器周期是12个时钟周期,也就是12/11059200秒。现在有不少增强型的51单片机,其速度都比较块,有的1个机器周期等于4个时钟周期,有的1个机器周期就等于1个时钟周期,也就是说大体上其速度可以达到标准51架构的3倍或12倍。因为我们是讲标准的51单片机,所以我们后边的课程如果遇到这个概念,全部是指12个时钟周期。$ k1 l( E6 I; e+ b3 O: q
这两个概念了解即可,下边就来我们的重头戏,定时器和计数器。定时器和计数器是单片机内部的同一个模块,通过配置SFR(特殊功能寄存器)可以实现两种不同的功能,我们大多数情况下是使用定时器功能,因此我们的课程也是主要来讲定时器功能,计数器功能大家自己了解下即可。
9 s3 C" K9 ]4 V$ I1 C6 P$ |; `顾名思义,定时器就是用来进行定时的。定时器内部有一个寄存器,我们让它开始计数后,这个寄存器的值每经过一个机器周期就会加1一次,因此,我们可以把机器周期理解为定时器的计数周期。我们的秒表,每经过一秒,数字加1,而这个定时器就是每过一个机器周期的时间,也就是12/11059200秒,数字加1。还有一个特别注意的地方,就是秒表是加到60后,秒就自动变成0了,这种情况在单片机和计算机里我们称之为溢出。那定时器加到多少才会溢出呢?定时器有几种模式,假如是16位的定时器,也就是2个字节,最大值就是65535,那么加到65535后,再加1就算溢出,如果有其他位数的话,道理是一样的,对于51单片机来说,溢出后,这个值会直接变成0。从某一个初值,经过计算确定的时间后溢出,这个过程就是其定时的含义。1 F# E# z, V) h, A5 i1 J( T2 Y' d
1.2.2 定时器的寄存器描述
/ u% F; j( N0 i标准的51里边只有定时器0和定时器1这两个定时器,现在很多单片机也有多个定时器的,在这里我们先讲定时器0和1。那么我前边提到过,对于单片机的每一个功能模块,都是由他的SFR,也就是特殊功能寄存器来控制。而和定时器有关的特殊功能寄存器,有以下几个,大家不需要去记忆这些寄存器的名字和作用,你只要大概知道就行,用的时候,随时可以查手册,找到每个寄存器的名字和每个寄存器所起到的作用。, ]1 O7 x$ f/ b7 t) h8 t
表5-2 定时值存储寄存器
: ^6 j+ n0 z) k, v5 H名称0 R4 b1 M' z, K7 r: L3 f
| 描述
9 i1 V5 H) H8 _/ |- f. N# m/ T | SFR地址
- R% t+ j1 p3 [% o | TH0
/ k5 g, ~. }5 ^8 l | 定时器0高字节4 w* C/ I% H: g0 X0 j$ X+ D
| 8CH+ m1 ]6 i; M+ n
| TL0
3 S8 b, ]" m" ~' b! x- f8 z7 y, U | 定时器0低字节
5 y0 r! H4 M# X* u | 8AH
, i+ i/ j, n N6 L# I | TH1
) R9 h4 z$ y4 m' t1 }8 [2 n | 定时器1高字节; y9 J' F) P% _4 E( ]
| 8DH
% Z; [- c4 _/ [: J$ r& I | TL1- F9 y* g! J2 b- i0 d5 o: s
| 定时器1低字节. b* @% b4 L, A" H
| 8BH
1 K$ j! o. N7 N& S5 \3 W C | 表5-3 TCON--定时器/计数器控制寄存器的位分配(地址:88H)
6 @0 [( A' U/ V% N1 ^ k5 a2 i 可位寻址;复位值:0x00;复位源:任何复位
0 ^* K! ~! G! d, l位$ I4 R1 {2 Y1 X- {9 R( W) d: L
| 75 b5 B1 |; \" ]3 k5 G* R# ^1 B& A
| 6& X1 r8 C# W8 R) k* E
| 5/ N2 z. y, f C% I0 C
| 4/ O) i/ l1 F3 s( b. ]+ n
| 3
?" K% }$ h8 H& I" M | 2
9 |, [( y, Q) U; T, \1 u | 1. L2 X2 }$ ?% Z) \8 a
| 04 ~& j4 C3 M% u
| 符号
p) q8 _$ }- `4 K/ ]# M) m | TF13 P" n$ U9 A5 M4 C
| TR1
' `7 E. ^* P( q3 _ | TF0
; O$ \4 z) h2 L* n! o% `8 ?; u) p | TR05 f1 K6 W1 Y" x, ~ ]
| IE13 W# _/ J! ?7 k7 \- [) M f
| IT1# X' J3 U% }& f- ~) a7 K( h
| IE0$ |1 X9 I9 V7 A# N
| IT0* G4 @: o+ {8 I0 X! o
| 表5-4 TCON--定时器/计数器控制寄存器的位描述
( a- B; W6 t( X1 g0 c位
* \! b- ~7 j8 w0 j2 \6 P6 n, O | 符号3 f- i% L- _9 r. q; W) R
| 描述8 d m5 y5 u" z3 \0 n* O. ^4 w, o
| 7
$ Y- b+ E8 K0 x; j4 R( m | TF1
! ?% {# C% k4 o/ z) }1 G# J | 定时器1溢出标志。一旦定时器1发生溢出时硬件置1。清零有两种方式:软件清零,或者进入定时器中断时硬件清零。
2 V2 h/ }8 r- E6 c; L' B2 h | 6
2 u9 G2 N! q! Q! b8 Y | TR1
( [0 E6 s) Q5 z6 `" f) U" Q! g | 定时1运行控制位。软件置位/清零来进行启动/关闭定时器。
8 p/ g5 }0 p" E g+ {/ a+ j2 \8 w | 5. S! q0 r! |' u- m; j9 O9 u- v
| TF0
" u- W/ X. H. h+ y; I# N' ^" Y | 定时器0溢出标志。一旦定时器0发生溢出时硬件置1。清零有两种方式:软件清零,或者进入定时器中断时硬件清零。$ X% |+ ^4 R6 a" p# t
| 4/ C+ e+ I- M2 k W' x8 l+ \
| TR0* ^1 e. w T8 k. l$ f" x0 b& p
| 定时0运行控制位。软件置位/清零来进行启动/关闭定时器。2 r* n0 B/ g7 C2 j! T: `2 Q+ R: k0 i
| 3+ L, x4 _( @- g o
| IE1) P- E$ S; z- s
| 外部中断部分,与定时器无关,暂且不看
7 H9 o5 C. _" H/ U3 I | 2
9 J5 @2 z E+ C8 b1 T$ W4 R | IT1/ a2 I2 i' X; h
| 15 r% ~1 K; C: u
| IE0
1 P7 d0 i {! ]! w | 0
, z$ o+ ?1 c! z& y( s | IT0/ }: G. W& v4 }5 M+ Z
| 大家注意在表5-4中的描述中,只要写到硬件置1或者清0的,就是指一旦符合条件,单片机自动完成的动作,只要写软件置1或者清0的,是指我们用程序去完成这个动作,后边课程中不再做说明。 p8 X& g0 ~: R8 S3 u
表5-2中的寄存器,是存储计数器的计数值的,两个字节的用于定时器1,两个字节用于定时器0。, O U1 ?/ [. \! E* J8 ]( C
表5-3中有TF1、TR1、TF0、TR0这4位需要我们理解清楚。两位定时器1的,两位定时器0的,我们只解释定时器1的,定时器0的同理。先看TR1,当我们程序中写TR1 = 1以后,定时器值就会每经过一个机器周期加1,当我们程序中写TR1 = 0以后,定时器值就会保持不变化。TF1,这个是一个标志位,他的作用是告诉我们定时器溢出了。比如我们的定时器设置成16位的定时器,那么每经过一个机器周期,TL1加1一次,当TL1加到255后,再加1,TL1变成0,TH1会加1一次,如此一直加到TH1和TL1都是255(即TH1和TL1组成的16位整型数为65535)以后,再加1一次,那么就会溢出,TH1和TL1同时都变为0,只要一溢出,TF1马上自动变成1,告诉我们定时器溢出了,仅仅是提供给我们一个信号,让我们知道定时器溢出了,它不会对定时器是否继续运行产生任何影响。& {' p* G1 D9 Q- b x% e6 L; a: n5 I
表5-5 TMOD--定时器方式控制寄存器的位分配(地址 89H)
& z- c: J, I, f$ b0 @0 W. P 不可位寻址;复位值:0x00;复位源:任何复位
& A% r; s3 L. Y/ P' u位
9 |& l4 C! C- m$ D3 j8 m6 N | 7, p: o( P9 X: [2 d. _3 G
| 6
- o6 h2 F7 }8 U1 z4 g, ~ | 59 }& N2 A! o# w8 e( R0 j+ j
| 4
* I* P' k& W& Y; b$ U) f | 3, s: Q% D! j5 a1 [9 \) v# U" z
| 20 e( `9 y3 ]8 `" b
| 1, |. o, _% c$ F; @. Y8 ?. s
| 0, l8 B9 p8 N+ F6 E/ m$ ?
| 符号# n( p4 X# ` i7 e, N4 b
| GATE
4 ~, q* y; a0 T3 y* {(T1)
8 v1 r( j, }! r5 h0 X | C/T) \3 H. ?, n" C4 s; w
(T1)4 Y9 B" ^- |- m- B" m
| M1
/ R0 A! m- i! u(T1)) }0 Q9 b: g: X c& p# R" ~/ D
| M0+ O/ ]" r5 o [2 `3 e
(T1)
. g- o0 N2 ?% c6 x! O, @ | GATE6 o* p* N6 B- `7 o+ Q
(T0)5 x% {" p( T! D4 }" l3 }
| C/T
; t$ Z a$ Y* D, n0 q _* {. U(T0)
! H+ K. C0 v4 N) K( f( } U1 W0 Y | M1
; X0 r0 Q" h1 m# V# f(T0)
2 S- e8 E( {5 D1 |7 x" | | M0, F; V$ N0 y5 v) f/ Q5 _+ C
(T0)2 K C* j% m6 s2 L
| 细心的同学会发现,TCON那个地方标注的是“可位寻址”,TMOD这里标注的是“不可位寻址”。这个地方的意思就是比如TCON有一位TR1,我们可以在程序中直接进行TR1 = 1;这样操作。但是(T1)M1 = 1;这样的操作就是错误的。我们要操作就必须一次操作一个字节,就是必须一次性对TMOD所有位操作,不能对其中某一位单独进行操作。- S: B( d/ G0 E
表5-6 TMOD--定时器/计数器方式控制寄存器的位描述4 v* ]2 ~. R2 [8 l
符号8 t( n/ [$ ^, x& O8 X
| 描述
+ k! v7 u/ b( D8 {+ ` | T1/T04 v* ?; U( Y7 j) i
| 在表5-5中,标T1的表示控制定时器1的位,标T0的表示控制定时器0的位。
* e6 R9 D3 V6 b+ H8 g; o, F- e | GATE
z& S/ F! v/ Q( L% M | 该位被置1时为门控位。仅当’INTx’脚为高并且’TRx’控制位被置1时使能定时器’x’,定时器开始计时,当该位被清0时,只要’TRx’位被置1,定时器x就使能开始计时,不受到单片机引脚’INTx’外部信号的干扰,常用来测量外部信号脉冲宽度。这是定时器一个额外功能,本节课暂不介绍。
! E# v/ X* z* I1 Z/ h! \ | C/T
6 T, X3 B; |% e: M8 O F | 定时器或计数器选择位。该位被清零时用作定时器功能(内部系统时钟),被置1用作计数器功能。3 p8 G" Z- p. r, I* @3 u0 Y" H5 j
( J& |8 j6 z5 A- i) S( F" K6 _% x | 表5-7 TMOD--定时器方式控制寄存器M1/M0工作模式) A& a- U2 Y) ^4 ]$ N
M18 F5 \, P' L& O$ M+ O) C" N
| M0
- B& A. c! n9 ~/ {0 N | 工作模式; \: t5 B' {1 D0 z/ }! V3 ]3 d
| 描述
" |6 v9 z2 x% M: m0 H+ a | 0
% f* k' A, d4 a3 D( _2 n8 c2 w& d | 02 j8 F8 [/ w* h
| 0
9 \3 S& P ^( A& X+ O* a | 兼容8048单片机的13位定时器,THn的8位和TLn的5位组成一个13位定时器* T2 k# t* ]0 ?! ?3 H- T" h p
| 0
' s; f& e: {+ b* H- \' Z | 1
$ R. K$ T* `+ l) p1 R; Z7 x | 1
% j: A. e1 w8 ~- _ | THn和TLn组成一个16位的定时器, b0 ^0 _# s: w; Q$ J- \' a
| 1
W; o; o' Z5 ^4 ?5 Q | 0
' T: u$ P! g8 [ | 2+ f V6 J& _" R; r4 N$ ]
| 8位自动重装模式,定时器溢出后THn重装到TLn中
+ C) O1 f& _9 Q' F F) T | 1
4 g2 u% Q! ?, c' Y" ]2 }# G$ y | 1
& o+ `, M4 h) ~" ]' x( D | 3
3 g0 Z/ }2 V1 @0 K# t! x( m | 禁用定时器1,定时器0变成2个8位定时器# Q/ ` R; k/ m. [& Z0 R* [
| 以上这4种模式的配置,其中模式0是为了兼容老的8048单片机而设的,现在的51几乎不会用到这种模式,而模式3根据我的应用经验,他的功能模式2完全可以取代,所以基本上也是不用,那么我们重点就学习模式1和模式2。
' y2 K1 D% J. m6 P. G0 ~3 C: L8 c模式1就是THn和TLn组成了一个16位的定时器,取值范围是0到65535,溢出后,只要不对THn和TLn重新赋值,则从0开始计数。模式2的功能是自动装载,就是TLn溢出后,TFn就直接置1了,并且THn的值直接赋给TLn,然后TLn从新赋值的这个数字开始计数。这个功能可以用来产生串口的通信波特率,我们讲串口的时候要用到,本章节我们重点来学习模式1。为了加深大家理解这个定时器原理,我们来看一下他的模式1的电路示意图5-1。
. j* r4 k2 S: L- i 9 e& S: J% f {# I
图5-1 定时器/计数器模式1示意图
& }" w* A# d; G7 r/ @我带着大家来分析一下这个示意图,后边如果手册中遇到,大家也就会自己研究了。OSC框表示时钟频率,因为我们1个机器周期等于12个时钟周期,所以那个d就等于12。下边GATA右边的那个门是一个非门电路,再右侧是一个或门,再往右是一个与门电路,大家可以对照一下5-1节的内容。
' T3 J( p2 j" O. z% z图上可以看出来,下边部分电路是控制了上边部分,那我们先来看下边是如何控制的,我们以定时器0为例。
8 n0 ?5 e" Q* A1、TR0和下边或门电路的结果要进行与门运算,TR0如果是0的话,与运算完了肯定是0,所以确定如果要让定时器工作,TR0 = 1。# M& K) e; e& f" ^
2、与门结果要想是1,那或门出来的信号必须也得是1才行。在GATE位为1的情况下,经过一个非门变成0,或门电路结果要想是1的话,那INT0即P3.2引脚必须是1的情况下,这个时候定时器才会工作,而INT0引脚是0的情况下,定时器不工作,这就是GATE位的作用。
) G' l8 L% ] L1 m3 r3、当GATE位为0的时候,经过一个非门变成1,不管INT0引脚是什么电平,经过或门电路后则肯定是1,定时器就会工作。/ F( b8 a' D5 U3 K4 y; q3 E7 A
4、要想让定时器工作,就是加1,从图上看有两种方式,第一种方式是那个开关打到上边的箭头,就是C/T = 0的时候,一个机器周期TL就会加1一次,当开关打到下边的箭头,即C/T =1的时候,T0引脚即P3.4引脚来一个脉冲,TL就加1一次,这也就是计数器功能。+ o, F3 V, q' S5 e; L" `4 t
INT0引脚是P3.2,INT1引脚是P3.3,T0引脚是P3.4,T1引脚是P3.5,这个可以从我们KST-51开发板原理图上看出来。
8 F9 O. V' ^9 U7 b. |1.2.3 定时器程序应用& z! |6 U1 g5 |' \+ j3 f2 b
了解了定时器相关的寄存器,那么我们下面就来做一个定时器的程序,巩固一下我们学到的内容。我们这节课的程序先使用定时器0,在使用定时器的时候,需要以下几个步骤:& M5 u3 Y$ }6 w3 i1 |1 z
第一步:设置特殊功能寄存器TMOD,配置好工作模式;
! {6 t; R4 D8 o4 x' V: L5 @; Z第二步:设置计数寄存器TH0和TL0的初值;# K3 k! U4 b' o& k" B
第三步:设置TCON,通过打开TR0位来让定时器开始计数。% ?1 p% {6 t9 i% w
第四步:判断TCON寄存器的TF0位,监测定时器溢出情况。
5 o* Q U: _& E+ x- a2 I写程序之前,我们要先来学会计算如何用定时器定时时间。我们的晶振是11.0592M,时钟周期就是1/11059200,机器周期就是12/11059200,我们假如要定时20ms,就是0.02秒,要经过x个机器周期得到0.02秒,我们来算一下x*12/11059200=0.02,得到x= 18432。那么我们现在16位的定时器溢出值是65536,我们可以这样,先给TH0和TL0一个初值,让他们经过18432个机器周期后刚好溢出,溢出后我们可以通过检测TF0位得知,就刚好是0.02秒。这个初值y = 65536 - 18432 = 47104,转成16进制就是0xB800,那么就是TH0 = 0xB8,TL0 = 0x00。- c9 x+ `5 J( J) @& D' R: x
那0.02秒我们已经定时出来了,细心的同学会发现,我们如果初值直接给一个0x0000,一直到65536溢出,定时器定时值最大也就是71ms左右,那么我们想定时更长时间怎么办呢?用你小学学过的逻辑,倍数关系就可以解决此问题。
! U& o0 [, o: g) L& h那好了,我们下面就用程序来实现以下这个功能。6 R7 ] Y* x" z9 K5 v
#include<reg52.h> //包含寄存器的库文件
) v( M* c# e8 a7 U& D( L; U. f! ^sbit LED = P0^0;
n. G$ C. Z, n/ S$ Nsbit ADDR0 = P1^0;# X& G2 H+ W( }3 G. Z' |5 \1 ~
sbit ADDR1 = P1^1;
2 [/ E. F! t+ S; M; b9 {* Rsbit ADDR2 = P1^2;
. S: S( p: }; Q' }sbit ADDR3 = P1^3;
+ p/ F; m! l: [, W( ^( V+ i3 {sbit ENLED = P1^4;
/ S D/ M- `" X' X+ C' Mvoid main() $ e9 V, b% @& `
{
9 T$ H$ q' e4 v( q1 k# Q" V/ W6 ? unsigned char counter = 0;8 X1 S7 h) e3 Z( V' [( Q' {' L9 U" t3 b2 G
ENLED = 0; ADDR0 = 0; ADDR1 = 1;
# A. a% _, y8 ]$ {1 I0 h0 M ADDR2 = 1; ADDR3 = 1; LED = 1; //74HC138和LED灯初始化部分 Q+ R- k. j8 {% S g0 x
TMOD = 0x01; //设置定时器0为模式13 x) s! V( ^" V" C0 ^- ~9 U: f
TH0 = 0xB8;# Y R( T5 ^+ k% a, S2 X
TL0 = 0x00; //定时值初值$ \+ e- r6 T1 i
TR0 = 1; //打开定时器0) B, j+ E5 S% P
while(1)
1 J6 I! C3 J/ g. r {) c7 Z# U; x4 A0 q, V
if(1 == TF0) //判断定时器0是否溢出
; [# O# g9 `/ r" B. D {
7 U4 j; F+ F4 U% I" O8 d TF0 = 0;; t" p, b# Q( S- [
TH0 = 0xB8; //一旦溢出后,重新赋值5 {' n* n: H+ L( y/ v
TL0 = 0x00;* t) x; B: K3 _0 p* C6 F- |
counter++;. l% E) Z3 E* l& q# E6 q
if(50 == counter) //判断定时器0溢出是否达到50次/ @& ?, z, G0 r3 Y- U+ `: z) V7 o
{3 A( c( \+ Z3 Y2 F- K: x1 N
counter = 0; //counter清0,重新计数
: _, i, C9 g& Y& [- X LED = !LED; //LED取反操作,0-->1,1-->0
3 ?4 H- h. [. T" H7 p' H( F } ! E% _/ k9 J1 \: T5 C8 f
} 6 n n. x, X! O, r
}5 u% h4 c1 z5 Z) B
} ( I9 {/ B# i5 E0 a1 j
程序都有注释,不难理解,这里要解释一个地方,就是两次if判断,细心的同学会发现,if(1 == TF0)这句,我把1写前边,这个地方我推荐新手按照我这样来写,因为如果我们写if(TF0 == 1),作为新手来说,不小心丢掉一个’=’号后,写成if(TF0 = 1),这样实际上在语法上是可以通过的,我们用的Keil4还会出一个警告说明一下,Keil以前的版本以及一些其他软件,可能根本不会出任何错误或者警告提示,但是这样产生的Hex文件下载到单片机里边,程序就错了,大家可以改改试试看。
+ s* N7 D ~" i% }( f9 B4 F0 E: i本程序实现的结果是我们板子上最右边的小灯点亮持续一秒,熄灭持续一秒,也就是以0.5HZ的频率进行闪烁。
& }/ M& b2 |7 a# `( q7 v1.3 数码管学习9 ~6 H3 w/ ]) s7 F& k3 @/ ?
小灯是一种简单的LED,给我们视觉效果只能通过亮和灭来表达简单信息。而这节课我们要来学习一种表达更加明确的器件,数码管。
0 \; V4 r/ ]5 r1.3.1 数码管的基本介绍4 o3 N2 r: T3 @, }
先给大家提供一张原理图看一下,如图5-1所示。9 m' @' o* m, f, j/ n: z- f/ i
8 H3 u* _! W* k
图5-2 数码管原理图
3 a7 c E- O) P2 Y: W$ p这是比较常见的数码管的原理图,我们板子上一共有6只数码管。前边有了LED小灯的学习,数码管学习就会轻松的多了。从图5-1能看出来,数码管共有a,b,c,d,e,f,g,dp这8个段,而实际上,这8个段每一段都是一个LED小灯,所以数码管就是由8个LED小灯所组成的。我们看一下数码管内部结构图5-2。* T3 [9 u6 H4 \$ G! l! ?3 w
0 N& Z: w2 M0 _3 T2 X$ z. b y
图5-3 数码管结构图
& `! U& E; R' R3 N数码管分为共阳数码管和共阴数码管,所谓的共阴数码管就是8只LED小灯的阴极是接在一起的,也就是阴极是公共端,由阳极来控制小灯是否亮灭。同理,共阳数码管就是阳极是接到一起的,大家可以仔细研究下图5-2。细心的同学也会发现,数码管上边有2个com,实际上就是我们数码管的公共端。为什么有2个,我个人认为,一方面有2个可以起到对称的效果,刚好是10个引脚,另外一个方面,公共端通过的电流较大,我们初中就学过,并联电路电流之和等于总电流,用2个com可以把公共电流平均到2个引脚上去,降低线路承受的电流。
; Q3 q+ p- e( w从我们板子的电路图上能看出来,我们所用的数码管是共阳数码管,如图5-3所示。
9 z- k% n" ^9 h* X& g% |* C 5 {5 W; _1 K, v
图5-4 共阳数码管电路# S( I, A- N7 M w/ S9 E
他们的com是接到了正极上,当然了,和LED小灯电路类似,也是由74HC138控制了三极管的导通来控制整个数码管的电流,我们先来看DS1这个数码管。原理图上可以看出来,控制DS1的三极管是Q17,控制Q17的引脚是LEDS0,对应到74HC138上边就是Y0端的输出。
, @! \2 E* _: U/ W![]()
5 \+ U* Y% `6 k! e: v2 t5 w A8 I图5-5 74HC138控制图7 D- C" [* S- h4 {
我们现在的目的是让LEDS0这个引脚输出低电平,相信大家现在可以独立根据前边学到的内容把ADDR0,ADDR1,ADDR2,ADDR3,ENLED这4个输入状态写出来,现在大家不要偷懒,都去根据138的手册去写一下,不需要你记住这些结论,但是遇到就写一次,锻炼过几次后,遇到同类芯片自己就知道如何去解决问题了。
% j4 U( v* s6 n! S0 j" M数码管通常是用来显示数字的,我们板子上的6个数码管,习惯上我们称之为6位,那控制位选择的就是74HC138了。而数码管内部的8个LED小灯我们称之为数码管的段,那么数码管的段选择(即该段的亮灭)是通过P0口控制,经过74HC245驱动。
0 V. o7 O9 w% f4 p1.3.2 数码管的真值表
2 u5 m# ]% o' S& m) F- G数码管的8个段,我们直接当成8个LED小灯来控制,那就是a、b、c、d、e、f、g、dp一共8个LED小灯。我们通过图5-1可以轻而易举的看出来,如果我们点亮b和c这两个LED小灯,也就是数码管的b段和c段,其他的所有的段都熄灭的话,就可以让数码管DS1显示一个数字1,那么这个时候实际上P0的值的二进制就是0b11111001,十六进制就是0xF9。也有可以自动生成数码管段位码的软件,那么我们写一个程序进去,看看让数码管显示一下看看。
9 R- ?6 \( O3 T #include<reg52.h> //包含寄存器的库文件
+ ?8 a/ w' Y7 K- X sbit ADDR0 = P1^0;% [7 V0 P5 X1 _
sbit ADDR1 = P1^1;: M2 h, ]. m' R l h. M# ^! N
sbit ADDR2 = P1^2;/ S$ ~- Q4 O( S1 |
sbit ADDR3 = P1^3;
& j5 f$ o% J& |2 C3 G6 d8 X sbit ENLED = P1^4;
9 X, s; I4 j+ Y1 w+ ~& S, U
2 ?/ q' ` v- F2 _+ U: k2 u& b$ } void main()
, n8 J. l4 F. Q: u; P {
- s4 M# P) w# L6 _& ~ unsigned char j = 0;9 q7 ]6 j/ a; N% d. v& O: B
unsigned int i = 0;
* ~! D0 [; d, \" d' x; p% l5 ]# m& q1 U2 J+ |* b, J. ~
ENLED = 0;4 D# A' i. X) d& v, [/ X4 U% o1 D3 V
ADDR0 = 0;6 r6 G. Y& h- n) A+ G7 h
ADDR1 = 0;
3 |( `% N) w# W7 l ADDR2 = 0;
7 A8 ^* l; z) A' ~ ADDR3 = 1; //74HC138开启三极管Q17
$ g c9 J5 c9 j2 {" ?/ g) x- w g while(1) //程序死循环
; ~! k; s! |, L- m8 H0 g {% M& o" H8 v% L# q* d
P0 = 0xF9; //打开数码管b和c段 - U. C$ b Y( ]6 g+ ^
}, q$ q h: ]( b: T. o
}# f( p$ N% @5 Q! E
大家把这个程序编译一下,下载到单片机里会发现,最右侧的数码管成功显示1这个数字。0 n: H2 t1 m5 Y, R) d- r. i9 q c# H h
同样的方法,我们可以把其他的数字都成功的在数码管上显示出来,而数码管显示的数字对应给P0的赋值,我们叫做数码管的真值表。我们来列一下我们这个电路图的数码管真值表,注意,这个真值表里显示的数字都不带小数点。
) _& P$ N/ s2 E$ N9 }* f表5-1 数码管真值表! H k% [4 y' N; _, i v+ w
数字
% n8 b' P& ~ M4 K5 t% n | 0: e; T9 v3 k- l! g1 E6 B6 I
| 1! j' C- e' x8 ~ B* Q" q! F" K
| 2; C/ e0 G, ^7 v
| 39 j% q( u+ g( ?2 X6 H1 ]$ {# x
| 4 k. e- B/ x/ G7 x3 l8 ? t# j
| 5
3 j3 a) C6 M3 U( x I | 6
5 B2 ]* \/ q) f: y | 7) q7 z4 y* k5 @
| 真值表
R. j9 {" X& b& {( s4 ^+ } | 0xC0
1 Q0 u' g) H ?* t, c/ D; O | 0xF9
1 H' m( d u+ \5 k: g, U | 0xA4
. T1 Z. @9 v: C | 0xB09 Z# ]& ~% L7 R8 ~7 Q
| 0x998 w/ x2 |( n% |0 v
| 0x92
8 F. N1 a8 n' {% @/ m | 0x82
. x# @/ @* v+ U ] | 0xF8
0 e: _$ ~' J- E% q* w | 数字9 O5 B1 @6 O' o! F8 Z6 _
| 8
L- i, N( B' V& i | 9
; R5 \' X5 `# q* H5 U" c0 v2 v | A1 U' X) U T% |1 B
| B; N9 b: ?: ~' K# ]. i
| C+ ]2 C" f! ~2 S( C9 N2 X
| D
9 }7 b/ `( ]# x2 F( j | E% H& c* g' \6 m( M P& J5 d, r
| F4 F0 i3 }! O/ q2 Y; A0 ]
| 真值表" ] D1 X. F" m
| 0x800 @& o6 s0 }' A) M3 g, y
| 0x90: x: ]: {0 t1 j. k8 U
| 0x88
$ C2 Y2 |* w0 y" L2 j | 0x835 W s; h5 ^8 A6 f% Z7 B
| 0xC6
1 t* h8 x+ b/ U; B | 0xA1
4 V/ b3 ?' E5 J | 0x86
" G: `! f, ~6 s3 }1 F. f | 0x8E' \9 w( O0 ], {3 n2 A: j6 `
| 大家可以把上边那个数码管显示1的那个程序中的P0的赋值随便修改成我们表5-1中的真值表里的数字试试看,把数码管显示的数字显示出来。( Q1 \) U# @: Y+ U
1.3.3 数码管的静态显示2 C- i5 c+ r2 \
从第三课我们学习74HC138以后,我们了解到74HC138同时一次只能让一个输出口为低电平,也就是在一个时刻内,我们只能让一个数码管显示,始终选通数码管并且可以根据我们的P0总线的信号来改变这个数码管的值,我们可以理解为数码管的静态显示。
* y% h$ ^ M Z" _- u; g数码管静态显示是对应动态显示而言的,静态显示对于一两个数码管还行,多个数码管,静态显示实现的意义就没有了。这节课我们先用一个数码管的静态显示来实现一个简单的秒表,为下节课的动态显示打下基础。
- E3 G& I C' }: I: e4 M. f先来介绍一个51单片机的关键字code。我们前边课程定义变量的时候,一般用到unsigned char或者unsigned int这两个关键字,这样定义的变量都是放在我们的单片机的RAM中,我们在程序中可以随意去改变这个变量的值。但是还有一种常数,我们在程序中要使用,但是却不进行对这个值的改变,这种值我们可以加一个code关键字修饰一下,修饰完毕后,这个值就会存储到我们的程序空间flash中,这样可以大大节省我们单片机的RAM的使用量,毕竟我们的RAM空间比较小,而程序空间是很大的。比如我们现在要使用的数码管真值表,我们来看一下我们下边的这个程序。
* c8 ~. H0 q8 r7 s+ N6 P#include<reg52.h> //包含寄存器的库文件
; t5 [7 p! m6 P5 x4 F7 M$ Wsbit LED = P0^0;/ d' S& p. y, p
sbit ADDR0 = P1^0;
$ L, |0 T" P$ p# o: Q" wsbit ADDR1 = P1^1;
" t7 }$ ?, y6 Y* J. U) m8 v3 Gsbit ADDR2 = P1^2;
" _* g/ Q) G! ?" P5 Usbit ADDR3 = P1^3;
: k. p2 X9 G$ [/ H% o4 R# osbit ENLED = P1^4;
# T. j; N: O5 \0 s+ o8 Nunsigned char code LedChar[] = {: g0 p5 {" F4 d) ~; f+ K
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
% s. O6 A# s+ n5 t7 G) P 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e! u8 Y( [, D- e3 e( J# H) ]# J2 L$ Y
}; //用数组来存储数码管真值表,下一课详细介绍数组" X. k; T/ J* y
void main()4 n( ]. ]! S* |& ^6 O% c9 r B8 S
{# ]! A5 Y9 ?8 H; @
unsigned char counter = 0;
" e9 B6 Z8 z, d unsigned char j = 0;8 W0 a7 T, Q0 M; P X# C% Q; R% Q
ENLED = 0; ADDR0 = 0; ADDR1 = 0;0 X' b# @! h6 r8 l( B1 c O% N
ADDR2 = 0; ADDR3 = 1; P0 = 0XFF; //74HC138和P0初始化部分
) s+ Y5 a: W& F1 V TMOD = 0x01; //设置定时器0为模式1
' ?3 @& q' E: I1 l7 X TH0 = 0xB8;% u' ~# {1 I4 [& W$ w
TL0 = 0x00; //定时值初值3 z2 S( A/ e- a: A' L- m1 A; b
TR0 = 1; //打开定时器0
- K A/ ^; o3 J9 U* o( O, t! G while(1)
8 a4 m, \* }3 Z6 I$ t {
z/ O* d! m: Y4 l if(1 == TF0) //判断定时器0是否溢出& l' z: k, E$ }
{
; V. a& a7 }* m- y TF0 = 0;
, Z( X( S" x, @+ y W TH0 = 0xB8; //溢出后,重新赋值2 q7 p8 k/ ` S" W
TL0 = 0x00;
B2 Z" r; N/ G1 j4 k4 v ?2 m, E counter++;9 v$ n4 d: Y, Q
if(50 == counter) //判断定时器0溢出是否达到50次
+ e( f, E& O0 Z# O3 F" m {
- e4 `6 K c) `+ P8 x0 z4 X) L6 i5 M counter = 0; //counter清0,重新计数, I% f# V- {& i) F: J b- c
P0 = LedChar[j++]; //把数组里的对应值送给P0; ~& Z+ l7 t9 u
if(16 == j) //当显示到F后,归0重新开始! p* B& O/ R- |! k3 F5 v1 @, P9 F6 Z
{* f" ?2 ?- _4 M6 r0 G( q! b! q6 S
j = 0;
" O/ V/ S- @6 ]+ t! z5 S }6 ^" i) B, @, s2 _! A/ h; T
}7 y7 H" p) ^! F& ]
}; [" H" W' S" x2 A1 {2 H
}; o/ ^- f* @; M: m- _
}* [; m" f% t/ u4 J
1.4 作业3 @3 Q+ K2 d7 d0 ~3 K! c( P
1、熟练掌握单片机定时器的原理和应用方法。- E+ K* n5 f' w# A6 h- H. v
2、通过研究定时器模式1的示意图,自己打开STC89C52RC数据手册的定时器部分,独立研究模式0、模式2和模式3的示意图,锻炼一下研究示意图的能力。* f; t$ g" T* @+ r* W
3、使用定时器来做流水灯的左右移动以及花样变化的跑马灯。
2 |1 Z B" {, t: x( V# t+ K4、了解数码管的原理,掌握数码管的真值表的计算方法。
o. M' w3 t, m' E5 W5、编程实现数码管静态显示秒表的倒计时。& f7 S5 l! ?7 V
. z! F& E5 Y! ?! L
5 k: e+ j3 W% ?- n3 g
8 v1 }, P% ^; o& b4 ^
+ f: Z7 B2 e1 u# \' Z7 G/ d6 \3 i
|
|