第八届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)

一.题目分析

(1).题目

(2).题目分析

1.按键功能分析----过程控制

a. 选择按键按下的个数和目标层数(每个按键都要在一秒之内按下,否则就结束)

b. 当升降机到达目标平台,LED灯熄灭

c. 按下当前平台对于按键无效

d.一次可以设定多个目标平台,上下平台都有按键的时候,运行顺序是先下后上

2.PWM输出

a.两路PWM输出控制上下行和开关门电机

b.频率转换成自动重装载值

c.比较值

  1. 逻辑导图

二.CubeMX配置

由于蓝桥杯使用的板子都是STM32G431RBT6,配置都是相同的,模板已经在https://blog.csdn.net/weixin_51089092/article/details/142604825?sharetype=blogdetail&sharerId=142604825&sharerefer=PC&sharesource=weixin_51089092&spm=1011.2480.3001.8118配置完成,大家可以前往学习

三.相关代码实现

(1)MAIN

  1. 全局变量声明

#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
//#include "UART\bsp_uart.h"
//#include "I2C\bsp_i2c.h"
//#include "ADC\bsp_adc.h"
#include "TIM\bsp_tim.h"
#include "RTC\bsp_rtc.h"

//***全局变量声明区
//*减速变量
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;

//*LED专用变量
uint8_t ucLed;

//*LCD显示专用变量
uint8_t Lcd_Disp_String[21];//最多显示20个字符
//*rtc相关变量
RTC_TimeTypeDef H_M_S_Time;
RTC_DateTypeDef Y_M_D_Date;

//全局变量
uint8_t ucPlat = 1;//1234表示当前所处的平台
//_Bool PA4_Voltage = 0;
//_Bool PA5_Voltage = 0;
uint8_t ucSet;//用于记录几号按键按下去了。只用低4位,分别对应LD4~LD1,按键B4~B1, _ _ _ _  [ _ _ _ _ ]
uint8_t ucState;//状态机,0~8.
uint8_t Dir;//电梯运行方向变量 0 -没运行,1-上,2-下
uint8_t Flow = 0x10;//流水的变量
__IO uint32_t uwTick_Set_Point = 0;//计时专用

//***子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
//void Usart_Proc(void);
void Elev_Proc(void);

2.系统主函数

int main(void)
{

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

	/*bsp资源的初始化*/
	KEY_LED_Init();
	LCD_Init();
	LCD_Clear(White);
    LCD_SetBackColor(White);
    LCD_SetTextColor(Blue);	
	PWM_OUTPUT_TIM3_Init();   // PA6     ------> TIM3_CH1
	PWM_OUTPUT_TIM17_Init();   //PA7     ------> TIM17_CH1
	RTC_Init();
	
  while (1)
  {
		Key_Proc();
		Led_Proc();
		Lcd_Proc();
		Elev_Proc();	
  }
}

3.子函数

按键扫描子函数

a.逻辑梳理

将平台和按键对应起来,根据对应的平台点灯

b.程序源码

void Key_Proc(void)
{
	if((uwTick -  uwTick_Key_Set_Point)<50)	return;//减速函数
		uwTick_Key_Set_Point = uwTick;

	ucKey_Val = Key_Scan();
	unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val); 
	ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);	
	ucKey_Old = ucKey_Val;
	if(ucState == 0)//状态机,0~8.
	{
		if(unKey_Down == 1)//B1按压
		{
			if(ucPlat != 1) ucSet |= 0x01; //ucPlat的1234表示当前所处的平台
		}	//ucSet用于记录几号按键按下去了。只用低4位,分别对应LD4~LD1,按键B4~B1
		else if(unKey_Down == 2)//B2按压
		{
			if(ucPlat != 2) ucSet |= 0x02; 
		}	
		else if(unKey_Down == 3)//B3按压
		{
			if(ucPlat != 3) ucSet |= 0x04; 	
		}		
		else if(unKey_Down == 4)//B4按压
		{		
			if(ucPlat != 4) ucSet |= 0x08; 	
		}		
		
		ucLed &= 0xF0;
		ucLed |= ucSet;		
		if(unKey_Down != 0)//当有按键按下去,启动计时
		{
			uwTick_Set_Point = uwTick;
		}		
	}
}

