【蓝桥杯嵌入式】第十一届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解

news2024/12/24 2:51:30

题目再现

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题目分析

    通过阅读本届试题可知,其功能比较单一,除了试题中常客——LED、LCD、按键三巨头外,还包含了定时器的PWM以及ADC读取这两个部分,考察的重点在于对定时器产生PWM的应用以及如何修改PWM的占空比,完全消化这套试题后对PWM的理解会更加精进。

详细题解

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

  • 由于LCD与LED有部分引脚是共用的,因此初始化完成LCD后,最好手动关闭LED
  • 由于每次LCD显示的长度可能不同,因此在本次显示前,要不先清屏,要不跟上次显示一样长

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;
}

LCD模块

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

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

定时器输出PWM波

CubeMX配置
在这里插入图片描述
在这里插入图片描述
题目中要求选手修改占空比,那么我们直接使用HAL库提供的函数修改即可。

__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,100);

其参数1表示定时器的名称,参数2表示定时器通道,参数3表示占空比。(PWM中占空比的取值范围为0~100)

使用ADC读取电位器R37的值

    由于题目中所用的开发板是STM32F103RBT6,但是小编实际使用的是STM32G431RBT6,所以会存在引脚不一样的情况。(因为蓝桥杯改革了,所以现在的比赛使用的开发板会跟之前有点不同)
    在G431开发板上,与电位器R37相连接的引脚是PB15,因此我们就使用PB15作为ADC的引脚来初始化。

CubeMX配置
在这里插入图片描述
读取ADC的值

    在这里需要注意的是,如果需要连续读取ADC的值,在相邻的两次读取中,最好留一点缓冲时间,否则会对数据产生影响。

/*******************************************************************
* 函数功能:获取ADC的值
* 函数参数:
* 			ADC_HandleTypeDef *hadc:ADC的通道值
* 函数返回值:
* 			double:转换后的ADC值
*******************************************************************/
double getADC(ADC_HandleTypeDef *hadc)
{
	unsigned int value = 0;
	//开启转换ADC并且获取值
	HAL_ADC_Start(hadc);
	value = HAL_ADC_GetValue(hadc);
	//ADC值的转换 3.3V是电压 4096是ADC的精度为12位也就是2^12=4096
	return value*3.3/4096;
}

完整的工作配置文件

config.c文件

    sysInit函数是自定义外加的系统配置文件,sysWork函数是配置的系统工作函数。使用时,直接将sysInit函数添加到CubeMx初始化后,将sysWork函数在while(1)函数中调用。

#include "config.h"

//定义存储显示数据的结构体
struct displayData*showData;

//用于计时
unsigned int count = 0;

