1787|1

6809

帖子

0

TA的资源

五彩晶圆(高级)

楼主
 

简易单片机计算器的实现 [复制链接]

单片机源程序如下:
#include           //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 无
  ******************************************************************************/
……………………


最新回复

按键消抖最好用定时器加状态机  详情 回复 发表于 2019-2-14 09:07
 
点赞 关注

回复
举报

1371

帖子

6

TA的资源

版主

沙发
 
按键消抖最好用定时器加状态机
 
个人签名专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表