草庐IT

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

☞黑心萝卜三条杠☜ 2023-04-13 原文

题目

    十三届省赛是要制作一个可由串口设置密码的密码锁。在本场比赛中,我们将用到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);
	HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE);
}

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);
	HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE);
}

有关蓝桥杯嵌入式|第十三届蓝桥杯嵌入式省赛程序设计试题及其题解的更多相关文章

  1. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  2. Hive SQL 五大经典面试题 - 2

    目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类

  3. 蓝桥杯备赛(二) - 2

    目录前言: 一、ASC分析代码实现二、 卡片分析代码实现三、 直线分析代码实现四、货物摆放分析代码实现小结:前言:  在刷题的过程中,发现蓝桥杯的题目和力扣的差别很大。让人有一种不一样的感觉,蓝桥杯题目偏向对于实际问题用编程去的解决,而力扣给人感觉很锻炼自己的编程思维,逻辑能力。两者结合去刷,相信会有不一样的收获。 一、ASC  已知大写字母A的ASCII码为65,请问大写字母L的ASCII码是多少?分析  这道题目看上去很简单,我们需确定自己计算的准确,所以我建议用编程去解决。代码实现publicclassTest8{publicstaticvoidmain(String[]args){Sy

  4. 蓝桥杯C/C++VIP试题每日一练之报时助手 - 2

    ?作者主页:静Yu?简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者?社区地址:前端知识交流社区?博主的个人博客:静Yu的个人博客?博主的个人笔记本:前端面试题个人笔记本只记录前端领域的面试题目,项目总结,面试技巧等等。接下来会更新蓝桥杯官方系统基础练习的VIP试题,依然包括解题思路,源代码等等。问题描述:给定当前的时间,请用英文的读法将它读出来。时间用时h和分m表示,在英文的读法中,读一个时间的方法是:  如果m为0,则将时读出来,然后加上“o’clock”,如3:00读作“threeo’clock”。  如果m不为0,则将时读出来,然后将分读出来,如5

  5. 对于体育新闻中文文本关键字提取有哪些关键字提取算法及其步骤 - 2

    对于体育新闻中文文本的关键字提取,常用的算法包括TF-IDF、TextRank和LDA等。它们的基本步骤如下:1.TF-IDF算法: -将文本进行分词和词性标注处理。-统计每个词在文本中的词频(TF)。-计算每个词在整个语料库中出现的文档频率(DF)和逆文档频率(IDF)。-计算每个词的TF-IDF值,并按照值的大小进行排序,选择排名前几的词作为关键字。2.TextRank算法:-将文本进行分词和词性标注处理。-将分词结果转化成图模型,每个词语为节点,根据词语之间的共现关系建立边。-对图模型进行迭代计算,计算每个节点的PageRank值,表示该节点的重要性。-选择排名前几的节点作为关键字。3.

  6. 光度学中的能量、通量、出度、照度、强度、亮度参数及其联系 - 2

    光度学中的能量、通量、出度、照度、强度、亮度参数及其联系光度学中评价光的强弱有两种方式,一种是将光作为电磁波,考察其辐射的能量;另一种是以人眼视觉体验来评价光的强弱。前者被称为辐射量,后者被称为光学量。辐射量包括辐射能、辐通量、辐出量、辐照度、辐强度、辐亮度参数,与之相对应,光学量包括光能量、光通量、光出量、光照度、光强度、光亮度参数。通过该文章的阅读,读者还能掌握光学中的几个单位:流明,勒克斯,坎德拉,尼特的意义以及他们之间的关系。辐射量1.辐射能光以电磁波形式发射、传输或接收的能量。单位:焦耳。2.辐通量单位时间发射、传输和接收的辐射能。单位:瓦特。3.辐出度单位面积的辐射源辐射出的辐通量

  7. ruby-on-rails - Rails 计数器缓存及其实现 - 2

    我正在尝试掌握Rails计数器缓存功能,但无法完全掌握它。假设我们有3个模型ABCA属于B或C,取决于字段key_type和key_id。key_type表示A属于B还是C,因此如果key_type="B"则记录属于B,否则属于C。在我的模型a.rb中,我定义了以下关联:belongs_to:b,:counter_cache=>true,:foreign_key=>"key_id"belongs_to:c,:counter_cache=>true,:foreign_key=>"key_id"和在b和c模型文件中has_many:as,:conditions=>{:key_type=>"

  8. ruby-on-rails - 如何在 RoR 中使用 content_tag 嵌入标签? - 2

    我有这个可以为我生成一个超链接:我希望它显示在td标签中,所以我想使用这个content_tag来帮助我:"example")%>我想要我的td中的超链接,所以我有这样的东西:,:class=>"example")%>但是我收到语法错误,我该怎么办? 最佳答案 内联:'example')%>或block形式:'example')do%> 关于ruby-on-rails-如何在RoR中使用content_tag嵌入标签?,我们在StackOverflow上找到一个类似的问题:

  9. 十四届蓝桥青少组模拟赛Python-20221108 - 2

    十四届蓝桥青少组模拟赛Python-20221108T1.二进制位数十进制整数2在十进制中是1位数,在二进制中对应10,是2位数。十进制整数22在十进制中是2位数,在二进制中对应10110,是5位数。请问十进制整数2022在二进制中是几位数?print(len(bin(2022))-2)#运行结果:11T2.晨跑小蓝每周六、周日都晨跑,每月的1、11、21、31日也晨跑。其它时间不晨跑。已知2022年1月1日是周六,请问小蓝整个2022年晨跑多少天?#样例代码1ls=[0,31,28,31,30,31,30,31,31,30,31,30,31]ans=0k=6foriinrange(1,13)

  10. 华为OD机试 -旋转骰子(Python) | 机试题算法思路 【2023】 - 2

    最近更新的博客华为OD机试-卡片组成的最大数字(Python)|机试题算法思路华为OD机试-网上商城优惠活动(一)(Python)|机试题算法思路华为OD机试-统计匹配的二元组个数(Python)|机试题算法思路华为OD机试-找到它(Python)|机试题算法思路华为OD机试-九宫格按键输入(Python)|机试算法备考思路华为OD机试-身高排序(Python)|备考思路使用说明参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。华为OD清单查看地址:blog.csdn.net/hihell/catego

随机推荐