/***********************************************
* 函数功能:自定义的系统初始化函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysInit(void)
{
	//LCD初始化
	lcdInit();
	//关闭LED
	changeAllLedByStateNumber(OFF);
	
	//打开定时器产生PWM的通道
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
	
	//初始化结构体
	showData = (struct displayData*)malloc(sizeof(struct displayData));
	showData->frdP6 = 10;
	showData->frdP7 = 10;
	showData->mode = 0;
	showData->vValue = 0;
	showData->page = 0;
}

/***********************************************
* 函数功能:自定义的系统工作函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysWork(void)
{
	unsigned char keyNumber = scanKey();
	switch(keyNumber)
	{
		//切换显示界面
		case 1:
			showData->page++;
			break;
		//手动模式  参数界面 修改PA6的参数
		case 2:
			if(showData->mode%2==1 && showData->page%2==1)
				showData->frdP6 += 10;
			if(showData->frdP6 == 100)
				showData->frdP6 = 10;
			break;
		//手动模式  参数界面 修改PA7的参数
		case 3:
			if(showData->mode%2==1 && showData->page%2==1)
				showData->frdP7 += 10;
			if(showData->frdP7 == 100)
				showData->frdP7 = 10;
			break;
		//切换模式
		case 4:
			showData->mode++;
			break;
		default:break;
	}
	
	//获取ADC的值 降低获取ADC值的频率
	if(count++ %5 == 0)
		showData->vValue = getADC(&hadc2);
	//切换PWM的频率
	changePwmFrd();
	//LCD显示
	display();
	//LED显示
	ledWork();
}

/***********************************************
* 函数功能:更改PWM占空比函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void changePwmFrd(void)
{
	//手动模式
	if(showData->mode%2 == 1)
	{
		__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,showData->frdP6);
		__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,showData->frdP7);
	}
	//自动模式 电压值为0V
	else if(showData->mode%2 == 0 && showData->vValue == 0)
	{
		__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,0);
		__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,0);
	}
	//自动模式  电压值为3.3V 
	else if(showData->mode%2 == 0 && showData->vValue == 3.3)
	{
		__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,100);
		__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,100);
	}
}

/***********************************************
* 函数功能:LED工作函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void ledWork(void)
{
	//关闭所有的LED灯
	changeAllLedByStateNumber(OFF);
	
	//自动模式
	if(showData->mode%2 == 0)
		changeLedStateByLocation(LED1,ON);
	
	//数据界面
	if( showData->page%2 == 0 )
		changeLedStateByLocation(LED2,ON);
}

/***********************************************
* 函数功能:显示函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void display(void)
{
	char temp[10];
	//数据显示界面
	if( showData->page%2 == 0 )
	{
		LCD_DisplayStringLine(Line0,(uint8_t*)"       Data");
		sprintf(temp,"     V:%.2fV",showData->vValue);
		LCD_DisplayStringLine(Line2,(uint8_t*)temp);
		//自动模式
		if(showData->mode%2 == 0)
			LCD_DisplayStringLine(Line4,(uint8_t*)"     Mode:AUTO     ");
		//手动模式
		else
			LCD_DisplayStringLine(Line4,(uint8_t*)"     Mode:MANU     ");		
	}
	//显示参数界面
	else if( showData->page%2 == 1)
	{
		LCD_DisplayStringLine(Line0,(uint8_t*)"       Pata");
		sprintf(temp,"    PA6:%d%%        ",showData->frdP6);
		LCD_DisplayStringLine(Line2,(uint8_t*)temp);
		sprintf(temp,"    PA7:%d%%        ",showData->frdP7);
		LCD_DisplayStringLine(Line4,(uint8_t*)temp);
	}
}

福利

下边是小编个人整理出来免费的蓝桥杯嵌入式福利,有需要的童鞋可以自取哟!🤤🤤🤤
也欢迎大家留言或私信交流,共同进步哟!😉😉😉

【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式省赛程序设计试题以及详细题解
【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛程序设计试题及其详细题解
【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解

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

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

相关文章

MPU-6000(6050)介绍

MPU-6000&#xff08;6050&#xff09;简介MPU-60X0是全球首例9 轴运动处理传感器。它集成了3 轴MEMS陀螺仪&#xff0c;3 轴MEMS加速度计&#xff0c;以及一个可扩展的数字运动处理器DMP&#xff08;DigitalMotion Processor&#xff09;&#xff0c;可用I2C接口连接一个数字传…

react PureComponent

这个 其实和我之前写的文章react组件优化&#xff0c;当父组件数据变化与子组件无关时&#xff0c;控制子组件不重新渲染实现的东西是一样的 只是用了一种更简洁的方式 我们还是重新来一次 创建一个react项目 然后 创建一个子组件 我这里就直接叫 subset.jsx 参考代码如下 i…

ADAM: A METHOD FOR STOCHASTIC OPTIMIZATION

核心 Adam: 一种基低阶矩的自适应估计的随机目标函数的一阶梯度优化算法&#xff0c;该方法实现简单**&#xff0c;计算效率高&#xff0c;内存需求很少**&#xff0c;对梯度的对角线重新缩放不变&#xff0c;并且非常适合于在数据或参数方面较大问题&#xff0c;该方法也适用…

ADI demo PS工程的编译-以adrv9371x_zc706为例子

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 ADI demo PS工程的编译-以adrv9371x_zc706为例子前言VITIS建工程总结前言 接《ADI demo PL工程的编译-以adrv9371x_zc706为例子之使用Cygwin》这篇。导出XSA文件&#xff0c…

VMware虚拟机搭建安装MacOS13及开发环境搭建

文章目录前言准备工作&#xff1a;安装虚拟机及MacOS系统1.安装VMware 虚拟机2. 运行解锁工具3. 安装macOS 134. 配置网络5. 安装VMware tools开发环境1. 安装xcode2. 安装HbuilderX前言 终于把macOS13虚拟机安装好了&#xff0c;比起上次安装macOS10,这次走了很多弯路。 先说…

108. 将有序数组转换为二叉搜索树

108. 将有序数组转换为二叉搜索树 难度简单1214 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&…

春节期间商城如何做好运营

有经验的商家都知道&#xff0c;春节对于店铺来说&#xff0c;这是一个全新的起点&#xff0c;所以商家们一定要抓住新年这个过渡期的时机。那么在春节期间的运营该如何做呢&#xff1f; 首先看看春节期间会有的几个问题&#xff1a; 推广竞争会比较小&#xff0c;在这个时间…

git使用日常问题记录【按日期频繁记录,欢迎收藏】

git系列文章 文章目录2023.1.14 将gitee仓库转移到github遇到的问题1-1 add到暂存区&#xff0c;commit到仓库之后&#xff0c;不想push了&#xff1f;&#xff1f;&#xff1f;1-2 git push大文件失败解决1-3 上传多余文件&#xff0c;如何从远端仓库中删除2023.1.151-1 githu…

销售管理系统 | 数据库课设

文章目录前言项目介绍E-R图表结构系统总体框架搭建项目环境介绍创建网站主页连接数据库注册功能登录功能管理员登录功能注销登录功能个人信息后台管理查看供应商名单删除功能修改功能登记货物信息功能购买商品功能总源码教训总结前言 为了期末的数据库课设&#xff0c;这是最初…

【C++】STL - Stack - Queue - PriorityQueue使用和模拟实现

&#x1f431;作者&#xff1a;傻响 &#x1f431;专栏&#xff1a;《数据结构_STL》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 栈 Stack介绍 模拟实现 队列 Queue介绍 常用的函数接口介绍 模拟实现 优先级队列 Priority…

【docker18】docker容器之CAdvisor+InfluxDB+Granfana

1.原生命令 1.1操作 命令&#xff1a; docke stats 1.2问题 通过docker stats命令可以很方便的看到当前宿主机上所有容器的CPU&#xff0c;内存以及网络流量控制等数据&#xff0c;一般的小公司够用了。 但是&#xff0c;docker stats统计结果只能是当前宿主机的全部容器&am…

669. 修剪二叉搜索树

669. 修剪二叉搜索树 难度中等 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&#xff0c…

SourceTree使用方法总结

SourceTree使用方法总结 SourceTree使用总结 添加仓库 mac下从url克隆&#xff1a; windows下从url克隆&#xff1a; 抓取、获取分支信息 抓取&#xff08;mac下的名字&#xff09;获取&#xff08;Windows下的名字&#xff09;指获取服务端git库的变更信息&#xff0c;比如…

基于FPGA的UDP 通信(五)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 基于FPGA的UDP 通信&#xff08;四&#xff09; 本文基于FPGA设计千兆以太网通信模块UDP数据发…

12.I/O复用

I/O复用 多进程方式跳过 基于I/O复用的服务器端 接下来讨论并发服务器实现方法的延伸。如果有读者已经跳过第10章和第11章&#xff0c;那就只需把本章内容当做并发服务器实现的第一种方法即可。将要讨论的内容中包含一部分与多进程服务器端的比较&#xff0c;跳过第10章和第…

Android WebView中H5调用Android原生方法

最近做项目&#xff0c;使用webView看一些网页&#xff0c;和网页开发一起找什么方法进行交互&#xff0c;还好解决&#xff0c;分享一下经验。 对于webView的使用就不写了&#xff0c;百度大法好&#xff0c;主要是交互方面&#xff0c;对WebView增加以下代码&#xff1a; bi…

五个了解自己天赋优势的分析工具(一)霍兰德兴趣测试

霍兰德兴趣测试 霍兰德职业兴趣自测&#xff08;Self-Directed Search&#xff09;是由美国职业指导专家霍兰德&#xff08;John Holland&#xff09;根据他本人大量的职业咨询经验及其职业类型理论编制的测评工具。 霍兰德认为&#xff0c;个人职业兴趣特性与职业之间应有一…

74、Beyond RGB: Scene-Property Synthesis with Neural Radiance Fields

简介 List item 论文地址&#xff1a;http://arxiv-export3.library.cornell.edu/abs/2206.04669v1 利用隐式三维表示和神经渲染的最新进展&#xff0c;从综合模型的角度提供了一种新的场景理解方法&#xff0c;能够从新颖的视点渲染照片逼真的RGB图像&#xff0c;而且还能够…

我们怎样才能过好这一生?

文章目录1. 日拱一卒&#xff0c;功不唐捐1.1 适当的时候给自己一个奖励1.2 一个人可能走的更快&#xff0c;但一群人才能走的更远1.3 通过一些事情去逼自己一把1.4 从真理中去感悟1.5 当你面临绝路时2. 梦想的意义不在于实现3. 孤独4. 烦恼5. 别总说来日方长6. 忍和韧性7. 事情…

低成本搭建一台家庭存储服务器:前篇

本篇文章&#xff0c;记录搭建备份服务器的过程。 写在前面 今年考虑专门搭建一台用于数据备份的机器&#xff0c;一来今年外出的需求比较多&#xff0c;历史的设备已经用了几年了&#xff0c;需要有更新的设备来“接力”&#xff1b;二来也想验证方案的靠谱程度&#xff0c;…