蓝桥杯嵌入式|第十三届蓝桥杯嵌入式省赛程序设计试题及其题解

news2025/1/20 10:51:02

题目

    十三届省赛是要制作一个可由串口设置密码的密码锁。在本场比赛中,我们将用到LED模块按键模块串口模块定时器的PWM模块以及官方会提供源码的LCD模块。下面就请看原题:

在这里插入图片描述

题解

    在正式题解前,大家需要注意以下几点:

  • 由于LCD与LED有部分引脚是共用的,因此初始化完成LCD后最好手动关闭LED;
  • 由于每次LCD显示的长度可能不同,因此在本次显示前,要不先清屏,要不跟上次显示一样长;
  • 使用CubeMX配置完成串口USART1后需要更改默认引脚为PA9PA10

LED模块

    通过查询产品手册知,LED的引脚为PC8~PC15,外加锁存器74HC573需要用到的引脚PD2。(由于题目要求除LED1、LED2外的其他LED都处于熄灭状态,此处特意将所有的LED都初始化)
CubeMX配置:

代码样例
    由于G431的所有LED都跟锁存器74HC573连接,因此每次更改LED状态时都需要先打开锁存器,写入数据后再关闭锁存器。

/*****************************************************
* 函数功能:改变所有LED的状态
* 函数参数:
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeAllLedByStateNumber(char LEDSTATE)
{
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
					|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	//打开锁存器    准备写入数据
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	//关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

/*****************************************************
* 函数功能:根据LED的位置打开或者是关闭LED
* 函数参数:
*			uint16_t LEDLOCATION:需要操作LED的位置
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
{
	HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

按键模块

    通过查询产品手册知,开发板上的四个按键引脚为PB0~PB2、PA0
CubeMX配置

代码样例
    由于G431开发板上按键数量较少以及本次按键不涉及长短按、单击双击等复杂按键的设计,因此,我们直接使用含锁机制的if判断即可。

  • 第一步,判断按键是否按键以及锁是否处于打开状态,如果两者有一个不满足函数直接返回;否则,进入下一步;
  • 第二步,上锁,延时消抖;
  • 第三步,再次读取各个按键,判断具体是哪个按键按下;
  • 第四步,判断按键是否松开,如果松开,则开锁;否则函数直接返回。
/*********************************************
 * 函数功能:按键扫描 含按键消抖 无长按短按设计
 * 函数参数:无
 * 函数返回值:按键的位置
 *            返回值说明:B1-1 B2-2 B3-3 B4-4
*********************************************/
unsigned char scanKey(void)
{
	//按键锁
	static unsigned char keyLock = 1;
    //记录按键消抖时间
    // static uint16_t keyCount = 0;

	//按键按下
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
      || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET) 
      && keyLock == 1){
        //给按键上锁 避免多次触发按键
        keyLock = 0;
        
        //按键消抖 这里最好不要使用延时函数进行消抖 会影响系统的实时性
        // if(++keyCount % 10 < 5) return 0;
        // if(HAL_GetTick()%15 < 10) return 0;
        HAL_Delay(10);

        //按键B1
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
            return 1;
        }
        //按键B2
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
            return 2;
        }
        //按键B3
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
            return 3;
        }
        //按键B4
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
            return 4;
        }
    }
    //按键松开
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
      && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET) 
      && keyLock == 0){
        //开锁
        keyLock = 1;
    }
    return 0;
}

串口

    本次试题中,串口功能比较简单,只需要能够完成简单的接收数据即可。
CubeMX配置
    配置时一定一定记得改引脚!!!
在这里插入图片描述
代码样例
    HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)函数解析:

  • UART_HandleTypeDef *huart:串口通道;
  • uint8_t *pData:存放数据的buff;
  • uint16_t Size:一次接收数据的长度
        不过使用时还需要初始化,否则不能够进入中断接收数据;
/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){
		// 重新使能中断
		HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); 
	}
}

定时器输出PWM波

CubeMX配置
在这里插入图片描述
修改PWM的频率及占空比样例代码(可直接调用)

