【51单片机】矩阵键盘逐行扫描法仿真实验+超详细Proteus仿真和Keil操作步骤
2024-08-26 来源:cnblogs
一、环境
我用的是Keil5做编译工具,用proteus仿真。除了Keil5不知道有没有其他好用的能生成.hex文件的软件(要单片机运行是需要生成.hex文件的),Proteus则是一款很好用的仿真软件,原件很多。当然,之前有试过multisim14,也是非常不错的软件,自带有可以编写代码的文本编辑器,但没找到我想要的原件。所以选择了Proteus。
二、硬件部分
我们可以先打开Proteus:
1. Proteus新建工程
点击开始界面的创建工程,先创建一个Proteus的工程。
(注意:最好每个项目单独一个文件夹,后期的文件很乱很杂)
工程名写好,选择好文件夹,后面的可以一直下一步。
2. 添加元件
可以直接点红色箭头或者先点击“元件模式”然后点击“P”进入元件库。
可以输入80C51进行筛选,我用的是第一个80C51。
再找到筛选keypad,我用的是keypad-smallcalc。
接着找LED,选择的是LED-BARGRAPH-GRN,作为输出,也方便调试。
选好的元件就在这了。然后点击就能放置元件。
3.连接线路
4、硬件效果
当然,红色和蓝色的点不是接上线就有的,这是仿真之后的效果。其中,红色是高电平,蓝色是低电平,无色是无电平或脉冲不稳定,黄色为短路。
注意:Proteus的部分原件默认接了电源和接地,所以找不到电源和接地管脚。比如T80C51就是默认了接电源接地,所以没有20、40管脚。
三、软件部分
刚刚完成了硬件部分,和真实的硬件一样,我们都需要有程序才能让单片机工作。现在我们来用Keil5编写程序。虽然课程是用的汇编,但由于个人不太习惯汇编的程序,所以我尝试的是C语言。目标是做成一个简单的计算器。
1、Keil5新建工程
菜单栏的project下的new uVision project,选择好芯片**T80C51,**选择好地址 (可以和Proteus的工程放一个文件夹)。
然后新建一个.c源文件,并把源文件添加到工程的 Source Group内。
2、代码:
(1 思路分析
想要做一个计算器,其中有“+、-、*、/ ” 。最开始我想到的是数字和运算符分开存放,然后再处理。后来发现无法预测输入的数字位数(因为每次只能输入一位,也不能像在黑窗口那样回车)。于是我决定把数字变成字符,跟运算符存放在一个char数列里,再分析处理数列,找出数字和运算符。
(2 添加头文件
添加头文件,并设置全局变量。
#include int cro[4] = {0xFE,0xFD,0xFB,0xF7};//存放行值。分别表示是P2.0口低电平,P2.1低电平………… char indata[50];//用于存放键盘输入的字符数列 int len=0;//数列的长度 int fnum=0;//用于存放第一个操作数(处理数列得到的数字) int lnum=0;//用于存放第二个操作数(处理数字得到的数字) int ans=0;//存放计算结果 char op;//存放运算符 这是51单片机的头文件,里面包含了51单片机的存储器、端口等 (3 延时程序 在单片机中延时程序经常用到,延时的方法也很多,有硬件延时、软件延时,汇编中可能会用nop,或者 MOV R0,100 DJNZ R0,$ 在C语言中可以通过空循环来延时,就像下面这样。当然也有其他方法。 void delay_ms(int n){ int i,j; for(i = 0; i < n; i++) for(j = 0; j < 1000; j++); }//这里的值只是大概写的,n==1时不一定真是1ms。也可以算准确值。 如果一个循环够用或者容易控制时间的话,可以不用嵌套。 最先尝试的是矩阵键盘的线路反转法,但是中间出了些问题,暂时放弃了,改用 逐行扫描法。 可见,我们的键盘连接了P2端口,低4位为行,高四位为列,LED则连接的P1端口。以此为例。 先使行端口(即P2端口低四位)输出低电平,读列值。若读入的列值不为0FFH,则有键按下 int keyscan(){ int temp; P2 = 0xf0;//给P12口送入11110000B temp = P2 & 0xf0;//读取列值 } 注意:在C语言里,二进制是前面加0b或0B,八进制是加0,十六进制加0x或0X。如十进制123,二进制0b123或0B123,八进制0123,十六进制0x123或0X123。 这样就能完成 行输出低电平,读取列值的操作。如果列值不为0xf0,表示有键按下,有一位变成低电平。 当有键按下时,可以加10毫秒延时去抖。再进行逐行扫描。 扫描的过程是第四位逐位输出低电平,读不为0xff 的列值 int keyscan(){ int n; int row; int temp; P2 = 0xf0; temp = P2 & 0xf0; if(temp != 0xf0){ delay_ms(10)//这里需要加10毫秒延时去抖, for(n = 0; n < 4; n++ ){//遍历,低4位逐位输出0,直到找到按下键的列值 P2 = cro[n];//P2口低位输出0 rol = P2 & 0xf0;//读高四维 if(rol != 0xf0){ return (row|(cro[n]&0x0f));//找到按下键的列值后合成键码,并返回 break; } } } } 这就是逐行扫描的主要程序。 (5 配置按键功能 这时候我们发现,键码找对了,该怎么让它执行特定的程序呢?比如现在按'1',它并不代表'1'这个数。这个时候我们就需要给键配置一个功能或者含义。 我是这样定义的: void act(int key){ switch(key){ case 0x77:indata[len++] = '+';P1 = 0x80;break;//输入的是运算符,输出运算符按键对应的行。并存放到前面定义的数组里,长度+1 case 0xB7: show(); break;//”=“键的功能是展示运算结果 case 0xD7:indata[len++] = '0';P1 = 0x00;break;//输入的是数字,输出对应的二进制数 case 0xE7:clear(); break;//清零键的功能是清零 case 0x7B:indata[len++] = '-';P1 = 0x40;break; case 0xBB:indata[len++] = '3';P1 = 0x03;break; case 0xDB:indata[len++] = '2';P1 = 0x02;break; case 0xEB:indata[len++] = '1';P1 = 0x01;break; case 0x7D:indata[len++] = '*';P1 = 0x20;break; case 0xBD:indata[len++] = '6';P1 = 0x06;break; case 0xDD:indata[len++] = '5';P1 = 0x05;break; case 0xED:indata[len++] = '4';P1 = 0x04;break; case 0x7E:indata[len++] = '/';P1 = 0x10;break; case 0xBE:indata[len++] = '9';P1 = 0x09;break; case 0xDE:indata[len++] = '8';P1 = 0x08;break; case 0xEE:indata[len++] = '7';P1 = 0x07;break; default:break; } } 到这里我们的基本要求已经完成。接下来是完善的部分。 (6 补坑 刚才用到而没有定义的函数,show()clear()。现在就让我们来把这两个函数写出来。 首先是'='号键的功能函数show() void show(){ decode();//这是一个把存放按键的字符数组变成可以运算的数字的函数。 operat();//把字符型的数字变成int型的数字后,就该计算了。这是运算函数。 P1 = ans;//最终要把运算结果输出到LED。由于之前定义的是全局变量,所以不需要传参数 } 接下来是清零按键的功能,清零。 void clear(){ int i; for(i=0;i(4 键盘扫描程序
根据书上的原理,结合以上面的电路图 写出程序。