LED扫描子函数

a.程序源码

void Led_Proc(void)
{
	if((uwTick -  uwTick_Led_Set_Point)<200)	return;//减速函数
		uwTick_Led_Set_Point = uwTick;
	LED_Disp(ucLed);
}

LCD扫描子函数

a.逻辑梳理

显示到达平台和时间

b.程序源码

void Lcd_Proc(void)
{
	if((uwTick -  uwTick_Lcd_Set_Point)<100)	return;//减速函数
		uwTick_Lcd_Set_Point = uwTick;
	
	//开机屏幕测试代码
	sprintf((char *)Lcd_Disp_String, " Current Platform",ucLed);
	LCD_DisplayStringLine(Line1, Lcd_Disp_String);	
	
	sprintf((char *)Lcd_Disp_String, "          %1d",(unsigned int)ucPlat);
	LCD_DisplayStringLine(Line3, Lcd_Disp_String);		
	//*RTC内容显示
	HAL_RTC_GetTime(&hrtc, &H_M_S_Time, RTC_FORMAT_BIN);//读取日期和时间必须同时使用
	HAL_RTC_GetDate(&hrtc, &Y_M_D_Date, RTC_FORMAT_BIN);
	sprintf((char *)Lcd_Disp_String, "       %02d-%02d-%02d",(unsigned int)H_M_S_Time.Hours,(unsigned int)H_M_S_Time.Minutes,(unsigned int)H_M_S_Time.Seconds);
	LCD_DisplayStringLine(Line6, Lcd_Disp_String);		
}

状态机子函数

a.逻辑框图

b.程序源码

void Elev_Proc(void)
{
	if(ucSet)//如果用户没有设置目标层,不执行此程序。只有按键设置了,才可以。
	{
		switch(ucState)
		{
			
			
			case 0://等待按键按下后1s时间的到来
				if((uwTick - uwTick_Set_Point) >= 1000)
					ucState = 1;
				else 
					break;
				
				
			case 1://控制电梯门关闭 PA7 -  PWM    2KHZ    D=50%     PA5= 0  
				
				HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);		//关门			
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 250);//修改占空比的基本操作 D=0.5	
				HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);		//PA7   2khz  黄色波形		
			
				sprintf((char *)Lcd_Disp_String, " Door Closing          ");
				LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
			
			  uwTick_Set_Point = uwTick;
				ucState = 2;
			
		
			case 2://等待电梯关门时间到达4s,到了之后,执行后边的操作,否则退出。
				if((uwTick - uwTick_Set_Point) >= 4000)
				{
					HAL_TIM_PWM_Stop(&htim17,TIM_CHANNEL_1);		//PA7   2khz  黄色波形	
					
					sprintf((char *)Lcd_Disp_String, " Door Closed          ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
					
					ucState = 3;				
				}
				else 
					break;	
				
//uint8_t ucPlat = 1;//1234表示当前所处的平台				
			case 3://判断当前层数变量和设定变量之间的关系,决定应该上行还是下行,并启动运行。
				//ucPlat=1~4=1(0001) 2(0010) 3(0100) 4(1000)      ucSet= 0010B
				if(ucSet > (1<<(ucPlat-1)))//上行 PA6  -  PWM    1KHZ   D=80%      PA4 = 1
				{
					Dir = 1;//上行
					HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);		//	电梯上行		
//					PA4_Voltage = 1;//为了屏幕测试上行方便
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800);//修改占空比的基本操作 D=0.8
					HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);	//PA6   1khz  绿色波形
					
					sprintf((char *)Lcd_Disp_String, "Elev Upping          ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
					
				}
				else if(ucSet < (1<<(ucPlat-1)))//下行  PA6  -  PWM    1KHZ    D=60%         PA4 = 0 
				{
					Dir = 2;//下行
					HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);		//	电梯下行		
