当前位置:网站首页>矩阵键盘&基于51(UcosII)计算器小项目
矩阵键盘&基于51(UcosII)计算器小项目
2022-08-10 12:34:00 【不知足额】
矩阵键盘
最近要给学弟学妹(应该没有)培训矩阵键盘,正好写一写我写矩阵键盘的思路,顺便分享一个之前做的基于ucosii的计算器小项目
原理
原理就不介绍了,CSDN有很多分享的,推荐两篇参考一下:
参考:矩阵键盘_丹山起凤的博客-CSDN博客_矩阵键盘
参考:一文带你详解矩阵键盘工作原理_Z小旋的博客-CSDN博客_矩阵键盘工作原理
代码
行列扫描法:
原理:
我习惯用x,y轴,把矩阵键盘抽象成二元函数
先对列扫描,行端口输出低电平,列端口输出高电平。读取列端口(是否被拉低)获取列值;
再对行扫描,列端口输出低电平,行端口输出高电平。读取行端口(是否被拉低)获取行值。
最后通过行值和列值计算得出键值。
PS:可通过方程组解出键值与行列值关系,参考下图设方程:ax+by+c=key_num(a,b,c为系数,x和y为行列值,key_num为键值,解出记得带入验证)
PS:行列坐标可自定义,只需要解出对应的计算公式即可
1 2 3 4 S7 S11 S15 S19
5 6 7 8 S6 S10 S14 S18
9 10 11 12 S5 S9 S13 S17
13 14 15 16 S4 S8 S12 S16
如下图:
S7: 1 * a + 4 * b + c = 1; S4: 1 * a + 1 * b + c = 13; S16: 4 * a + 1 * b + c = 16
解得: x + 4 * y - 16 = key_num

