|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
基于1602液晶的简易计算器1 ?% ]$ C7 w8 B8 u( J6 r# S
) r7 }5 @, B8 E+ [* D* _
* R) r1 M* p1 F7 R: T8 e#include <reg52.h>* ?5 y( R- H y" V5 s
" M$ c: Z0 e! k2 Vunsigned char step = 0; //操作步骤. _' v8 [0 ~, C3 r4 I4 a% U3 R+ H
unsigned char oprt = 0; //运算类型
2 y: J( d3 V( f7 @# K8 [( |signed long num1 = 0; //操作数1
" o$ P% y `; a/ csigned long num2 = 0; //操作数2
& u% Z2 o3 v+ msigned long result = 0; //运算结果' P8 C% ~3 b6 J5 j7 W9 L' w0 L
unsigned char T0RH = 0; //T0重载值的高字节
* [% l2 y, W. i+ O- {* Q3 s) aunsigned char T0RL = 0; //T0重载值的低字节. W; P7 I' S3 s& U+ \
2 ]0 M% \3 a: d: R8 e9 G
void ConfigTimer0(unsigned int ms);
& f9 x' P/ v# B3 X( n# sextern void KeyScan();
) K% c3 F0 j( @0 @) y& S0 c8 s5 oextern void KeyDriver();3 I8 l' P+ V/ |3 ]
extern void InitLcd1602();
3 b, g% i/ J2 u5 K7 s* J2 Sextern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);5 ] e5 P& W7 m0 L. l6 E7 S. J; _
extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);2 A( ^, v, T4 A1 ^
extern void LcdFullClear();( G# [3 E! d$ S! s- m
{3 d/ N2 R* I- L4 ?
void main()' O' ]: Y; ~8 U2 q
{
4 g! a+ Y" z' ?6 y) ?* S EA = 1; //开总中断; K% s; w- G. i d/ B' \0 Y+ @
ConfigTimer0(1); //配置T0定时1ms- {* G9 p9 g: G" L9 L
InitLcd1602(); //初始化液晶
* p3 X. m$ @9 `9 B, U3 X LcdShowStr(15, 1, "0"); //初始显示一个数字0
5 M/ C: r+ a' [
+ l2 i5 y" Q, N7 ?% I# v# ? while (1)
, x. ~" d5 Y/ C9 H8 n7 ] {8 o5 b u W& Q+ W7 l6 i2 D" ~3 Q
KeyDriver(); //调用按键驱动
8 _( D. z. u/ E- s0 w1 _, N* v }7 H9 Z6 d+ Z- K$ i* N/ a4 Z
}' q% x) u% c8 P
/* 长整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */' T+ Z" @6 `9 Y$ U9 q, x4 C
unsigned char LongToString(unsigned char *str, signed long dat)
. V$ _: }! b, |' y& [8 ^4 ]. @. g+ c{
& Z8 Z5 L7 E# N* |& E signed char i = 0;
+ |2 f6 Q( R* m @! x unsigned char len = 0;
( t7 G3 G: m# }, j unsigned char buf[12];
( A% e. g+ p* Q7 a
, Y. t# P6 T% {" c if (dat < 0) //如果为负数,首先取绝对值,并在指针上添加负号+ x- Z& r" v* {1 b. l
{5 `4 S* s8 P3 B, l; M6 r8 ~9 R
dat = -dat;( ?3 R+ R5 ?+ X# P
*str++ = '-';
# x1 K: Y* \3 F& W6 j/ w' m len++;
0 [5 H, w" |8 h4 S2 `) C }
6 u7 Y( d) ?/ z" T do { //先转换为低位在前的十进制数组
* \: g* ^: t) X* }7 A/ c0 a9 s& t) j buf[i++] = dat % 10;! B; i$ p7 f' ~. W& [
dat /= 10;( g* r. g0 k9 B$ |
} while (dat > 0);
: Y1 M* i; ^; Q( U# s len += i; //i最后的值就是有效字符的个数3 h8 l& v/ [" Z+ [, X! Q$ h. R
while (i-- > 0) //将数组值转换为ASCII码反向拷贝到接收指针上
: c4 X, |: _! {- Z2 f) w { }! U; r1 W1 V$ k5 V0 ]! ?
*str++ = buf + '0';4 n& a& f$ E/ q5 O
}
+ b; h' _, _5 ?) s- |# S2 e3 @1 f *str = '\0'; //添加字符串结束符: R7 S! S' V7 k; C J* x* I+ d
! B1 g5 R1 I Q7 O5 Q
return len; //返回字符串长度 w* F' O* g9 P- ], f
}- L# t' {, V( w3 ^0 V% E$ o
/* 显示运算符,显示位置y,运算符类型type */
$ o; H- d# J/ P, y8 f3 X% Cvoid ShowOprt(unsigned char y, unsigned char type)
9 _, T/ E, U% Y/ I{5 D7 P' {3 D1 J* U
switch (type)
$ ?8 r% g# _8 ] F2 v/ \ {! r: J* V8 z# Z3 M4 S2 N' j
case 0: LcdShowStr(0, y, "+"); break; //0代表+! F2 k9 d2 h9 e9 w: x
case 1: LcdShowStr(0, y, "-"); break; //1代表-
# S7 s* {/ a& B* P5 t9 _$ ~$ j" { case 2: LcdShowStr(0, y, "*"); break; //2代表*7 m; Y3 \. z8 ]" t i8 B
case 3: LcdShowStr(0, y, "/"); break; //3代表/
$ I5 r7 f6 \9 s. ?/ `- j default: break;
( [5 _$ b% U& z5 g }
+ p& C' h, y0 t2 x. y: ^; w6 p}
5 R c" P( p6 K* F5 w/* 计算器复位,清零变量值,清除屏幕显示 */
! e- }' g1 M- K" Wvoid Reset()
, h8 j8 {8 e$ a{
) v5 G7 w* B# j% |0 B9 w# n* ~ num1 = 0;! e- {! ?* C! S) x/ N) r
num2 = 0;
( E5 f% V2 z3 H* ]8 u h step = 0;
" K; U- _2 ?- g' l LcdFullClear();
0 @; P0 u& E2 [% Y3 t- [3 u}
& n2 P3 Q5 J! Z" V! u/* 数字键动作函数,n-按键输入的数值 */
]1 S; O$ } {void NumKeyAction(unsigned char n). ^6 H1 H: S' }5 |; f! z
{9 u8 r; ~0 q, `/ r7 S) [
unsigned char len;
% p& \: U' u' @, h/ D: W unsigned char str[12];
2 m5 E: F$ L9 |# @# x) |' ?1 U. ?
8 }- X9 J3 u7 y0 f8 ^( @9 C. r5 o: J if (step > 1) //如计算已完成,则重新开始新的计算* D; t8 |2 ]& H. F2 P
{/ b3 ~2 [; {$ t* V" ?1 y
Reset();
) Y% H% G, h4 k7 ~, b }9 [1 \5 `( @& t: M
if (step == 0) //输入第一操作数% u% ?' \# [6 K4 W! A
{: ~, K0 Z! M4 i' G( h4 y! g1 m
num1 = num1*10 + n; //输入数值累加到原操作数上
7 d* K' O# W3 X+ r* {, V len = LongToString(str, num1); //新数值转换为字符串
- O) c7 S% N3 `+ ?- @ LcdShowStr(16-len, 1, str); //显示到液晶第二行上/ {. i5 x. C7 u% b3 g. U
}
6 ~" _8 r( A7 \+ \2 B" f( d else //输入第二操作数
1 e) @& X. y l, S$ T; Y { ~$ f# l7 { E
num2 = num2*10 + n; //输入数值累加到原操作数上! R5 y: f7 q: a5 N0 J0 X% q+ o- N
len = LongToString(str, num2); //新数值转换为字符串' b2 N- o0 a* d1 e. _, A, g" z
LcdShowStr(16-len, 1, str); //显示到液晶第二行上
. v! H* I- Q; Y, y0 @ Z2 Y }- \1 K/ g- c/ `8 ?$ Y( p
}
# k) ^; c/ F7 x. {/* 运算符按键动作函数,运算符类型type */
) R5 {6 Y1 y" A. ^& pvoid OprtKeyAction(unsigned char type)% x7 V% q% \7 R9 n
{" u* u- F* K9 u, o& i6 ?
unsigned char len;
2 t/ V7 ?8 Y& V( g4 J8 M7 g4 ]/ }; ?4 n unsigned char str[12];
% L2 B* L6 Z- ~2 ~ v, i2 t e& K' n5 L( @
if (step == 0) //第二操作数尚未输入时响应,即不支持连续操作
& }5 h/ _8 W- T, X# d2 | {$ e8 {. T5 F) U7 c
len = LongToString(str, num1); //第一操作数转换为字符串6 F* I' `+ e( Y. V
LcdAreaClear(0, 0, 16-len); //清除第一行左边的字符位
/ S* F: P7 T2 W: t7 M+ h' I& @ LcdShowStr(16-len, 0, str); //字符串靠右显示在第一行
K& N7 G! X' I( n: f ShowOprt(1, type); //在第二行显示操作符
- _ \1 R. r$ v6 G; G% k LcdAreaClear(1, 1, 14); //清除第二行中间的字符位
) L ?8 W" g: o5 b. y% J2 I LcdShowStr(15, 1, "0"); //在第二行最右端显示0
$ O* l, R+ a5 Q oprt = type; //记录操作类型) p9 ?! c+ Y1 _8 J/ w% `
step = 1;
3 T% }& a C; L, N9 Z4 x" g }
- f& C* G9 y x' A: h; k" j: k1 u1 [}* q# v( K' M, m, y$ O- o, H
/* 计算结果函数 */
- @$ H6 I% g1 T2 F% Uvoid GetResult()
. A- M; P- J$ q$ k{
M2 q" [) m. @! w- ], m- H8 z' H unsigned char len;
6 W9 U2 l. R. _ unsigned char str[12];
3 v0 a& U* q* n4 n+ k
* T/ ^# i2 W, j' ~3 p8 ~& ?, C if (step == 1) //第二操作数已输入时才执行计算
4 c }/ N& [, c6 ?2 u { w# f4 ^0 y# p: D0 `; p# o) b
step = 2;
* ~; q0 y. P. ~ switch (oprt) //根据运算符类型计算结果,未考虑溢出问题6 Z+ d9 }) ]$ K* n8 c5 w
{
0 ~0 Q9 k6 R1 }3 s0 k! ~" ` case 0: result = num1 + num2; break;
" H: M: ]* j$ T2 m case 1: result = num1 - num2; break;
9 k/ m; E8 }# p- p- l& D6 }& d case 2: result = num1 * num2; break;
; @8 l0 K) _1 k3 U case 3: result = num1 / num2; break;
' a& m8 J6 K# Q0 o! x9 W5 u% N1 K( h2 R default: break;
. o4 u1 ~7 O6 p }# t3 R. h7 R8 ~ c% \
len = LongToString(str, num2); //原第二操作数和运算符显示到第一行
' W! L! S8 m2 B' u8 I ShowOprt(0, oprt);
8 s) X! G. r- x8 ~ LcdAreaClear(1, 0, 16-1-len);
+ Q: T9 Y5 k! z LcdShowStr(16-len, 0, str);6 U7 ?" M9 J8 I: \+ g! g$ D8 o2 z
len = LongToString(str, result); //计算结果和等号显示在第二行8 `/ x3 E) e. t |
LcdShowStr(0, 1, "=");1 }& ], ]2 e' p% W
LcdAreaClear(1, 1, 16-1-len);
: ]7 B$ q/ n1 H9 q4 A9 `( f LcdShowStr(16-len, 1, str);7 M+ u( C% `( \, s3 P9 x! Q" ^0 s% v
}
6 O! v: ~0 C2 F8 w: U}
( a o7 M( w4 N% }' S* a( Y/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
' j3 F, B/ l! ]0 f* i$ s) gvoid KeyAction(unsigned char keycode)
& s4 x3 L% s4 b! v" o{
- r* g, o) j9 K' k8 t if ((keycode>='0') && (keycode<='9')) //输入字符0 t; ^7 k5 Z# b6 e
{; V7 [3 ?- J% z) w4 x: n
NumKeyAction(keycode - '0'); P/ h) A$ u( g. S8 ^- Q" _
}
5 O$ k/ `+ m4 j# Q else if (keycode == 0x26) //向上键,+8 @0 g" W7 {( G- J/ o; n
{
* w% ?4 A" f& l" B+ U7 j v; Q0 a OprtKeyAction(0);
+ H$ z4 K {9 c8 f# D }8 k) ~4 G4 D% E5 z
else if (keycode == 0x28) //向下键,-
+ A2 m8 T" Y; k, @+ R {. z$ @/ q- X4 G+ }) X/ D6 i0 J5 |) G3 f3 [
OprtKeyAction(1);! M& T% f9 D5 P* d
}6 O# E! t" W$ Q2 e! E
else if (keycode == 0x25) //向左键,*
' [7 S) k8 i1 z! n. A f5 G {
6 ]" ~; d) y+ u OprtKeyAction(2);
; Z: D( c; c$ U( p; u% z7 \% w5 j }3 A0 j" K. y! K" Q) X
else if (keycode == 0x27) //向右键,÷
* I/ C0 R- J5 C/ v- W" A {
Z/ H s2 n8 i# J4 k OprtKeyAction(3);0 ~2 Z e: L3 J7 k6 Q
}
4 L) p8 Q3 j$ d6 L* S" i else if (keycode == 0x0D) //回车键,计算结果$ E: [, p# O. z2 b# j' p# c
{* C) l; U2 y) o. t
GetResult();
( a! R4 g' J" z& |) O7 o7 ` }) Z7 k. k( B# C8 s3 Q& d- \# b, E; ^
else if (keycode == 0x1B) //Esc键,清除
7 s$ U2 _ q+ f) s, C {: [/ Z5 ^4 e/ z$ i
Reset();/ t! ?8 e! m- _3 g
LcdShowStr(15, 1, "0");8 W0 y0 {' C9 m1 p( D0 m0 ^
}
4 H: x% d0 ?& h# K}- ^, Q; Z Z+ q, y, n# f: }# ?8 y2 `
/* 配置并启动T0,ms-T0定时时间 */
. X9 S4 y: D" f6 N; t; V% {void ConfigTimer0(unsigned int ms), c( e+ D: q- `7 B; t. m0 Z
{' p2 j! C6 }1 y V$ C; {6 t. G
unsigned long tmp; //临时变量
. ?: u v b! a2 `% l
( M: H( i1 N: M5 h9 e, D) V tmp = 11059200 / 12; //定时器计数频率
$ {' M2 ^; l- R4 F$ K tmp = (tmp * ms) / 1000; //计算所需的计数值
1 G- `- I/ d1 S9 A3 x2 Q: Q4 A tmp = 65536 - tmp; //计算定时器重载值* O9 c c7 O- t8 S
tmp = tmp + 28; //补偿中断响应延时造成的误差4 |: [" [: H1 f3 k9 E+ |1 g- `
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
$ R; f0 Q3 Y8 Y5 t( L+ Q1 { T0RL = (unsigned char)tmp;
! l3 z# T$ l$ {8 B TMOD &= 0xF0; //清零T0的控制位: D! T" f; I t o1 i$ l' {2 q
TMOD |= 0x01; //配置T0为模式1
4 g, D9 u6 [3 N" r2 ^# M5 g, Q. g TH0 = T0RH; //加载T0重载值0 A }: B2 U' A* r
TL0 = T0RL;
- n: Y" V' _$ \+ ~& Y ET0 = 1; //使能T0中断
/ L4 S# r5 r2 [" d3 N( l ~ TR0 = 1; //启动T0
( P' l5 ~) Y6 y% U2 F}; |2 [ T7 a# E1 l) B+ }2 j$ x
/* T0中断服务函数,执行按键扫描 */8 i, F" K. e3 b+ I# C4 U0 h+ ]
void InterruptTimer0() interrupt 1
2 X. ^; X0 i+ d/ n3 b{' m: H7 L& b' T l% [" m
TH0 = T0RH; //重新加载重载值& n2 g, g) g7 A* _
TL0 = T0RL;
7 B5 I% y& J1 ]! v; P6 I KeyScan(); //按键扫描" P5 e0 j$ i2 v0 P( z8 s: I
}
) b4 ]3 v% m& h- z |
|