飞行者 发表于 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

页: 1 2 [3]
查看完整版本: Ai8051u擎天柱学习冲哥《8051U深度入门到32位51大型实战视频》记录贴