目录
四、题目链接:第十五届单片机模拟考试III (4t.wiki)
一、硬件框图
二、功能描述
2.1基本功能描述
1、通过超声波传感器完成距离测量功能; 2、通过 PCF8591完成试题要求的模拟功能: 3、通过LED指示灯完成试题要求的状态指示功能; 4、通过数码管、按键完成试题要求的数据显示、界面切换和参数设置等功能。
2.2显示功能
1.测距界面
测距界面如图2所示,显示内容包括标识符(A)和距离值组成。
距离值固定使用3位数码管显示,数码管显示不足3位时,高位(左侧)数码管熄灭。
距离值单位为:cm。
2.参数界面
参数界面如图3所示,由标识符(P))、模式值(按键模式:1;旋钮模式:2),参数下固定使用2位数码管显示参数下限/上限。限、间隔符和参数上限组成。参数上限、参数下限单位为:cm。
3.记录界面
记录界面如图4所示,由标识符(E)和报警次数组成。
测量的距离值由“参数下限≤测量的距离值≤参数上限”变为“测量的距离值>参数上限”或“测量的距离值<参数下限”时,报警次数加1;
注意:持续处于测量的距离值>参数上限或测量的距离值<参数下限时,报警次数不改变
4.显示要求
(1)按照题目要求的界面格式和切换方式进行设计。 (2)数码管显示无重影、闪烁、过暗、亮度不均匀等严重影响显示效果的缺陷。
2.3按键功能
(1)S4:定义为“切换”,按下S4按键,切换“测距界面”、“参数界面”和“记录界面”
(2)S5:在“参数界面”下,定义为“模式”,按下按键S5,切换参数调整模式。切换顺序如图6所示。
(3)S9:在“参数界面”下“按键模式”下,定义为“上限调整”,按下按键S9,参数上限加10。参数上限调整顺 序:50 60 70 80 90 50.“旋钮模式”下,定义为“上限”,按下按键S9,可使用旋钮调整当前参数上限。调整顺序详见2.4(1)。
(4)S8:在“参数界面”下, “按键模式”下,定义为“下限调整”,按下按键S8,参数下限加10。参数下限调整顺序: 0 10 20 30 40 0 10 . “旋钮模式”下,定义为“下限”,按下按键S8,可使用旋钮调整当前参数上限。调整顺序详见2.4(2)。
(5)按键要求 1、按键应做好消抖处理,避免出现一次按键动作导致功能多次触发。
2、按键动作不影响数码管显示等其他功能。
3、当前界面或模式下无功能的按键按下,不触发其它界面的功能。
4、参数上限调整范围“50-90” 参数下限调整范围“0-50”
2.4旋钮模式
使用PCF8591的ADC功能采集RB2的模拟信号,模拟旋钮功能调整参数上限或参数下限:
(1)“上限”调整过程如图7所示:
(2)“下限”调整过程如图8所示:
2.5LED指示灯功能
1>测距界面下指示灯L1 点亮,否则指示灯L1 熄灭。 2>参数界面下,指示灯 L2 点亮,否则指示灯 L2 熄灭。 3>记录界面下,指示灯L3 点亮,否则指示灯 L3 熄灭。 4>参数下限≤测量的距离值≤参数上限,指示灯L8点亮,否则L8以0.1s为间隔切换亮灭状 5>除L1、L2、L3和L8指示灯外,其余指示灯均处于熄灭状态。
2.6初始状态
请严格按照以下要求设计作品的上电初始状态。
1、处于测距界面 2、按键模式 3、参数上限60;参数下限10
三、代码实现
1>主函数:
// Header:
// File Name:
// Author:
// Date:
/********************预编译、宏和变量********************************************/
#include "main.h"
#include <stdio.h>
#include "iic.h"
#include "key.h"
#include "ultrasonic.h"
/*************************************************************
宏定义区域
**************************************************************/
#define CONTROL(x,y) P0=y;P2=x;P2=0 //为锁存器的引脚操作封装一个带参宏
enum PERIPHERAL
{
LED = 0x80,
DEVICE = 0xa0,
BIT = 0xc0,
SEG = 0xe0
};
/*******************************************************************************
变量定义
********************************************************************************/
/********************模板驱动变量****************************/
//控制LED0~7的开关量,正逻辑
bit LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7;
//控制蜂鸣器的自定义变量,正逻辑
bit Buzzer;
//控制继电器的自定义变量,正逻辑
bit Relay;
//Dsp_Bit是数码管显示的字符串数组,考虑到小数点的同位显示,所以字符串长度最长为16
uchar Dsp_Bit[16] = {0};
//数码管显示的各位段码内容,由 Dsp_Bit 通过函数Seg_Tran转换更新
uchar Dsp_Code[8] = {0};
//定时器计数
uint Tick;
/********************中断区域***************************************************/
/*******************************************************************************
函数名程: void Timer_2Init(void)
函数功能: 定时器T2 中断 1ms 配置
参数列表:
返回值 :
*******************************************************************************/
void Timer_2Init(void) //1000微秒@12.000MHz
{
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0x20; //设置定时初值
T2H = 0xD1; //设置定时初值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x04; //ET2 = 1;
EA = 1;
}
/*******************************************************************************
函数名程: void Timer2_Service() interrupt 12
函数功能: 定时器T2 中断 1ms 中断处理
参数列表:
返回值 :
*******************************************************************************/
void Timer2_Service() interrupt 12
{
static uchar dsp_com;
//定时任务
Tick++;
/***********控制外设的引脚操作*****/
//更新LED的引脚操作
CONTROL(LED,~((uchar)LED0|(uchar)LED1<<1|(uchar)LED2<<2|(uchar)LED3<<3|(uchar)LED4<<4|(uchar)LED5<<5|(uchar)LED6<<6|(uchar)LED7<<7));
//更新蜂鸣器、继电器的引脚操作
CONTROL(DEVICE,(uchar)Buzzer<<6|(uchar)Relay<<4);
//更新一位数码管的引脚操作
CONTROL(BIT,0); //消影
CONTROL(SEG,Dsp_Code[dsp_com]); //放入要显示的数字到段选锁存
CONTROL(BIT,1 << (dsp_com)); //选择对应的位选锁存
if(++dsp_com > 7) //显示完一次后自增位选,位选到8之后回到1
dsp_com = 0;
}
/*******************************************************************************
函数名程: void Seg_Tran(void)
函数功能: 将Dsp_Bit[]的内容,转换成对应的段码放入Dsp_Code[];
参数列表:
返回值 :
*******************************************************************************/
void Seg_Tran(void)
{
unsigned char i, j=0, temp;
for (i=0; i<8; i++, j++)
{
switch (Dsp_Bit[j])
{ // 低电平点亮段,段码[MSB...LSB]对应码顺序为[dp g f e d c b a]
case '0': temp = 0xc0; break;
case '1': temp = 0xf9; break;
// case '1': temp = 0xcf; break;
case '2': temp = 0xa4; break;
case '3': temp = 0xb0; break;
// case '3': temp = 0x86; break;
case '4': temp = 0x99; break;
// case '4': temp = 0x8b; break;
case '5': temp = 0x92; break;
case '6': temp = 0x82; break;
// case '6': temp = 0x90; break;
case '7': temp = 0xf8; break;
// case '7': temp = 0xc7; break;
case '8': temp = 0x80; break;
case '9': temp = 0x90; break;
// case '9': temp = 0x82; 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;
case '^': temp = 0xfe; break; // 1 1 1 1 1 1 1 0
case '_': temp = 0xf7; break; // 1 1 1 1 0 1 1 1
default: temp = 0xff;
}
if (Dsp_Bit[j+1] == '.')
{
temp = temp&0x7f; // 点亮小数点
j++;
}
Dsp_Code[i] = temp;
}
}
/********************应用层变量****************************/
//两个参数
char KeyNum;
//界面
uchar Page;
//按键返回值
uchar Key_Value;
//距离值
uchar Distance;
//参数下限值
int LB;
//参数上限值
int UB;
//报警次数计数
int Count = 0;
// 假设初始时Distance在范围内,1表示在范围内,0表示超出范围
int lastInRange = 1;
//是否切换L8的切换状态
bit LED7_Flag;
//切换模式标志位
bit Ms_Flag;
//转化值
uint adc;
//旋钮模式
uchar rot;
uint adc;
/*******************************************************************************
函数名程: void Display_Task(void)
函数功能: 显示内容执行任务,0.5s刷新一次显示内容
参数列表:
返回值 :
*******************************************************************************/
void Display_Task(void)
{
static uint Display_Tick;
if((Tick - Display_Tick) > 500)
{
Display_Tick = Tick;
switch (Page)
{
case 0://测距界面
sprintf(Dsp_Bit, "A %3u",(uint)Distance);//显示参数距离值
LED0=1;LED1=0;LED2=0;
break;
case 1://参数界面
//sprintf(Dsp_Bit, "P1 %3u",adc);//显示参数下限和上限
if(!Ms_Flag)//模式1,按键
{
sprintf(Dsp_Bit, "P1 %2u-%2u",(uint)LB,(uint)UB);//显示参数下限和上限
}
else//模式2,旋钮
{
//sprintf(Dsp_Bit, "P1 %3u",adc);//显示参数下限和上限
sprintf(Dsp_Bit, "P2 %2u-%2u",(uint)LB,(uint)UB);//显示参数下限和上限
}
LED0=0;LED1=1;LED2=0;
break;
case 2://记录界面
if(Count<10)
{
sprintf(Dsp_Bit, "E %d",(int)Count);//报警次数
}
else
{
sprintf(Dsp_Bit, "E -%d",(int)Count);//报警次数大于9
}
LED0=0;LED1=0;LED2=1;
break;
default:
break;
}
//sprintf(Dsp_Bit, "%9.2f", (float)KeyNum);
}
Seg_Tran();
}
/*******************************************************************************
函数名程: void checkDistance(int currentDistance)
函数功能: 检查Distance并更新Count
参数列表:
返回值 :
*******************************************************************************/
// 检查Distance并更新Count(这里Distance作为参数传入以模拟实际使用场景)
void checkDistance(int currentDistance) {
int currentInRange = ((currentDistance >= LB) && (currentDistance <= UB)); // 检查当前Distance是否在范围内
// 如果当前状态与上一次状态不同,并且当前状态是超出范围,则增加Count
if (currentInRange != lastInRange && !currentInRange) {
Count++;
}
// 更新上一次的状态
lastInRange = currentInRange;
}
//void taskrot(void)
//{
// if(rot == 1){
// if((adc >= 0)&&(adc < 20)){
// LB = 0;
// }else if((adc >= 20)&&(adc < 40)){
// LB = 10;
// }else if((adc >= 40)&&(adc < 60)){
// LB = 20;
// }else if((adc >= 60)&&(adc < 80)){
// LB = 30;
// }else if((adc >= 80)&&(adc < 100)){
// LB = 40;
// }
// }else if(rot == 2){
// if((adc >= 0)&&(adc < 20)){
// UB = 50;
// }else if((adc >= 20)&&(adc < 40)){
// UB = 60;
// }else if((adc >= 40)&&(adc < 60)){
// UB = 70;
// }else if((adc >= 60)&&(adc < 80)){
// UB = 80;
// }else if((adc >= 80)&&(adc < 100)){
// UB = 90;
// }
// }
//}
/*******************************************************************************
函数名程: void Key_Task(void)
函数功能: 按键执行任务,每10ms执行一次
参数列表:
返回值 :
*******************************************************************************/
void Key_Task(void)
{
static uint Key_Tick;
if((Tick - Key_Tick) > 10)
{
Key_Tick = Tick;
Key_Value = MatKeyScan_4x4();
switch (Page)
{
case 0://测距界面
switch (Key_Value)
{
case 4:
Page=1;//切换到参数界面
break;
//case:
//break;
default:
break;
}
break;
case 1://参数界面
switch (Key_Value)
{
case 4:
Page=2;//切换到记录界面
break;
case 5://模式切换
Ms_Flag=!Ms_Flag;
break;
case 9://上限调整(+10),初始值为50
if(!Ms_Flag)//按键模式
{
rot=0;
// if(UB<90)//参数上限调整顺序:50 60 70 80 90 50.
// {
// UB=UB+10;
// }
// else
// {
// UB=50;
// }
UB = (UB + 10) % 50 +50 ;
}
else//旋钮模式
{
rot = 1;//调整上限
}
break;
case 8://下限调整(+10),初始值为0
if(!Ms_Flag)//按键模式
{
rot=0;
// if(LB<=40)//参数下限调整顺序:0 10 20 30 40 0 10 .
// {
// LB=LB+10;
// }
// else
// {
// LB=0;
// }
LB = (LB + 10) % 50;
}
else//旋钮模式
{
rot = 2;//调整下限
}
default:
break;
}
break;
case 2://记录界面
switch (Key_Value)
{
case 4:
Page=0;//切换到测距界面
break;
//case:
//break;
case 5:
Count=0;//清0功能
default:
break;
}
//if(Distance>UB||Distance<LB)//测量的距离值由“参数下限≤测量的距离值≤参数上限”变为“测量的距离值>参数上限”或“测量的距离值<参数下限”时,报警次数加1;注意:持续处于测量的距离值>参数上限或测量的距离值<参数下限时,报警次数不改变
//{
checkDistance(Distance);
//}
break;
default:
break;
}
}
}
/*******************************************************************************
函数名程: void PCF8591_Data_Task(void)
函数功能: 定时处理数据的采集、处理、传输任务
参数列表:
返回值 :
*******************************************************************************/
void PCF8591_Data_Task(void)
{
static uint PCF8591_Data_Task;
if((Tick - PCF8591_Data_Task) > 500)
{
PCF8591_Data_Task = Tick;
adc =(uint) PCF8591_Adc(3)*100/255;
}
}
/*******************************************************************************
函数名程: void Data_Task(void)
函数功能: 定时处理数据的采集、处理、传输任务
参数列表:
返回值 :
*******************************************************************************/
void Data_Task(void)
{
static uint Data_Tick;
if((Tick - Data_Tick) > 100)
{
Data_Tick = Tick;
Distance=Dist_Meas();
}
}
/*******************************************************************************
函数名程: void Logic_Task(void)
函数功能: 定时按一定逻辑执行任务
参数列表:
返回值 :
*******************************************************************************/
void Logic_Task(void)
{
static uint Logic_Tick;
if((LB<=Distance)&&(UB>=Distance))//参数下限≤测量的距离值≤参数上限,指示灯L8点亮
{
LED7=1;
}
else//否则L8以0.1s为间隔切换亮灭状
{
if((Tick - Logic_Tick) > 100)
{
Logic_Tick = Tick;
LED7=!LED7;
}
}
}
/*******************************************************************************
函数名程: void taskrot_Logic_Task(void)
函数功能: 定时按一定逻辑执行任务
参数列表:
返回值 :
*******************************************************************************/
void taskrot_Logic_Task(void)
{
static uint taskrot_Logic_Tick;
if((Tick - taskrot_Logic_Tick) > 300)
{
taskrot_Logic_Tick = Tick;
if(rot == 2)//调整下限
{
if((adc >= 0)&&(adc < 20))
{
LB = 0;
}
else if((adc >= 20)&&(adc < 40))
{
LB = 10;
}
else if((adc >= 40)&&(adc < 60))
{
LB = 20;
}
else if((adc >= 60)&&(adc < 80))
{
LB = 30;
}
else if((adc >= 80)&&(adc < 100))
{
LB = 40;
}
}
else if(rot == 1)//调整上限
{
if((adc >= 0)&&(adc < 20))
{
UB = 50;
}
else if((adc >= 20)&&(adc < 40))
{
UB = 60;
}
else if((adc >= 40)&&(adc < 60))
{
UB = 70;
}
else if((adc >= 60)&&(adc < 80))
{
UB = 80;
}else if((adc >= 80)&&(adc < 100))
{
UB = 90;
}
}
}
}
/*******************************************************************************
函数名程: void P_Init(void)
函数功能: 外设初始化
参数列表:
返回值 :
*******************************************************************************/
void P_Init(void) //外设初始化
{
CONTROL(LED,0xff); //初始化LED熄灭
CONTROL(DEVICE,0x00); //初始化所有外设不工作
CONTROL(BIT,0x00); //初始化数码管位选无效
CONTROL(SEG,0xff); //初始化数码管段选无效
//定时器T2时基1ms初始化
Timer_2Init();
LB=10;UB=60;//初始值
}
/*************************************************************
主函数
**************************************************************/
void main(void)
{
/********************初始化********************************************/
P_Init();
/********************功能函数*******************************************/
while(1)
{
PCF8591_Data_Task();
Display_Task();
taskrot_Logic_Task();
//taskrot();
Key_Task();
Data_Task();
Logic_Task();
}
}
2>测距驱动函数
#include "ultrasonic.h"
sbit TX = P1^0;
sbit RX = P1^1;
unsigned char Dist_Meas(void)
{
unsigned int uiNum = 10; //每次发射产生10个周期的方波
TMOD &= 0x0f;
TMOD |= 0x10; // 设置T1为16位定时方式
#ifndef SRF04
/*********TX引脚发送40KHz方波信号驱动超声波发送探头*******/
TX = 0;
TL1 = 0xf4; // 设置T1低8位定时初值
TH1 = 0xff; // 设置T1高8位定时初值
TR1 = 1; // 定时器1计时,大概为12us,产生40KHz左右的方波
while (uiNum--) //产生10个周期的方波,比较保险
{
while (!TF1); // 这里不用中断,阻塞判断TF,等待定时
TX ^= 1; // 12us时间到,翻转发射引脚,产生40KHz左右的方波
TL1 = 0xf4; // 重装载T1低8位定时初值
TH1 = 0xff; // 重装载T1高8位定时初值
TF1 = 0;
}
TR1 = 0;
#else
TX = 1; // 触发测距
while (uiNum--); // 延时约10us
TX = 0;
uiNum = 5000;
while (!RX && (uiNum>0)) // 等待测距脉冲
uiNum--;
if(uiNum==0) // 超时返回255
return 255;
#endif
/************* 接收计时**************/
TL1 = 0; // 设置定时初值
TH1 = 0; // 设置定时初值
TR1 = 1;
while (RX && !TF1); // 阻塞等待收到脉冲或定时器溢出
TR1 = 0; // 收到脉冲或定时器溢出停止计时
if(TF1) // 超时返回255
return 255;
else
return ((TH1<<8)+TL1)*0.017+1; // 计算距离:340*100/1000000/2
}
3>PCF8591驱动函数
#include "iic.h"
// I2C引脚定义
//sfr P2 = 0xA0;
sbit SCL = P2^0; // 时钟线
sbit SDA = P2^1; // 数据线
void I2C_Delay(unsigned char i)
{
while(i--);
}
void Delay100ms() //@11.0592MHz
{
unsigned char data i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
// I2C起始条件
void I2C_Start(void)
{
SDA = 1;
SCL = 1;
I2C_Delay(DELAY_TIME);
SDA = 0;
I2C_Delay(DELAY_TIME);
}
// I2C停止条件
void I2C_Stop(void)
{
SDA = 0;
SCL = 1;
I2C_Delay(DELAY_TIME);
SDA = 1;
I2C_Delay(DELAY_TIME);
}
// I2C发送应答:0-应答,1-非应答
void I2C_SendAck(bit bAck)
{
SCL = 0;
SDA = bAck;
I2C_Delay(DELAY_TIME);
SCL = 1;
I2C_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
I2C_Delay(DELAY_TIME);
}
// I2C等待应答
bit I2C_WaitAck(void)
{
bit bAck;
SCL = 1;
I2C_Delay(DELAY_TIME);
bAck = SDA;
SCL = 0;
I2C_Delay(DELAY_TIME);
return bAck;
}
// I2C发送数据
void I2C_SendByte(unsigned char ucData)
{
unsigned char i;
for (i=0; i<8; i++)
{
SCL = 0;
I2C_Delay(DELAY_TIME);
if (ucData & 0x80)
SDA = 1;
else
SDA = 0;
I2C_Delay(DELAY_TIME);
SCL = 1;
ucData <<= 1;
I2C_Delay(DELAY_TIME);
}
SCL = 0;
}
// I2C接收数据
unsigned char I2C_RecvByte(void)
{
unsigned char i, ucData;
for (i=0; i<8; i++)
{
SCL = 1;
I2C_Delay(DELAY_TIME);
ucData <<= 1;
if (SDA)
ucData |= 1;
SCL = 0;
I2C_Delay(DELAY_TIME);
}
return ucData;
}
#define AT24C02
#ifdef AT24C02
// AT24C02缓存器写:pucBuf-数据,ucAddr-地址,ucNum-数量
void AT24C02_WriteBuffer(unsigned char *pucBuf,
unsigned char ucAddr, unsigned char ucNum)
{
I2C_Start();
I2C_SendByte(0xa0); // 发送器件地址及控制位(写)
I2C_WaitAck();
I2C_SendByte(ucAddr); // 发送数据地址
I2C_WaitAck();
while (ucNum--)
{
I2C_SendByte(*pucBuf++); // 发送数据
I2C_WaitAck();
I2C_Delay(200);
}
I2C_Stop();
Delay100ms();
Delay100ms();
}
// AT24C02缓存器读:pucBuf-数据,ucAddr-地址,ucNum-数量
void AT24C02_ReadBuffer(unsigned char *pucBuf,
unsigned char ucAddr, unsigned char ucNum)
{
I2C_Start();
I2C_SendByte(0xa0); // 发送器件地址及控制位(写)
I2C_WaitAck();
I2C_SendByte(ucAddr); // 发送数据地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0xa1); // 发送器件地址及控制位(读)
I2C_WaitAck();
while (ucNum--)
{
*pucBuf++ = I2C_RecvByte(); // 接收数据
if (ucNum)
I2C_SendAck(0);
else
I2C_SendAck(1);
}
I2C_Stop();
Delay100ms();
Delay100ms();
}
#endif
#define PCF8591
#ifdef PCF8591
// PCF8591 ADC:ucAin-ADC通道(0~3),返回值-ADC值
//1:光敏 3:电位器
unsigned char PCF8591_Adc(unsigned char ucAin)
{
unsigned char ucAdc;
EA = 0;
I2C_Start();
I2C_SendByte(0x90); // 发送器件地址及控制位(写)
I2C_WaitAck();
I2C_SendByte(ucAin + 0x40); // 发送控制字(ADC通道,允许DAC)
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0x91); // 发送器件地址及控制位(读)
I2C_WaitAck();
ucAdc = I2C_RecvByte(); // 接收ADC值
I2C_SendAck(1);
I2C_Stop();
EA = 1;
return ucAdc;
}
// PCF8591 DAC: ucDac-DAC值
void PCF8591_Dac(unsigned char ucDac)
{
EA = 0;
I2C_Start();
I2C_SendByte(0x90); // 发送器件地址及控制位(写)
I2C_WaitAck();
I2C_SendByte(0x40); // 发送控制字(允许DAC)
I2C_WaitAck();
I2C_SendByte(ucDac); // 发送DAC值
I2C_WaitAck();
I2C_Stop();
EA = 1;
}
#endif
4>矩阵按键驱动函数
#include "key.h"
/*******************************************************************************
函数名程: KeyEvent_TypeDef KeyScan(void)
函数功能: 检测独立按键是否按下,按下则返回对应键值
返回值 : S4_PRESS(4):S4按下
S5_PRESS(5):S5按下
S6_PRESS(6):S6按下
S7_PRESS(7):S7按下
UNPRESS(0):未有按键按下
*******************************************************************************/
uchar KeyScan(void)
{
static uchar S4_value = 0xFF;
static uchar S5_value = 0xFF;
static uchar S6_value = 0xFF;
static uchar S7_value = 0xFF;
static uchar lock = 0;
//2ms读取一次,读取前左移,将新读取的状态放在最低位,0为按下,1为抬起
//连续8次都读取到按键按下,keyx_value值变为0x00时方才触发按下事件
S4_value <<= 1;
S4_value |= S4;
S5_value <<= 1;
S5_value |= S5;
S6_value <<= 1;
S6_value |= S6;
S7_value <<= 1;
S7_value |= S7;
if((!S4_value||!S5_value||!S6_value||!S7_value) && !lock) //如果任意一个按键触发且按键未上锁
{
lock = 1; //上锁,并返回相应键值
if(!S4_value)
return 4;
else if(!S5_value)
return 5;
else if(!S6_value)
return 6;
else if(!S7_value)
return 7;
}
else if(S4_value && S5_value && S6_value && S7_value && lock) //如果已上锁且所有按键都不触发(触发按键已抬起)
{
lock = 0;
}
else{ //
}
// Delayms(2);
return 0;
}
/*******************************************************************************
函数名程: uchar MatKeyScan_4x4(void)
函数功能: 检测矩阵按键是否按下,按下则返回对应键值
返回值 : S4~S19
*******************************************************************************/
uchar MatKeyScan_4x4(void)
{
static uchar pre_scan = 0, //continue value
pre_trg = 0; //last trigger value
uchar scan = 0, //trigger value
trg = 0, //current value
value = 0, //必须初始化为3
key_x = 0,
key_y = 0;
P3 = 0x0f;
P4 = 0x00;
if(!P30) key_x = 3; //获取X轴坐标
else if(!P31) key_x = 2;
else if(!P32) key_x = 1;
else if(!P33) key_x = 0;
P3 = 0xf0;
P4 = 0xff;
if(!P34) key_y = 4; //获取Y轴坐标
else if(!P35) key_y = 3;
else if(!P42) key_y = 2;
else if(!P44) key_y = 1;
scan = key_x + key_y * 4; //根据按键连线和键值设置得出的键值计算式
if(scan == pre_scan) //消抖:只有连续两次检测相同才触发,否则无效
trg = scan;
else
trg = 0;
if(trg == pre_trg) //自锁:连续相同的触发,不再产生键值(0)
value = 0;
else
value = trg; //前后不同的触发,更新成后一次的键值。如果是按下S15,即前一次触发0,后一次触发15,更新15
pre_scan = scan; //每次检测后,保存值至PRE里以便下次比较
pre_trg = trg;
return value; //返回独立按键的键值
}
#define LONG_PRESS_THRESHOLD 100 // 长按阈值设定,单位为10ms
uchar MatKeyScan_2x4(void)
{
static uchar scanState = 0; // 扫描状态
static uchar pressDuration = 0; // 按下持续时间计数器
static uchar keyValue = 0; // 按键值局部变量
// 设置列线为高电平
P44 = 1;
P42 = 1;
P34 = 1;
P35 = 1;
// 设置行线为高电平
P33 = 0;
P32 = 0;
switch (scanState) {
case 0: // 准备扫描
if (P44 == 0 || P42 == 0 || P34 == 0 || P35 == 0) { // 检查是否有键被按下
scanState = 1;
}
break;
case 1: // 确定按键按下
if (P44 == 0) { // 检查第一列
keyValue = 4;
} else if (P42 == 0) { // 检查第二列
keyValue = 8;
} else if (P35== 0) { // 检查第一列
keyValue = 12;
} else if (P34 == 0) { // 检查第二列
keyValue = 16;
} else {
// 如果两行都不是低电平,说明之前的检测可能是干扰
scanState = 0;
break;
}
// 设置列线为低电平
P44 = 0;
P42 = 0;
P34 = 0;
P35 = 0;
// 设置行线为高电平
P33 = 1;
P32 = 1;
if (keyValue == 4) { // 按键在第一列
if (P33 == 0) keyValue = 4; // S4
if (P32 == 0) keyValue = 5; // S5
} else if (keyValue == 8) { // 按键在第二列
if (P33 == 0) keyValue = 8; // S8
if (P32 == 0) keyValue = 9; // S9
}else if (keyValue == 12) { // 按键在第三列
if (P33 == 0) keyValue = 12; // S12
if (P32 == 0) keyValue = 13; // S13
}else if (keyValue == 16) { // 按键在第四列
if (P33 == 0) keyValue = 16; // S16
if (P32 == 0) keyValue = 17; // S17
}
scanState = 2;
pressDuration = 0; // 开始计时
break;
case 2: // 按键释放,检测长按
if (P44 && P42 & P34 & P35) {
// // 按键释放
// if (pressDuration == LONG_PRESS_THRESHOLD) { // 长按了
// keyValue = keyValue | 0x80; // 输出长按键值
// } else { // 不是长按 键值不变
// }
// pressDuration = 0; // 计时清零
scanState = 0; // 重置状态
return keyValue; // 返回短按键值
// } else {
// // 按键持续被按下
// if (pressDuration < LONG_PRESS_THRESHOLD) {
// pressDuration++; // 更新持续时间
// }
}
break;
}
return 0; // 无按键操作
}
5>CT107D头文件
#ifndef __MAIN_H
#define __MAIN_H
#include "stc15.h"
typedef unsigned int uint; //对系统默认数据类型进行重定义
typedef unsigned char uchar;
#endif