用蓝桥杯CT107D单片机做了一个简易计算器,能实现int型的加减乘除运算
使用1602显示屏作为显示器,矩阵键盘作为按键输入
本文的代码只是刚学单片机随性而为的,并没有考虑可读性,最近很多人因为这篇博客,订阅了专栏,实在诚惶诚恐,所以今天略作修改,如有进一步需要,可以私信我提供帮助
main函数
#include<Matrix_keyboard.h> //矩阵键盘
#include<1602.h> //液晶显示,
#include<stdio.h>
#include<stdlib.h>
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
#define N 5
long n1, m1;
char ysf; //运算符
long digit[N];
char xdata sym[N];
u8 i = 0; //判断数字冲突
unsigned char xdata flag = 0; //判断运算符冲突
unsigned char xdata dengyuflag = 0;
unsigned char xdata index1 = 0;
unsigned char xdata index2 = 0;
u8 m = 16;
u8 x = 0; //第二行
u8 y = 0; //第一行
void push1(long num) {
digit[index1++] = num;
}
long gettop1() {
return digit[index1 - 1];
}
void pop1() {
index1--;
}
void clear1() {
index1 = 0;
}
int feikong1() {
if (index1 > 0)
return 1;
else return 0;
}
void push2(unsigned char num) {
sym[index2++] = num;
}
char gettop2() {
return sym[index2 - 1];
}
void pop2() {
if (index2 > 0)
index2--;
}
void clear2() {
index2 = 0;
}
int feikong2() {
if (index2 > 0)
return 1;
else return 0;
}
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
u8 weishu(long n) {
u8 cnt = 0;
if (n < 0)cnt++;
while (n) {
n /= 10;
cnt++;
}
return cnt;
}
u8 code symbol[] = "789/456*123-c0=+";
bit key_flag=1;
void main() {
Timer0Init();
LCD1602_Init( );
while (1) {
if(key_flag)
{
key_flag=0;
m = read_keyboard();
if (m != 16) {
if (symbol[m] <= '9' && symbol[m] >= '0') {
flag = 0;
if (dengyuflag == 1) {
y = 0;
dengyuflag = 0;
i = 1;
pop1();
push1(symbol[m] - '0');
disnum_LCD(symbol[m] - '0', 0, y++);
} else {
disnum_LCD(symbol[m] - '0', 0, y++);
if (i == 1) { //如果上次还是数字
long st = gettop1();
pop1();
push1(st * 10 + symbol[m] - '0');
}
if (i == 0) { //如果上次是运算符
i = 1;
push1(symbol[m] - '0');
}
}
} else if (symbol[m] == 'c') {
if (dengyuflag == 1) {
dengyuflag = 0;
}
x = 0;
y = 0;
Clear_LCD();
clear1();
clear2();
disch_LCD('0', 0, 0);
} else if (symbol[m] == '=') {
if (flag == 1)
pop2();
dengyuflag = 1;
flag = 1;
i = 0;
if (feikong2() == 1) {
m1 = gettop1();
pop1();
n1 = gettop1();
pop1();
ysf = gettop2();
pop2();
switch (ysf) {
case '+':
push1(m1 + n1);
Clear_LCD();
y = 0;
disnum_LCD(n1 + m1, 0, 0);
y += weishu(n1 + m1);
break;
case '-':
push1(n1 - m1);
Clear_LCD();
y = 0;
disnum_LCD(n1 - m1, 0, 0);
y += weishu(n1 - m1);
break;
case '*':
push1(m1 * n1);
Clear_LCD();
y = 0;
disnum_LCD(n1 * m1, 0, 0);
y += weishu(n1 * m1);
break;
case '/': {
if (m1 == 0) {
Clear_LCD();
clear1();
clear2();
y = 0;
disstr_LCD("NAN", 0, 0);
} else {
push1(n1 / m1);
Clear_LCD();
y = 0;
disnum_LCD(n1 / m1, 0, 0);
y += weishu(n1 / m1);
}
}
break;
}
} else if (feikong2() == 0) {
y = 0 + weishu(gettop1());
Clear_LCD();
disnum_LCD(gettop1(), 0, 0);
}
} else if (flag == 0) { //如果上次不是运算符
flag = 1;
disch_LCD(symbol[m], 0, y++);
i = 0;
if (feikong2() == 1) {
m1 = gettop1();
pop1();
n1 = gettop1();
pop1();
ysf = gettop2();
pop2();
switch (ysf) {
case '+':
push1(m1 + n1);
disnum_LCD(m1 + n1, 1, 0);
break;
case '-':
push1(n1 - m1);
disnum_LCD(n1 - m1, 1, 0);
break;
case '*':
push1(m1 * n1);
disnum_LCD(m1 * n1, 1, 0);
break;
case '/':
if (m1 == 0) {
Clear_LCD();
clear1();
clear2();
y = 0;
disstr_LCD("NAN", 0, 0);
break;
}
push1(n1 / m1);
disnum_LCD(n1 / m1, 1, 0);
break;
}
push2(symbol[m]);
}
if (feikong2() == 0) {
push2(symbol[m]);
}
} else if (flag == 1) { //上次也是运算符
if (dengyuflag == 1) {
dengyuflag = 0;
disch_LCD(symbol[m], 0, y++);
push2(symbol[m]);
} else {
disch_LCD(symbol[m], 0, y - 1);
pop2();
push2(symbol[m]);
}
}
}
}
}
}
void Time0(void) interrupt 1 { //1ms中断一次
static u8 i;
if(++i==10){
i=0;
key_flag=1;
}
}
矩阵键盘.C模块
#include<Matrix_keyboard.h>
#include "STC15F2K60S2.H"
unsigned char read_keyboard(void)
{
static unsigned char state=0;//state=0,进入第一次扫描;state=1;进入第二次扫描
static unsigned char value=16;//按键返回值,直到按键释放后返回该值
static unsigned char hang;//第一次获得哪一行?
switch(state)
{
//进入第一次检测,检测哪一行被按下
case 0:
P3=0X0F;P42=0;P44=0;//按键的行值置1,列值置0;
if(P3!=0X0F)//如果按键值改变了,就判断是哪一行
{
if(P30==0){hang=0;state=1;}//第一行
if(P31==0){hang=1;state=1;}//第二行
if(P32==0){hang=2;state=1;}//第三行
if(P33==0){hang=3;state=1;}//第四行
}else state=0;//无按键按下,停在第一次检测
break;
//第二次检测,检测哪一列被按下
case 1:
P3 = 0XF0;P42=1;P44=1;//列值置1,行值置0
if(P44==0)value=hang*4;//第一列四行的值,0 4 8 12
if(P42==0)value=hang*4+1;//第二列四行的值,1 5 9 13
if(P35==0)value=hang*4+2;//第三列四行的值,2 6 10 14
if(P34==0)value=hang*4+3;//第四列四行的值,3 7 11 15
break;
}
//等待按键释放
P3=0X0F;P42=0;P44=0;//按键的行值置1,列值置0;
if(state==1&&P3==0X0f)
{
state =0;
return value;
}
return 16;
}
矩阵键盘.h
#ifndef __Matrix_keyboard_H_
#define __Matrix_keyboard_H_
#include "STC15F2K60S2.H"
unsigned char read_keyboard( void );
#endif
1602.c显示模块
#include "STC15F2K60S2.H"
#include<1602.h>
#include<intrins.h>
#define Data P0
sbit LCDEN = P1^2;
sbit RS = P2^0;
sbit RW = P2^1;
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
unsigned char Judg_LCD(void)//判断LCD忙/闲
{ //result忙闲标志,最高位D7为标志位,1忙0闲
unsigned char result;
Data = 0xff;//先置高电平,防止误判
RS = 0;
Delay5ms();
RW = 1;//RS=0,RW = 1;忙标志和地址计数器写出
LCDEN = 1;//使能信号开
Delay5ms();
result = Data & 0x80;//
LCDEN = 0;//使能信号关
return result;
}
void Wr_LCD(unsigned char m)//写入命令函数
{
while(Judg_LCD()==1);
RS = 0;
Delay1ms();
RW = 0; //RS = RW =0;命令寄存器写入
LCDEN = 1;
Delay1ms();
Data = m;//写入数据
Delay1ms();
LCDEN = 0;
}
void Write_LCD(unsigned char Datval)//写入数据
{
while(Judg_LCD()==1);
RS = 1;
Delay1ms();
RW = 0;//RS = 1;RW =0;数据寄存器写入
LCDEN = 1;
Data = Datval;//写入数据
Delay1ms();
LCDEN = 0;
}
unsigned char RdCAdr(void)//读取当前光标地址
{
unsigned char result;
Data = 0xff;//先置高电平
RS = 0;
Delay5ms();
RW = 1;//RS =0;RW=1;地址计数器读出
LCDEN =1;
Delay5ms();
result = Data&0x7f;//去掉最高位忙闲标记,得低七位数据地址
LCDEN = 0;
return result;
}
void retCadr(unsigned char add)
{
Wr_LCD(0x80+add);
}
void LCDpos(unsigned char x,unsigned char y)
{
unsigned char t;
t = x?0x40:0x00;
Wr_LCD(0x80+t+y);
}
void LCD1602_Init( )//1602初始化函数
{
Wr_LCD(0x38);
Wr_LCD(0x38);
Wr_LCD(0x38);
Wr_LCD(0x01);//清屏
Wr_LCD(0x06);//光标自增,屏幕不动
Delay1ms();
Wr_LCD(0x0c);//开显示、关光标
LCDpos(0,0);//从第一行第一个字符开始显示
Delay5ms();
}
//从指定位置显示字符串
void disstr_LCD(unsigned char *p,unsigned char x,unsigned char y)//
{
unsigned char i = 0;
LCDpos(x,y);//指定坐标显示
while(p[i]!='\0')
{
Write_LCD(p[i]);
i++;
Delay5ms();
}
}
void disch_LCD(unsigned char ch,unsigned char x,unsigned char y)//指定位置显示字符
{
LCDpos(x,y);
Write_LCD(ch);
Delay5ms();
}
void disnum_LCD(long n,unsigned char x,unsigned char y)
{
unsigned char i=0,j=0;
unsigned char p[17],q[17]=" ";//清除第二行
long m;
if(n<0) m=-n;
else m =n;//显示正数
if(n==0)
{
disch_LCD('0',x,y);
return;
}
while(m)
{
p[i++] = m%10+'0';
m=m/10;
}
p[i--]='\0';
if(n<0)
q[j++]='-';
while(i)
q[j++] = p[i--];
q[j++] = p[i];
disstr_LCD(q,x,y);
}
void ch_LCD(unsigned ch)//当前位置显示字符
{
Write_LCD(ch);
Delay5ms();
}
void Clear_LCD(void)//清屏
{
Wr_LCD(0x01);
}
void Return_LCD(void)//返回
{
Wr_LCD(0x02);
}
void Backspace_LCD()//向左删除一个字符
{
Wr_LCD(0X10);//光标左移
Write_LCD(' ');//输出空格
Wr_LCD(0X10);//光标左移
}
void Cursor_LCD(unsigned char d)//d=0,光标左移,d=1,右移
{
if(d)
Wr_LCD(0x14);//右移
else
Wr_LCD(0X10);//左移
}
void Screen_LCD(unsigned char d)//d=0,左移,d=1,右移
{
if(d)
Wr_LCD(0x1C);//右移
else
Wr_LCD(0x18);//左移
}
void OpCl_LCD(bit d)//LCD开关
{
if(d==1)
Wr_LCD(0x0f);
else if(d==0)
Wr_LCD(0x08);
}
1602.h
//在调用1602相关函数前,需要做1602的初始化
#ifndef _1602_H_
#define _1602_H_
#include "STC15F2K60S2.H"
void LCD1602_Init( );
void disstr_LCD(unsigned char *p,unsigned char x,unsigned char y);
void disnum_LCD(long n,unsigned char x,unsigned char y);
void disch_LCD(unsigned char ch,unsigned char x,unsigned char y);
void ch_LCD(unsigned ch);//当前位置显示字符
void Clear_LCD(void);//清屏
void Return_LCD(void);//返回
void Backspace_LCD();//向左删除一个字符
void Cursor_LCD(unsigned char d);//d=0,光标左移,d=1,右移
void Screen_LCD(unsigned char d);//d=0,左移,d=1,右移
void OpCl_LCD(bit d);//LCD开关
#endif