标题:
用STC52的单片机做的一个简单的4位数的加减,乘除计算器
[打印本页]
作者:
happy112
时间:
2016-6-6 09:50
标题:
用STC52的单片机做的一个简单的4位数的加减,乘除计算器
用STC52的单片机做的一个简单的4位数的加减,乘除计算器,电路设计简单。附件内容包括四位数计算器仿真电路和实现源码。
仿真图:
0.png
(14.56 KB, 下载次数: 66)
下载附件
2016-6-6 09:44 上传
0.png
(65.06 KB, 下载次数: 65)
下载附件
2016-6-6 09:45 上传
原代码:
#include <REG52.H> //51单片机标准寄存器声明头文件
#include "bsp_GOG1.h" //这个头文件用于映射GOG1学习板载硬件接口
/*计算器的运算状态定义:*/
#define NoKey 0xaa //没有按键按下的状态
#define ErrKey 0xff //错误的按键状态/干扰
#define DpyErr 0x0e //错误显示状态(码表数组第14个元素:'E')
#define DpyCle 0x10 //清屏(码表数组第16个元素:0xff 关闭数码管)
#define InCount 0xf0 //有运算符输入状态
#define InErrEqu 0x0f //有等号输入状态
#define NoCountFlag 0xa5 //没有运算符的状态
/*矩阵按键 功能定义: */
#define ADD 15 //'#':加法 S15
#define SUB 12 //'C':减法 S12
#define MUL 14 //'*':乘法 S13
#define DIV 11 //'B':除法 S8
#define EQU 13 //'D':等于 S16
#define CLE 10 //'A':清除 S4
/*相关子函数的声明:*/
void delayms(unsigned int ms); //延时函数
void SegDisplay(unsigned char casebit); //数码管显示函数
unsigned char ReadKeyPad(void); //读取矩阵键盘函数
void Timer0Init(void); //定时器0初始化函数
unsigned char CheckInput(void); //计算器检查输入状态函数
void DatUnpack(unsigned int dat); //计算器数据拆分函数
bit CheckNum(unsigned int dat); //计算器数据有效性检查函数
void WarmDpy(unsigned char err); //计算器错误显示函数
void ComputeState(unsigned char dat); //计算器计算过程函数
//数码管段码表 共阳 17个元素: 0~F & 0xff
unsigned char code SegCode[17]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};
unsigned int Ans; //运算结果
bit AnsFlag=0; //运算结果存在标志
bit InFlag1=0; //输入有效数字1标志
bit InFlag2=0; //输入有效数字2标志
unsigned char CountFlag; //运算符
unsigned int temp1=0,temp2=0;//输入的2个运算的数字
unsigned char DisBuff[4]={DpyCle,DpyCle,DpyCle,DpyCle};//数码管显示缓存
/*******************************************************************************
* @brief mian (简介)
* @param 无 (参数)
* @retval 无 (返回值)
******************************************************************************/
void main(void) //程序从这里开始
{
unsigned char in; //保存单个按键值的变量
CountFlag=NoCountFlag; //运算符初始状态
Timer0Init(); //定时器0初始化
while(1)
{
in=CheckInput();
while(in == NoKey) in=CheckInput(); //没有按键按下,在此等待
if(in !=ErrKey) //按键有效
{
ComputeState(in);
}
else WarmDpy(DpyErr); //按键无效,报错 'E'
}
}
/*******************************************************************************
* @brief ComputeState: 计算过程程序
* @param unsigned char dat
* @retval 无
*******************************************************************************/
void ComputeState(unsigned char dat)
{
unsigned int num; //保存运算操作数的变量
if(AnsFlag == 1) //判断上一次运算结果完成标志
{
WarmDpy(DpyCle); //清屏
AnsFlag=0; //清除有效运算完成标志
}
if((dat !=InCount)&(dat !=InErrEqu)&(dat <10)) //按下的键为数字
{
if(CountFlag == NoCountFlag) //没有运算符存在,保存第一个数
{
num = temp1;
num *= 10; //输入的数字依次进高位
num += dat;
if( CheckNum(num)==1 ) //判断数据有效性
{ //有效在数字范围
temp1 = num; //保存第一个数字
InFlag1 = 1; //输入有效数字1标志
DatUnpack(temp1); //拆分数据,更新显示缓存
}
else WarmDpy(DpyErr); //超出范围报错
}
else //运算符存在 ,保存第二个数
{
num = temp2;
num *= 10;
num += dat;
if(CheckNum(num)==1)
{
temp2 = num;
InFlag2 = 1; //输入有效数字2标志
WarmDpy(DpyCle); //清除第一个数的显示,消除'余晖'
DatUnpack(temp2);//更新显示缓存
}
else WarmDpy(DpyErr);
}
}
else //按下的键为非数字(4则运算符被保存,这里考虑 清除键和 错误等号)
{
if(CLE == dat) //按下的为清除键
{
CountFlag = NoCountFlag; //清除运算符
InFlag1 =0; //清除输入有效数字1标志
InFlag2 =0; //清除输入有效数字2标志
AnsFlag=0; //清除运算结果存在标志
temp1=0,temp2=0; //清除输入的2个运算数
WarmDpy(DpyCle); //清屏
Ans=0; //清除上一次运算结果
}
if(InErrEqu == dat) //运算表达式不完整时,按下等号的情况
WarmDpy(DpyErr); //报错 'E'
}
}
/*******************************************************************************
* @brief ReadKeyPad 用于读取矩阵键盘键值
* @param 无
* @retval 矩阵键盘键值
* @note 键盘键值设置请修改case分支临时变量b的赋值
0xff 作为错误代码,表示读取出错
******************************************************************************/
unsigned char ReadKeyPad(void)
{
unsigned char a,c,b=NoKey; //b 初始值为无按键的状态
KeyPad = 0x0f; //初始状态,行号(P3^0~P3^3)高电平,列号(P3^4~P3^7)低电平
if(KeyPad != 0x0f)
{
delayms(20); //按键消抖动(延时实现)
if(KeyPad != 0x0f) //按键被按下,初始状态改变
{
a = KeyPad; //读取矩阵键盘的行号
}
KeyPad = 0xf0; //初始状态反转
c = KeyPad; //读取矩阵键盘的列号
a |= c; //按位'或',通过行号,列号唯一确定矩阵按键值
switch (a) {
case 0xee:
b = 1; //S1
break;
case 0xed:
b = 4; //S5
break;
case 0xeb:
b = 7; //S9
break;
case 0xe7:
b = MUL; //S13'MUL'
break;
case 0xde:
b = 2; //S2
break;
case 0xdd:
b = 5; //S6
break;
case 0xdb:
b = 8; //S10
break;
case 0xd7:
b = 0; //S14
break;
case 0xbe:
b = 3; //S3
break;
case 0xbd:
b = 6; //S7
break;
case 0xbb:
b = 9; //S11
break;
case 0xb7:
b = ADD; //S15 'ADD'
break;
case 0x7e:
b = CLE; //S4 'CLE'
break;
case 0x7d:
b = DIV; //S8 'DIV'
break;
case 0x7b:
b = SUB; //S12 'SUB'
break;
case 0x77:
b = EQU; //S16 '='
break;
default : //没有和 a 的匹配项
b = ErrKey; //错误的按键值
break;
}
KeyPad = 0xf0; //松手检测
while (KeyPad != 0xf0); //当没有松手,将在此一直等待
}
return (b); //返回读取的按键值
}
/******************************************************************************
* @brief delayms 毫秒级延时函数
* @param ms 延时的毫秒数 允许值 unsigned int范围
* @retval 无
* @attention 这个函数只是用于12T 8051内核的单片机运行于12Mhz
*****************************************************************************/
void delayms(unsigned int ms) //延时子程序(晶振12Mhz)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}
/******************************************************************************
* @brief SegDisplay 数码管显示&定时器中断程序组成动态显示
* @param casebit 用于选择数码管的位 允许值 0~4
* @retval 无
* @attention 这个函数需配合定时器中断服务程序
*****************************************************************************/
void SegDisplay(unsigned char casebit)
{
Seg7_Bits = 0xff; //关闭所有数码管
Seg7_Data =SegCode[DisBuff[casebit]];//先把段码值赋给P1(段选端口)
switch(casebit)
{
case 0:
Seg7_Bit1 = 0;
break;
case 1:
Seg7_Bit2 = 0;
break;
case 2:
Seg7_Bit3 = 0;
break;
case 3:
Seg7_Bit4 = 0;
break;
default :
Seg7_Bits = 0xff; //关闭所有数码管
Seg7_Data = 0xff;
break;
}
}
/************************************************************************************
* @brief DatUnpack:数据拆分,同时把数据的千位,百位,十位,个位赋给显示缓存数组DisBuff[]
* @param unsigned int dat
* @retval 无
***********************************************************************************/
void DatUnpack(unsigned int dat)
{
if((dat<10)) //1位数
DisBuff[0]=dat;
else if((dat<100)&&(dat>=10)) //2位数
{
DisBuff[1]=dat/10;
DisBuff[0]=dat%10;
}
else if((dat<1000)&&(dat>=100)) //3位数
{
DisBuff[2]=dat/100;
DisBuff[1]=dat%100/10;
DisBuff[0]=dat%100%10;
}
else if ((dat<10000)&&(dat>=1000))//4位数
{
DisBuff[3]=dat/1000;
DisBuff[2]=dat%1000/100;
DisBuff[1]=dat%1000%100/10;
DisBuff[0]=dat%1000%100%10;
}
}
/*******************************************************************************
* @brief unsigned char CheckInput(): 检查矩阵键盘输入按键的类型(简介)
* @param 无 (参数)
* @retval 数字,功能键(CLE,EQU,+,-,*,/),无按键的状态 (返回值)
******************************************************************************/
unsigned char CheckInput(void)
{
unsigned char x;
x=ReadKeyPad(); //调用读按键子程序
if(x != ErrKey) //是否为错误按键值
{
if((x<10)|(x == NoKey)| (x == CLE))//按下的为数字,或没有按键按下,或清除键
{
return (x);
}
else //按下的为 运算符(四则运算 和 等号)
{
if(x == EQU) // 按下的为 等号"="
{
switch (CountFlag)
{
case ADD: Ans = temp1+temp2; break;
case SUB: Ans = temp1-temp2; break;
case MUL: Ans = temp1*temp2; break;
case DIV: if (temp2 == 0) //除法分母为0,报错
{
WarmDpy(DpyErr);
break;
}
else
{
Ans = temp1/temp2;//只计算除法商的整数,暂没有考虑小数
break;
}
}
if( CheckNum(Ans)&&(InFlag1 ==1)&&(InFlag2 ==1))//检测运算的有效性
{
DatUnpack(Ans); //运算结果拆分,更新显示缓存
CountFlag = NoCountFlag; //清除运算符
temp1=0,temp2=0; //清除运算数字
AnsFlag = 1; //运算结果存在标志
InFlag1 =0; //清除有效数字输入标志
InFlag2 =0;
Ans=0; //清除运算结果
return (NoKey); //此次运算完成,返回无按键状态
}
else //WarmDpy(DpyErr) ;//运算表达式不完整或结果超出范围,报错
return (InErrEqu); //返回有等号输入的状态
}
else //按下的为 4则 运算符(+ ,-,*, /)
{
CountFlag = x; //保存运算符
return (InCount); //返回有运算符输入的状态
}
}
}
else return (ErrKey); //返回错误按键值状态
}
/*******************************************************************************
* @brief CheckNum: 检查计算器运算数的有效性
* @param dat
* @retval bit 有效: 1,无效: 0
******************************************************************************/
bit CheckNum(unsigned int dat)
{
if (dat < 10000)
return 1; //数据有效
else
return 0;
}
/*******************************************************************************
* @brief WarmDpy:数码管错误显示
* @param err 实则:'E'的码段值元素的下标
* @retval 无
******************************************************************************/
void WarmDpy(unsigned char err)
{
DisBuff[0]=err; // 错误提示显示 'E'
DisBuff[1]=err;
DisBuff[2]=err;
DisBuff[3]=err;
}
/******************************************************************************
* @brief InitTimer0 用于初始化51单片机定时器0
* @param 无
* @retval 无
* @attention 这个函数只是用于12T 8051内核的单片机运行于12Mhz晶体的情况
******************************************************************************/
void Timer0Init(void) //250微秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x02; //设置定时器模式
TL0 = 0x06; //设置定时初值
TH0 = 0x06; //设置定时重载值
ET0 = 1; //打开定时器0中断
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1; //打开总中断
}
/******************************************************************************
* @brief Timer0Interrupt 51单片机定时器0中断服务程序
* @param 无
* @retval 无
* @attention 这个函数后的修饰符 “interrupt 1” 不可修改
******************************************************************************/
void Timer0Interrupt(void) interrupt 1
{
static unsigned char SegDbit;
if(SegDbit == 4)
{
SegDbit=0;
Led2=~Led2; //D3:1ms闪烁一次,系统正常运行
}
SegDisplay(SegDbit);//数码管逐位显示
SegDbit++; //每进一次中断,自加1,把下一个数据赋给下一个数码管
}
复制代码
/*注意上面头文件"bsp_GOG1.h"包含一个可选宏定义
若需使用扩展板请增加全局宏定义 _GOG1Plus
定义方式
菜单 Project->Options for Target->C51->Define
在此处填入 “_GOG1Plus”
在此代码完全兼容,第三次课的矩阵按键程序,在此感谢魏同学的帮助 */
/*
计算器功能实现:4位数字的加,减,乘法,除法没有做商的小数部分
1.对输入运算数的有效性检查,即超过9999时,显示错误;
2,用LED--》D3来指示系统的正常运行状态
3,运算的结果超过9999时,提示错误显示;
4,当系统运行错误时,或者显示乱码时,按A键清除数码管显示;
5,除法运算时,的二个运算数为0时,提示错误显示
6, 运算表达式不完整时,报错:
(1)无数 * 第二个数 = 报错
(2)第一个数 * 无数 = 报错
(3)直接按等号,报错
7,D3 LED灯实际是隔1ms 闪烁一次,
*/
/*矩阵按键 功能定义:
#define ADD 15 //'#':加法 S15
#define SUB 12 //'C':减法 S12
#define MUL 14 //'*':乘法 S13
#define DIV 11 //'B':除法 S8
#define EQU 13 //'D':等于 S16
#define CLE 10 //'A':清除 S4
全部资料(压缩包)下载:
仿真文件:
counter.rar
(16.63 KB, 下载次数: 36)
2016-6-6 09:47 上传
点击文件名下载附件
下载积分: 黑币 -5
GOG1计算器程序(v2.0).zip
(98.76 KB, 下载次数: 38)
2016-6-6 09:47 上传
点击文件名下载附件
下载积分: 黑币 -5
readme.rar
(787 Bytes, 下载次数: 25)
2016-6-6 09:47 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
wangqiming000
时间:
2017-7-4 08:43
看上去好厉害
作者:
紫山
时间:
2017-12-31 18:08
不行,下载后不能直接用,要重新编译和导入hex文件
作者:
zhenghua007
时间:
2018-1-7 07:33
谢谢楼主分享,下载了研究看看。
作者:
o泡果奶
时间:
2018-2-13 15:57
编译出错 太黑了 3文件 可以赚15黑币 有心计
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1