//					PA4_Voltage = 0;//为了屏幕测试下行方便
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 600);//修改占空比的基本操作 D=0.6
					HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);	//PA6   1khz  绿色波形		

					sprintf((char *)Lcd_Disp_String, "Elev Downing           ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);									
				}
				
			  uwTick_Set_Point = uwTick;
				ucState = 4;				
			
				
			case 4://判断电梯走6s时间是否到来,如果到来,当前所在层数变化1个单位。如果没有到6s,流水灯效果。
				if((uwTick - uwTick_Set_Point) >= 6000)
				{
					if(Dir == 1) ucPlat++;
					else if(Dir == 2) ucPlat--;
					
					sprintf((char *)Lcd_Disp_String, "          %1d",(unsigned int)ucPlat);
					LCD_DisplayStringLine(Line3, Lcd_Disp_String);					

					sprintf((char *)Lcd_Disp_String, "Elev Runned 1 Floor        ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);						
					
					ucLed &=	0x0F;
					Flow = 0x10;
					ucState = 5;				
				}
				else 
				{
					if(Dir == 1)//上行,左到右
					{
						ucLed &=	0x0F;
						ucLed |= Flow;
						Flow = (Flow >> 1);//0001 0000  -  0000 1000
						if(Flow == 0x08)
							Flow = 0x80;
					}
					else if(Dir == 2)//下行,右到左
					{
						ucLed &=	0x0F;
						ucLed |= Flow;
						Flow = (Flow << 1);//1000 0000  -  0000 0000
						if(Flow == 0x00)
							Flow = 0x10;					
					}
					HAL_Delay(300);
					break;	
				}
				
				
				
				
			case 5://ucPlat当前层 3    ucSet 0000 1100
				if((1<<(ucPlat-1))&ucSet)//当不为零的时候,表示到了目标层
				{
					HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);	//PA6   1khz  绿色波形,停止波形的发生						

					HAL_Delay(300);					
					sprintf((char *)Lcd_Disp_String, "             ");
					LCD_DisplayStringLine(Line3, Lcd_Disp_String);	
					HAL_Delay(300);								
					sprintf((char *)Lcd_Disp_String, "          %1d",(unsigned int)ucPlat);
					LCD_DisplayStringLine(Line3, Lcd_Disp_String);						
					HAL_Delay(300);					
					sprintf((char *)Lcd_Disp_String, "             ");
					LCD_DisplayStringLine(Line3, Lcd_Disp_String);	
					HAL_Delay(300);								
					sprintf((char *)Lcd_Disp_String, "          %1d",(unsigned int)ucPlat);
					LCD_DisplayStringLine(Line3, Lcd_Disp_String);					
					
					//电梯开门,  PA7 -  PWM    2KHZ    D=60%          PA5= 1 。
					HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);		//开门			
//					PA5_Voltage = 1;//为了屏幕测试关门方便
					__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300);//修改占空比的基本操作 D=0.6
					HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);		//PA7   2khz  黄色波形		
					
					sprintf((char *)Lcd_Disp_String, "Comed , Door Opening            ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
					
					uwTick_Set_Point = uwTick;
					ucState = 6;					
				}		
				else //没有到达目标层
				{
					uwTick_Set_Point = uwTick;
					ucState = 4;
					break;		
				}
				
			case 6://等待电梯开门4s时间是否到达
				if((uwTick - uwTick_Set_Point) >= 4000)
				{
					HAL_TIM_PWM_Stop(&htim17,TIM_CHANNEL_1);		//PA7   2khz  黄色波形	
					
					sprintf((char *)Lcd_Disp_String, "Door Opened           ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
					
					ucSet &= (~(1<<(ucPlat-1)));//用户设置的目标层达成目标 1000
					ucLed &= 0xF0;
					ucLed |= ucSet;					
					LED_Disp(ucLed);					
					
					ucState = 7;				
				}
				else 
					break;	
				
				
			case 7://判别是否还有别的目标层
				if(ucSet)//如果还有别的目标平台,等待2秒
				{
					uwTick_Set_Point = uwTick;
					
					sprintf((char *)Lcd_Disp_String, "Waitting 2s           ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);						
					
					ucState = 8;						
				}
				else //没有别的目标了
				{
					ucState = 0;
					
					sprintf((char *)Lcd_Disp_String, "                  ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);	
					
					break;					
				}
						
			case 8://判断到达目标层之后,电梯开门后,是否等待了2s的时间已经到达。
				if((uwTick - uwTick_Set_Point) >= 2000)
				{		
					sprintf((char *)Lcd_Disp_String, "                  ");
					LCD_DisplayStringLine(Line8, Lcd_Disp_String);					
					ucState = 1;										
				}	
		}	
	}
}

