## 所需硬件以及软件 ## 1.CT107D开发板 ![2.png][1] 2.8段数码管 ![IMG_20191207_220425.jpg][2] 3.矩阵键盘 ![IMG_20191207_220729.jpg][3] 4.STC89C52RC单片机 ![IMG_20191207_220448.jpg][4] 5.keil V5 6.visual studio code 7.stc-isp ## 程序思路 ## 基于51单片机的计算器我觉得实现加、减、乘、除的功能就已经很满足了。 所以我们这里采用的8段数码管,所以计算的最大数为0~99999999。 以下是键位图 ![QQ截图20191207221833.png][5] 这里说一下大体思路。 首先,我们数码管初始化是全部灭掉。 然后我们数码管动态扫描显示的话需要用到定时器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)); } } ``` [1]: https://qingfengblog.com/usr/uploads/2019/12/2048839565.png [2]: https://qingfengblog.com/usr/uploads/2019/12/2140437599.jpg [3]: https://qingfengblog.com/usr/uploads/2019/12/4074866667.jpg [4]: https://qingfengblog.com/usr/uploads/2019/12/2809847214.jpg [5]: https://qingfengblog.com/usr/uploads/2019/12/2666173744.png Last modification:December 7, 2019 © Reprint prohibited Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