蓝桥杯单片机学习17——第十届省赛题详解

第十届省赛题

  • 前言
  • 任务要求
  • 1.基本要求
  • 2.竞赛板配置要求
  • 4.硬件框图
  • 4.功能描述
  • 实现思路
  • 代码实现
  • 1.变量定义
  • 2.初始化函数
  • 3.显示界面
  • 4.按键扫描
  • 5.逻辑处理
  • 6.数据处理
  • 7.main函数
  • 8.中断处理
  • 总结
  • 前言

    上一期我们完成了十一届省赛题,这一期我们一起来完成第十届省赛题。首先,让我们来看一看任务要求

    任务要求

    1.基本要求

    2.竞赛板配置要求

    4.硬件框图

    4.功能描述




    4.LED指示灯功能


    以上就是本期赛题的全部要求,对比上几期的赛题上没有很大的区别,值得一提的是,这次使用到了独立按键,并且通过按键控制数码管和LED的开启与关闭。

    实现思路

    整体来说,这次的省赛题是没有很大的难度的,我这里也就不在多说了,主要讲一下按键控制LED/数码管开启与关闭的思路吧

    在前几期的省赛题中,我们都是通过创建数组来分别对数码管/LED进行操作的,现在题目中,需要我们控制数码管/LED的开启与关闭,那我们就可以,再次创建两个数组,数组对应到LED中就是全不亮,对应到数码管中就是全部消隐,这样,我们只需要通过按键改变需要显示的数组即可,需要显示的时候就调用正常使用的数组,不需要显示的时候就显示消隐的数组

    另外:由于消隐的数组是不需要进行读写操作的,所以可以把它放在code(代码段)

    u8  code LED1[8] = {0,0,0,0,0,0,0,0};   //关闭所有LED时显示
    u8  code SEG1[8] = {33,33,33,33,33,33,33,33};   //关闭所有数码管时显示
    
    u8 SEG[8] = {33,33,33,33,33,33,33,33};  //控制数码管的数组
    u8 LED[8] = {0,0,0,0,0,0,0,0};          //控制所有led的数组
    
          if(SEG_Flag)     //选择是否需要开启数码管
           {
                SEG_Control(SEG);   //开启则显示正常内容
           }
           else
           {
                SEG_Control(SEG1);  //关闭则将数码管全部关闭
           }
            
            if(LED_Flag)    //选择是否需要开启LED
            {   
                LED_Control(LED);   //开启则正常开启
            }
            else
            {
                LED_Control(LED1);  //关闭则关闭所有LED
            }
    

    代码实现

    1.变量定义

    #define u8 unsigned char //宏定义部分
    #define u16 unsigned int
    
    
    u8  code LED1[8] = {0,0,0,0,0,0,0,0};   //关闭所有LED时显示
    u8  code SEG1[8] = {33,33,33,33,33,33,33,33};   //关闭所有数码管时显示
    
    u8 SEG[8] = {33,33,33,33,33,33,33,33};  //控制数码管的数组
    u8 LED[8] = {0,0,0,0,0,0,0,0};          //控制所有led的数组
    
    u8 Key_Flag = 1;            //按键扫描标志,通过定时器控制,每隔一段时间扫描依次按键
    bit LED_Flag = 1;           //是否显示LED标志
    bit SEG_Flag = 1;           //是否显示数码管标志
    u16 Voltage = 341;          //读取的电压
    u16 Frequence = 0;          //测得的频率
    u16 Out_Mode = 1;           //DAC输出模式   为1固定输出2V,为2则输出Voltage
    u16 Frq_Count = 0;          //频率测量计数值
    u16 Page_Num = 1;           //界面值       为1显示频率界面,为2显示电压界面
    u8 Key_Num = 0;             //按键值
    

    2.初始化函数

    //初始化函数,
    void Init(void)
    {
        LS_Init();  //LS相关外设初始化
        Timer1_Init();  //定时器1初始化,1ms产生一次中断
        Count0_Init();  //计数器0初始化,每计数一次,产生一次中断
    }
    

    3.显示界面

    //频率界面显示函数
    void Fre_Page1(void)
    {
        SEG[0] = 25;        //显示F
        SEG[1] = 33;        //消隐
        SEG[2] = 33;
        SEG[3] = 33;
        SEG[4] = 33;
        SEG[5] = 33;
        SEG[6] = 33;
        SEG[7] = 33;
        SEG[7] = Frequence%10;  //显示个位
        if(Frequence/10)
        {
            SEG[6] = Frequence/10%10;   //显示十位
            if(Frequence/100)
            {
                SEG[5] = Frequence/100%10;  //显示百位
                if(Frequence/1000)
                {
                    SEG[4] = Frequence/1000%10; //显示千位
                    if(Frequence/10000)
                    {
                        SEG[3] = Frequence/10000%10;    //显示万位
                        if(Frequence /100000)
                        {
                            SEG[2] = Frequence /100000%10;  //显示十万位
                        }
                    }
                }
            }
        }
        
    }
    //电压显示函数
    void V_Page2(void)
    {
        SEG[0] = 30;
        SEG[1] = 33;
        SEG[2] = 33;
        SEG[3] = 33;
        SEG[4] = 33;
        SEG[5] = Voltage/100%10 +10;
        SEG[6] = Voltage/10%10;
        SEG[7] = Voltage %10;
    }
    void Show_Task(void)
    {
        switch(Page_Num)
        {
            case 1: Fre_Page1(); LED[0] = 0;LED[1] = 1; //显示频率界面。打开LED2,关闭LED1
                break;  
            case 2: V_Page2();   LED[0] = 1;LED[1] = 0; //显示电压界面。打开lED1,关闭LED2
                break;
        }
    
           if(SEG_Flag)     //选择是否需要开启数码管
           {
                SEG_Control(SEG);   //开启则显示正常内容
           }
           else
           {
                SEG_Control(SEG1);  //关闭则将数码管全部关闭
           }
            
            if(LED_Flag)    //选择是否需要开启LED
            {   
                LED_Control(LED);   //开启则正常开启
            }
            else
            {
                LED_Control(LED1);  //关闭则关闭所有LED
            }
    
    }
    

    4.按键扫描

    ///独立按键扫描函数
    u8 Read_Button(void)
    {
    	static u8 pre_scan = 0;	//前一次的扫描值
    	static u8 pre_trg = 0;	//前一次的触发值
    	u8 scan = 0;      		//此次的扫描值
    	u8 trg = 0;				//此次的触发值
    	u8 lock = 0;             //自锁位
    	u8 key = 0;				//最终的按键检测结果
        u8 value = 3;            //必须初始化为3,配合后面的代码根据KEY得出键值。
    	
        scan = P3^0xff;    			//取得当前的扫描值,取反后1表示按下,0表示抬起
    	trg = scan & pre_scan;		//只有连续两次扫描到按下,才会被触发,起到延时消抖作用
    	lock = trg & pre_trg;		//如果按键连续两次触发,则进入自锁
    	key = trg & (~lock);		//当前被触发,且未自锁的按键被识别为key字节
    	
    	pre_scan = scan;			//前次扫描值和触发值被记录到static变量,重进函数时使用
    	pre_trg = trg;
    	
        if(key)
        {
            //计算独立按键的键值
            while(!(key & 1<<(7-(++value))));
        }
    
        return value;
        //返回独立按键的键值
    }
    //按键扫描
    void Key_Task(void)
    {
        if(Key_Flag)    //每10ms扫描一次按键
        {
            Key_Num = Read_Button();    
            Key_Flag = 0;
        }
    
    
    }
    

    这里的按键扫描函数,我是用的我们老师的(自己懒得写),不过原理不是很好懂,感兴趣的可以去看看我之前的 “ 独立按键&矩阵按键 ” ,里面原理啥的都挺清楚的。

    5.逻辑处理

    void Logic_Task(void)
    {
        //按键扫描
        if(Key_Num == 4)        //按键4按下,改变显示界面
        {
            Page_Num++;
            if(Page_Num >= 3)
            {
                Page_Num = 1;
            }
        }   
        if(Key_Num == 5)        //按键5按下,改变带你呀输出模式
        {
            Out_Mode++;
            if(Out_Mode >= 3)
            {
                Out_Mode = 1;
            }
        }
        if(Key_Num == 6)    //按键6按下,选择开启或关闭LED
        {
            LED_Flag = ~LED_Flag;
        }
        if(Key_Num == 7)    //按键7按下,选择开启或关闭数码管
        {
            SEG_Flag = ~SEG_Flag;
        }
        
        //L3控制判断
        if(Out_Mode == 1)   //输出模式为固定输出2V,LED5熄灭
        {
            LED[2] = 1;     //LED3亮起
            LED[4] = 0;
        }
        if(Out_Mode == 2)   //输出模式为Vout = Voltage
        {
            LED[2] = 0; 
            LED[4] = 1;     //LED5亮起
            if(250 > Voltage && Voltage >= 150)
            {
                LED[2] = 1;     ///符合条件LED3亮起,否则熄灭
            }
            if(Voltage >= 350)
            {
                LED[2] = 1;     ///符合条件LED3亮起,否则熄灭
            }
        }
        //L4控制判断
        LED[3] = 0;
        if(Frequence >= 1000 && Frequence < 5000)   //频率符合条件LED4亮起,否则熄灭
        {
            LED[3] = 1; 
        }
        if(Frequence >= 10000)
        {
            LED[3] = 1;
        }
        
        
    }
    

    6.数据处理

    void Data_Task(void) //数据相关函数
    {
    
            Voltage = PCF8591_AD_Conversion(3); //读取电压
            if(Out_Mode == 1)
            {
                IIC_DAC(0x66);      //固定输出2V
            }
            if(Out_Mode == 2)
            {
                IIC_DAC(Voltage * 0.51);    //输出电压等于读取电压
            }
    
        
    }
    

    7.main函数

    //main函数
    void main()
    {
        Init();
        while(1)
        {
            Show_Task();
            Key_Task();
            Logic_Task();
            Data_Task();
        }
    }
    

    8.中断处理

    void Count0_Init()
    {
            TMOD |= 0x06;    //设置计数器0工作模式0,16位自动重装载
    //    TMOD |= 0x01;    //设置定时器0工作模式1
    //    TMOD |= 0x02;    //设置定时器0工作模式2,8位自动重装载
        TL0 = 0xFF;     //设置定时器溢出时间
        TH0 = 0xFF;
        ET0 = 1;        //开启定时器0中断允许
        EA=1;           //开启总中断允许
        TR0 = 1;        //开启定时器0
        PT0 = 0;        //设置优先级为低优先级,为1时设置为高优先级
    }
    //定时器0初始化函数,1ms进来一次
    void Timer1_Init(void)
    {
        TMOD |= 0x00;    //设置定时器1工作模式0,16位自动重装载
    //    TMOD |= 0x10;    //设置定时器1工作模式1
    //    TMOD |= 0x20;    //设置定时器1工作模式2,8位自动重装载
        TL1 = 0x18;     //设置定时器溢出时间
        TH1 = 0xFC;
        ET1 = 1;        //开启定时器0中断允许
        EA=1;           //开启总中断允许
        TR1 = 1;        //开启定时器0
        PT1 = 0;        //设置优先级为低优先级,为1时设置为高优先级
    }
    
    //计数器0中断服务函数
    void External_Hander0() interrupt 1
    {
        Frq_Count++;
    }
    
    //定时器1服务函数
    void External_Hander2() interrupt 3
    {
        static u16 Timer1_Count = 0;
        Timer1_Count++;
        if(Timer1_Count % 10 == 0)  //10ms扫描一次按键
        {
                Key_Flag = 1;
        }
        if(Timer1_Count >= 250)
        {
            Frequence = Frq_Count*4;        //每0.25s更新一次频率
            Frq_Count = 0;
            Timer1_Count = 0;
        }
    }
    

    由于这次省赛题使用到了NE555波形发生器,所以我们要同时使用定时器0和1,其中,定时器0设置为计数器模式,具体原理,可以看 “ NE555方波发生器&频率测量 ” 这里不做赘述。

    总结

    这次省赛题比较简单,外加注释也写的比较全面,所以就这样把,结束!

    作者:不想写代码的我

    物联沃分享整理
    物联沃-IOTWORD物联网 » 蓝桥杯单片机学习17——第十届省赛题详解

    发表评论