使用atmega16,lcd1602,MM74C922芯片,4x4矩阵键盘,实现加减计算器仿真
atmega16-加减计算器仿真1.main.c。
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
四.说明与注意事项
使用如下版本的编程和仿真软件,请注意版本兼容
五.成果展示

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