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

该文详细介绍了第十届省赛题的任务要求,包括基本要求、竞赛板配置、硬件框图和功能描述。主要关注点在于使用独立按键控制数码管和LED的开启与关闭。实现思路是通过创建数组来切换显示状态,以及利用中断处理和定时器进行按键扫描和数据更新。文章还提供了初始化函数、显示界面、按键扫描、逻辑处理和数据处理的代码实现,展示了如何通过编程实现这一功能。
摘要由CSDN通过智能技术生成

前言

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

任务要求

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方波发生器&频率测量 ” 这里不做赘述。

总结

这次省赛题比较简单,外加注释也写的比较全面,所以就这样把,结束!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不想写代码的我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值