第九届蓝桥杯国赛之“多功能测量仪表”

将昨日事,归欢喜处


代码参考:<我的Github>第九届国赛


昨天刷了第九届蓝桥杯国赛的题目,emmm,虽然自己没进国赛,但是也可以准备国赛鸭,关键是有一种紧张感,督促自己学习,哈哈。感觉国赛题是跟省赛不一样哈,要写的底层更多,要实现的功能也更多一点,学到辽芝士!!!Hin Nice~


还是先看一下功能要求:

  1. 显示功能( 三个主要显示界面)
    (1)数据显示界面
    在这里插入图片描述
    (2)数据回显界面在这里插入图片描述
    (3)电压阈值设置界面
    在这里插入图片描述
  2. 按键功能:
    在这里插入图片描述
  3. 存储功能:
    在这里插入图片描述
  4. 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!!!!几个需要注意的点:

  1. 电压测量状态下点亮了L3,那么在进入回显界面时,一定不要忘记关闭L3!!
  2. 注意!!!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的闪烁啦~~


我学到的:

  1. 关于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

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ReRrain

觉得写的不错,不妨请我喝杯~

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

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

打赏作者

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

抵扣说明:

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

余额充值