蓝桥杯省三保底代码——数显+按键功能实现

目录

前言

一、为什么能保底省三

二、数显模块的实现

1.数码管显示​编辑

1)断码表

2)位选

3)段选

4)扫描

2.菜单

三、按键功能的实现

1.按键扫描

2.菜单切换

四、完整代码演示

五、结语


前言

上一期介绍全家桶时,跟大家提到:比赛时可以做出来按键切换菜单的部分,这样子起码省三就有了。这篇文章就去教大家如何实现省三保底。当然,这些代码只靠背可能不太够,比赛的时候需要根据题目小改一下才行。

一、为什么能保底省三

数显+按键切换菜单完成,基本就可以拿到省三,这个比赛是看你完成度给你“打分”的,数显和按键功能其实已经占了题目不少的一部分了,这里的数显是指:读取不到有效数据,仅能显示一个不会更新的数据。

如果你能灵活使用底层驱动的那几个外设,读取到有效数据,顺便完成一点LED灯,那冲击省二都有可能,如果你又能灵活地对数据进行处理,完成题目对数据处理方面的要求,基本完成赛题上的所有功能,那基本省一就稳了。

当然,这也只是我个人说的哈,但也确实听不少人说“完成数显+按键切换菜单可以拿省三”“基本完成题目要求的功能就能稳省一”,我也绝不是空穴来风。

我去年参加蓝桥杯时,NE555读取部分没有实现,其他功能基本实现,也顺利进入了国赛。

二、数显模块的实现

这里以第十三届比赛题目为例,介绍一下数显和按键部分的实现。

1.数码管显示

这部分都是十分基础的代码,不过毕竟这篇文章就是“保底省三”的代码,这部分还是有必要详细介绍一下的。

1)断码表

首先,比赛时会给你一个断码表,所以断码自己会使用计算机算即可,没必要记,真需要显示某个符号了,就使用计算机算一算,下面是23年给的断码表

code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
}

前边那个“code”,抄的时候删不删都可以,没影响。现在不管是个人的电脑,或者赛点的电脑,上边的计算机都有程序员模式,可以切换单位键盘,帮助完成断码

2)位选

在让数码管显示数据之前,我们先需要位选,何为位选呢?其实就是选择你要显示那个数码挂的数据。下图中圈住的,就是用于控制位选的锁存器。我们只需要把需要传输的数据放到P0里,在使能锁存器,并失能锁存器(说人话就是开关一下锁存器,让他更新一下数据就好),就可以把P0的数据存到锁存器里了。P0需要放到数据就是,我们要控制哪一个数码管,高电平有效。一般情况下我们都是一次控制一个数码管,所以P0当中只有一个数据是1,其他都是0.

比如P0=0000 0001就是控制第0个数码管(或者说最左边那个),P0=0000 0010就是控制第一个数码管。P0=0000 1111就是同时控制数码管,给P0赋值之后,开关一次锁存器,就可以选中我们要控制的数码管了,接下来的段选,只影响当前被选中的锁存器。

刚才提到开关锁存器,也就是使能和失能锁存器,锁存器的控制端连在了Y6C,高电平有效,我们只需要给Y6C高电平,在给低电平即可完成开关锁存器。

Y6C连接在一个三线八线译码器上如下图(右边的东西这里不过多介绍)

P27,P26,P25是3线输出,Y0到Y7是7线输出。下边P27,P26,P25简称输入:

如果输入000,则选中Y0;如果输入110,也就是二进制的6,则选择Y6,经过有右边的与非门之后,就可以选中Y6C,打开刚才位选的锁存器。

因此,我们只需要给P2的高三位为110就可以打开位选,给高三位000就可以关闭位选。我们可以定义一个宏定义,来实现位选

#define NIXIE_CHECK()    P2|=0xC0;P2&=0xDF;P2&=0x1F;

3)段选

段选与上述位选类似,就是把数码管需要显示的数据放在P0然后再开关一次锁存器即可。也就是下图中左上角的锁存器。P0放的数据,就是我们最开始提到的断码数据。

我们同样使用一个宏定义来完成段选

#define NIXIE_ON()        P2|=0xE0;P2&=0xFF;P2&=0x1F;

4)扫描

为了让数码管显示不同的数据,我们一般是一次性控制一个数码管,如果我们想让八个数码管同时显示不同的数据,就需要扫描。扫描通俗点说就是先控制第一个数码管显示数据,趁着第一个还没熄灭就再去控制第二个数码管显示数据,在控制第三个......完成一轮之后再控制第一个,已知循环下去。

