第一章 硬件部分
1.1 电路的组成部分
1.1.1 按键电路
-
原理图:
-
功能:
(1) J5:当1和2相接,电路就变成一个4*4的矩阵键盘电路;当2和3相接时,电路变成了一个S4-S7的独立按键 (2)对于独立按键的分析:https://blog.csdn.net/weixin_63568691/article/details/128374461 (3)如果学过矩阵的人应该都知道,矩阵中的位置是由行列标号决定,所以我们确定按下按键的行列就能确定按下哪个按键,这时候我们就要用到按键扫描 (4)按键扫描:我们的思路是,按键是连接行和列的桥梁,如果往某行或者某列输入低电平,其他行或列为高电平,我们只需要检测所有列或者行是否有低电平即可。
第二章 具体实验
2.1 实验一:独立按键
按下S7-S4分别点亮L1-L4,同时要求松手熄灭和消抖
2.2 无操作系统代码
注:一开始接触可以就写这个代码,过省赛应该是没问题的
2.1.1 按照以上思路写的代码
// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "STC15F2K60S2.H"
#define u8 unsigned char
#define u16 unsigned int
void Close_All(void)
{
//关闭蜂鸣器和继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xA0;
P2 &= 0x1f;
//关闭LED灯
P0 = 0xff;
P2 = (P2 & 0x1F) | 0x80;
P2 &= 0x1f;
}
u8 key_old,key_1ms = 1;
void LED_ON(u8 L_X);
void Key_Proc();
void Timer_0_Init(u16 time);
// 主函数
void main(void)
{
Close_All();
Timer_0_Init(1000);//1ms
while(1)
{
Key_Proc();
}
}
/*****************LED*******************/
/*
输入变量:P0口的对应的16进制数
输出变量:无
功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
P0 = L_X;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
}
/*****************按键*******************/
/*
输入变量:无
输出变量:7-4代表第几个按键被按下;0代表没有按键按下
功能:检测哪个独立按键被按下
注意:只有消抖,没有松手检测
*/
u8 Key_read()
{
u8 i;
for(i=0;i<4;i++)
{
if(!(P3 & (0x01<<i)))
{
return 7-i;
}
}
return 0;
}
void Key_Proc()
{
u8 key_now,key_down,key_up;
//消抖,同时节省CPU资源
if(key_1ms < 20) return;
key_1ms = 1;
//读取按键按下的编号
key_now = Key_read();
//按键按下:通过判断按键状态是否发生变化,并且变化后状态是否为按下
key_down = key_now & (key_old ^ key_now);
//按键抬起
key_up = ~key_now & (key_old ^ key_now);
//记录当前状态为下一次检测做准备
key_old = key_now;
if(key_down)//按键按下
LED_ON(~(0x01<<(7-key_now)));
else if(key_up)//按键抬起
LED_ON(0xff);
}
/**************定时器**************************/
/*
输入变量:定时时长___us
输出变量:无
功能:配置并开启定时器0
*/
void Timer_0_Init(u16 time)
{
//12T模式
AUXR &= 0x7f;
//定时器0 模式0
TMOD &= 0xf0;
//设置初值
TH0 = (65536-time)/256;
TL0 = (65536-time)%256;
//打开中断
ET0 = 1;
EA = 1;
//开始计数
TR0 = 1;
}
void Timer_0_IT(void) interrupt 1
{
key_1ms++;
}
2.3 RTX51代码
国赛必备,优点是可以在延时的时候畅通无阻的执行其他东西
#include <rtx51tny.h>
#include <STC15F2K60S2.H>
#define u8 unsigned char
#define u16 unsigned int
u8 l_data;
void All_Close();
void LED_Show(u8 l_data);
void Key_Proc();
void Startup() _task_ 0
{
All_Close();
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
void Key_Task() _task_ 1
{
while(1)
{
os_wait2(K_IVL,19);
Key_Proc();
}
}
void LED_Task() _task_ 2
{
while(1)
{
os_wait1(K_SIG);
LED_Show(l_data);
}
}
/******************************/
//选择锁存器,输出数据
void Select(u8 cs,u8 s_data)
{
P0 = s_data;
P2 = P2 & 0x0f | (cs<<5);
P2 &= 0x0f;
}
//关闭外设
void All_Close()
{
//关闭蜂鸣器
Select(5,0x00);
//关闭LED
Select(4,0xff);
//关闭数码管
Select(7,0xff);
}
//输入8位数据,亮灯,1为亮
void LED_Show(u8 l_data)
{
Select(4,~l_data);
}
//独立按键按键检测
u8 D_Key()
{
u8 i=0;
for(i=0;i<4;i++)
if((P3 & (0x08>>i)) == 0)
return i+4;
return 0;
}
void Key_Proc()
{
static u8 key_old=0;
u8 key_now=0,key_down=0,key_up;
key_now = D_Key();
key_down = key_now & (key_now ^ key_old);
key_up = ~key_now & (key_now ^ key_old);
key_old = key_now;
if(key_down)
{
l_data = 0x01<<(key_down-4);
os_send_signal(2);
}
else if(key_up)
{
l_data ^= (0x01<<(key_up-4));
os_send_signal(2);
}
}
2.1 实验二:矩阵按键
用16个按键控制数码管第一个显示0-F
2.2 无操作系统代码
注:一开始接触可以就写这个代码,过省赛应该是没问题的
2.1.1 按照以上思路写的代码
// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "stdio.h"
#include "STC15F2K60S2.H"
#define u8 unsigned char
#define u16 unsigned int
u8 SEG_COT[9];
u8 SEG_Code[8];
u8 temp1;
u8 key_old,SEG_POS,key_delay,seg_delay;
/*
输入变量:无
输出变量:无
功能:关闭蜂鸣器和继电器
*/
void Close_All(void)
{
//关闭蜂鸣器和继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xA0;
P2 &= 0x1f;
//关闭LED灯
P0 = 0xff;
P2 = (P2 & 0x1F) | 0x80;
P2 &= 0x1f;
}
void Key_Proc();
void Timer_0_Init(u16 time);
void SEG_TSL(u8 *input,u8 *output);
void SEG_Proc();
// 主函数
void main(void)
{
Close_All();
Timer_0_Init(1000);//1ms
while(1)
{
Key_Proc();
SEG_Proc();
}
}
/********************数码管********************************/
/*
输入变量:input,输入字符数组;output:输出16进制数数组
输出变量:无
功能:将字符串转化为对应数码管显示的16进制数
*/
void SEG_TSL(u8 *input,u8 *output)
{
//j一定要赋值
u8 i=0,temp=0,j=0;
for(i=0;i<8;i++,j++)
{
switch(input[j])
{
case '0': temp = 0xc0; break;
case '1': temp = 0xf9; break;
case '2': temp = 0xa4; break;
case '3': temp = 0xb0; break;
case '4': temp = 0x99; break;
case '5': temp = 0x92; break;
case '6': temp = 0x82; break;
case '7': temp = 0xf8; break;
case '8': temp = 0x80; break;
case '9': temp = 0x90; break;
case 'A': temp = 0x88; break;
case 'B': temp = 0x83; break;
case 'C': temp = 0xc6; break;
case 'D': temp = 0xA1; break;
case 'E': temp = 0x86; break;
case 'F': temp = 0x8E; break;
case 'H': temp = 0x89; break;
case 'L': temp = 0xC7; break;
case 'N': temp = 0xC8; break;
case 'P': temp = 0x8c; break;
case 'U': temp = 0xC1; break;
case '-': temp = 0xbf; break;
case ' ': temp = 0xff; break;
default: temp = 0xff;
}
if(input[j+1] == ".")
{
temp &= 0x7f;
j++;
}
output[i] = temp;
}
}
/*
输入变量:num,要显示数据;PIS,显示位置,从左到右分别为0-7
输出变量:无
功能:操作138译码器,4-7分别对应Y4-Y7,其余都会使译码器不起作用
注意:需要把存放从1-f对应的16进制数数组也移植
*/
void SEG_Show(u16 num,u16 PIS)
{
//消影
P0 = 0xff;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
//改变显示位置
P0 = 0x01<<PIS;
P2 = (P2 & 0x1f) | 0xC0;
P2 &= 0x1f;
//改变数据
P0 = num;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
}
void SEG_Proc()
{
if(seg_delay) return;
seg_delay = 1;
sprintf(SEG_COT, "%02u",(u16)temp1);
SEG_TSL(SEG_COT,SEG_Code);
}
/*****************按键*******************/
/*
输入变量:无
输出变量:检测到的按键,如果返回0代表没有检测到任何按键按下
功能:矩阵键盘按键检测
*/
u8 M_Buttun()
{
u16 temp = 0;
u8 i = 0;
u16 sign = 0x8000;
//往每行中输入低电平
//L1:P44 L2:P42 L1:P44 L3:P35 L4:P34
P44=0;P42=1;P35=1;P34=1;//第一列
temp = temp | (P3&0x0f);//只保存低四位数据
P44=1;P42=0;P35=1;P34=1;//第二列
temp = temp<<4 | (P3&0x0f);//只保存低四位数据
P44=1;P42=1;P35=0;P34=1;//第三列
temp = temp<<4 | (P3&0x0f);//只保存低四位数据
P44=1;P42=1;P35=1;P34=0;//第四列
temp = temp<<4 | (P3&0x0f);//只保存低四位数据
for(i=0;i<16;i++)
{
if((~temp) & (sign>>i))//从最高位检测到最低位
return i+4;
}
return 0;
}
void Key_Proc()
{
u8 key_now,key_down,key_up;
//延时一段时间消抖,同时节省CPU资源
if(key_delay) return;
key_delay = 1;
//读取按键按下的编号
key_now = M_Buttun();
//按键按下:通过判断按键状态是否发生变化,并且变化后状态是否为按下
key_down = key_now & (key_old ^ key_now);
//按键抬起
key_up = ~key_now & (key_old ^ key_now);
//记录当前状态为下一次检测做准备
key_old = key_now;
if(key_down)//按键按下
{
temp1 = key_now;
}
}
/**************定时器**************************/
/*
输入变量:定时时长___us
输出变量:无
功能:配置并开启定时器0
*/
void Timer_0_Init(u16 time)
{
//12T模式
AUXR &= 0x7f;
//定时器0 模式0
TMOD &= 0xf0;
//设置初值
TH0 = (65536-time)/256;
TL0 = (65536-time)%256;
//打开中断
ET0 = 1;
EA = 1;
//开始计数
TR0 = 1;
}
void Timer_0_IT(void) interrupt 1
{
//按键10ms检测一次
if(++key_delay == 10) key_delay = 0;
//数码管500ms检测一次
if(++seg_delay == 500) seg_delay = 0;
SEG_Show(SEG_Code[SEG_POS],SEG_POS);
if(++SEG_POS == 8)SEG_POS = 0;
}
2.3 RTX51代码
国赛必备,优点是可以在延时的时候畅通无阻的执行其他东西
#include <rtx51tny.h>
#include <STC15F2K60S2.H>
#include <stdio.H>
#define u8 unsigned char
#define u16 unsigned int
u8 COD[8],COT[9],PSI;
code unsigned char Seg_Table[] =
{
0xc0,
//0
0xf9,
//1
0xa4,
//2
0xb0,
//3
0x99,
//4
0x92,
//5
0x82,
//6
0xf8,
//7
0x80,
//8
0x90,
//9
0x88,
//A
0x83,
//b
0xc6,
//C
0xa1,
//d
0x86,
//E
0x8e
//F
};
void All_Close();
void SEG_TSL(u8 *input,u8 *output);
void SEG_Show(u8 COD,u8 PSI);
void Startup() _task_ 0
{
All_Close();
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
void Key_Proc();
void Key_Task() _task_ 1
{
while(1)
{
os_wait2(K_IVL,19);
Key_Proc();
}
}
void SEG_Task() _task_ 2
{
while(1)
{
SEG_Show(COD[PSI],PSI);
if(++PSI == 8)PSI = 0;
os_wait2(K_IVL,2);
}
}
/******************************/
//选择锁存器,输出数据
void Select(u8 cs,u8 s_data)
{
P0 = s_data;
P2 = P2 & 0x0f | (cs<<5);
P2 &= 0x0f;
}
//关闭外设
void All_Close()
{
//关闭蜂鸣器
Select(5,0x00);
//关闭LED
Select(4,0xff);
//关闭数码管
Select(7,0xff);
}
//矩阵按键按键检测
u8 M_Key()
{
u8 i=0;
u16 key_temp=0;
//置零检测
P44=0;P42=1;P35=1;P34=1;
key_temp = P3 & 0x0f;
P44=1;P42=0;P35=1;P34=1;
key_temp = (key_temp<<4) | (P3 & 0x0f);
P44=1;P42=1;P35=0;P34=1;
key_temp = (key_temp<<4) | (P3 & 0x0f);
P44=1;P42=1;P35=1;P34=0;
key_temp = (key_temp<<4) | (P3 & 0x0f);
for(i=0;i<16;i++)
if((key_temp & (0x8000>>i)) == 0)
return i+4;
return 0;
}
void Key_Proc()
{
u8 key_now,key_down;
static u8 key_old;
key_now = M_Key();
key_down = key_now & (key_now ^ key_old);
key_old = key_now;
if(key_down)
{
sprintf(COT,"%02u",(u16)key_down);
SEG_TSL(COT,COD);
}
}
//输入字符串,输出数码管段码
void SEG_TSL(u8 *input,u8 *output)
{
u8 i=0;
for(i=0;i<8;i++)
{
switch(input[i])
{
case '0':output[i] = Seg_Table[0];break;
case '1':output[i] = Seg_Table[1];break;
case '2':output[i] = Seg_Table[2];break;
case '3':output[i] = Seg_Table[3];break;
case '4':output[i] = Seg_Table[4];break;
case '5':output[i] = Seg_Table[5];break;
case '6':output[i] = Seg_Table[6];break;
case '7':output[i] = Seg_Table[7];break;
case '8':output[i] = Seg_Table[8];break;
case '9':output[i] = Seg_Table[9];break;
case '-':output[i] = ~0x40;break;
default:output[i] = 0xff;
}
}
}
void SEG_Show(u8 COD,u8 PSI)
{
//消隐
Select(7,0xff);
//位选
Select(6,0x01<<PSI);
//段选
Select(7,COD);
}