基于51单片机+数码管的计算器程序

所需硬件以及软件

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));
	}
}
好的,下面为您提供一个51单片机静态数码管计算器的实例代码: ``` #include<reg52.h> #define uchar unsigned char sbit dula=P2^6; sbit wela=P2^7; uchar code table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//数码管显示0~9,A~F uchar num1,num2,result;//记录输入的数值和结果 uchar flag=0;//标志位,用于选择运算符 void delay(uchar z)//延时函数 { uchar x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void display(uchar num)//数码管显示函数 { P0 = table[num]; } void keyscan()//按键扫描函数 { uchar keyvalue = P1; keyvalue &= 0x0f; if(keyvalue != 0x0f) { delay(10); keyvalue = P1; keyvalue &= 0x0f; if(keyvalue != 0x0f) { switch(keyvalue) { case 0x0e: num1=1; display(num1); break;//1键 case 0x0d: num1=2; display(num1); break;//2键 case 0x0b: num1=3; display(num1); break;//3键 case 0x07: num1=4; display(num1); break;//4键 case 0x0e: num1=5; display(num1); break;//5键 case 0x0d: num1=6; display(num1); break;//6键 case 0x0b: num1=7; display(num1); break;//7键 case 0x07: num1=8; display(num1); break;//8键 case 0x0e: num1=9; display(num1); break;//9键 case 0x0d: num1=0; display(num1); break;//0键 case 0x0b: flag=1; break;//加法键 case 0x07: flag=2; break;//减法键 case 0x0e: flag=3; break;//乘法键 case 0x0d: flag=4; break;//除法键 case 0x0b: flag=5; break;//等于键 case 0x07: num1=0; num2=0; result=0; display(num1); break;//清零键 } } } } void main() { while(1) { keyscan(); if(flag != 0) { keyscan(); num2 = num1; num1 = 0; display(num1); switch(flag) { case 1: result = num1 + num2; break;//加法运算 case 2: result = num1 - num2; break;//减法运算 case 3: result = num1 * num2; break;//乘法运算 case 4: result = num1 / num2; break;//除法运算 } display(result); flag = 0; } } } ``` 这是一个简单的静态数码管计算器,通过按键输入数字和运算符,然后在数码管上显示结果。代码中使用了延时函数、数码管显示函数和按键扫描函数等。需要注意的是,这个程序中只能进行加减乘除四则运算,如果需要扩展其他运算,可以自行添加代码。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值