目录
总览
数码管和按键两个大方向模块,以及delay和定时器中断(用于判断按键是否按下)边缘模块
数码管:显示逻辑模块+显示什么内容模块
按键:按键状态模块(判断是否按下)+按键扫描(哪个按键被按下了)+按键执行的动作
本次任务核心就是修改上面两个标识重点的模块
边缘模块代码
4x4矩阵键盘模块
#include <REGX52.H>
// 定义键盘和输出端口的引脚
sbit k1 = P3^0; // 第一行按键输入
sbit k2 = P3^1; // 第二行按键输入
sbit k3 = P3^2; // 第三行按键输入
sbit k4 = P3^3; // 第四行按键输入
sbit o1 = P3^4; // 第一列输出
sbit o2 = P3^5; // 第二列输出
sbit o3 = P3^6; // 第三列输出
sbit o4 = P3^7; // 第四列输出
// 当前状态矩阵,用于记录按键的当前状态
unsigned char cur_sta[4][4] = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
};
// 扫描键盘,返回按键值
char key_scan()
{
static unsigned char pre_sta[4][4] = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
};
char key_value = -1;
char i, j;
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
if (pre_sta[j][i] != cur_sta[j][i])
{
if (pre_sta[j][i])
{
key_value = 4 * j + i;
}
pre_sta[j][i] = cur_sta[j][i];
}
}
}
return key_value;
}
// 更新按键当前状态
void cur_sta_update()
{
char i;
static char j = 0;
static unsigned char buff[4][4] = {
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}
};
buff[j][0] = (buff[j][0] << 1) | k1;
buff[j][1] = (buff[j][1] << 1) | k2;
buff[j][2] = (buff[j][2] << 1) | k3;
buff[j][3] = (buff[j][3] << 1) | k4;
for (i = 0; i < 4; i++)
{
if (buff[j][i] == 0xFF)
{
cur_sta[j][i] = 1;
}
else if (buff[j][i] == 0x00)
{
cur_sta[j][i] = 0;
}
}
j++;
if (j == 4)
{
j = 0;
}
switch (j)
{
case 0: o1 = 0; o2 = 1; o3 = 1; o4 = 1; break;
case 1: o1 = 1; o2 = 0; o3 = 1; o4 = 1; break;
case 2: o1 = 1; o2 = 1; o3 = 0; o4 = 1; break;
case 3: o1 = 1; o2 = 1; o3 = 1; o4 = 0; break;
default: break;
}
}
数码管显示逻辑模块
#include <REGX52.H>
#include "delay.h"
extern unsigned char DispCode[]; // 声明外部变量,存储要显示的段码
// 动态控制LED段码显示的函数
void segled_dynamic()
{
static unsigned char i = 0; // 静态变量,用于跟踪当前显示的段
P0 = DispCode[i]; // 设置要显示的段码
P2 = (0xfe << i) | (0xfe >> (8 - i)); // 控制显示的位置
delay(1); // 稍微延时以保持显示
P2 = 0xff; // 关闭所有段的显示
i++; // 移至下一个段
if (i == 8)
{
i = 0; // 如果已经显示了最后一个段,回到第一个段
}
}
核心模块代码
显示什么内容模块
void Show_value(unsigned int Value,unsigned char point_pos,bit point_flag)//point_pos判断小数点位置
{ unsigned int tmp; //point_flag判断是否有小数点
unsigned char i, k=0;
tmp=Value;
for(i=0;i<8;i++)
DispCode[i]=0x00;
do
{
DispCode[7-k]=led_mode[tmp%10];
k++;
tmp=tmp/10;
}while(tmp);
if(point_flag)
DispCode[7-point_pos]=DispCode[7-point_pos]|0x80;//显示小数点在指定位置
}
按键执行动作模块
void key_action(char key_value)
{
static unsigned int result=0;
static unsigned int cur_value=0;
static unsigned char point_pos=0;
static bit point_flag=0;
char op;
if(key_value>=0 && key_value<=8)// 数字按键,数字往前推
{
cur_value=cur_value*10+key_value;
if(point_flag)
{
point_pos++;//小数点位置也往前推
}
Show_value(cur_value,point_pos,point_flag);//显示
}
else if(key_value==9) // 小数点按键
{
if(point_flag==0)
{
point_flag=1;
DispCode[7]=DispCode[7]|0x80;
}
}
else if(key_value>=10 && key_value<=13)//+ - * /
{
switch(key_value)
{
case 10:op='+';break;
case 11:op='-';break;
case 12:op='*';break;
case 13:op='/';break;
default:break;
}
result=cur_value;
point_pos_pre=point_pos;
point_pos=0;
point_flag=0;
cur_value=0;
}
else if(key_value==14) // =,没写完的左移!!
{
if(point_pos>point_pos_pre)
{
left_shift_dec(result,point_pos-point_pos_pre);
}
else
{
left_shift_dec(cur_value,point_pos_pre-point_pos);
}
switch(op)
{
case '+': result+=cur_value;break;
case '-': result-=cur_value;break;
case '*': result*=cur_value;break;
case '/': result/=cur_value;break;
default:break;
}
cur_value=result;
Show_value(result, point_pos,point_flag);
}
else if(key_value==15) //归零
{
result=0;
cur_value=0;
Show_value(result,point_pos,point_flag);
}
}
主函数和定时器
void main()
{
char key_value=-1;
timer_config();
while(1)
{
key_value=key_scan();
key_action(key_value);
segled_dynamic();
}
}
void timer_2ms() interrupt 1
{
TH0=63536/256;
TL0=63536%256;
cur_sta_update();
}
思考
- 51无法完成浮点数精度的计算,所以得用点手段才能得到一些高精度的除法结果,按照你要保留的小数点位数n,先给被除数扩大10的n次方倍,再最后的结果去“回点”小数点就可以
- 显示小数点有很多种逻辑,这种是最简易的,多用一个变量放小数点位置,一个变量判断是否有小数点
- 减法输出负号就是很简单的一个if判断就好了