将昨日事,归欢喜处
代码参考:<我的Github>第九届国赛
昨天刷了第九届蓝桥杯国赛的题目,emmm,虽然自己没进国赛,但是也可以准备国赛鸭,关键是有一种紧张感,督促自己学习,哈哈。感觉国赛题是跟省赛不一样哈,要写的底层更多,要实现的功能也更多一点,学到辽芝士!!!Hin Nice~
还是先看一下功能要求:
- 显示功能( 三个主要显示界面)
(1)数据显示界面
(2)数据回显界面
(3)电压阈值设置界面
- 按键功能:
- 存储功能:
- LED指示功能:
还是先看一下要用到那些底层(看硬件框图吧):
emmm,蛮多的对不,所以说打字速度不快也是个硬伤。。。。
底层成功写完之后,如果没问题的话,我们应该就可以显示数据显示界面的全部内容了,也就是电压、频率和温度,现在我们再来考虑如何实现这三个数据的切换,也就是按键S4的功能,很简单呀,我们只要设置一个标志量就可以了(0-温度显示,1-电压显示,2-频率显示),S4每按下一次就改变一次数值,看一下代码:
u8 flagShow = 0;//0-温度,1-电压,2-频率
if(keycode == '4')
{
if(flagShow < 2)
{
flagShow += 1;
}
else if(flagShow >= 2)
{
flagShow = 0;
}
}
下面进入到数据回显功能的实现(这一部分涉及到了数据的存储和显示,也就是按键S5和S6的功能):
(这三个数据显示的方法大致是相同的,故这里只以电压显示为例详细说明)
仔细分析一下题目,我们不难发现,LED指示灯的功能(电压测量状态下,点亮L3),数据回显功能,数据显示功能我们是可以放在一个函数(电压显示)里实现的,至于回显界面和显示界面的区分问题一个标志量就可以完美解决了,看一下代码:
u8 flagShowSave = 0;//0-非回显,1-回显
u8 val = 0;
void RefreshVal()
{
u8 i;
for(i = 7; i>1;i--)
{
LedBuff[i] = 0xFF;
}
if(flagShowSave == 0)//非回显界面
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFB;//电压测量状态,点亮L3
P2 &= 0x1F;
val = GetADCValue(3);//获取通道3的电压值,也就是RB2的电压
LedBuff[6] = 0xFF;//关闭第六个数码管
LedBuff[7] &= 0xC1;//第7个数码管显示 “C”
}
else//回显界面
{
CloseLED();//关闭L3
LedBuff[6] &= 0xC1;//第六个数码管显示 “C”
LedBuff[7] &= 0x89;//第7个数码管显示 “H”
}
LedBuff[0] = LedChar[val % 10];//显示电压的最低位
LedBuff[1] = LedChar[(val / 10) % 10];//显示电压的最高位
LedBuff[1] &= 0x7F;//显示小数点
}
NOTICE!!!!几个需要注意的点:
- 电压测量状态下点亮了L3,那么在进入回显界面时,一定不要忘记关闭L3!!
- 注意!!!E2PROM最多存储八位数据,但是我们的温度(16位)和频率(32位)数据都是大于8位的,所以我们就需要将这两个数据分成两个8位数据!!怎么分呢?很简单啊,假如这个数据是2345,我们只需要把"23"存储到E2PROM的一个地址中,然后再把"45"存到另一个地址就可以了,现在的问题就是怎么实现数据的分离,想一下我们是怎么实现数据显示在数码管上的?
LedBuff[0] = LedChar[val % 10];//显示电压的最低位
除10取余得到最低位,现在我们想要得到后两位数据,那么就除以100然后取余数不就可以了吗,高2位怎么得到呢?除以100取整鸭~ 看一下代码:
int temp = 0;
u8 tempH;//存储温度数据的前两位数据
u8 tempL;//存储温度数据的后两位数据
void RefreshTemp()
{
u8 i;
tempL = temp % 100;//存储温度数据的前两位数据
tempH = temp / 100;//存储温度数据的后两位数据
Start18B20();
for(i = 7; i>3; i--)
{
LedBuff[i] = 0xFF;
}
if(flagShowSave == 0)
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFE;//温度测量状态,点亮L1
P2 &= 0x1F;
Get18B20Temp(&temp);//获取实时温度
LedBuff[7] &= LedChar[12];
LedBuff[6] = 0xFF;
}
else
{
CloseLED();
LedBuff[7] &= 0x89;
LedBuff[6] &= LedChar[12];
}
LedBuff[0] = LedChar[tempL % 10];
LedBuff[1] = LedChar[(tempL / 10) % 10];
LedBuff[2] = LedChar[tempH % 10];
LedBuff[3] = LedChar[(tempH / 10) % 10];
LedBuff[2] &= 0x7F;
}
同理,频率:
u32 cnt = 0;//记录脉冲个数
u8 cntH;//存储频率数据的高三位数据(0-255)
u8 cntL;//存储频率数据的后两位数据
void RefreshFr()
{
cntL = cnt % 100;
cntH = cnt / 100;
if(flagShowSave == 0)
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFD;
P2 &= 0x1F;
ShowNumber(cnt);
LedBuff[7] = LedChar[15];
}
else
{
CloseLED();
E2Read(&cntL,0x03,1);
E2Read(&cntH,0x04,1);
cnt = cntL + (cntH*100);
ShowNumber(cnt);
LedBuff[6] &= LedChar[15];
LedBuff[7] &= 0x89;
}
}
看完这三段代码,读者可能注意到了我们在程序里定义的变量都是全局变量!!!
回显界面功能与按键S5,S6的功能不可分离,那么我们就来看一下这两个按键功能的实现代码(看注释):
else if(keycode == '3')//按下S5
{
SaveData();//将电压,温度,频率写入E2PROM
}
else if(keycode == '2')//按下S6
{
if(flagset == 0)//数据显示模式
{
ReadData();//从E2PROM中读取写入的数据
flagShowSave = 1;//进入数据回显界面
}
else//电压阈值设置模式
{
setval++;//阈值电压加1
E2Write(&setval,0x05,1);//将阈值电压写入E2PROM
}
}
数据的写入和读取:
void SaveData()
{
E2Write(&val,0x00,1);
E2Write(&tempL, 0x01, 1);
E2Write(&tempH, 0x02, 1);
E2Write(&cntL,0x03,1);
E2Write(&cntH,0x04,1);
}
void ReadData()
{
E2Read(&val,0x00,1);
E2Read(&tempL, 0x01, 1);
E2Read(&tempH, 0x02, 1);
E2Read(&cntL,0x03,1);
E2Read(&cntH,0x04,1);
E2Read(&setval,0x05,1);
}
好的,下面进入电压阈值设置界面的功能实现部分:
这一部分也比较简单,难点可能是长按键的部分(按下S6,长按8秒以上,实现快速增加功能),这一部分之前的博客里面有写过,大家可以把这个也当做底层去记,大体上不要改变,(我在写长按键的时候,改动了一下,就是么有用数组,只定义了一个单变量,思想还是一样的,,,然鹅,,,出错了,功能也木有办法实现,我现在也没找着原因,,额),再看一下长按键的实现:
u8 Keysta[4] = {1, 1, 1, 1};
u8 KeyCodeMap[4] = {'1', '2', '3', '4'};
u16 KeyDownTime[4] = {0, 0, 0, 0};//按键按下的时间,注意这里也要用u16!!!!!!!!!!!!
extern void KeyAction(u8 keycode);
extern u8 flagset;//0-数据显示,1-电压阈值设置
void KeyScan()//消抖
{
u8 i;
static u8 keybuff[4] = {0xFF,0xFF,0xFF,0xFF};
keybuff[0] = (keybuff[0] << 1) | KEY_IN_1;
keybuff[1] = (keybuff[1] << 1) | KEY_IN_2;
keybuff[2] = (keybuff[2] << 1) | KEY_IN_3;
keybuff[3] = (keybuff[3] << 1) | KEY_IN_4;
for(i = 0;i<4;i++)
{
if(keybuff[i] == 0xFF)//扫描8次,8位都为1
{
Keysta[i] = 1;//说明当前按键并没有被按下
KeyDownTime[i] = 0;//这里注意也要考虑到!!!!!当按键没有按下时,keydowntime[i]是为0的
}
else if(keybuff[i] == 0x00)//扫描8次,8位都为0
{
Keysta[i] = 0;//说明按键被按下
if(flagset)
{
if(i == 1)//按下S6
{
KeyDownTime[i] += 1;//按键按下时间每增加1ms,KeyDownTime就+1
}
}
}
else
{}
}
}
void KeyDriver()
{
u8 i;
static u8 backup[4] = {1,1,1,1};//存储数码管上一次的状态,注意要定义为静态变量,因为状态值每一次按键按下都需要更新
static u16 TimeMax[4] = {800, 800, 800, 800};//注意这里要定义为u16,不然就溢出了!!!!!!
for(i = 0;i<4;i++)
{
if(Keysta[i] != backup[i])//表明按键状态发生改变
{
if(backup[i] == 1)//如果上一次的状态为1,说明当前状态为0(为0就表示按键被按下)
{
KeyAction(KeyCodeMap[i]);//执行相应按键的功能
}
backup[i] = Keysta[i];//更新按键状态
}
if(KeyDownTime[i] > 0)//按键按下时间大于2ms
{
if(KeyDownTime[i] > TimeMax[i])//按键按下时间大于最大按键时间(800ms),说明处于长按状态
{
KeyAction(KeyCodeMap[i]);//执行相应按键的功能
TimeMax[i] += 100;//增加长按键判断时间
}
}
else//要加在if循环外,考虑到KeyDownTime[i]<=0(按键未按下)的情况
{
TimeMax[i] = 800;
}
}
}
阈值电压设置界面的显示:
void RefreshSetVal()//阈值电压设置界面
{
u8 i;
for(i = 7; i>1; i--)
{
LedBuff[i] = 0xFF;
}
if(setval > 50)//阈值电压的范围是0.1-5.0
{
setval = 1;
}
LedBuff[0] = LedChar[(setval % 10)];
LedBuff[1] = LedChar[(setval / 10) % 10];
LedBuff[1] &= 0x7F;//显示小数点
LedBuff[7] &= 0x8C;//第7个数码管显示P
}
按键S7的功能:
else if(keycode == '1')
{
if(flagset == 0)//非设置模式
{
flagset = 1;//进入设置模式
}
else if(flagset == 1)//设置模式
{
flagset = 0;//进入非设置模式
flagShowSave = 0;//进入电压显示界面
}
}
然后,现在还剩下L8闪烁的功能(嘻嘻,这个跟第8届电子钟的那个是一样的哦):
u8 flagLedBlink = 0;//0-灭,1-亮(状态每隔200ms改变一次)
void LedBlink()
{
if(flagShowSave == 0)//非回显界面
{
P2 = (P2 & 0x1F) | 0x80;
if(flagLedBlink == 0)//L8熄灭
{
P0 = 0xFB;
}
else//L8点亮
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0x7B;
P2 &= 0x1F;
}
P2 &= 0xFF;
}
else//回显界面
{
CloseLED();
}
}
标志量每200ms改变一次就实现了LED的闪烁啦~~
我学到的:
- 关于E2PROM存储温度数据和频率数据的部分:一开始我想的方法是:
tempH = (u8)(temp >> 8);//将高8位给tempH
tempL = (u8)temp;//低8位给tempL
借鉴的是定时器配置的思想,但是这种办法得到的tempH和tempL的数据是什么呢?是temp(十进制)的16进制表示下的高8位和低8位,然后我们后面再进行运算时,会自动转换成十进制数再计算,这样意味着什么呢?举个例子一看便知(还是举2345的例子):
2345对应的16进制表示:0x0929
tempH = 09;tempL = 29
对应的十进制:tempH = 9;tempL = 41
那么我们的数码管上会显示什么呢?0941!!!!,显然不是我们想要的2345
tempL = temp % 100;//存储温度数据的前两位数据
tempH = temp / 100;//存储温度数据的后两位数据
对比这个方法,有什么不同呢?后者我们直接存储的就是一个十进制数,也就是说我们是直接把"23"给了tempH,把"45"给了tempL