所需硬件以及软件

1.CT107D开发板
2.png
2.8段数码管
IMG_20191207_220425.jpg
3.矩阵键盘
IMG_20191207_220729.jpg
4.STC89C52RC单片机
IMG_20191207_220448.jpg
5.keil V5
6.visual studio code
7.stc-isp

程序思路

基于51单片机的计算器我觉得实现加、减、乘、除的功能就已经很满足了。
所以我们这里采用的8段数码管,所以计算的最大数为0~99999999。
以下是键位图
QQ截图20191207221833.png
这里说一下大体思路。
首先,我们数码管初始化是全部灭掉。
然后我们数码管动态扫描显示的话需要用到定时器0。
代码如下:

void Init_timer0()   //定时器初始化函数
{
    TMOD = 0x01; //设置定时器0的模式为1
    TH0 = 45535/256;//给寄存器初值
    TL0 = 45535%256;//定时时间为20000us动态扫描一次
    EA = 1;         //开总中断
    ET0 = 1;        //开定时器0允许中断
    TR0 = 1;        //开定时器0中断
}

void timer0() interrupt 1        //定时器0中断服务函数
{
    uchar x;
    TH0 = 45535/256;         //重装初值
    TL0 = 45535%256;
    for(x = 0;x < 8;x ++)
    {
        Digital_Tube_main(x,*(LED_Value+x));  //循环8次,也就是8个段都要扫描一遍
    }
}

可能有些单片机初学者会问为什么会在定时器里来执行显示函数?
答:我们在main函数里会用扫描的方式来实现矩阵键盘的输入,在扫描的过程中数码管就无法正常显示,所以我们这里用定时器中断来实现数码管的显示。

接下来就是一直等待键盘输入了。
每输入一位,那么数码管就往左移一位。
代码如下:

uchar LED_num=0;   //定义按键次数的变量
void Key_get_Value_up()   //数码管左移子程序
{
    uchar x = 7,z = 0,y = 0;
    if (LED_num != 0)   //如果一次没按则不执行
        {
            y = LED_num;
            for (z = LED_num;z > 0;z--)   //根据按键次数来决定右移几次
            {
            LED_Value[x-y] = LED_Value[(x-y+1)];  //试数据右移一位
            y--;
            }
        }
}

等我们输入完毕后,当我们按下加减乘除键中的任意一个,那么我们要先来一次数据拼接。因为我们暂存输入进来的数据是用的数组来存储的。所以我们需要先来一次数据拼接。
代码如下:

void Key_value_and1()        //拼接第一次输入的数据
{
    uchar y=0,i = 0;
    y = LED_num;
    for (i = 0; i < LED_num; i++)  //利用for循环实现数据拼接
    {
        if(i!=0)
        Num_save1 *= 10;   
        Num_save1 += LED_Value[8 - y];
        y--;
    }
    LED_num = 0;
    memset(LED_Value,16,sizeof(LED_Value));//把缓存数组全部给16,也就是数码管全部熄灭,等待下一次输入
}

拼接完成后,我们等待下一次输入。
同样当我们按下等于键后,先数据拼接,再来运算,最后输出。
代码如下:

void Key_value_add()          //数据处理
{
    unsigned long flag;
    uchar y = 8,i = 0;
    Key_value_and2();    //获取第二次输入的数据
    switch (flag1)
    {
    case 1:flag = Num_save1 + Num_save2;    //加法
        break;
    case 5:flag = Num_save1 - Num_save2;    //减法
        break;
    case 9:flag = Num_save1 * Num_save2;    //乘法
        break;
    case 13:flag = Num_save1 / Num_save2;    //除法
        break;
    /*default:
        break;*/
    }
    Num_save2 = Num_save1 = 0;
    *(LED_Value + 0) = flag % 100000000 / 10000000;//拆
    *(LED_Value + 1) = flag % 10000000 / 1000000;  //分
    *(LED_Value + 2) = flag % 1000000 / 100000;    //数
    *(LED_Value + 3) = flag % 100000 / 10000;      //据
    *(LED_Value + 4) = flag % 10000 / 1000;        //
    *(LED_Value + 5) = flag % 1000 / 100;          //
    *(LED_Value + 6) = flag % 100 / 10;            //
    *(LED_Value + 7) = flag % 10;                  //
    for(i = 0;i < 8;i ++)
    {
        if(*(LED_Value + i) == 0)
        {
            *(LED_Value + i) = 16;             //如果值是零则设置16
        }
        else 
        {
            break;                            //如果不是零则退出
        }
    }
}

我们运算完成后输出的时候需要拆分一下数据,不然会乱码的。