由于它对于时间有要求,所以我们需要把它放在定时器完成扫描。使用location记录当前扫描到第几个数码管了,使用Nixie_num数组记录八个数码管各自需要显示的数据,定时器时间为1ms。

中断服务函如下:

void Timer0_Isr(void) interrupt 1
{
    P0=0x01<<location;NIXIE_CHECK();
    P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
    
    if(++location==8)
        location=0;
}

我们只需在main或者菜单显示函数中给Nixie_num数组对应位置赋值,即可让他显示不同的数据

2.菜单

我们定义一个标志位mod,当mod=0时显示第一个菜单,mod=1时显示第二个菜单以此类推。

这里我们手动添加一个断码表,使之可以显示U和“-”,以及对应的带小数点的数字。

我们注意到题目要求数显部分(第二章开头有介绍),第一位数码管显示的都是U,第二位是当前是第几个菜单,所以我们把这连个单独写出来。注意不需要显示的位需要熄灭,避免切换菜单时不这个菜单不显示的位被异常点亮。

这样我们就可以通过改变mod的值,来显示不同的菜单了

void show_menu(void)
{
    Nixie_num[0]=21;//显示U
    Nixie_num[1]=mod+1;//显示菜单数
    if(mod==0)
    {
        Nixie_num[2]=20;//熄灭
        Nixie_num[3]=20;//熄灭
        Nixie_num[4]=20;//熄灭
        Nixie_num[5]=2;//显示2
        Nixie_num[6]=3+10;//显示3.
        Nixie_num[7]=5;//显示5
    }
    else if(mod==1)
    {
        Nixie_num[2]=20;
        Nixie_num[3]=2;
        Nixie_num[4]=3;
        Nixie_num[5]=22;
        Nixie_num[6]=2;
        Nixie_num[7]=3;
    }
    else if(mod==2)
    {
        Nixie_num[2]=20;
        Nixie_num[3]=20;
        Nixie_num[4]=20;
        Nixie_num[5]=20;
        Nixie_num[6]=2;
        Nixie_num[7]=3;
    }
}

三、按键功能的实现

按键功能要求,以第十三届为例。这里仅完成“切换”按键

1.按键扫描

相信大家单片机入手的第二堂课就是按键(第一堂课应该是点亮LED),这里按键扫描就不过多介绍了,这里放一个我自己用的矩阵按键扫描代码

void get_key(void)
{
    unsigned char key_P3=P3;
    unsigned char key_P4=P4;
    
    P3=0xFF;
    
    P44=0;
    if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=7;}
    else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=6;}
    else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=5;}
    else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=4;}
        
    P42=0;
    if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=11;}
    else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=10;}
    else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=9;}
    else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=8;}
        
    P35=0;
    if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=15;}
    else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=14;}
    else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=13;}
    else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=12;}
        
    P34=0;
    if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=19;}
    else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=18;}
    else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=17;}
    else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=16;}
    
    key_value=0;
    P3=key_P3;
    P4=key_P4;
}

函数末尾把key_value又置为了0,是因为后续所有按键处理都要在按键扫描函数里完成,如果你是刚写完按键扫描,想测试一下的话,可以吧key_value=0给删掉,在main里使用数码管显示key_value的值。

2.菜单切换

简单点说,如果按下了S12,就把菜单从1切换到2或者从2切换到3或者从3切换到1。我们只需要把下面的代码放到上述key_value=0之前即可

    if(key_value==12)
    {
        if(mod==0)
            mod=1;
        else if(mod==1)
            mod=2;
        else if(mod==2)
            mod=0;
    }

可能会有人说,我这样写真的好LOW,不过我感觉,我只是为了应对这个比赛才写这样的代码的,这样写纯属是为了少犯错误,容易修改,仅此而已。你当然也可以把代码写的很华丽,包括阿刚才show_menu函数里的代码,只要能达到预期效果就行。

这里仅仅实现了按键切换菜单,有的按键还有切换参数的功能,需要把数码管显示的数据改为对应的参数,在通过按键对参数进行加减,这里不再介绍。

四、完整代码演示

main.c

#include <stc15.h>
#include <intrins.h>
#include "onewire.h"
#include "ds1302.h"

code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
//0.到9.
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
0xFF,//20 熄灭
0xC1,//21 U
0xBF//22 -
};


#define NIXIE_CHECK()	P2|=0xC0;P2&=0xDF;P2&=0x1F;
#define NIXIE_ON()		P2|=0xE0;P2&=0xFF;P2&=0x1F;

