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

news2024/9/30 0:57:41

一.题目分析

(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.coloradmin.cn/o/2178265.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

负载均衡(Load Balancing)是一种计算机技术,用于在网络应用中分配工作负载,以优化资源使用、最大化吞吐量、减少响应时间以及避免过载。

负载均衡&#xff08;Load Balancing&#xff09;是一种计算机技术&#xff0c;用于在网络应用中分配工作负载&#xff0c;以优化资源使用、最大化吞吐量、减少响应时间以及避免过载。通过将任务均匀地分布在多个组件上&#xff0c;如服务器、网络链接、CPU、硬盘等&#xff0c…

【AG 创新工坊】探索存内计算的未来,共话 AGI 时代

目录 ⚛️1. 会议详情 ☪️2. 会议回顾 ♋2.1 多模态时代&#xff0c;存内计算架构的应用与发展 ♏2.2 分布式环境下深度学习任务的高效可靠执行研究 ♐2.3 IGZO在后道单片三维集成中的机遇与挑战 ♑2.4 witin-nn:神经网络算法模型在存内开发板上的应用开发 ♉2.5 茶歇交…

讯飞星火编排创建智能体学习(一)最简单的智能体构建

目录 开篇 智能体的概念 编排创建智能体 创建第一个智能体 ​编辑 大模型节点 测试与调试 开篇 前段时间在华为全联接大会上看到讯飞星火企业级智能体平台的演示&#xff0c;对于拖放的可视化设计非常喜欢&#xff0c;刚开始以为是企业用户才有的&#xff0c;回来之后查…

X86架构(九)——保护模式的进入

全局描述符表 全局描述符表(Global Descriptor Table,GDT)是保护模式下非常重要的一个数据结构。 在保护模式下&#xff0c;对内存的访问仍然使用段地址和偏移地址&#xff0c;在每个段能够访问之前&#xff0c;必须先行设置好 GDT 的地址&#xff0c;并加载全局描述符表寄存…

推荐4款2024年大家都在用的高质量翻译器。

翻译器在我们的生活中有着很重要的作用&#xff0c;不管是我们在学习还是工作&#xff0c;生活娱乐&#xff0c;出国旅游等场合都会派上用场&#xff0c;它是我们解决沟通的障碍&#xff0c;提高阅读效率的好帮手。我自己使用的翻译器有很多&#xff0c;可以给大家列举几款特别…

依赖倒置原则(学习笔记)

抽象不应该依赖细节&#xff0c;细节应该依赖抽象。简单的说就是要求对抽象进行编程&#xff0c;不要对实现进行编程&#xff0c;这样就降低了客户与实现模块间的耦合。 依赖倒转原则是基于这样的设计理念&#xff1a;相对于细节的多变性&#xff0c;抽象的东西要稳定的多。 以…

了解输出电源优先级

主要又SUB&#xff0c;SBU以及USB三种模式。 调试10kW逆变器存在的输出电源优先级的问题&#xff0c;当优先级为SUB时&#xff0c;利用电压源模拟电池&#xff0c;当电池电压超过58.4V&#xff0c;即过压&#xff0c;在接入市电&#xff0c;市电继电器仍然闭合&#xff0c;仍然…

pyboard405意外故障,micropython OLED例程无法运行,折腾了大半天。

thonny报告&#xff1a; Traceback (most recent call last): File "<stdin>", line 3, in <module> RuntimeError: name too mode # main.py -- put your code here! from machine import I2C,Pin #从machine模块导入I2C、Pin子模块 from ss…

javascript:监听浏览器页签切换

监听页面的可见性变化&#xff0c;在很多场景下非常实用&#xff0c;比如跟踪用户行为、节省资源、优化性能等。 1 代码示例 document.addEventListener("visibilitychange", () > {if (document.visibilityState "visible") {alert("当前页面已…

上交所系统被股民买崩了?原因竟然是...

在A股市场迎来久违的牛市之际&#xff0c;上海证券交易所&#xff08;上交所&#xff09;却在关键时刻遭遇了技术挑战。9月27日&#xff0c;上交所的交易系统出现了罕见的宕机现象&#xff0c;持续时间长达两个小时&#xff0c;导致投资者在火热的交易期间无法正常进行交易操作…

通信工程学习:什么是OFDM正交频分复用

OFDM&#xff1a;正交频分复用 OFDM&#xff08;Orthogonal Frequency Division Multiplexing&#xff0c;正交频分复用&#xff09;是一种在通信领域中广泛应用的多载波调制技术。该技术通过将高速数据流分割成多个低速子流&#xff0c;并在不同频率上同时传输这些子流&#x…

论文笔记:Gradient Episodic Memory for Continual Learning

1. Contribution 提出了一组指标来评估模型在连续数据上的学习情况。这些指标不仅通过测试准确性来表征模型&#xff0c;还通过其跨任务迁移知识的能力来表征模型。针对持续学习任务&#xff0c;提出了GEM模型&#xff08;Gradient Episodic Memory&#xff09;&#xff0c;它…

【预备理论知识——1】深度学习:概率论概述

简单地说&#xff0c;机器学习就是做出预测。 概率论 掷骰子 假设我们掷骰子&#xff0c;想知道看到1的几率有多大&#xff0c;而不是看到另一个数字。 如果骰子是公平的&#xff0c;那么所有六个结果{1,…, 6}都有相同的可能发生&#xff0c; 因此我们可以说 1 发生的概率为1…

Linux date命令(用于显示和设置系统的日期和时间,不仅可以显示时间,还能进行复杂的时间计算和格式化)

文章目录 深入探讨 Linux Date 命令1. Date 命令详细功能解析1.1 命令概述1.2 命令语法 2. 时间显示与格式化2.1 标准时间输出2.2 自定义格式输出 3. 设置系统日期和时间3.1 基本用法3.2 注意事项 4. 实用示例与脚本应用4.1 生成时间戳秒级时间戳毫秒时间戳 4.2 时间戳转换4.3 …

什么是后仿

什么是后仿 参考 查了一圈发现网上对post netlist simulation 介绍的比较少&#xff0c;今天和大家聊聊post netlist simulation。 首先什么是post netlist simulation(后面简称 postsim )&#xff1f; Netlist simulation 有些公司也叫gate level simulation&#xff0c;是指将…

问:全国产业园数量增长,对中小企业意味着什么?

随着全国产业园数量的持续增长&#xff0c;这一趋势无疑为中小企业带来了前所未有的机遇与可能。产业园作为产业集聚的重要载体&#xff0c;不仅为中小企业提供了更广阔的发展空间&#xff0c;还通过资源共享、成本降低、创新协同等方式&#xff0c;助力企业快速成长。 对于中…

高效的视频压缩标准H.264介绍,以及H.264在视频监控系统中的应用

目录 一、概述 二、 工作原理 三、技术特点与优势 1、高效压缩率 2、高质量视频 3、错误恢复能力 4、灵活性 四、编解码过程 1、编码过程 2、解码过程 五、帧类型与结构 1、I帧 2、P帧 3、B帧 六、应用与优势 1、节省存储空间和带宽 2、提高视频质量 3、适应…

中间件技术

在Java开发中&#xff0c;中间件技术是一种非常关键且广泛使用的技术。中间件通常被定义为位于操作系统、网络和数据库之上的软件层&#xff0c;用于简化分布式系统的开发、部署和管理。它们提供了一系列服务&#xff0c;如消息传递、事务管理、安全控制等&#xff0c;以帮助开…

Eth-trunk的介绍以及实验配置

目录 技术背景 Eth-Trunk概念 LACP模式下优先级 LACP的抢占机制 Eth-Trunk 配置 LACP模式 手工模式 Eth-Trunk接口负载分担 技术背景 随着网络中部署的业务量不断增长&#xff0c;单条物理链路的带宽已不能满足正常的业务流量需求。这时可以选择使用更高带宽的接口板或…

招联金融内推-2025校招

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…