当我们运算完成一次后,再次点击等于键就相当于清屏键。
对了,还有一个退格键,其实思路跟输入数据的时候差不多。
退格就是数据右移。我们直接看代码:

void Key_get_Value_down()//数码管右移子程序
{
    uchar x = 7,z = 0;
    if (LED_num != 0)       //如果一次没按则不执行
        {
            for (z = 7;z > 0;--z)
            {
                LED_Value[z] = LED_Value[z-1];      //根据按键次数来决定左移几次
                LED_Value[z-1] = 16;                //左移后空出来的位数清零
                
            }
            LED_num--;
            if (!(LED_num >= 0))
            {
                LED_num = 0;         //防止LED_num等于负数
            }
            
        }
}

以上就是全部思路。

总结

1.我觉得这个程序最难的就在于数据处理,因为单片机这东西不像计算机这么厉害,还必须考虑单片机的内存大小。
2.数据的左移和右移,可能这对精通C语言的大神不是啥问题。
3.消影的问题,我推荐各位朋友以后写关于数码管的程序的时候都加上消影。很简单,就是把位选全部打开,然后给个0x00或者0xff 具体是啥根据你的数码管是共阳极还是共阴极。

最后

下面我将贴出我写的代码,大神别喷哟!

#include <regx52.h>   //51单片机标准头文件
#include <intrins.h>  //空操作需要的头文件
#include <string.h>   //对数组操作的头文件
#include <math.h>     //数学公式

#define uint unsigned int      //宏定义
#define uchar unsigned char
    
#define dataa P0   //数据口,连接几个锁存芯片

uchar code wei[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0x00};     //数码管位码
uchar code duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};//数码管段码

uchar LED_Value[8]={16,16,16,16,16,16,16,16};      //数码管8位缓存数组
void delay1ms(uint i)   //延迟子程序 time:1ms
{
    uchar x,y;
    for(x=i;x>0;x--)
    {
        for(y=110;y>0;y--)
        {
            _nop_();
        }
    }
}

void delay1us(uint i)//延迟子程序 time:1us
{
    while(i--);
}

void Write_wei(uchar datt)  //写位码
{
    P2 |= 0xc0;     //打开数码管位所对应的锁存器
    dataa = datt;   //送数据
    P2 &= 0x1f;     //关锁存
}

void Write_duan(uchar datt) //写段码
{
    P2 |= 0xe0;      //打开数码管段选所对应的锁存器
    dataa = 0xff;    //清屏
    delay1us(100);   //延迟
    dataa = datt;    //送数据
    delay1us(100);   //延迟
    dataa = 0xff;    //清屏
    P2 &= 0x1f;      //关锁存器
}

void Digital_Tube_main(uchar x,y) //数码管显示子程序
{
    Write_wei(~(*(wei+x)));       //送位码
    delay1us(1);           
    Write_duan(*(duan+y));       //送段码
    delay1us(1);
}
uchar flag1=0;     //定义记录Key_Value 的变量
void Key_Value();  //声明子程序
uchar LED_num=0;   //定义按键次数的变量
void Key_get_Value_up()   //数码管左移子程序
{
    uchar x = 7,z = 0,y = 0;
    if (LED_num != 0)   //如果一次没按则不执行
        {
            y = LED_num;
            for (z = LED_num;z > 0;z--)   //根据按键次数来决定右移几次
            {
            LED_Value[x-y] = LED_Value[(x-y+1)];  //试数据右移一位
            y--;
            }
        }
}

void Key_get_Value_down()//数码管右移子程序
{
    uchar x = 7,z = 0;
    if (LED_num != 0)       //如果一次没按则不执行
        {
            for (z = 7;z > 0;--z)
            {
                LED_Value[z] = LED_Value[z-1];      //根据按键次数来决定左移几次
                LED_Value[z-1] = 16;                //左移后空出来的位数清零
                
            }
            LED_num--;
            if (!(LED_num >= 0))
            {
                LED_num = 0;         //防止LED_num等于负数
            }
            
        }
}

unsigned long Num_save1 = 0;       //暂存第一次输入的数据
unsigned long Num_save2 = 0;       //暂存第二次输入的数据
 
void Key_value_and1()        //拼接第一次输入的数据
{
    uchar y=0,i = 0;
    y = LED_num;
    for (i = 0; i < LED_num; i++)  //利用for循环实现数据拼接
    {
        if(i!=0)
        Num_save1 *= 10;
        Num_save1 += LED_Value[8 - y];
        y--;
    }
    LED_num = 0;
    memset(LED_Value,16,sizeof(LED_Value));
}
void Key_value_and2()        //拼接第二次输入的数据
{
    uchar y=0,i = 0;
    y = LED_num;
    for (i = 0; i < LED_num; i++)     //利用for循环实现数据拼接
    {
        if(i!=0)
        Num_save2 *= 10;
        Num_save2 += LED_Value[8 - y];
        y--;
    }
    LED_num = 0;
}

