当前位置:网站首页>矩阵键盘&基于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来仿真。这个工程我用的是移植好的,下载下来删删改改就用上了,不是自己移植的都很难修改,很多文件都有权限,所以经供参考了。
目前我的改进思路:
- 用栈代替结构体(万物皆为数据结构)
- 优化输出任务(还没想好,大体就是可以移植数码管或者其他显示方式)
- …
仿真工程和项目源码正在打包上传中,可以关注公众号,发送:计算器,自动回复云盘链接。欢迎留言讨论~
边栏推荐
- AICOCO AI Frontier Promotion (8.10)
- 基础 | batchnorm原理及代码详解
- 【百度统计】用户行为分析
- Custom filters and interceptors implement ThreadLocal thread closure
- shell:正则表达式及三剑客grep命令
- Deploy the project halfway through the follow-up
- 可视化服务编排在金融APP中的实践
- AtCoder Beginner Contest 077 D - Small Multiple
- Code Casual Recording Notes_Dynamic Programming_70 Climbing Stairs
- ArcMAP出现-15的问题无法访问[Provide your license server administrator with the following information:Err-15]
猜你喜欢
交换机的基础知识
kubernetes介绍
机器学习实战(2)——端到端的机器学习项目
ABAP 里文件操作涉及到中文字符集的问题和解决方案试读版
“68道 Redis+168道 MySQL”精品面试题(带解析)
Keithley DMM7510精准测量超低功耗设备各种运作模式功耗
Keithley DMM7510 accurate measurement of ultra-low power consumption equipment all kinds of operation mode power consumption
Ethernet channel Ethernet channel
Is there a problem with the CURRENT_TIMESTAMP(6) function?
【百度统计】用户行为分析
随机推荐
海外邮件发送指南(二)
BEVDet4D: Exploit Temporal Cues in Multi-camera 3D Object Detection 论文笔记
神了!阿里数据库专家纯手写了这份604页的Oracle+MySQL攻坚指南
ArcMAP has a problem of -15 and cannot be accessed [Provide your license server administrator with the following information:Err-15]
11 + chrome advanced debugging skills, learn to direct efficiency increases by 666%
娄底植物细胞实验室建设基本组成要点
Chapter9 : De Novo Molecular Design with Chemical Language Models
MySQL相关问题整理
camshift achieves target tracking
LeetCode简单题之合并相似的物品
动态规划之最长回文子串
Solve the idea that unit tests cannot use Scanner
娄底石油化工实验设计、建设规划概述
协程与任务
Loudi Cosmetics Laboratory Construction Planning Concept
【mysql索引实现原理】
Mysql—— 内连接、左连接、右连接以及全连接查询
“68道 Redis+168道 MySQL”精品面试题(带解析)
LeetCode·每日一题·640.求解方程·模拟构造
【论文+代码】PEBAL/Pixel-wise Energy-biased Abstention Learning for Anomaly Segmentation on Complex Urban Driving Scenes(复杂城市驾驶场景异常分割的像素级能量偏置弃权学习)