1.CT107D开发板
2.8段数码管
3.矩阵键盘
4.STC89C52RC单片机
5.keil V5
6.visual studio code
7.stc-isp
基于51单片机的计算器我觉得实现加、减、乘、除的功能就已经很满足了。
所以我们这里采用的8段数码管,所以计算的最大数为0~99999999。
以下是键位图
这里说一下大体思路。
首先,我们数码管初始化是全部灭掉。
然后我们数码管动态扫描显示的话需要用到定时器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 //51单片机标准头文件
#include //空操作需要的头文件
#include //对数组操作的头文件
#include //数学公式
#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));
}
}