(2)BSP(https://blog.csdn.net/weixin_51089092/article/details/142604825?sharetype=blogdetail&sharerId=142604825&sharerefer=PC&sharesource=weixin_51089092&spm=1011.2480.3001.8118里面有详细的讲解,大家可前往此链接学习)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/884881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【YOLO目标检测车牌数据集】共10000张、已标注txt格式、有训练好的yolov5的模型

目录 说明图片示例 说明 数据集格式&#xff1a;YOLO格式 图片数量&#xff1a;10000&#xff08;2000张绿牌、8000张蓝牌&#xff09; 标注数量(txt文件个数)&#xff1a;10000 标注类别数&#xff1a;1 标注类别名称&#xff1a;licence 数据集下载&#xff1a;车牌数据…

excel导出图片---HSSFWorkbook--SXSSFWorkbook

1 概述 平时在工作中&#xff0c;excel导出图片经常会用到&#xff0c;但奈何HSSFWorkbook导出数据数量有限制问题&#xff0c;所以企业里大多都用SXSSFWorkbook格式&#xff0c;很少用HSSFWorkbook。所以今天以这两种格式分别记录下&#xff0c;图片的导出过程。 2 HSSFWork…

C++:模拟实现vector

目录 成员变量与迭代器 size capacity empty 迭代器有关函数 实现默认成员函数的前置准备 reserve ​编辑 ​编辑 push_back 构造函数 无参构造 迭代器区间构造 n个val来进行构造 析构函数 拷贝构造函数 赋值重载 增删查改 clear resize pop_back inser…

Java SE 总结

Java SE&#xff08;Standard Edition&#xff09;是Java编程语言的标准版本&#xff0c;提供了基础的编程环境和API&#xff0c;适用于开发和运行Java应用程序。下面是Java SE的几个重要方面的知识回顾与总结。 1. Java环境基础 具体可参考这里对三者的介绍 传送门 1.1 JVM…

大厂面试真题-说一下Mybatis的缓存

首先看一下原理图 Mybatis提供了两种缓存机制&#xff1a;一级缓存&#xff08;L1 Cache&#xff09;和二级缓存&#xff08;L2 Cache&#xff09;&#xff0c;旨在提高数据库查询的性能&#xff0c;减少数据库的访问次数。注意查询的顺序是先二级缓存&#xff0c;再一级缓存。…

MySQL Performance Schema 详解及运行时配置优化

引言 MySQL 的 Performance Schema 是一套性能监控与诊断工具&#xff0c;帮助开发者和数据库管理员收集、分析 MySQL 实例的运行状态&#xff0c;找出性能瓶颈并进行优化。通过 Performance Schema&#xff0c;我们能够监控不同的内部事件、线程、会话、语句执行等关键性能指…

Java-数据结构-Map和Set-(二)-哈希表 |ू・ω・` )

文本目录&#xff1a; ❄️一、哈希表&#xff1a; ☑ 1、概念&#xff1a; ☑ 2、冲突-概念&#xff1a; ☑ 3、冲突-避免&#xff1a; ☞ 1&#xff09;、避免冲突-哈希函数的设计&#xff1a; ☞ 2&#xff09;、避免冲突-负载因子调节(重点)&#xff1a; ☑ 4、冲突-解决&…

cloud-(Nacos)--注册中心原理-服务注册-服务发现

并且通过(RestTemplate)Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题 在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下: 流程如下: 服务启动…

Mac安装Manim并运行

1.在macOS上创建Python虚拟环境&#xff0c;可以使用venv模块&#xff0c;这是Python自带的库&#xff0c;也可以使用conda。以下是使用venv创建和使用Python虚拟环境的步骤&#xff1a; 打开终端。 创建一个新的目录来存放你的项目&#xff0c;并进入该目录&#xff1a; mk…

15.安卓逆向-frida基础-HOOK类方法1

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

WEB服务器——Tomcat

服务器是可以使用java完成编写&#xff0c;是可以接受页面发送的请求和响应数据给前端浏览器的&#xff0c;而在开发中真正用到的Web服务器&#xff0c;我们不会自己写的&#xff0c;都是使用目前比较流行的web服务器。 如&#xff1a;Tomcat 1. 简介 Tomcat 是一个开源的轻量…

【CAM350】使用总结 <二>{ 光绘Gerber 比较 }

一、 比较两份版本不同的光绘文件&#xff1a; //Analysis-Compare layers// 二、参数默认&#xff0c;比较完成给出结果 三、也可以直接在一份文件上选择“Draw on top” 四、对比差距直观可见

家中浮毛太多怎么办?希喂、米家、安德迈更推荐哪款?

在现代养宠家庭生活中&#xff0c;宠物空气净化器已经成为不可或缺的家电之一。 而在众多空气净化器类型中&#xff0c;宠物空气净化器以其独特的设计和卓越的净化效果&#xff0c;逐渐赢得了越来越多养宠家庭的青睐。 它不仅能有效地吸附空中飞舞的浮毛&#xff0c;还能高效…

基于SSM+小程序的在线课堂微信管理系统(在线课堂1)(源码+sql脚本+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 &emsp1、管理员实现了首页、个人中心、用户管理、课程分类管理、课程信息管理、课程订阅管理、课程视频管理、公告栏管理、留言板管理、系统管理。 2、用户实现了首页、课程信息、公…

此连接非私人连接

当你手机浏览器输入网站打开提示“此连接非私人连接&#xff0c;此网站可能在冒充来窃取你的个人或财务信息。你应回到之前的页面”这是因为该网站的SSL数字证书到期导致&#xff0c;需要此网站的管理员重新申请数字证书替换之前的文件才可以实现。 注意&#xff1a;如果你不是…

实用SQL小总结

WHERE 条件 column 为纯英文字符 或 不包含任何字符 语法&#xff1a; SELECT * FROM your_table WHERE REGEXP(your_column,^[A-Za-z]$); SELECT * FROM your_table WHERE NOT REGEXP(your_column,^[A-Za-z]$);例&#xff1a; SELECT DISTINCT t.pldlibho FROM kibb_pldlyw…

LLM - 使用 vLLM 部署 Qwen2-VL 多模态大模型 (配置 FlashAttention) 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142528967 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 vLLM 用…

02-ZYNQ linux开发环境安装,基于Petalinux2022.2和Vitis2022.2

petalinux安装 Petalinux 工具是 Xilinx 公司推出的嵌入式 Linux 开发套件&#xff0c;包括了 u-boot、Linux Kernel、device-tree、rootfs 等源码和库&#xff0c;以及 Yocto recipes&#xff0c;可以让客户很方便的生成、配置、编译及自定义 Linux 系统。Petalinux 支持 Ver…

uniapp EChars图表

1. uniapp EChars图表 &#xff08;1&#xff09;Apache ECharts 一个基于 JavaScript 的开源可视化图表库   https://echarts.apache.org/examples/zh/index.html &#xff08;1&#xff09;官网图例 &#xff08;2&#xff09;个人实现图例 1.1. 下载echart 1.1.1. 下…

docker - 迁移和备份

文章目录 1、docker commit1.1、查询 容器 docker ps1.2、docker commit zookeeper zookeeper:3.4.13 2、docker save -o2.1、宿主机 切换到 /opt 目录下2.2、将镜像保存到 宿主机/opt目录下 3、docker load -i 对某一个容器修改完毕以后&#xff0c;我们可以把最新的容器部署到…