unsigned char Nixie_num[]={20,20,20,20,20,20,20,20};
void Timer0_Init(void);		//1毫秒@12.000MHz
void Delay100ms(void);	//@12.000MHz
void get_key(void);
void show_menu(void);

unsigned char location=0;
unsigned char key_value=0;
unsigned char mod=0;

void main()
{
	Timer0_Init();
	EA=1;
	while(1)
	{
		get_key();
		Delay100ms();
	}
}
void Timer0_Isr(void) interrupt 1
{
	P0=0x01<<location;NIXIE_CHECK();
	P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
	
	if(++location==8)
		location=0;
}

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x80;			//定时器时钟1T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0x20;				//设置定时初始值
	TH0 = 0xD1;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
}

void Delay100ms(void)	//@12.000MHz
{
	unsigned char data i, j, k;

	_nop_();
	_nop_();
	i = 5;
	j = 144;
	k = 71;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay5ms(void)	//@12.000MHz
{
	unsigned char data i, j;

	i = 59;
	j = 90;
	do
	{
		while (--j);
	} while (--i);
}
void get_key(void)
{
	unsigned char key_P3=P3;
	unsigned char key_P4=P4;
	
	P3=0xFF;
	
	P44=0;
	if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=7;}
	else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=6;}
	else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=5;}
	else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=4;}
		
	P42=0;
	if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=11;}
	else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=10;}
	else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=9;}
	else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=8;}
		
	P35=0;
	if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=15;}
	else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=14;}
	else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=13;}
	else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=12;}
		
	P34=0;
	if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=19;}
	else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=18;}
	else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=17;}
	else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=16;}
	
	//S12
	if(key_value==12)
	{
		if(mod==0)
			mod=1;
		else if(mod==1)
			mod=2;
		else if(mod==2)
			mod=0;
	}
	key_value=0;
	P3=key_P3;
	P4=key_P4;
}
void show_menu(void)
{
	Nixie_num[0]=21;//显示U
	Nixie_num[1]=mod+1;//显示菜单数
	if(mod==0)
	{
		Nixie_num[2]=20;//熄灭
		Nixie_num[3]=20;//熄灭
		Nixie_num[4]=20;//熄灭
		Nixie_num[5]=2;//显示2
		Nixie_num[6]=3+10;//显示3.
		Nixie_num[7]=5;//显示5
	}
	else if(mod==1)
	{
		Nixie_num[2]=20;
		Nixie_num[3]=2;
		Nixie_num[4]=3;
		Nixie_num[5]=22;
		Nixie_num[6]=2;
		Nixie_num[7]=3;
	}
	else if(mod==2)
	{
		Nixie_num[2]=20;
		Nixie_num[3]=20;
		Nixie_num[4]=20;
		Nixie_num[5]=20;
		Nixie_num[6]=2;
		Nixie_num[7]=3;
	}
}

五、结语

不同人写代码的风格不一样,我只是分享了我写的一套省三代码。如果你自己之前有跟其他人学过,现在不妨把代码整理整理,至少做到连续两次,可以写出一模一样的代码,这样可以减少比赛时犯错误。

最后,希望没有准备充分的同学不要放弃,现在冲一冲那个省二省三也不是问题,提前预祝冲击省一的同学,国赛再见!

相关推荐

  1. [ 2021 AB2] 完全平方

    2024-03-29 00:24:02       35 阅读
  2. [ 2021 AB2] 完全平方

    2024-03-29 00:24:02       43 阅读
  3. [ 2019 B] 特别的和

    2024-03-29 00:24:02       37 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-29 00:24:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-29 00:24:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-29 00:24:02       82 阅读
  4. Python语言-面向对象

    2024-03-29 00:24:02       91 阅读

热门阅读

  1. Go语言教程和案例

    2024-03-29 00:24:02       40 阅读
  2. 2020校招面试

    2024-03-29 00:24:02       43 阅读
  3. 程序员35岁会失业吗?

    2024-03-29 00:24:02       41 阅读
  4. 【MySQL】MySQL小结

    2024-03-29 00:24:02       36 阅读
  5. 详解 WebWorker 的概念、使用场景、示例

    2024-03-29 00:24:02       43 阅读
  6. ASTM D7032-21 木塑地板、踏板、围栏和扶手检测

    2024-03-29 00:24:02       40 阅读
  7. # Python 编程入门教程

    2024-03-29 00:24:02       51 阅读
  8. 多项式分布采样

    2024-03-29 00:24:02       40 阅读