void Key_value_add()          //数据处理
{
    unsigned long flag;
    uchar y = 8,i = 0;
    Key_value_and2();    //获取第二次输入的数据
    switch (flag1)
    {
    case 1:flag = Num_save1 + Num_save2;    //加法
        break;
    case 5:flag = Num_save1 - Num_save2;    //减法
        break;
    case 9:flag = Num_save1 * Num_save2;    //乘法
        break;
    case 13:flag = Num_save1 / Num_save2;    //除法
        break;
    /*default:
        break;*/
    }
    Num_save2 = Num_save1 = 0;
    *(LED_Value + 0) = flag % 100000000 / 10000000;//拆
    *(LED_Value + 1) = flag % 10000000 / 1000000;  //分
    *(LED_Value + 2) = flag % 1000000 / 100000;    //数
    *(LED_Value + 3) = flag % 100000 / 10000;      //据
    *(LED_Value + 4) = flag % 10000 / 1000;        //
    *(LED_Value + 5) = flag % 1000 / 100;          //
    *(LED_Value + 6) = flag % 100 / 10;            //
    *(LED_Value + 7) = flag % 10;                  //
    for(i = 0;i < 8;i ++)
    {
        if(*(LED_Value + i) == 0)
        {
            *(LED_Value + i) = 16;             //如果值是零则设置16
        }
        else 
        {
            break;                            //如果不是零则退出
        }
    }
}

void Key_text()
{
    uchar flag3;
    //static uchar x=7,flag2; 
    P3 = 0x0f;
    delay1us(10);
    while(P3 == 0x0f);
    flag3 = P3;
    if(flag3 != 0x0f)
    {
        delay1ms(10);
        if(flag3 != 0x0f)
        {
            flag3 = P3;
            P3 = 0xf0;
            delay1us(1);
            flag3 += P3;
        }
        switch (flag3)
        {
        case 0x7e:
            flag1 = 1;
            Key_value_and1();
            break;
        case 0xbe:
            //flag1 = 2;
            Key_get_Value_up();
            LED_Value[7] = 1;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
            break;
        case 0xde:
            //flag1 = 3;
            Key_get_Value_up();
            LED_Value[7] = 2;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xee:
            //flag1 = 4;
            Key_get_Value_up();
            LED_Value[7] = 3;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0x7d:
            flag1 = 5;
            Key_value_and1();
            break;
        case 0xbd:
            //flag1 = 6;
            Key_get_Value_up();
            LED_Value[7] = 4;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xdd:
            //flag1 = 7;
            Key_get_Value_up();
            LED_Value[7] = 5;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xed:
            //flag1 = 8;
            Key_get_Value_up();
            LED_Value[7] = 6;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0x7b:
            flag1 = 9;
            Key_value_and1();
            break;
        case 0xbb:
            //flag1 = 10;
            Key_get_Value_up();
            LED_Value[7] = 7;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xdb:
            //flag1 = 11;
            Key_get_Value_up();
            LED_Value[7] = 8;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xeb:
            //flag1 = 12;
            Key_get_Value_up();
            LED_Value[7] = 9;
            LED_num++;
            if (LED_num == 9)
            {
                LED_num = 8;
            }
            break;
        case 0x77:
            flag1 = 13;
            Key_value_and1();
            break;
        case 0xb7:
            if(LED_num != 0)
            {
                Key_value_add();    
            }
            else
            {
                memset(LED_Value,16,sizeof(LED_Value));
            }
            LED_num = 0;
            break;
        case 0xd7:
            //flag1 = 15;
            Key_get_Value_up();
            LED_Value[7] = 0;
              LED_num++;
              if (LED_num == 9)
            {
                LED_num = 8;
            }
    
            break;
        case 0xe7:
            flag1 = 16;
            Key_get_Value_down();
            break;
        default:
            flag1 = 0;
        }
        while(P3 != 0x0f)P3 = 0x0f;
    }
}

void Init_timer0()
{
    TMOD = 0x01;
    TH0 = 45535/256;
    TL0 = 45535%256;
    EA = 1;
    ET0 = 1;
    TR0 = 1;
}

void main()
{
    Init_timer0();
    while(1)
    {
        Key_text();
    }
}

void timer0() interrupt 1
{
    uchar x;
    TH0 = 45535/256;
    TL0 = 45535%256;
    for(x = 0;x < 8;x ++)
    {
        Digital_Tube_main(x,*(LED_Value+x));
    }
}
Last modification:December 7th, 2019 at 10:55 pm
如果觉得我的文章对你有用,请随意赞赏