当前位置: 首页 > article >正文

蓝桥杯单片机第16届4T模拟赛三思路讲解

声明:以下代码仅供参考

蓝桥杯单片机第16届4T模拟赛三于3月2日结束,做完题目忘记把题目截下来了,成绩出了再补上,本篇文章只讲思路(思路大体上是没问题的,有些地方可能存在不足,见谅)。
本次模拟赛涉及的知识点都是很常见的,没有像13届、14届的几次模拟赛一样考到串口通信。涉及的模块:基础三件套(Led&Relay,按键、数码管)+ 进阶单件套(pcf8591的AD模块),考的模块虽然很少,数码管显示页面也很好实现,但是也是存在一个难点的,就是后面Led模块的如何正确的让继电器吸合,这个是本题最大的难点。

一、数码管部分

1.页面1

页面1要显示的格式是:
最左边的数码管显示字母C,最后两位显示湿度的十位和个位(不足两位十位熄灭)

01234567
C30

湿度是由pcf8591的AD模块读取通道3的电压值转换来的,他们的关系是(从图转换成函数)
h u m i d i t y = { 10 , U ≤ 1 0.266 ∗ U − 16.666 ,    1 ≤ x ≤ 4 90 , U ≥ 4 humidity = \begin{cases} 10,U\le1\\ 0.266*U-16.666,\,\,1\le x \le 4\\ 90,U\ge 4\\ \end{cases} humidity= 10,U10.266U16.666,1x490,U4
所以在AD的处理函数就可以先将通道3的电压读出来后直接进行湿度转换。下面给出的是用rb2_100x接收放大100倍后的电压值,你也可以直接使用float型变量接收未放大的电压值。

void ADProc()
{
   rb2_100x = ADRead() * 100 / 51;
   if(rb2_100x <= 100)
   	humidity = 10;
   else if(rb2_100x >= 400)
   	humidity = 90;
   else
   	humidity = 0.266 * rb2_100x - 16.666;
}

然后把humidity放到数码管显示时,最好是将中间三位显示成电压值(这样可以直观的看到湿度转换是不是正确的),比如这样:

switch(SegMode)
{
	case 0:
		SegBuf[0] = 11;
		SegBuf[1] = 10;
		SegBuf[2] = rb2_100x / 100;
		SegBuf[3] = rb2_100x / 10 % 10;
		SegBuf[4] = rb2_100x % 10;
		SegBuf[5] = 10;
		SegBuf[6] = humidity / 10;
		SegBuf[7] = humidity % 10;
	break;
}

实物显示:
电压为4时,对应湿度90
在这里插入图片描述
电压为1时,对应湿度10
在这里插入图片描述

2.页面2

页面2要显示的格式是:
最左边的数码管显示字母E,最后两位显示湿度参数的十位和个位(不足两位十位熄灭)

湿度参数默认值为50

01234567
E50

这个就特别简单了,页面1的代码直接复制下来就可以了,唯一要改的地方就是页面2的显示要对湿度参数的十位进行判断,为0时熄灭。

case 1:
	SegBuf[0] = 12;
	SegBuf[1] = 10;
	SegBuf[2] = 10;
	SegBuf[3] = 10;
	SegBuf[4] = 10;
	SegBuf[5] = 10;
	SegBuf[6] = (humidity_set / 10) ? (humidity_set / 10) : 10;
	SegBuf[7] = humidity_set % 10;
break;

3. 页面3

页面3要显示的格式是:
最左边的数码管显示字母H,最后两位显示时间间隔的十位和个位(不足两位十位熄灭)

时间间隔默认值为3。
时间间隔是继电器点亮的间隔,跟数码管没关系,直接显示即可。

01234567
E3

二、按键部分

按键只用到了按键S4、S5、S8、S9。
按键S4是切换页面,实现也很简单。
按键5是继电器启动标志位,在任何界面按一次标志位取反。
按键8、按键9:参数控制按键
在页面2按下,湿度参数每次加/减5,页面3按下,时间间隔每次加/减1,湿度参数取值范围:30 ~ 90,时间间隔取值范围:1 ~ 10。
当湿度参数达到最大值时,下次按下S8,回到最小值开始加,时间间隔也是如此。
比如:

S8
S8
S8
S9
S9
S9
S8
80
85
90
30
40
35
90
35
case 8:
	if(SegMode == 1)
	{
		humidity_set += 5;
		if(humidity_set == 95)
			humidity_set = 30;
	}
	else if(SegMode == 2)
	{
		if(++relay_light_time == 11)
			relay_light_time = 1;
	}
break;

三、继电器部分

no
yes
no
yes
继电器工作?
继电器断开
湿度参数>湿度?
继电器吸合,经过时间间隔后断开

这边继电器吸合的判断是继电器工作标志位为1而且湿度参数大于湿度的时候,继电器吸合,经过时间间隔设置的时间后,继电器闭合。

题目出的不是很严谨,因为第一次湿度参数大于湿度的时候,继电器进行吸合-断开操作后,它还可以重复吸合吗?当湿度参数或者湿度或者时间间隔数据变了,它还可以吸合吗?
根据正常人的思维,这边继电器应该是可以重复吸合的(当湿度数据或者湿度参数变了后),也就是说,湿度为30,湿度数据为35,继电器工作的时候,继电器吸合,3秒后断开,此时不能重复吸合,然后将湿度调成33,继电器吸合,3秒后断开。保持湿度不变,湿度数据变成40,此时继电器吸合,3秒后断开。

按照上面分析的继电器吸合逻辑,难点如下:

  • 让继电器吸合很简单,让继电器闭合也很简单,要怎么让继电器吸合后经过时间间隔设定的时间后断开呢?
  • 怎么避免继电器重复吸合的情况下又不影响湿度/湿度参数改变后让继电器吸合呢?
    碰到这种逻辑性很强的题目,采取的做法是从大到小,也就是先写继电器不工作时断开、继电器工作时吸合,然后观察板子的现象是否正确(防止继电器底层代码书写错误)
void LedProc()
{
	/*Relay*/
	if(!relay_work)
		relay_flag = 0;
	else
		relay_flag = 1;
		
	Relay(relay_flag);
}

然后开始写继电器工作时,如果湿度参数大于湿度时,继电器吸合,否则断开。

void LedProc()
{
	/*Relay*/
	if(!relay_work)
		relay_flag = 0;
	else
	{
		if(humidity < humidity_set)
		{
			relay_flag = 1;
		}
		else
			relay_flag = 0;
	}
		
	Relay(relay_flag);
}

然后就可以开始写继电器吸合后经过时间间隔设定的时间后断开了。
定义全局变量unsigned int time_relay_light为时间计时变量,这边对time_relay_light处理防止数据越界,然后当time_relay_light大于等于时间间隔设定的时间时,继电器闭合。(relay_light_time为时间间隔,由于定时器为1秒产生1次中断,所以将time_relay_lightrelay_light_time * 1000进行比较)

void LedProc()
{
	/*Relay*/
	if(!relay_work)
		relay_flag = 0;
	else
	{
		if(humidity < humidity_set)
		{
			relay_flag = 1;
			if(time_relay_light >= (relay_light_time * 1000))
				relay_flag = 0;
		}
		else
			relay_flag = 0;
	}
		
	Relay(relay_flag);
}

void Timer0_Isr(void) interrupt 1
{
	if(relay_work && relay_flag)
	{
		if(++time_relay_light >= 10000)
			time_relay_light = 10000;
	}
}

然后就要开始写防止重复吸合了(湿度、湿度参数、继电器工作模式没有改变的情况下重复吸合)

idata bit relay_has_light;

void LedProc()
{
	/*Relay*/
	if(!relay_work)
		relay_flag = 0;
	else
	{
		//防止重复吸合
		if(!relay_has_light)
		{
			relay_flag = 1;//继电器吸合
			relay_has_light = 1;//继电器重复点亮判断生效
		}


		if(humidity < humidity_set)
		{
			relay_flag = 1;
			if(time_relay_light >= (relay_light_time * 1000))
				relay_flag = 0;
		}
		else
			relay_flag = 0;
	}
		
	Relay(relay_flag);
}

防止重复吸合写完,就要写在哪里让relay_has_light标志位清零了,这个时候,我们可以模仿一下Led的底层代码中定义的temp temp_old,也就是说,第一次湿度<湿度参数吸合继电器的时候,用humidity_oldhumidity_set_old保存当前的humidityhumidity_set,所以判断relay_has_light标志位清零只需要在下次的湿度<湿度参数的时候判断一下(humidity_old != humidity)或者(humidity_set_old != humidity_set)就可以了。

else
{
	if(humidity < humidity_set)
	{
		if((humidity_old != humidity) || (humidity_set_old != humidity_set))
		{
			time_relay_light = 0;//重置计时器
			relay_has_light = 0;//继电器重复点亮判断失效
			humidity_old = humidity;
			humidity_set_old = humidity_set;
		}
		if(!relay_has_light)
		{
			relay_flag = 1;//继电器吸合
			relay_has_light = 1;//继电器重复点亮判断生效
		}
		if(time_relay_light >= (relay_light_time * 1000))
		{
			relay_flag = 0;//达到时间后继电器断开
		}
	}
	else
	{
		relay_flag = 0;
		time_relay_light = 0;
	}
}

然后就要考虑最后一种情况了,当湿度参数和湿度都没有改变,只改变继电器标志位的时候,继电器也是要吸合的。

if(!relay_work)
{
	relay_has_light = 0;
	relay_flag = 0;
	humidity_old = humidity_set_old = 0;//重置保存值
}
else
{
	//...
}

这样写出的程序显示时是没有明显问题的(如果继电器吸合是按照上面我的理解来说的话),如果你有别的看法或者发现了错误,请在评论区告诉我。


http://www.kler.cn/a/567156.html

相关文章:

  • 基于Spring Boot的产业园区智慧公寓管理系统设计与实现(LW+源码+讲解)
  • Ansys Zemax | 使用衍射光学器件模拟增强现实 (AR) 系统的出瞳扩展器 (EPE):第 3 部分
  • Linux云计算SRE-第十五周
  • 机器学习基础概念详解:从入门到应用
  • 《OpenCV》——人脸检测
  • Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例
  • 【Java项目】基于SpringBoot和Vue的“智慧食堂”系统
  • Android 布局系列(五):GridLayout 网格布局的使用
  • 一文掌握 Scrapy 框架的详细使用,包括实战案例
  • 两数之和 Hot100
  • Mysql 语法再巩固
  • GitHub 语析 - 基于大模型的知识库与知识图谱问答平台
  • 从零搭建Tomcat:深入理解Java Web服务器的工作原理
  • 【Linux基础】Linux下的C编程指南
  • redis slaveof 命令 执行后为什么需要清库重新同步
  • springboot集成langchain4j-实现简单的智能问答机器人
  • Android逆向:一文掌握 Frida 详细使用
  • SpringBoot 项目集成 Prometheus 和 Grafana
  • JAVA版本GDAL安装使用教程(详细步骤)
  • Lucene硬核解析专题系列(三):查询解析与执行