第十届省赛题
前言
上一期我们完成了十一届省赛题,这一期我们一起来完成第十届省赛题。首先,让我们来看一看任务要求
任务要求
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方波发生器&频率测量 ” 这里不做赘述。
总结
这次省赛题比较简单,外加注释也写的比较全面,所以就这样把,结束!