源码:
/*********4*4键盘扫描*********** 对应键值关系 1 2 3 4 S7 S11 S15 S19 5 6 7 8 S6 S10 S14 S18 9 10 11 12 S5 S9 S13 S17 13 14 15 16 S4 S8 S12 S16 在stc15上无P36 P37,对应的是P42 P44!!!!!!!! 函数返回值为0-16 ***************************/
unsigned char key_scanf()
{
uchar x=0,y=0;
//io口赋值
P30=0;P31=0;P32=0;P33=0;//拉低行
P34=1;P35=1;P42=1;P44=1;//拉高列
if(!(P34&P35&P42&P44))//判断是否有按下
{
Delay20us(); //消抖
if(!(P34&P35&P42&P44))//再次判断是否有按下
{
if(!P34)
x=4;
if(!P35)
x=3;
if(!P42)
x=2;
if(!P44)
x=1;//获得列值
P30=1;P31=1;P32=1;P33=1;//拉高行
P34=0;P35=0;P42=0;P44=0;//拉低列
if(!P30)
y=1;
if(!P31)
y=2;
if(!P32)
y=3;
if(!P33)
y=4;//获取行值
while(!(P30&P31&P32&P33));//等待释放
return (x-4*y+16); //计算键值
}
}
return 0;//无按键按下返回0
}
计算方法2:
在使用Proteus仿真时,上一种计算方法效果不太好,不清楚原因,在网上查到了一种方法用仿真效果还可以。
源码:
unsigned char code a[]={
0xFE,0xFD,0xFB,0xF7};
//按键扫描函数
int Sacn_Key(void)
{
unsigned char row,col,i;
P1=0xf0;
if((P1&0xf0)!=0xf0)
{
//delay(10);
if((P1&0xf0)!=0xf0)
{
row=P1^0xf0; //确定行线
i=0;
P1=a[i]; //精确定位
while(i<4)
{
if((P1&0xf0)!=0xf0)
{
col=~(P1&0xff); //确定列线
break; //已定位后提前退出
}
else
{
i++;
P1=a[i];
}
}
}
else
{
return 0;
}
while((P1&0xf0)!=0xf0);
return (row|col); //行线与列线组合后返回
}
else return 0; //无键按下时返回0
}
这种扫描方法可以配合一个解析程序分析按下的按键,我用的是switch函数实现,按键参考上图:
//解析按键扫描函数的值
char coding(int k)
{
char res;
switch (k)
{
case 0: res = 0; break;
case 17:res = '7';break;
case 18:res = '4';break;
case 20:res = '1';break;
case 24:res = ' ';break;
case 33:res = '8';break;
case 34:res = '5';break;
case 36:res = '2';break;
case 40:res = '0';break;
case 65:res = '9';break;
case 66:res = '6';break;
case 68:res = '3';break;
case 72:res = '=';break;
case 129:res = '/';break;
case 130:res = '*';break;
case 132:res = '-';break;
case 136:res = '+';break;
}
return res;
}
小项目:基于51的计算器:
简介:
基于Proteus仿真,51芯片,搭载ucosii。完整仿真工程及项目代码打包好会上传滴(会在文章末尾)。
PS:1. 小黑框Virtual Terminal 在菜单栏Debug中最后一个哦
2. 仿真要把串口波特率配置成19200(双击原理图上的那个外接的黑框框),点击ok
设计思路:
三个任务:计算任务,输出任务,按键任务
- 计算任务:获得算式,传送结果
- 输出任务:串口输出需要显示的内容
- 按键任务:检测矩阵键盘,获取输入内容
结构体
用于存储输入算式,由按键任务投递到计算任务
typedef struct { int num1;//数字1 int num2;//数字2 char cal;//运算符 }my_cal_t; my_cal_t my_cc;邮箱
计算器结构体邮箱:用于按键函数输出计算任务给计算任务
串口输出数据邮箱:串口输出数据邮箱,用于输出内容到虚拟串口
OS_EVENT * Num_Box;//计算器结构体邮箱 OS_EVENT * Mes_Box;//串口显示数据邮箱代码实现:
代码:
部分代码:
主函数
//初始化系统,初始化外设,初始化任务,启动调度 void main(void) { OSInit(); InitTimer0(); InitSerial(); InitSerialBuffer(); Num_Box = OSMboxCreate((void*)0); Mes_Box = OSMboxCreate((void*)0); //矩阵键盘任务 OSTaskCreate(TaskStartyya, (void *)0, &TaskStartStkyya[0],4); //计算任务 OSTaskCreate(TaskStartyyb, (void *)0, &TaskStartStkyyb[0],3); //串口显示任务 OSTaskCreate(TaskStartyyc, (void *)0, &TaskStartStkyyc[0],2); OSStart(); }键盘任务
//循环获取计算公式,投递至邮箱 for(;;){ my_cc.num1 = 0; my_cc.num2 = 0; my_cc.cal = 0; PrintStr("\nPlease enter num1 (end by /,*,+,-):\n"); //数字一及运算符输入 for(i = 0;i < 4;) { //循环获取按键 temp = 0; temp = Sacn_Key(); t = coding(temp); //判断是否为运算符 if(t == '/'||t == '*'||t == '+'||t == '-') { sprintf(txt, "%c", t); OSMboxPost(Mes_Box, txt); my_cc.cal = t; break; } //判断输入是否为数字 if(t <= '9'&&t >= '0') { num1[i] = t - 0x30; sprintf(txt, "%d", num1[i]); OSMboxPost(Mes_Box, txt); i++; if(i==4) { //输入满4位退出,获取运算符号 PrintStr("\nPlease enter /,*,+,-\n"); //循环等待运算符输入 while(1) { temp = Sacn_Key(); t = coding(temp); if(t == '/'||t == '*'||t == '+'||t == '-') { my_cc.cal = t; sprintf(txt, "%c", t); OSMboxPost(Mes_Box, txt); break; //是运算符,退出数字一输入 } } } } OSTimeDlyHMSM(0,0,0,10); } //计算数字一 for(temp=0;temp<i;temp++) { my_cc.num1*=10; my_cc.num1+=num1[temp]; } //输入数字二 PrintStr("\nPlease enter num2 (end by =):\n"); for(i = 0;i < 4;) { temp = 0; temp = Sacn_Key(); t = coding(temp); //判断是否输入等于号 if(t == '=') { break; } //判断是否输入数字 if(t <= '9'&&t >= '0') { num2[i] = t - 0x30; sprintf(txt, "%d", num2[i]); OSMboxPost(Mes_Box, txt); i++; } OSTimeDlyHMSM(0,0,0,10); } //计算数字二 for(temp=0;temp<i;temp++) { my_cc.num2*=10; my_cc.num2+=num2[temp]; } //将结构体投入邮箱 OSMboxPost(Num_Box, &my_cc); }计算任务
//挂死等着按键任务唤醒,计算完了把结果投递输出任务 for(;;){ //等待邮箱 cc = OSMboxPend(Num_Box,0,&err); res = 0; //PrintStr("\nok\n"); //根据运算符计算结果 switch (cc->cal) { case '/':if(cc->num2 == 0) { sprintf(txt,"\nillegal value!\n",res); OSMboxPost(Mes_Box, txt); continue; } else{ res=(float)cc->num1/(float)cc->num2;}break; case '*':res=cc->num1*cc->num2;break; case '+':res=cc->num1+cc->num2;break; case '-':res=cc->num1-cc->num2;break; } //输出结果 sprintf(txt,"\nresult is %.02f\n",res); OSMboxPost(Mes_Box, txt); }输出任务
//没啥实际意义,简单封装了的任务 void TaskStartyyc(void *yydata) reentrant { char *txt; int err; yydata=yydata; for(;;){ //接收邮件,并用串口打印 txt = OSMboxPend(Mes_Box,0,&err); PrintStr(txt); } }
感想
因为写的比较急,我自己都感觉代码很粗糙,很多地方可能还需要推敲;当然因为写的急,功能也很局限,只能进行双目计算。考虑到用的51系列芯片,还跑了ucosii,所以后期完善可能会换成32来仿真。这个工程我用的是移植好的,下载下来删删改改就用上了,不是自己移植的都很难修改,很多文件都有权限,所以经供参考了。
目前我的改进思路:
- 用栈代替结构体(万物皆为数据结构)
- 优化输出任务(还没想好,大体就是可以移植数码管或者其他显示方式)
- …
仿真工程和项目源码正在打包上传中,可以关注公众号,发送:计算器,自动回复云盘链接。欢迎留言讨论~
边栏推荐
猜你喜欢

Ethernet channel 以太信道

九宫格抽奖动效

LeetCode中等题之搜索二维矩阵

Open Office XML 格式里如何描述多段具有不同字体设置的段落

ABAP 里文件操作涉及到中文字符集的问题和解决方案试读版

燃炸!字节跳动成功上岸,只因刷爆LeetCode算法面试题

机器学习实战(2)——端到端的机器学习项目
What are the five common data types of Redis?What is the corresponding data storage space?Take you to learn from scratch

神经网络学习-正则化

实践为主,理论为辅!腾讯大佬MySQL高阶宝典震撼来袭!
随机推荐
Overseas media publicity. What problems should domestic media pay attention to?
kubernetes介绍
啥?他一个人写了个价值100万的软件,却用来开源了!
AtCoder Beginner Contest 077 D - Small Multiple
中科院深圳先进技术院合成所赵国屏院士组2022年招聘启事
Chapter9 : De Novo Molecular Design with Chemical Language Models
LeetCode·297.二叉树的序列化与反序列化·DFS·BFS
Redis 定长队列的探索和实践
大佬们有遇到过这个问题吗? MySQL 2.2 和 2.3-SNAPSHOT 都这样,貌似是
协程与任务
“68道 Redis+168道 MySQL”精品面试题(带解析)
Comparison version number of middle questions in LeetCode
BEVDet4D: Exploit Temporal Cues in Multi-camera 3D Object Detection 论文笔记
C#WPF 图片在显示时没有问题,但在运行时图片显示不出来的解决
LeetCode简单题之合并相似的物品
A detailed explanation of implementation api embed
2022 Recruitment Notice for Academician Zhao Guoping Group of Shenzhen Institute of Advanced Technology, Chinese Academy of Sciences
rpn:def concat_box_prediction_layers
Jenkins修改默认主目录
教育Codeforces轮41(额定Div。2)大肠Tufurama