atmega16-加减计算器仿真

一.电路仿真图

二.程序及注释

1.main.c

/*******************************************************************************
* 版权:     
* 单片机:   ATMAGE16
* 晶振:     外部8MHz
* 编译器:   ICC 7.13
* 文件名:   main.c
* 作者:     
* 版本:     1.0
* 完成日期: 2023-05-14
* 功能描述: 1602液晶显示、通过MM74C922读取矩阵键盘、模拟计算器计算
*******************************************************************************/
#include <iom16v.h>
#include <macros.h>
#include "lcd_1602.h"
/******************************************************************************* 
* 函数名称: void port_init(void)
* 入口参数: 
* 出口参数: 
* 功能描述: IO口类型设置   	  	
*******************************************************************************/ 
void port_init(void)
{
  DDRA=0X00;
  PORTA &= ~(BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(6));
  PORTB=0XFF;   
  DDRB=0XFF;
  PORTD |= BIT(5)|BIT(6)|BIT(7);   
  DDRD |= BIT(5)|BIT(6)|BIT(7);
}
/******************************************************************************* 
* 函数名称: void init_devices(void)
* 入口参数: 
* 出口参数: 
* 功能描述: 单片机系统初始化设置  	  	
*******************************************************************************/ 
void init_devices(void)
{
 //stop errant interrupts until set up
 CLI(); //disable all interrupts
 port_init(); 
 SEI(); //re-enable interrupts
 //all peripherals are now initialized
}
/******************************************************************************* 
* 函数名称: void main(void)
* 入口参数: 
* 出口参数: 
* 功能描述: 1602液晶显示、通过MM74C922读取矩阵键盘、模拟计算器计算  	  	
*******************************************************************************/
void main(void)
{
   unsigned char c;//储存按下的键盘按键名称
   unsigned char keynum[16]="                ";//lcd第一行的显示内容(用户输入的算式)
   int i=0;//lcd第一行的光标位置
   int r;//for循环中的临时变量
   int keyinteger[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//将keynum[16]中的对应位置转化为对应整数(用以计算结果)
   int keypower[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//将keynum[16]中的对应位置转化为对应指数(底数为10)(用以计算结果)
   int plusORminus[16]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//判断keynum[16]中的对应位置是正还是负,1为正,-1为负(用以计算结果)
   //unsigned char keycharacter[16];
   long int num;//用以计算结果
   long int numabs;//处理结果为负数的情况
   unsigned char result[16]="                ";//lcd第二行的显示内容(展示结果)
   
   
   init_devices();//初始化系统
   LCD_init();//LCD初始化
   LCD_clear();//LCD清屏
   delay_ms(100); 
   

   LCD_write_str(0,0,"   Qi Weiyuan  ");//展示个人信息
   LCD_write_str(0,1,"  202010216026  ");
   delay_ms(1500);
   LCD_clear();
   
   while(1)
   {
	if(PINA&BIT(6)&&i<16)//有按钮按下且lcd第一行的光标位置没超出右界
	{
	   c=key_scan();//储存按下的键盘按键名称
	   if(c=='c')//按下清屏键
	   {
	      LCD_clear();
		  i=0;
		  for(r=0;r<16;r++)
		  {
		     keynum[r]=' ';
		  }
	   }
	   else if(c=='b')//按下空格键
	   {
	      i--;
	      keynum[i]=' ';
		  LCD_write_str(0,0,&keynum[0]);
	   }
	   else if(c=='=')//按下=
	   {
	      for(r=0;r<16;r++)
		  {
		     result[r]=' ';
		  }
		  num=0;
		  numabs=0;
		  for(r=0;r<16;r++)
		  {
		     keyinteger[r]=0;
		  }
		  for(r=0;r<16;r++)
		  {
		     keypower[r]=0;
		  }
		  for(r=0;r<16;r++)
		  {
		     plusORminus[r]=1;
		  } 
          //以上是将与计算过程相关的变量恢复初始值,以避免上次结果的影响
	      for(r=0;r<16;r++)
		     {
		        keyinteger[r]=keyint(keynum,r);//将keynum[16]中的对应位置转化为对应整数(用以计算结果)
			    //keycharacter[r]=int2char(keyinteger[r]);
		     }	
          //LCD_write_str(0,1,&keycharacter[0]);

	      for(r=0;r<16;r++)
		     {
		        keypower[r]=keypow(keynum,r);//将keynum[16]中的对应位置转化为对应指数(底数为10)(用以计算结果)
			    //keycharacter[r]=int2char(keypower[r]);//用来调试的
		     }
	      //LCD_write_str(0,1,&keycharacter[0]);//用来调试的
	      for(r=0;r<16;r++)
		     {
		        plusORminus[r]=plus_minus(keynum,r);//判断keynum[16]中的对应位置是正还是负,1为正,-1为负(用以计算结果)
			    //keycharacter[r]=int2char(plusORminus[r]);
		     } 
	  	  //LCD_write_str(0,1,&keycharacter[0]);
	      for(r=0;r<16;r++)
		     {
		        num=num+((long int)keyinteger[r]*pow10(keypower[r])*(long int)plusORminus[r]);
		     }
		  if(num<0) 
		  {	 
		     numabs=abs(num);
		     for(r=15;r>0;r--)//r不是大于-1,是为了避免num的位数特别多时,在result[0]处显示负号(即便少显示一位数,也要显示负号)(虽然long int 的最大值也不会超出显示范围)
		     {
			    if(numabs!=0) 
				{  //按位分解,右对齐
		           result[r]=longint2char(numabs%10);
				   numabs=numabs/10;
				}
				else
				{
				   result[r]='-';
				   r=0;//跳出for
				}
		     } 
		  }
		  else 
		  {
		     for(r=15;r>-1;r--)
		     {
			    if(num!=0) 
				{
		           result[r]=longint2char(num%10);
				   num=num/10;
				}
				else
				{
				   r=-1;
				}
		     } 
		  }	 
	      LCD_write_str(0,1,&result[0]);//显示结果
	   }
	   else //用户按下其他键
	   {
	      keynum[i]=c;//存储用户按下的键
	      LCD_write_str(0,0,&keynum[0]);//将其显示出来
	      i++;//光标后移
	   }
	}
	else if(i>=16)//光标出界
	{
	   i=0;//光标回到最左端
	}	
   }
}

 2.lcd_1602.c

/*******************************************************************************
* 函数名称: SetCurPosition_off(X,Y)
* 入口参数: X:X坐标,Y:Y坐标
* 函数作者: 
* 功能描述: 在第X行Y列关闭光标
*******************************************************************************/
void SetCurPosition_off(unsigned char X, unsigned char Y)//光标关闭
{
	Y &= 0x01;
	X &= 0x0F;              // 限制X不能大于15,Y不能大于1
	if (Y)
	{
		X |= 0xc0;  // 当要显示第二行时地址码:0xc0
	}
	X |= 0x80;        // 第一行的地址码:0x80
	Write_Instruction(X);//WriteCommandLCM(X,   0);  // 这里不检测忙信号,发送地址码
	Write_Instruction(0X0c);//WriteCommandLCM(0x0d,0);  //关光标
}

//显示屏初始化函数
void LCD_init(void) 
{
	DDRB = 0xFF;						    //I/O口方向设置
	DDRD|=(1<<PD5)|(1<<PD6)|(1<<PD7);
	delay_ms(15);                           //上电延时一段时间,使供电稳定
	Write_Instruction(0x38);				//8bit interface,2line,5*7dots
	delay_ms(5);
	Write_Instruction(0x38);	
	delay_ms(5);
	Write_Instruction(0x38);	
	
	Write_Instruction(0x08);	//关显示,不显光标,光标不闪烁
	Write_Instruction(0x01);	//清屏
	delay_ms(5);
	
	Write_Instruction(0x04);	//写一字符,整屏显示不移动
	//Write_Instruction(0x05);	//写一字符,整屏右移
	//Write_Instruction(0x06);	//写一字符,整屏显示不移动
	//Write_Instruction(0x07);	//写一字符,整屏左移
	delay_ms(5);
	
	//Write_Instruction(0x0B);	//关闭显示(不显示字符,只有背光亮)
	Write_Instruction(0x0C);	//开显示,光标、闪烁都关闭
	//Write_Instruction(0x0D);	//开显示,不显示光标,但光标闪烁
	//Write_Instruction(0x0E);	//开显示,显示光标,但光标不闪烁
	//Write_Instruction(0x0F);	//开显示,光标、闪烁均显示
}

 3.key_scan.c

#include <iom16v.h>
#include <macros.h>

unsigned char key_scan(void)  
{
 unsigned char keychar;	
 unsigned int keycode;
 
 while(PINA&BIT(6))//有按钮按下
 {		 
    keycode=PINA&(BIT(0)|BIT(1)|BIT(2)|BIT(3));//获取键盘的引脚变化
    switch(keycode)
    {
       case 0:
		  keychar='0';
		  break;
	   case 1:
		  keychar='1';
		  break;
	   case 2:
		  keychar='2';
		  break;
	   case 3:
		  keychar='3';
		  break;
	   case 4:
		  keychar='4';
		  break;
	   case 5:
		  keychar='5';
		  break;
	   case 6:
		  keychar='6';
		  break;
	   case 7:
		  keychar='7';
		  break;
	   case 8:
		  keychar='8';
		  break;
	   case 9:
		  keychar='9';
		  break;
	   case 10:
		  keychar='+';
		  break;
	   case 11:
		  keychar='-';
		  break;	
	   case 12:
		  keychar='=';
		  break;
	   case 13:
		  keychar=' ';
		  break;
	   case 14:
		  keychar='b';
		  break;
	   case 15:
		  keychar='c';
		  break;
    }
 }
 
 return keychar; 
}

4.calculation.c

/******************************************************************************* 
* 函数名称: int keyint(unsigned char *keynum , int r)
* 入口参数: unsigned char *keynum:显示屏第一行的显示内容;int r:显示屏第一行第r-1列的显示内容,即keynum【0】
* 出口参数: keynum[r]对应的整数
* 功能描述: 算出keynum[r]对应的整数   	  	
*******************************************************************************/ 
int keyint(unsigned char *keynum , int r)
{
   int n=r;
   int intkey;

      if(keynum[n]=='0'||keynum[n]=='1'||keynum[n]=='2'||keynum[n]=='3'||keynum[n]=='4'||keynum[n]=='5'||keynum[n]=='6'||keynum[n]=='7'||keynum[n]=='8'||keynum[n]=='9')
   	  {
	     intkey=char2int(keynum[n]); 
	  }
	  if (keynum[n]=='+'||keynum[n]=='-'||keynum[n]==' ')
	  {
	  	 intkey=0; 
	  }

   return intkey;
}
/******************************************************************************* 
* 函数名称: int keypow(unsigned char *keynum , int r)
* 入口参数: unsigned char *keynum:显示屏第一行的显示内容;int r:显示屏第一行第r-1列的显示内容,即keynum【0】
* 出口参数: keynum[r]对应的指数
* 功能描述: 将keynum[r]转化为对应的指数(底数为10) 	  	
*******************************************************************************/ 
int keypow(unsigned char *keynum , int r)
{
   int n;
   int pow=-1;//因为个位对应的是0,数字位返回的是pow++后的pow值
   
   for(n=15;n>=r;n--)
   {
   	   if(keynum[n]=='0'||keynum[n]=='1'||keynum[n]=='2'||keynum[n]=='3'||keynum[n]=='4'||keynum[n]=='5'||keynum[n]=='6'||keynum[n]=='7'||keynum[n]=='8'||keynum[n]=='9')
   	  {
	     pow++;
	  }		
	   if (keynum[n]=='+'||keynum[n]=='-')
	  {
	  	 pow=-1; 
	  }	 
   }
   
   if(pow==-1||keynum[r]==' ') 
   {
     pow=0;
   }
   
   return pow;
}
/******************************************************************************* 
* 函数名称: int plus_minus(unsigned char *keynum , int r)
* 入口参数: unsigned char *keynum:显示屏第一行的显示内容;int r:显示屏第一行第r-1列的显示内容,即keynum【0】
* 出口参数: keynum[r]对应的符号
* 功能描述: 判断keynum[r]是正还是负,1为正,-1为负	  	
*******************************************************************************/ 
int plus_minus(unsigned char *keynum , int r)
{
   int n;
   int pm=1;//默认+,因为正数的符号可以省略

      for(n=0;n<r;n++)
   {
   	   if(keynum[n]=='+')
   	  {
	     pm=1;
	  }		
	   if(keynum[n]=='-')
	  {
	  	 pm=-1; 
	  }	 
   }
   
   return pm;
}
/******************************************************************************* 
* 函数名称: long int pow10(int m)
* 入口参数: int m:10的次方数
* 出口参数: 以10为底数,m为指数的幂
* 功能描述: 算出以10为底数m为指数的幂  	
*******************************************************************************/ 
long int pow10(int m)
{
   int n;
   long int power10=1;
   
   for(n=0;n<m;n++)
   {
   	  power10=power10*10; 
   }
   
   return power10;
}

5.delay.c

#include <iom16v.h>
#include <macros.h>

//us延时函数
void delay_us(unsigned int n)   //8(晶振频率)*0.125=1us
{
    int i,j;
	for(j=0;j<8;j++)
	{
	  for (i=0;i<n;i++) 
	  NOP();//“空指令”;它不执行操作,但占一个程序步。
	}  
}

//ms延时函数
void delay_ms(unsigned int i)
{
    while(i--)
    {                          
	    unsigned int j;                
        for(j=1;j<=1332;j++);               
    } 
}

6.char2int.c

int char2int(unsigned char c)//将unsigned char型的变量转化为int型的
{
   int t;
   switch(c)
    {
       case '0':
		  t=0;
		  break;
	   case '1':
		  t=1;
		  break;
	   case '2':
		  t=2;
		  break;
	   case '3':
		  t=3;
		  break;
	   case '4':
		  t=4;
		  break;
	   case '5':
		  t=5;
		  break;
	   case '6':
		  t=6;
		  break;
	   case '7':
		  t=7;
		  break;
	   case '8':
		  t=8;
		  break;
	   case '9':
		  t=9;
		  break;
    }
   return t; 
}

7.int2char.c

unsigned char int2char(int t)//将int型的变量转化为unsigned char型的
{
   unsigned char c;
   switch(t)
    {
	   case -1:
	      c='-';
		  break;
       case 0:
		  c='0';
		  break;
	   case 1:
		  c='1';
		  break;
	   case 2:
		  c='2';
		  break;
	   case 3:
		  c='3';
		  break;
	   case 4:
		  c='4';
		  break;
	   case 5:
		  c='5';
		  break;
	   case 6:
		  c='6';
		  break;
	   case 7:
		  c='7';
		  break;
	   case 8:
		  c='8';
		  break;
	   case 9:
		  c='9';
		  break;
    }
   return c; 
}

8.longint2char.c

unsigned char longint2char(long int t)//将long int型的变量转化为unsigned char型的
{
   unsigned char c;
   switch(t)
    {
	   case -1:
	      c='-';
		  break;
       case 0:
		  c='0';
		  break;
	   case 1:
		  c='1';
		  break;
	   case 2:
		  c='2';
		  break;
	   case 3:
		  c='3';
		  break;
	   case 4:
		  c='4';
		  break;
	   case 5:
		  c='5';
		  break;
	   case 6:
		  c='6';
		  break;
	   case 7:
		  c='7';
		  break;
	   case 8:
		  c='8';
		  break;
	   case 9:
		  c='9';
		  break;
    }
   return c; 
}

9.LCD_1602.h

#ifndef _LCD_1602_H
#define _LCD_1602_H

#include "iom16v.h"
#include "macros.h"

#define RS_ON  PORTD |= BIT(7);//RS置1
#define RS_OFF PORTD &= ~BIT(7);//RS置0

#define RW_ON  PORTD |= BIT(6);//RW置1
#define RW_OFF PORTD &= ~BIT(6);//RW置0

#define E_ON   PORTD |= BIT(5);//E置1
#define E_OFF  PORTD &= ~BIT(5);//E置0

void Port_init(void);
void LCD_init(void);
void LCD_en_write(void);
void LCD_clear(void);
void Write_Instruction(unsigned char command);
void Write_Data(unsigned char Wdata);
void LCD_SET_XY(unsigned char X,unsigned char Y);
void LCD_write_str(unsigned char X,unsigned char Y,unsigned char *s);
void LCD_write_char(unsigned char X,unsigned char Y,unsigned char Wdata);
void SetCurPosition(unsigned char X, unsigned char Y);
void SetCurPosition_off(unsigned char X, unsigned char Y);//光标关闭

#endif

三.计算过程

获取按下“=”那一瞬间,获取lcd第一行的算式(存入unsigned char 型的名为keynum的数组中),撤回的、被因超出右界而从左输入而覆盖掉的通通不考虑。

为方便计算,把lcd显示的16位(不足16位的用空格代替)中的每一位作为一个算子去计算(如:-123可表示为:[(-1)×1×102]+[(-1)×2×101]+[(-1)×3×100],[]里为一个算子 )(可类比不同进制的转换),下面将详细说明。

   int keyinteger[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//keynum[16]中的对应位置转化为对应整数(用以计算结果)

   int keypower[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//keynum[16]中的对应位置转化为对应指数(底数为10)(用以计算结果)

   int plusORminus[16]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//判断keynum[16]中的对应位置是正还是负,1为正,-1为负(用以计算结果)

例如

当 keynum=’1’ ‘2’ ‘3’ ‘ ‘ ‘+’ ‘4’ ‘5’ ‘ ‘ ‘6’ ‘ ‘ ‘7’ ‘-‘ ‘-’ ‘0’ ‘6‘ ‘ ’

表示要计算123+4567-6的结果

于是通过calculation.c可算出:

 plusORminus=1 1 1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1

 keyinteger=1 2 3 0 0 4 5 0 6 0 7 0 0 0 6 0

 keypower=3 2 1 0 0 3 2 1 1 0 0 0 0 1 0 0

即下表

keynum

’1’

‘2’

‘3’

‘ ‘

‘+’

‘4’

‘5’

‘ ‘

‘6’

‘ ‘

‘7’

‘-’

‘-’

‘0’

‘6‘

‘ ’

plusORminus

1

1

1

1

1

1

1

1

1

1

1

-1

-1

-1

-1

-1

keyinteger

1

2

3

0

0

4

5

0

6

0

7

0

0

0

6

0

keypower

2

1

0

0

0

3

2

1

1

0

0

0

0

1

0

0

于是,计算过程为:+1×1×102++1×2×101++1×3×100++1×0×100++1×0×100++1×4×103++1×5×102++1×0×101++1×6×101++1×0×100++1×7×100+-1×0×100+-1×0×100+-1×0×101+-1×6×100+-1×0×100

四.说明与注意事项

使用如下版本的编程和仿真软件,请注意版本兼容

五.成果展示

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