飞行者
发表于 2025-8-4 12:02:20
11.矩阵按键
io.c代码
#include "io.h"
u8 State1 = 0; //LED1初始状态
u8 State2 = 0; //LED2初始状态
u8 State3 = 0; //LED3初始状态
u16 Key_Vol = 0; //按键按下持续时间(几个十秒)
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
void LED0_Blink(void)
{
State1 = !State1;
P00 = State1;
}
void LED1_Blink(void)
{
State2 = !State2;
P01 = State2;
}
void LED2_Blink(void)
{
State3 = !State3;
P02 = State3;
}
void Key_Task(void)
{
if( P32 == 0 )
{
Key_Vol++;
if(Key_Vol == 5)
{
printf("按键单击\r\n");
}
}
else
{
Key_Vol = 0;
}
}
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
//任务1:数码管显示当前的按键号
u8 key_num = 0xff; //key_num =0 给按键传递一个键值
void Task_1(void)
{
//static静态变量,只有第一次进来时才赋值,key_num =0 给按键传递一个键值
// ①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if((ROW1 == 0) || (ROW2 == 0)) //"||"或的符号,只要满足其中之一的条件(行按键按下)
{
if((ROW1 == 0) && (ROW2 == 0)) //如果两行都有按键按下,不处理
{
}
else if ( ((ROW1 == 1)&&(ROW2 == 0)) || ((ROW1 == 0)&&(ROW2 == 1)) ) //如果有按键按下,而且只有一颗
{
if( ROW1 == 0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if(ROW2 == 0)
key_num = 4;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//判断哪一列,叠加按键的序号
if(COL1 ==0)
{
// key_num = key_num +0;
}
else if( COL2 == 0)
{
key_num = key_num +1;
}
else if( COL3 == 0)
{
key_num = key_num +2;
}
else if( COL4 == 0)
{
key_num = key_num +3;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else
{
key_num =0xff;
}
// ③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
//void SEG_Task(void)
//{
// if(key_num == 255)
// Display_Seg( SEG_NUM, ~T_NUM); //数码管段码和位码
// else
// Display_Seg( SEG_NUM, ~T_NUM); //数码管段码和位码
//}
//任务二:简易密码箱
u8 passward = { 16,16,16,16,16,16,16,16};
u8 Seg_no = 0;
void SEG_Task(void)
{
u8 num = 0;
if( Seg_no ==0 ) //小时十位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==1 ) //小时的个位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==2 ) //第一个横杠
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==3 ) //分钟的十位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==4 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==5 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==6 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==7 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else
{
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
u8 Key_Vol3 = 0; //判断按钮是否按下
u8 Key_no = 0; //数码管位计数
//密码输入显示
void PW_write_Task(void)
{
if( key_num <0xff )
{
Key_Vol3 ++;
if(Key_Vol3 == 5)
{
if(Key_no == 0) //数码管没有按下时显示“-”
{
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
}
passward = key_num;
Key_no++;
if( Key_no == 8 ) //密码输入到了八位
{
if((passward==1)&&(passward==2)&&(passward==3)&&(passward==4)&&(passward==5)&&(passward==6)&&(passward==7)&&(passward==8))
{
//密码正确八位数码管显示为1
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 1;
}
else //密码输入错误八位数码管显示为空
{
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
}
Key_no = 0;
}
}
}
else
{
Key_Vol3 = 0;
}
}io.h代码
#ifndef __IO_H
#define __IO_H
#include "config.h" //调用头文件
//定义引脚名称
#define HC595_SER P34
#define HC595_RCK P35
#define HC595_SCK P32
/*-----------定义端口-------------*/
#define ROW1 P06 //定义端口
#define ROW2 P07
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
extern u8 key_num ;
/*-----------函数声明-------------*/
void LED0_Blink(void);
void LED1_Blink(void);
void LED2_Blink(void);
void Key_Task(void);
void Task_1(void);
void SEG_Task(void);
void Init_595(void);
void PW_write_Task(void);
#endiftask.c代码
#include "task.h"
#include "io.h"
//========================================================================
// 本地变量声明
//========================================================================
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
// {0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */
// {0, 600, 600, LED1_Blink}, /* task 1 Period: 600ms */
// {0, 900, 900, LED2_Blink}, /* task 1 Period: 900ms */
// {0, 10, 10, Key_Task}, /* task 1 Period: 50ms */
{0, 10, 10, Task_1}, /* task 1 Period: 10ms */
{0, 1, 1, SEG_Task}, /* task 1 Period: 10ms */
{0, 10, 10, PW_write_Task}, /* task 1 Period: 100ms */
};
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps);
//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
void Task_Marks_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.TIMCount) /* If the time is not 0 */
{
Task_Comps.TIMCount--; /* Time counter decrement */
if(Task_Comps.TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps.TIMCount = Task_Comps.TRITime;
Task_Comps.Run = 1; /* The task can be run */
}
}
}
}
//========================================================================
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
void Task_Pro_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.Run) /* If task can be run */
{
Task_Comps.Run = 0; /* Flag clear 0 */
Task_Comps.TaskHook(); /* Run task */
}
}
}
898
飞行者
发表于 2025-8-10 21:34:44
11课 矩阵按键 课后小练 简易洗衣机
核心工作原理
1. 矩阵按键扫描原理
硬件结构:采用 2 行(ROW1、ROW2)4 列(COL1-COL4)的矩阵按键,通过行列交叉定位按键。
扫描逻辑:
行检测:列输出低电平,行输出高电平,检测行电平(低电平表示该行有按键按下),确定行号(ROW1=0 对应行 0,ROW2=0 对应行 4)。
列检测:行输出低电平,列输出高电平,检测列电平(低电平表示该列有按键按下),确定列号(COL1-COL4 对应 0-3)。
键值计算:键值 = 行号 + 列号(如行 0 列 1 对应键值 1,行 1 列 2 对应键值 6)。
2. 数码管显示原理
硬件驱动:通过 74HC595 芯片级联驱动 8 位数码管,实现串行数据转并行输出,节省 IO 口。
动态扫描:通过 Seg_no 循环切换数码管位选(0-7),每次仅点亮一位数码管,利用人眼视觉暂留效应实现 “同时显示”。
显示逻辑:仅使用第 6、7 位数码管(从 0 开始计数),非计时模式显示工作模式(如 “01” 表示模式 1),计时模式显示倒计时(如 “10” 表示 10 秒)。
3. 洗衣机工作流程
开机:按下开机键(key_num=0),ON_OFF=1 上电,默认模式 1(workingMode=1),数码管显示 “01”。
选模式:按下模式键 1-5(key_num=1-5),workingMode 更新为对应值,数码管同步显示。
启动:按下启动键(key_num=6),Start_work=1 开始计时,Work_Time=1 切换到计时模式,数码管显示倒计时。
倒计时:TIMECOUNT_Task 定期递减 timeVol(单位:ms),数码管实时刷新剩余时间。
结束:倒计时结束(timeVol=0),自动关机(ON_OFF=0),数码管熄灭。
IO.C
#include "io.h"
bit ON_OFF = 0; //洗衣机的加电和去电,对应的开和关。1 洗衣机加电,0 洗衣机断电
bit Start_work = 0; //洗衣机的启动和停止。1 洗衣机启动工作,0 洗衣机停止工作。
bit Work_Time = 0; //工作计数。 1 工作模式开始倒计时,0 暂停工作模式。
u8 Run_Key =0 ; //运行
u8 key_num = 17; //定义按键的初始值,11对应段码的空字符
u8 workingMode = 17; //定义密码初始值,17对应段码的空字符
u8 Seg_no = 0; // 数码管扫描位置(0-7,控制8位数码管的刷新)
u16 key_vol = 0; //按键延时(用于防抖)
u16 timeVol = 0; //计时
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
//u8 key_num = 0xff; //key_num =0 给按键传递一个键值
void Task_1( void )
{
// ①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if((ROW1 == 0) || (ROW2 == 0)) //"||"或的符号,只要满足其中之一的条件(行按键按下)
{
if((ROW1 == 0) && (ROW2 == 0)) //如果两行都有按键按下,不处理
{
}
else if ( ((ROW1 == 1)&&(ROW2 == 0)) || ((ROW1 == 0)&&(ROW2 == 1)) ) //如果有按键按下,而且只有一颗
{
if( ROW1 == 0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if(ROW2 == 0)
key_num = 4;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//判断哪一列,叠加按键的序号
if(COL1 ==0)
{
// key_num = key_num +0;
}
else if( COL2 == 0)
{
key_num = key_num +1;
}
else if( COL3 == 0)
{
key_num = key_num +2;
}
else if( COL4 == 0)
{
key_num = key_num +3;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else
{
key_num =0xff;
}
// ③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
void SEG_Task(void)
{
// 只需使用两位数码管,其它位全部为空白
if (Seg_no == 0)
{
// 第0位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 1)
{
// 第1位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 2)
{
// 第2位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 3)
{
// 第3位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 4)
{
// 第4位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 5)
{
// 第5位数码管:显示空字符
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 6)
{
// 第6位数码管:计时模式显示十位数,否则显示空
u8 seg_index;// 定义段码下标变量
if (Work_Time == 1)// 判断是否为计时模式
{
seg_index = timeVol / 10000;// 计时模式:取倒计时十位数
}
else
{
//seg_index = workingMode / 10; // 工作模式:显示空字符
seg_index = (workingMode == 17) ? 17 : (workingMode / 10);
}
Display_Seg(SEG_NUM, ~T_NUM);
}
else if (Seg_no == 7)
{
// 第7位数码管:计时模式显示个位数,否则显示工作模式
u8 seg_index;// 定义段码下标变量
if (Work_Time == 1)// 判断是否为计时模式
{
seg_index = timeVol / 1000 % 10;// 计时模式:取倒计时个位数
}
else
{
seg_index = workingMode;// 工作模式:显示当前模式编号
}
Display_Seg(SEG_NUM, ~T_NUM);
}
// 更新数码管扫描位置(循环0-7)
Seg_no++;
if (Seg_no > 7)
{
Seg_no = 0;
}
}
//倒计时
void TIMECOUNT_Task(void)
{
if(Start_work == 1)
{
timeVol--;
if(timeVol == 0)
{
Start_work = 0; //停止计时
Work_Time = 0; //切换到工作模式状态
workingMode = 17; //清屏
ON_OFF = 0; //关机
}
}
}
/*简易洗衣机面板
1.按下开机键后,数码管显示1,表示默认为清洗模式1;
2.用矩阵按键模拟洗衣机的操作面板,前五个按键模拟1-5的清洗模式按键,选择几的时候数码管显示数字几,表示以当前为第几个功能;
3.按下启动后,按照选择的模式对应的时间开始倒计时,倒计时结束后,数码管熄灭,表示清洗完成(清洗时间自己随意设置)
*/
void Task_2( void )
{
if(key_num < 17)
{
key_vol++;
if(key_vol >= 20)
{
// 现有case处理逻辑...
// 处理完成后可临时锁定,避免重复触发(可选)
key_vol = 20;// 保持阈值,避免长按重复触发
}
else// 按键释放状态
{
key_vol = 0;// 重置防抖计数
}
switch(key_num)
{
case 0:
if(ON_OFF == 0)
{
ON_OFF = 1;
workingMode = 1;// 开机默认模式1
timeVol = 10000;// 默认模式时间
}
break;// 新增break,避免穿透
case 1: //1:常用模式,定时10秒
if(ON_OFF == 1 && Start_work == 0)
{
workingMode = 1; //与case0共用本条,开机与设置为常用模式都置1
timeVol = 10000;
}
break;
case 2: //2:标准模式,定时15秒
if(ON_OFF ==1 && Start_work == 0)
{
workingMode = key_num;
timeVol = 15000;
}
break;
case 3: //3:快洗模式,定时5秒
if(ON_OFF ==1 && Start_work == 0)
{
workingMode = key_num;
timeVol = 5000;
}
break;
case 4: //4:毛毯模式,定时30秒
if(ON_OFF ==1 && Start_work == 0)
{
workingMode = key_num;
timeVol = 30000;
}
break;
case 5: //5:风干模式,定时20秒
if(ON_OFF ==1 && Start_work == 0)
{
workingMode = key_num;
timeVol = 20000;
}
break;
case 6: //启动模式
if(ON_OFF == 1)
{
Start_work= 1; //定时器开始工作
Work_Time = 1; //切换到计时工作状态
}
break;
default: //其它情况默认不执行直接退出,比如按下按键7
break;
}
}
}
main.c
#include "config.h"
#include "task.h"
#include "io.h"
void main(void)
{
Sys_init(); //系统初始化
usb_init(); //USB CDC 接口配置
IE2 |= 0x80; //使能USB中断
Timer0_Init(); //定时器初始化
Init_595();
P0 = 0XFF; // 初始化IO口(置高,确保输入有效)
EA = 1;
P40 = 0;
while(1)
{
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done();
}
Task_1(); // 扫描按键
Task_2(); // 处理按键
SEG_Task(); // 刷新数码管
TIMECOUNT_Task(); // 可选:添加定时器中断触发
}
}
void Timer0_Isr(void) interrupt 1
{
Task_Marks_Handler_Callback(); //系统计时
;
}903
飞行者
发表于 2025-8-10 21:42:46
12 复位系统
IO.C
#include "io.h"
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x54, /*'N', 16*/
0x5C, /*'O', 17*/
0x73, /*'P', 18*/
0x50, /*'R', 19*/
0x40, /*'-', 20*/
0x00, /*' ', 21*/
0x80, /*'.', 22*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
u8 key_num = 0xff; //key_num =0 给按键传递一个键值
void Task_1( void )
{
// ①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if((ROW1 == 0) || (ROW2 == 0)) //"||"或的符号,只要满足其中之一的条件(行按键按下)
{
if((ROW1 == 0) && (ROW2 == 0)) //如果两行都有按键按下,不处理
{
}
else if ( ((ROW1 == 1)&&(ROW2 == 0)) || ((ROW1 == 0)&&(ROW2 == 1)) ) //如果有按键按下,而且只有一颗
{
if( ROW1 == 0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if(ROW2 == 0)
key_num = 4;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//判断哪一列,叠加按键的序号
if(COL1 ==0)
{
// key_num = key_num +0;
}
else if( COL2 == 0)
{
key_num = key_num +1;
}
else if( COL3 == 0)
{
key_num = key_num +2;
}
else if( COL4 == 0)
{
key_num = key_num +3;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else
{
key_num =0xff;
}
// ③第三步:行列组合一下就可以判断出是哪个按键按下了。
}u8 Key_vol = 0;
void KEY_Task(void)
{
if(P33 == 0)
{
Key_vol++;
if(Key_vol == 5)
{
USB_Reset_U();
IAP_CONTR = 0X20;
}
}
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
//void SEG_Task(void)
//{
// if(key_num == 255)
// Display_Seg( SEG_NUM, ~T_NUM); //数码管段码和位码
// else
// Display_Seg( SEG_NUM[ key_num ], ~T_NUM); //数码管段码和位码
//}
//任务二:简易密码箱
u8 passward = { 20,20,20,20,20,20,20,20};
u8 Seg_no = 0;
void SEG_Task(void)
{
u8 num = 0;
switch(Seg_no){
case 0: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 1: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 2: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 3: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 4: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 5: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 6: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 7: Display_Seg( SEG_NUM], ~T_NUM ); break;
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
u8 Key_Vol3 = 0; //判断按钮是否按下
u8 Key_no = 0; //数码管位计数
//密码输入显示
void PW_write_Task(void)
{
if( key_num <0xff )
{
Key_Vol3 ++;
if(Key_Vol3 == 5) //是否延时50m秒
{
if(Key_no == 0) //数码管没有按下时显示“--------”
{
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
}
passward = key_num; //键值传递
Key_no++;
if( Key_no == 8 ) //密码输入到了八位
{
if((passward==1)&&(passward==2)&&(passward==3)&&(passward==4)&&(passward==5)&&(passward==6)&&(passward==7)&&(passward==0))
{
//密码正确八位数码管显示为1
passward = 20;
passward = 20;
passward = 17;
passward = 18;
passward = 14;
passward = 16;
passward = 20;
passward = 20;
}
else //密码输入错误八位数码管显示为空
{
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
}
Key_no = 0;
}
}
}
else
{
Key_Vol3 = 0;
}
}config.c部分代码
//config.c部分代码
#include "config.h"
void Delay10ms(void) //@24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
_nop_();
i = 59998UL;
while (i) i--;
}
void USB_Reset_U(void)
{
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
USBCON = 0X00;
USBCLK = 0X00;
IRC48MCR = 0X00;
Delay10ms();
}
void Sys_init(void)
{
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
USB_Reset_U();
}config.h代码
//config.h代码
#ifndef __CONFIG_H
#define __CONFIG_H
#include "ai8051u.h" //调用头文件
#include "AI_usb.h" //调用头文件
#include "intrins.h" //d调用头文件
void Sys_init(void); //函数声明
void Timer0_Init(void);
void USB_Reset_U(void);
#endif
//main.c
//main.c代码
#include "config.h"
#include "task.h"
#include "io.h"
void main(void)
{
Sys_init(); //系统初始化
usb_init(); //USB CDC 接口配置
IE2 |= 0x80; //使能USB中断
Timer0_Init(); //定时器初始化
Init_595();
EA = 1;
P40 = 0;
WDT_CONTR = 0X24;
while(1)
{
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done();
}
Task_Pro_Handler_Callback(); //执行功能函数
WDT_CONTR = 0X34;
}
}
void Timer0_Isr(void) interrupt 1
{
Task_Marks_Handler_Callback(); //系统计时
;
}904
飞行者
发表于 2025-8-12 07:25:49
12 复位系统课后小练
密码锁
1.没有输入时,显示“- - - - - - - -”
2.有输入时,按下一个按键,开始按顺序写入
例如,第一个按下1,显示“1 - - - - - - -”
例如,第二个按下3,显示“1 3 - - - - - -”
3.当按下的密码为“ 1 2 3 4 5 6 7 0”时,数码管显示open的字符,否则,还是显示“- - - - - - - -”
新增:
1.看门狗,超时1秒自动复位
2.增加开机版本号,开机显示三秒的U 1.00 版本号
3.增加手动复位,P33按钮按下时重启(方便查看版本号和清除密码)
io.c
#include "io.h"
u8 State1 = 0; //LED1初始状态
u8 State2 = 0; //LED2初始状态
u8 State3 = 0; //LED3初始状态
u16 Key_Vol ; //按键按下持续时间
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x54, /*'N', 16*/
0x5C, /*'O', 17*/
0x73, /*'P', 18*/
0x3E, /*'U', 19*/
0x40, /*'-', 20*/
0x86, /*'1', 21*/
0x00, /*' ', 22*/
0x80, /*'.', 23*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
void LED1_Blink(void)
{
State2 = !State2;
P01 = State2;
}
void LED2_Blink(void)
{
State3 = !State3;
P02 = State3;
}
void KEY_Task(void)
{
if( P33 == 0 )
{
Key_Vol++;
if( Key_Vol==5 )
{
//按键按下的任务
// printf( "按键单击\r\n" );
USB_Reset_U();
IAP_CONTR = 0X20;
}
}
else
{
Key_Vol = 0;
}
}
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
u8 key_num = 0xff; //key_num =0 给按键传递一个键值
void Task_1( void )
{
// ①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if((ROW1 == 0) || (ROW2 == 0)) //"||"或的符号,只要满足其中之一的条件(行按键按下)
{
if((ROW1 == 0) && (ROW2 == 0)) //如果两行都有按键按下,不处理
{
}
else if ( ((ROW1 == 1)&&(ROW2 == 0)) || ((ROW1 == 0)&&(ROW2 == 1)) ) //如果有按键按下,而且只有一颗
{
if( ROW1 == 0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if(ROW2 == 0)
key_num = 4;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//判断哪一列,叠加按键的序号
if(COL1 ==0)
{
// key_num = key_num +0;
}
else if( COL2 == 0)
{
key_num = key_num +1;
}
else if( COL3 == 0)
{
key_num = key_num +2;
}
else if( COL4 == 0)
{
key_num = key_num +3;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else
{
key_num =0xff;
}
// ③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
//void SEG_Task(void)
//{
// if(key_num == 255)
// Display_Seg( SEG_NUM, ~T_NUM); //数码管段码和位码
// else
// Display_Seg( SEG_NUM[ key_num ], ~T_NUM); //数码管段码和位码
//}
//任务二:简易密码箱
u8 passward = { 22,22,22,22,19,21,0,0};
extern u16 Ms_Time;
u8 Start_ms=0;
//延时三秒函数
void Timing_Start(void)
{
if(Start_ms==0)
{
if(Ms_Time==3000)
{
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
Start_ms=1;
}
}
}
u8 Seg_no = 0;
void SEG_Task(void)
{
u8 num = 0;
switch(Seg_no){
case 0: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 1: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 2: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 3: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 4: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 5: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 6: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 7: Display_Seg( SEG_NUM], ~T_NUM ); break;
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
u8 Key_Vol3 = 0; //判断按钮是否按下
u8 Key_no = 0; //数码管位计数
//密码输入显示
void PW_write_Task(void)
{
if( key_num <0xff )
{
Key_Vol3 ++;
if(Key_Vol3 == 5) //是否延时50m秒
{
passward = key_num; //键值传递
Key_no++;
if( Key_no == 8 ) //密码输入到了八位
{
if((passward==1)&&(passward==2)&&(passward==3)&&(passward==4)&&(passward==5)&&(passward==6)&&(passward==7)&&(passward==0))
{
//密码正确八位数码管显示为1
passward = 21;
passward = 21;
passward = 17;
passward = 18;
passward = 14;
passward = 16;
passward = 21;
passward = 21;
}
else //密码输入错误八位数码管显示为空
{
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
passward = 20;
}
Key_no = 0;
}
}
}
else
{
Key_Vol3 = 0;
}
}915
飞行者
发表于 2025-8-16 15:33:28
13 外部中断
io.c
//外部中断INT1 函数
void INT1_Init(void)
{
IT1 = 1; //下降沿中断
EX1 = 1; //下降中断允许
EA = 1; //打开总中断
}
//中断服务函数,一旦发生了中断就先执行“中断服务函数”
void INI1_Isr(void) interrupt 2
{
P01 = !P01;
}
io.h
void INT1_Init(void);外部中断INT1 函数声明
main.c
#include "config.h"
#include "task.h"
#include "io.h"
//延时函数
void Delay3000ms(void) //@24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 17999998UL;
while (i) i--;
}
void main(void)
{
Sys_init(); //系统初始化
usb_init(); //USB CDC 接口配置
IE2 |= 0x80; //使能USB中断
Timer0_Init(); //定时器初始化
Init_595();
INT1_Init(); //外部中断int1初始化
EA = 1;
P40 = 0;
// WDT_CONTR = 0X24;
while(1)
{
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done();
}
// Task_Pro_Handler_Callback(); //执行功能函数
// WDT_CONTR = 0X34;
/*延时三秒钟P00LED灯取反一次 */
P00 = !P00;
Delay3000ms();
}
}
void Timer0_Isr(void) interrupt 1
{
Task_Marks_Handler_Callback(); //系统计时
;
}920
飞行者
发表于 昨天 20:03
13.外部中断 课后小练 雕刻机保护系统
io.c代码
#include "io.h"
#define LED04 P04
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x54, /*'N', 16*/
0x5C, /*'O', 17*/
0x73, /*'P', 18*/
0x50, /*'R', 19*/
0x40, /*'-', 20*/
0x00, /*' ', 21*/
0x80, /*'.', 22*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
u8 key_num = 0xff; //key_num =0 给按键传递一个键值
//矩阵键盘扫描
void Task_1( void )
{
// ①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if((ROW1 == 0) || (ROW2 == 0)) //"||"或的符号,只要满足其中之一的条件(行按键按下)
{
if((ROW1 == 0) && (ROW2 == 0)) //如果两行都有按键按下,不处理
{
key_num = 0xff;
}
else if ( ((ROW1 == 1)&&(ROW2 == 0)) || ((ROW1 == 0)&&(ROW2 == 1)) ) //如果有按键按下,而且只有一颗
{
if( ROW1 == 0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if(ROW2 == 0)
key_num = 4;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//判断哪一列,叠加按键的序号
if(COL1 == 0) key_num += 0;
else if(COL2 == 0) key_num += 1;
else if(COL3 == 0) key_num += 2;
else if(COL4 == 0) key_num += 3;
}
}
else
{
key_num = 0xff; // 无按键按下
}
// ③第三步:行列组合一下就可以判断出是哪个按键按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 1;
ROW2 = 1;
}
void LED_Control_Task(void)
{
// 根据工作状态控制LED0
if(is_working)
LED0 = 0;// 低电平点亮LED0
else
LED0 = 1;// 高电平熄灭LED0
}
u8 Key_vol = 0;
u8 is_working = 0; // 工作状态:0-停止,1-工作
u8 key_press_flag = 0;// 按键消抖标志(防止重复触发)
void KEY_Task(void)
{
//static u8 key_press_flag = 0;// 按键按下标志
// 检测到按钮1按下且未处理过
if(key_num == 1 && key_press_flag == 0)
{
key_press_flag = 1; // 标记为已处理
is_working = !is_working; // 切换工作状态
}
// 按键释放后重置标志
else if(key_num == 0xff)
{
key_press_flag = 0;
}
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
u8 Seg_no = 0;
void SEG_Task(void)
{
// 显示工作状态:1表示工作,0表示停止
u8 disp_num = is_working ? 1 : 0;
switch(Seg_no)
{
case 0: Display_Seg(SEG_NUM, ~T_NUM); break;
case 1: Display_Seg(SEG_NUM, ~T_NUM); break;// 空格
// 其他位不显示
case 2: Display_Seg(SEG_NUM, ~T_NUM); break;
case 3: Display_Seg(SEG_NUM, ~T_NUM); break;
case 4: Display_Seg(SEG_NUM, ~T_NUM); break;
case 5: Display_Seg(SEG_NUM, ~T_NUM); break;
case 6: Display_Seg(SEG_NUM, ~T_NUM); break;
case 7: Display_Seg(SEG_NUM, ~T_NUM); break;
}
Seg_no = (Seg_no + 1) % 8;
}
//外部中断INT1 函数
void INT1_Init(void)
{
IT1 = 1; //下降沿中断
EX1 = 1; //下降中断允许
EA = 1; //打开总中断
P33 = 1;
}
//中断服务函数,一旦发生了中断就先执行“中断服务函数”
void INT1_Isr(void) interrupt 2
{
// 测试:按下P33时,数码管0位显示0(模拟切断电源)
is_working = 0;// 停止工作状态
// 清除中断标志(部分单片机需手动清除,防止重复触发)
IE1 = 0; // 显式清除INT1中断标志
}
922
飞行者
发表于 昨天 20:08
14.IO中断(所有普通IO都支持“外部中断”)
io.c
//IO口中断函数
//数码管显示0:执行while函数1:执行P3_IO中断 2:执行P4_IO中断
void P3_IO_Init(void)
{
P3IM0 = 0x00; //选择合适的中断模式--下降沿中断
P3IM1 = 0xff;
P3INTE = 0x08; //使能端口中断功能
}
void P3_IO_ISR(void) interrupt 40
{
u8 intf;
intf = P3INTF;
if(intf)
{
P3INTF = 0;
if(intf & 0x08)
{
passward = 1;
//P01 = !P01;
}
}
}
void P4_IO_Init(void)
{
P4IM0 = 0x00; //选择合适的中断模式--低电平中断
P4IM1 = 0xff;
P4INTE = 0x80; //使能端口中断功能
PINIPH = 0X00;
PINIPL = 0X10;
}
void P4_IO_ISR(void) interrupt 41
{
u8 intf;
intf = P4INTF;
if(intf)
{
P4INTF = 0;
if(intf & 0x80)
{
passward = 2;
//P01 = !P01;
}
}
}main.c文件部分代码
main.c文件部分代码
void main(void)
{
Sys_init(); //系统初始化
usb_init(); //USB CDC 接口配置
IE2 |= 0x80; //使能USB中断
Timer0_Init(); //定时器初始化
Init_595();
// INT1_Init(); //外部中断int1初始化
P3_IO_Init(); //IO中断初始化
P4_IO_Init();
EA = 1;
P40 = 0;
// WDT_CONTR = 0X24;
while(1)
{
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done();
}
Task_Pro_Handler_Callback(); //执行功能函数
passward = 0;
// WDT_CONTR = 0X34;
/*延时三秒钟P00LED灯取反一次 */
// P00 = !P00;
// Delay3000ms();
}
}
config.c部分代码
void Timer0_Init(void) //1毫秒@24.000MHz //定时器函数定义
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x30; //设置定时初始值
TH0 = 0xF8; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
IPH |= (1<<1);
IP|= (1<<1);
}923
飞行者
发表于 昨天 22:07
14 IO中断(所有普通IO都支持“外部中断”) 课后小练多路抢答器
功能说明
1核心功能:
上电后第一位数码管显示0(等待抢答)。
矩阵键盘按键 1(第一行第一列)对应用户 1,按下后显示1并锁定。
矩阵键盘按键 2(第一行第二列)对应用户 2,按下后显示2并锁定。
矩阵键盘按键 3(第一行第三列)对应用户 3,按下后显示3并锁定。
锁定后不再响应其他按键,确保优先抢答的公平性。
2复位功能:
按矩阵键盘按键 4(第一行第四列)可复位,数码管恢复显示0,解除锁定,允许新一轮抢答。v
io.c
#include "io.h"
u8 SEG_NUM[] =
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x54, /*'N', 16*/
0x5C, /*'O', 17*/
0x73, /*'P', 18*/
0x50, /*'R', 19*/
0x40, /*'-', 20*/
0x00, /*' ', 21*/
0x80, /*'.', 22*/
};
u8 T_NUM =
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
/*
----------行------------
#define ROW1 P06 //定义端口
#define ROW2 P07
----------列-------------
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
u8 key_num = 0xff; //key_num =0 给按键传递一个键值
// 抢答核心变量
u8 answerFlag = 0; // 抢答结果(0-未抢答,1-3对应用户1-3)
bit isAnswered = 0; // 抢答锁定标志(0-未锁定,1-已锁定)
//显示缓存
u8 passward = { 21,21,21,21,21,21,21,21};
u8 Seg_no = 0;
// 延时函数(消抖用)
void delay_ms(u16 ms)
{
u16 i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 120; j++);
}
void Init_595(void)
{
HC595_SER= 0;
HC595_RCK= 0;
HC595_SCK= 0;
}
void Send_595( u8 dat )
{
u8 i;
for( i=0; i<8; i++ )
{
dat <<= 1; //数值左移一位
HC595_SER= CY; //数据写到引脚
HC595_SCK= 1; //输出上升沿的时钟信号
HC595_SCK= 0;
}
}
void Display_Seg( u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //数码管段码输出 高电平点亮
Send_595(HC595_2); //数码管位码输出 低电平点亮
HC595_RCK= 1; //数据输出
HC595_RCK= 0;
}
void SEG_Task(void)
{
u8 num = 0;
switch(Seg_no){
case 0: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 1: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 2: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 3: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 4: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 5: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 6: Display_Seg( SEG_NUM], ~T_NUM ); break;
case 7: Display_Seg( SEG_NUM], ~T_NUM ); break;
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
// 矩阵键盘扫描任务(核心修改:识别1、2、3键并处理抢答)
void Task_1(void) {
static u8 key_stable = 0;// 按键稳定计数(消抖)
// 第一步:扫描行
COL1 = 0; COL2 = 0; COL3 = 0; COL4 = 0;// 列线拉低
ROW1 = 1; ROW2 = 1; // 行线拉高
delay_ms(1);// 稳定电平
// 检测是否有按键按下
if((ROW1 == 0) || (ROW2 == 0)) {
key_stable++;
if(key_stable >= 3) {// 连续3次检测到按键(消抖)
// 第二步:确定行
if(ROW1 == 0) {// 第一行(对应按键1、2、3、4)
// 第三步:扫描列确定具体按键
ROW1 = 0; ROW2 = 1;// 锁定第一行
COL1 = 1; COL2 = 1; COL3 = 1; COL4 = 1;// 列线拉高
delay_ms(1);
if(COL1 == 0) key_num = 0;// 按键1(第一行第一列)
else if(COL2 == 0) key_num = 1;// 按键2(第一行第二列)
else if(COL3 == 0) key_num = 2;// 按键3(第一行第三列)
else if(COL4 == 0) key_num = 3;// 按键4(作为复位键)
}
// 处理抢答逻辑
if(!isAnswered) {// 未锁定时响应
switch(key_num) {
case 0:// 用户1(按键1)
answerFlag = 1;
passward = 1;// 显示1
isAnswered = 1;// 锁定
break;
case 1:// 用户2(按键2)
answerFlag = 2;
passward = 2;// 显示2
isAnswered = 1;// 锁定
break;
case 2:// 用户3(按键3)
answerFlag = 3;
passward = 3;// 显示3
isAnswered = 1;// 锁定
break;
}
}
// 复位功能(按键4作为复位键)
if(key_num == 3) {
delay_ms(10);
if(COL4 == 0) {
answerFlag = 0;
passward = 0;// 恢复显示0
isAnswered = 0;// 解除锁定
}
}
}
} else {
key_num = 0xff;// 无按键
key_stable = 0;// 重置稳定计数
}
// 恢复引脚状态
COL1 = 0; COL2 = 0; COL3 = 0; COL4 = 0;
ROW1 = 1; ROW2 = 1;
}
// 初始化显示(上电显示0)
void Display_Init(void) {
Init_595();
passward = 0;// 初始显示0
}
// 注释不需要的IO中断函数(改用矩阵键盘扫描)
void P3_IO_Init(void) {
// 禁用原P3中断,避免冲突
P3INTE = 0x00;
}
void P4_IO_Init(void) {
// 禁用原P4中断,避免冲突
P4INTE = 0x00;
}
924