/****************************************
* 函数功能:修改PWM频率工作
* 函数参数:
*			unsigned int autoreloadDate:重装载值
*			unsigned int compareDate:PWM的比较值
* 函数返回值:无
****************************************/
void pwmWorkByFre(unsigned int autoreloadDate,unsigned int compareDate)
{
	//设置重装载值
	__HAL_TIM_SetAutoreload(&htim2,autoreloadDate-1);
	__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,compareDate);
}

LCD模块

    LCD模块官方会提供源码,内含初始化,大家会用即可。如下面是一段将LCD初始化成——文字颜色为白色、背景为蓝色的LCD屏:

/******************************************************************************
* 函数功能:LCD初始化
* 函数参数:无
* 函数返回值:无
*******************************************************************************/
void lcdInit(void)
{
	//HAL库的初始化
	LCD_Init();
	//设置LCD的背景色
	LCD_Clear(Blue);
	//设置LCD字体颜色
	LCD_SetTextColor(White);
	//设置LCD字体的背景色
	LCD_SetBackColor(Blue);
}

完整的配置文件

    文件说明:

  • sysInit()函数:自己添加的初始化配置;
  • sysWork()函数:系统工作逻辑函数;
#include "config.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"


//外部定义变量
extern TIM_HandleTypeDef htim7,htim2;
extern UART_HandleTypeDef huart1;
extern TIM_HandleTypeDef htim3;

//记录目前LCD处于的界面 0-密码输入界面  1-显示输出界面
int lcd_view_mod = 0;
//定时器7计数  计数值加1表示定时器触发一次也就是过了10ms
int time7_count = 0;
//保存上一次定时器7计数值
int time7_count_start_flag = 0;
//密码输入界面显示的数据
char psd_txt[20];
//输出状态界面显示数据
char sta_txt[20];

char temp[20];
unsigned int crrl_t = 0;
uint32_t frd = 0;

//用于循环
int i,j;

//* 存储串口1接收的数据
uint8_t Rxbuff[7];

//密码锁密码 注意 这里一定一定要加上字符串结束符'\0' 否则会出现字符串拼接的情况
char passwd[4] = {'1','2','3','\0'};
//输入的密码
char passwd_test[4] = {'0','0','0','\0'};
//记录密码输错次数
unsigned int passwd_wrong_count = 0;
//记录是否重头开始输入密码 0-是重头开始输入且没开始输入的  1-不是重头开始输入密码
char passwd_flag = 0;

//记录按键的值
unsigned char key_num = 0;

/***********************************************
* 函数功能:自定义的系统初始化
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysInit(void)
{
	//LCD初始化
	lcdInit();
	//关闭所有的LED
	changeAllLedByStateNumber(0);
	//打开定时器7中断
	HAL_TIM_Base_Start_IT(&htim7);
	//打开串口的中断接收功能
	HAL_UART_Receive_IT(&huart1,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); 
	//打开定时器2通道2的PWM输出功能
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
}


/***********************************************
* 函数功能:系统工作逻辑函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysWork(void)
{
	//更新密码
	getPasswdByUsart();
	
	//密码的相关操作以及显示密码界面
	if(lcd_view_mod == 0)
	{
		keyProc();
		PSDViewDisplay();
		pwmWorkByFre(1000,400);
	}
	//显示输出界面
	else if(lcd_view_mod == 1)
	{
		STAViewDisplay();
		pwmWorkByFre(500,50);
		passwd_flag = 0;
		time7_count_start_flag = 1;
	}
	//LED灯显示
	ledDisplay();
	
	//5秒时间到 定时器值以及标志位归零 界面显示密码界面 密码输错次数归零
	if(time7_count>500)
	{
		time7_count = 0;
		time7_count_start_flag = 0;
		lcd_view_mod = 0;
		passwd_wrong_count = 0;
	}
}

/***非阻塞模式下定时器中断回调函数***/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	//定时器7的中断回调函数
	if(htim->Instance == TIM7)
	{
		key_num = scanKey();
		if(time7_count_start_flag)
			time7_count++;
	}
}

/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){
		// 重新使能中断
		HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); 
	}
}

