复现大一51单片机计算器----lcd1602

居家隔离,闲来无事,手上刚好有一块51单片机和一些显示屏,就想着复现一下当时的计算器。

首先,51单片机的原理就不细讲了,这些网上一搜一大堆,随便跟一个学几天就能会的八九不离十,毕竟是几乎所有这行人的入门板子。

这里就讲一下lcd1602的原理以及引脚的连接方法,先上图:

复现大一51单片机计算器----lcd1602_第1张图片

有刚入门的同学可能会问:不是51单片机嘛,这是个神马玩意儿。

这个叫做Arduino,通俗来讲算是单片机的一种,既然都是单片机,那么他们和lcd1602的连线方式想必差不了多少,上图是我玩儿arduino时练手的截图,其中引脚怎么连接都有说明,把他移到51单片机上,也就实现了lcd1602的显示。这里还要说明一点,有的单片机上边有已经模块化的lcd1602,这时就不用我们自己连线了,直接插上去就行,但我们给它下载的程序必须要和板子上的接线方式对应起来。所以上帝给你打开一扇窗,也会给你关闭一扇门。 这里我没有去找我的51板子的原理图,直接用面包板自己连线,放一张仿真软件里的连线:

如图所示,计算器的功能可以应付大多数计算问题了,其中还有对程序员非常友好的进制转换。

为了防止有同学再把线接错,我再放一张图:复现大一51单片机计算器----lcd1602_第2张图片

 好了,现在线接好了,程序怎么写呢?直接上源码:

/*------------------------------------------------
  			| 1 | 2 | 3 | + |  			       | c | ^ |十进制转换2进制|删除|  
  			| 4 | 5 | 6 | - |  
  			| 7 | 8 | 9 | * |  
  			| 0 | . | = | / | 
------------------------------------------------*/
#include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include
#include
#define uint unsigned int
#define uchar unsigned char
#define keyport1 P3 	 //矩阵1
#define keyport2 P1		//矩阵2			
sbit rs = P2^4;  
sbit rw = P2^5;
sbit en = P2^6;

uchar num,num1,i,sign;  //i计数|num,num1记按键|sign记标志位             
uchar temp[16];        //最大输入16个
bit firstflag;			 //等号判断	标志位置
float a=0,b=0;			 //a第一个数,b第二个数
uchar s;				//计数

void delayms(uint xms)		  //延时
{
 uint i,j;
 for(i=xms;i>0;i--)
 for(j=110;j>0;j--); 
 } 
void zhiling(uchar com)	      //写指令
{
 rs=0;rw=0;en=0;P0=com;
 delayms(5);
 en=1;
 delayms(5); 
 en=0;
}
void shuju(uchar dat)	      //写数据
{
 rs=1;rw=0;en=0;P0=dat;
 delayms(5);
 en=1;
 delayms(5);
 en=0;
}
void init()				      //初始化
{	
 zhiling(0x38);				  //8总线2行显示
 delayms(5);
 zhiling(0x06);				  //左移
 delayms(5);
 zhiling(0x0f);
 delayms(5);
 zhiling(0x01);				  //清屏
 delayms(5);             
}
void Clear(void) 												  // 清屏函数
 { 
 zhiling(0x01); 
 delayms(5);
 }

void shanchu()			 											 //删除函数
{
	zhiling(0x10);
	shuju(26);
	zhiling(0x10);
}
void zifu(uchar x,uchar y,uchar Data) 									// 写入字符函数
 {     
 if(y==0) 
 	{     
 	zhiling(0x80+x);     
 	}    
 else 
 	{     
 	zhiling(0xc0+x);     
 	}        
 shuju( Data);  
 }
void zifuchuan(uchar x,uchar y,uchar *s) 								// 写入字符串函数
 {     
       
 while (*s) 
 	{     
 zifu(x,y,*s);     
 s++;x++;   
 	}
 }
uchar keyscan1(void)  											//键盘扫描函数,使用行列反转扫描法
{
  uchar hang,lie;       	 // 行`列值中间变量
  keyport1=0x0f;          	  //行线输出全为0
  hang=keyport1&0x0f;    	 //读入列线值
 if(hang!=0x0f)       	 	//先检测有无按键按下
 {							
  delayms(10);       		 //去抖
  if((keyport1&0x0f)!=0x0f)
  {
    hang=keyport1&0x0f;  	//读入列线值
    keyport1=hang|0xf0;  	//输出当前列线值
    lie=keyport1&0xf0;  		//读入行线值
    while((keyport1&0xf0)!=0xf0);	//等待松开并输出
    return(hang+lie);		//键盘最后组合码值
   }
  }return(0xff);    		 //返回该值
}

uchar anjian1(void)
{
 switch(keyscan1())
 {
  case 0x7e:return '/';break;/// 按下相应的键显示相对应的码值
  case 0x7d:return '*';break;//*
  case 0x7b:return '-';break;//-
  case 0x77:return '+';break;//+

  case 0xbe:return '9';break;//9
  case 0xbd:return '6';break;//6
  case 0xbb:return '3';break;//3
  case 0xb7:return '=';break;//=

  case 0xde:return '8';break;//8
  case 0xdd:return '5';break;//5
  case 0xdb:return '2';break;//2
  case 0xd7:return '0';break;//0

  case 0xee:return '7';break;//7
  case 0xed:return '4';break;//4
  case 0xeb:return '1';break;//1
  case 0xe7:return '.';break;//.
  default:return 0xff;break;
 }
}