/***********************************************
* 函数功能:显示密码输入界面
* 函数参数:无
* 函数返回值:无
***********************************************/
void PSDViewDisplay(void)
{
	LCD_DisplayStringLine(Line2,(uint8_t*)"       PSD");
	//在第一次输入密码前显示
	if(passwd_flag == 0)
	{
		sprintf(psd_txt,"    B1 :%c       ",'@');
		LCD_DisplayStringLine(Line3,(uint8_t*)psd_txt);
		sprintf(psd_txt,"    B2 :%c       ",'@');
		LCD_DisplayStringLine(Line4,(uint8_t*)psd_txt);
		sprintf(psd_txt,"    B3 :%c       ",'@');
		LCD_DisplayStringLine(Line5,(uint8_t*)psd_txt);
	}
	else
	{
		sprintf(psd_txt,"    B1 :%c       ",passwd_test[0]);
		LCD_DisplayStringLine(Line3,(uint8_t*)psd_txt);
		sprintf(psd_txt,"    B2 :%c       ",passwd_test[1]);
		LCD_DisplayStringLine(Line4,(uint8_t*)psd_txt);
		sprintf(psd_txt,"    B3 :%c       ",passwd_test[2]);
		LCD_DisplayStringLine(Line5,(uint8_t*)psd_txt);
	}
}

/***********************************************
* 函数功能:显示输出界面
* 函数参数:无
* 函数返回值:无
***********************************************/
void STAViewDisplay(void)
{
	LCD_DisplayStringLine(Line2,(uint8_t*)"       STA");
	LCD_DisplayStringLine(Line3,(uint8_t*)"    F :2000Hz");
	LCD_DisplayStringLine(Line4,(uint8_t*)"    D :10%");
	LCD_ClearLine(Line5);
}

/***********************************************
* 函数功能:LED的相关显示
* 函数参数:无
* 函数返回值:无
***********************************************/
void ledDisplay(void)
{
	static int LED2_state = 1;
	//为了避免LED显示受到LCD影响 应该先关闭所有LED
	changeAllLedByStateNumber(0);
	
	//密码输入错误三次  LED2以0.1秒间隔闪烁 (5秒后熄灭)
	if(passwd_wrong_count>=3)
	{
		time7_count_start_flag = 1;
		if(time7_count <= 500)
		{
			//定时器计数值刷新  时间到了 LED2状态需要反转
			changeLedStateByLocation(LED2,LED2_state);
			LED2_state ^= 1;
		}
	}
	
	//密码验证成功 LED1点亮5秒 (5秒后熄灭)
	if(lcd_view_mod && time7_count <= 500)
	{
		changeLedStateByLocation(LED1,1);
	}
}

/********************************************
* 函数功能:
* 函数参数:无
* 函数返回值:无
********************************************/
void getPasswdByUsart(void)
{
	//分析串口接收到数据  需:旧密码匹配才能够设置新密码
	for(i=0;i<7;i++)
	{
		//判断输入是否合理
		if(!((Rxbuff[i]>='0'&&Rxbuff[i]<='9') || Rxbuff[i]=='-'))
			return ;
		//判断旧密码是否正确
		if(i < 3)
		{
			if(Rxbuff[i]!=passwd[i])
				return ;
		}
		//设置新密码
		if(i>3)
		{
			passwd[i%4] = Rxbuff[i];
		}
	}
	//重置密码后也需要重置旧密码输入错误的次数
	passwd_wrong_count = 0;
}

/********************************************
* 函数功能:
* 函数参数:无
* 函数返回值:无
********************************************/
void keyProc(void)
{
	//按键按下
	if(key_num)
	{
		//按键B0-B2
		if(key_num!= 3)
		{
			if(++passwd_test[key_num]>'9') passwd_test[key_num] = '0';
			passwd_flag = 1;
		}
		//按键B3
		else
		{
			//输入密码正确
			if(!strcmp(passwd,passwd_test)) 
			{
				lcd_view_mod = 1;
				passwd_wrong_count = 0;
			}
			//输入密码错误
			else
			{
				passwd_flag = 0;
				passwd_wrong_count++;
			}
		}
	}
}

/****************************************
* 函数功能:修改PWM的频率工作
* 函数参数:
*			unsigned int autoreloadDate:重装载值
*			unsigned int compareDate:PWM的比较值
* 函数返回值:无
****************************************/
void pwmWorkByFre(unsigned int autoreloadDate,unsigned int compareDate)
{
	//设置重装载值
	__HAL_TIM_SetAutoreload(&htim2,autoreloadDate-1);
	__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,compareDate);
}

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

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

相关文章

《计算机网络》——第二章知识点

物理层:在连接各种计算机的传输媒体上传输数据比特流&#xff0c;确定与传输媒体接口有关的一些特性。1.机械特性 定义物理连接的特性&#xff0c;规定物理连接时所采用的规格、接口形状、引线数目、引脚数量和排列情况。 2.电气特性 规定传输二进制位时&#xff0c;线路上信…

【深度学习框架TensorFlow】使用TensorFlow框架构建全连接的神经网络,实现手写数字识别

文章目录一.TensorFlow1.1 内容介绍二.开始实验2.1TensorFlow的基本使用2.2基于全连接神经网络的手写数字识别2.3 结论一.TensorFlow 使用深度学习框架TensorFlow。 目标&#xff1a; 1.了解TensorFlow的基本用法&#xff1b; 2.学习使用TensorFlow构建全连接的神经网络&…

双周赛(三)

T1&#xff1a; 如果你仍然再用二指禅打字&#xff0c;那我建议你重新学习打字&#xff0c;这样你打字会更快&#xff0c;感觉更舒适和愉快。 有很多网站教授正确的打字。下图描述了基本原理: 用同一手指按压颜色相同的键。黄色键需要用小指按压&#xff0c;蓝色的用无名指&a…

最新 iOS 更新后 iPhone 联系人和数据丢失/丢失

我两天前做了最新的更新&#xff0c;现在我有丢失的联系人。帮助&#xff01;&#xff01; 许多 iPhone 用户犹豫是否将他们的设备升级到最新的 iOS 系统有一个&#xff08;也许是几个&#xff09;充分的理由。每次 iOS 发布后&#xff0c;总会有新功能震撼我们的世界。但是&am…

Unity 小积累

** Unity 学习小积累 ** 1.FindObjectsOfType和FindObjectOfType 前者返回所有个体&#xff08;集合&#xff09; 后者返回第一个个体 &#xff08;单个&#xff09; 2.uinty打包问题 打包webgl遇到了 实际上和py没有关系 c盘不够了 单纯 3.Unity 默认下载位置 坑 1.Uni…

【css样式】页面实现一侧固定一侧滚动的效果,到底部后一起滚动

文章目录position的定位类型position的定位类型 static&#xff1a;默认值&#xff0c;没有定位&#xff0c;遵循正常的文档流 fixed&#xff1a;固定定位&#xff0c;元素的位置是相对于浏览器窗口 relative&#xff1a;相对定位&#xff0c;相对于其正常的位置&#xff0c;移…

BOSS直聘自动投简历的实现过程

这两年疫情&#xff0c;公司业务越来越差&#xff0c;必须得准备后路了&#xff0c;每天睡前都会在直聘上打一遍招呼&#xff0c;一直到打哈欠有睡意为止...,这样持续了一周&#xff0c;发现很难坚持&#xff0c;身为一名资深蜘蛛侠&#xff0c;怎么能这样下去呢&#xff1f;于…

3D模型的生成式AI

生成式 AI 席卷了 2022 年&#xff0c;我们最近决定 Physna 不应错过这个热点。 因此&#xff0c;尽管生成 AI 并不是我们的商业模式—Physna 是一家 3D 搜索和分析公司&#xff0c;专注于 AR/VR 和制造中的工程和设计应用—我们还是决定为 3D 模型和场景生成 AI 构建一个非常基…

Node.js——初识Node.js与内置模块(一)

1.初识 Node.js 1.1 浏览器中的 JavaScript运行环境 1.浏览器中的 JavaScript 的组成部分 2.为什么 JavaScript 可以在浏览器中被执行 3.为什么 JavaScript 可以操作 DOM 和 BOM 4.浏览器中的 JavaScript 运行环境 Javascript可以借助node,js进行后端开发 1.2 Node.js 简介 …

virtio前端驱动通知机制分析