uchar KeyScan2(void)  					//键盘扫描函数,使用行列反转扫描法
{
  uchar hang1,lie1;       	 //  行`列值中间变量
  keyport2=0x0f;          	  //行线输出全为0
  hang1=keyport2&0x0f;    	 //读入列线值
 if(hang1!=0x0f)       	 	//先检测有无按键按下
 {							
  delayms(10);       		 //去抖
  if((keyport2&0x0f)!=0x0f)
  {
    hang1=keyport2&0x0f;  	//读入列线值
    keyport2=hang1|0xf0;  	//输出当前列线值
    lie1=keyport2&0xf0;  		//读入行线值
    while((keyport2&0xf0)!=0xf0);	//等待松开并输出
    return(hang1+lie1);		//键盘最后组合码值
   }
  }return(0xff);    		 //返回该值
}  
uchar anjian2(void)
{
 switch(KeyScan2())
 {
  case 0xee:return' ';break;//清屏 按下相应的键显示相对应的码值
  case 0xde:return'^';break;//次幂
  case 0xbe:return '&';break;//&
  case 0x7e:return 'x';break;//删除
	
  default:return 0xff;break;
 }
}  
void main()
{
 init();        	 //初始化液晶屏
 delayms(2);        //延时用于稳定,可以去掉
 Clear();  

 while (1)        	 //主循环
  {
 num=anjian1();num1=anjian2();  	//扫描键盘
/*************************************************************************
	  第一块按键板P3
*********************************************************************************/
 if(num!=0xff) 		 //如果扫描是第一块矩阵有效值则进行处理
   { 
    if(i==0)   		 //输入是第一个字符的时候需要把改行清空,方便观看
		 Clear(); 	 
	if((i==16)||('+'==num)||('-'==num)||('*'==num)||('/'==num)||('='==num))		//输入数字最大值16,输入符号表示输入结束
	  {
	  i=0;  			//计数器复位
	 if(firstflag==0) 	 
	     {
	     sscanf(temp,"%f",&a);		//如果是输入的第一个数据,赋值给a,
         firstflag=1;				//并标志位置1,
		 }
	  else  
	     {		 
		 sscanf(temp,"%f",&b);
		 }		//到下一个数据输入时可以跳转赋值给b	

	  for(s=0;s<16;s++)  	//赋值完成后把缓冲区清零,防止下次输入影响结果
		    temp[s]=0;		   
      zifu(0,1,num); 
/***********************************************************************************/

/****************************************************************************************/
	  if(num!='=')     	 //如果不是等号记下标志位
	     {sign=num;}     //
	  else
	     {  
		 firstflag=0;  	 //检测到输入=号,判断上次读入的符号
         switch(sign)
	       {
		    case '+':a=a+b;
			break;
			case '-':a=a-b;
			break;
			case '*':a=a*b;
			break;
			case '/':a=a/b;
			break;

			case'^':a=pow(a,b);		//math函数:a等于a的b次方
			break;
			default:break;
		   }
		sprintf(temp,"%g",a);    //输出浮点型,无用的0不输出
         zifuchuan(1,1,temp);     //显示到液晶屏
		 sign=0;a=b=0;            //用完后所有数据清零
		 for(s=0;s<16;s++)		  
		    temp[s]=0;
		 }	
	  }
  else	if(i<16)
	  {
	   if((1==i)&& (temp[0]=='0') )		//如果第一个字符是0,判读第二个字符
	     {
		 if(num=='.')  			//如果是小数点则正常输入,光标位置加1
		    {
		    temp[1]='.';
			zifu(1,0,num);			//输出数据
			i++;
            }          		 //这里没有判断连续按小数点,如0.0.0 
		 else
		  {
		   temp[0]=num; 		//如果是1-9数字,说明0没有用,则直接替换第一位0
		   zifu(0,0,num);		//输出数据
		   }
		 }
	   else
	     {
         temp[i]=num; 
         zifu(i,0,num);			//输出数据
	     i++;   				//输入数值累加
		 }
	   } 
    }		   
/***********************************************************************
	 第二块按键P1
**************************************************************************/
	if(num1!=0xff) 		 //如果扫描的是第二矩阵有效值则进行处理
	{ 
    if(i==0)   		     //输入是第一个字符的时候需要把改行清空,方便观看
		 Clear(); 		
	if(' '==num1)	    	//清屏
	  {  Clear();
		 a=b=0;				//数据清零
		 for(s=0;s<16;s++)
		 temp[s]=0;
	  	 temp[i]=num;
		 i=0; 
	 	}
	if('x'==num1)			//删除键
	 {						
	  shanchu();			//不能删除符号
	  temp[i]=num; 
	  i--;i--;
	  }	
	 if((i==16) || ('^'==num1) || ('&'==num1) || ('='==num))//输入数字最大值16,输入符号表示输入结束
	  {
	  i=0;  			//计数器复位
	 if(firstflag==0) 	 //如果是输入的第一个数据,赋值给a,并把标志位置1,到下一个数据输入时可以跳转赋值给b
	     {
	     sscanf(temp,"%f",&a);
         firstflag=1;
		 }
	  else
	  {  
	     sscanf(temp,"%f",&b);
	  }
	  for(s=0;s<16;s++)  	//赋值完成后把缓冲区清零,防止下次输入影响结果
		    temp[s]=0; 		 //可以去掉
      zifu(0,1,num1);  
	  if(num!='=')     	 //判断当前符号位并做相应处理
	     sign=num1;     	 //如果不是等号记下标志位
			}
	}			 
  }
}

把源码ctrl+v到你的keil里边,编译一下,输出.hex文件,然后用烧录软件把代码下载到你的单片机里就大功告成了!

上视频效果:

     

居家隔离闲来无事,花一天时间复现大一计算器

因为板子上只有4X4个按键,所以只展示了一部分功能,后面上的仿真效果。

有不足的地方还请大佬提出改正!

你可能感兴趣的:(嵌入式,51单片机,嵌入式硬件,单片机)