virtio前端驱动通知机制分析 virtio 前后端主要通过PCI配置空间的寄存器来完成通信&#xff0c;I/O 请求的数据地址存放于 vring 中&#xff0c;并通过共享vring这个区域来实现 I/O 请求数据的共享。 由上图可知&#xff0c;虚拟机与主机之间交互用到了两个结构体&#xff1a;p…

智能网联汽车行业发展

智能网 联汽车信息安全发展趋势 智能网联汽车行业发展 根据工信部发布的《国家车联网产业标准体系建设 指南&#xff08;智能网联汽车&#xff09;》的定义&#xff0c;智能网联汽车是指搭载先进的车载传感器 、控制器、执行器等装置&#xff0c;并融合现代通信与网络技术&a…

明道云联合思迈特打造会员管理应用可视化联合解决方案

背景介绍 明道云在协助企业数字化转型过程中&#xff0c;发现客户对利用业务数据形成企业级报表和数据可视化大屏的需求十分强烈。为了满足这种需求&#xff0c;企业通常需要成立专门的数据分析团队&#xff0c;但这需要巨大的人力和财力投入&#xff0c;时间周期也较长。为了…

信息数智化招采系统源码——信息数智化招采系统

信息数智化招采系统 服务框架&#xff1a;Spring Cloud、Spring Boot2、Mybatis、OAuth2、Security 前端架构&#xff1a;VUE、Uniapp、Layui、Bootstrap、H5、CSS3 涉及技术&#xff1a;Eureka、Config、Zuul、OAuth2、Security、OSS、Turbine、Zipkin、Feign、Monitor、Stre…

React 学习笔记总结(五)

文章目录1. React 嵌套路由(多级路由)2. params参数 与 query参数3. React路由组件 传递params参数数据4. React路由组件 传递search参数5. React路由组件 传递search参数6. React路由组件 特殊情况: 刷新页面7. React路由 的 push 和 replace8. React的 编程式路由9. React路由…

Transformer架构:位置编码

2017年&#xff0c;Google等人提出了Vaswani提出了一种新颖的纯注意力序列到序列架构&#xff0c;闻名学术界与工业界的Transformer架构横空出世&#xff0c;它的可并行化训练能力和优越的性能称为自然语言处理领域和计算机视觉领域研究人员的热门选择&#xff0c;本文将重点讨…

elasticsearch--Master选举

最近一直在学习elasticsear相关的东西&#xff0c;在这学习的过程中记录一下比较重要的学习内容。方便以后看的时候加深印象。 假如宕机的节点是Master节点 下面是Maste节点选举 的流程图 在findMaster的方法中每隔一段时间就会ping所有的节点&#xff0c;看看有没有哪个节…

java设计模式三

文章目录4) 创建IOC容器相关的类5) 自定义IOC容器测试6) 案例中使用到的设计模式7.2 剖析MyBatis框架中用到的经典设计模式7.2.1 MyBatis回顾7.2.1.1 MyBatis与ORM框架7.2.1.1 MyBatis的基础使用7.2.2 MyBatis中使用到的设计模式7.2.2.1 Builder模式7.2.2.2 工厂模式7.2.2.3 单…

基于Java开发(PC)小说自检测系统【100010061】

Java 语言与系统设计课程&#xff08;小说自检测系统&#xff09; 一、目的与要求 ​ 自行下载最喜爱的小说 1 部。存到服务器中&#xff0c;格式自定。一般存储为文本文档。要求长篇小说&#xff0c;20 万字以上。举例说明&#xff1a;下载《三国演义》保存在服务器端。 ​…

Secure Boot功能简析

在数据中心中&#xff0c;云服务器由各种处理设备和外围组件&#xff08;如加速器和存储设备&#xff09;组成&#xff0c;这些设备通常都运行着固件。对云平台服务商来说&#xff0c;为保证这些设备的安全可靠&#xff0c;需要一种或多种机制&#xff0c;来保证这些设备在测试…

XYplorer使用教程

XYplorer使用教程 XYplorer是Windows的文件管理器。它具有标签式浏览&#xff0c;强大的文件搜索功能&#xff0c;多功能预览&#xff0c;高度可定制的界面&#xff0c;可选的双窗格以及一系列独特的方法&#xff0c;可以有效地自动执行频繁重复的任务。它快速&#xff0c;轻便…