蓝桥杯嵌入式第十二届初赛题目解析
把蓝桥杯嵌入式第十二届的题目写完了,拿出来和大家一起分享。
目录
客观题
程序设计题
题目解析:
CubeMX配置
代码演示
客观题
收集的一些历年的比赛客观题和解析,以及程序设计题的PDF,在这里分享给大家。
链接:https://pan.baidu.com/s/1hTw0inSbLjX57hOtankgKw
此次客观题比较简单,都是基础知识,又不理解的地方,可以在评论区留言。
程序设计题
题目解析
程序设计题用到的模块为LCD,LED,按键,串口,PWM,也都是非常熟悉的模块了。
其中PWM主要用到了一个用来改变PWM占空比的函数。
//修改PWM的占空比(CCRX)
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 100);
注意:函数的参数是根据自动重装载值来设置的,例如题目要求PA7输出2KHz,占空比为20%,我设置的自动重装载值是500,那么500的20%是100,所以函数中最后一个参数填100
而串口主要是处理接收的字符串,就可以使用sscanf()函数来切割字符串,以及重写printf()函数。
函数原型:int sscanf(const char *str, const char *format, ...)
详细用法感兴趣的可以百度一下,简单说一下这个函数的用法是从字符串读取格式化输入,其实和scanf()用法差不多,举个例子吧 sscanf(rxarr,"%4s:%4s:%12s",TempCar.type,TempCar.id,TempCar.time);以上用法是,从串口接收的字符串rxarr假如是“CNBR:A392:200202120000”,那么就把前4个字符转化成字符串赋值给TempCar.type,把中间4个字符转化成字符串赋值给TempCar.id,后面12个字符转化成字符串赋值给TempCar.time
//重定义printf函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
这次程序设计题,最主要是最难的就是停车计费,也就是时间的换算。我的想法是以一个过去的时间点为起始时间如2020年1月1日0点0分0秒,然后计算入库时间和出库时间距离起始时间有多少秒钟,之后在计算具体实现看代码吧。这样比较繁琐,但还没找到比较好的方法,如果有更好的方法也可以在评论区交流喔。
说完了解题思路就开始配置CubeMX吧。
CubeMX配置
时钟配置完了,需要按下回车(Enter)来保存。
根据原理图配置GPIO引脚,其中lcd和led的引脚都设置为output,按键设置为input,串口使用USART1也就是PA10和PA9,PWM是PA7设置为定时器17的第一通道,需要把PD2也设置为output用来作为led的锁存器。
在GPIO中选中按键的引脚,设置为上拉输入模式。
在GPIO中,选中led的引脚,设置为初始状态为高电平,推挽输出模式,既不上拉也不下拉。其他引脚使用默认设置就是行。
定时器17的通道1设置为PWM通道1,然后设置预分频器值和自动重装值以及占空比,初始默认是低电平,那就把占空比设置为0。
设置定时器3每10ms中断一次。
设置串口1的波特率为9600,开启中断,其他默认即可。
设置项目名字和保存路径(建议不要有中文),以及IDE的版本。
勾选这个主要是让.c和.h文件单独分开,之后就可以生成代码了,CubeMX配置就完成了,如果之后想要添加新的模块或者修改配置好了模块的值,可以直接在文件中打开CubeMX的工程进行修改,改完后再点击GENERATE CODE就行了。
代码演示
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "show.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
const float EPSINON = 0.00001;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t rxdata; //串口接收数据
extern uchar btn; //按键值
extern uchar num; //有多少辆车进入车库
extern bool L1; //LED1 标识
extern bool L2; //LED2 标识
bool jm = 0; //界面切换标识
bool Duty_Flag = 0; //占空比切换标识
float CNBR = 3.50f, VNBR = 2.00f; //初始停车费
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM3_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
HAL_UART_Receive_IT(&huart1, &rxdata, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
switch(btn)
{
case 1:
{
jm ^= 1;
btn = 0;
LCD_Clear(Black);
}
break;
case 2:
{
if(1 == jm)
{
CNBR += 0.50f;
VNBR += 0.50f;
}
btn = 0;
}
break;
case 3:
{
if(1 == jm)
{
if((CNBR - 0.50f)<=EPSINON) //浮点数不能直接与'0'值比较
CNBR = 0.00f;
else
CNBR -= 0.50f;
if((VNBR - 0.50f)<=EPSINON)
VNBR = 0.00f;
else
VNBR -= 0.50f;
}
btn = 0;
}
break;
case 4:
{
Duty_Flag ^= 1;
if(Duty_Flag)
{
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 100);
L2 = 1;
}
else
{
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 0);
L2 = 0;
}
btn = 0;
}
break;
}
if(num<8)
L1 = 1;
else
L1 = 0;
LED_Hint();
UART_RX();
if(0 == jm)
Data();
else
Para();
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
main.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32g4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
#define uchar unsigned char
#define uint unsigned int
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
main.h中添加了几个头文件和define。其中注意当停车费用不能为负值,但是浮点数不能直接与'0'值相比较。
key.c
#include "key.h"
Btn key[4];
uchar btn = 0;
void KEY_Scan(void)
{
uchar i = 0;
key[0].press = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key[1].press = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key[2].press = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key[3].press = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
for(i=0;i<4;i++)
{
switch(key[i].state)
{
case 0:
if(0 == key[i].press)
key[i].state = 1;
break;
case 1:
if(0 == key[i].press)
{
key[i].state = 2;
btn = i+1;
}
else
key[i].state = 0;
break;
case 2:
if(1 == key[i].press)
key[i].state = 0;
break;
}
}
}
key.h
#ifndef __KEY_H
#define __KEY_H
#include "main.h"
typedef struct{
bool press;
uchar state;
}Btn;
void KEY_Scan(void);
#endif
led.c
#include "led.h"
bool L1;
bool L2;
uint32_t LED = 0xFF00;
void LED_SET(void)
{
GPIOC->ODR = LED;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
void LED_Hint(void)
{
if(1 == L1)
LED &= ~(0x01FF);
else
LED |= 0x0100;
if(1 == L2)
LED &= ~(0x02FF);
else
LED |= 0x0200;
LED_SET();
}
led.h
#ifndef __LED_H
#define __LED_H
#include "main.h"
void LED_SET(void);
void LED_Hint(void);
#endif
led使用寄存器,目的是单独控制一个LED灯时不干扰其他LED灯,使用HAL库函数,改变一个灯的值,会干扰到其他灯的显示,有没有好心人在评论区告知一下怎么使用HAL库才不会有这种情况,感谢。
show.c
#include "show.h"
const uchar mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
char text[21];
uchar C = 0,V = 0; //CNBR类型的车已入库多少辆,VNBR类型的车已入库多少辆
CarData Car[NUM] = {0};
uchar num = 0; //有多少辆车进入车库
extern float CNBR,VNBR;
extern uchar arrow;
extern char rxarr[23];
void LCD_Print(uchar line, char *text)
{
LCD_DisplayStringLine(line*24, (u8 *)text);
}
//检查车辆是否入库
uchar Check_Car(const char *temp_id)
{
uchar i = 0;
for(i=0;i<NUM;i++)
{
if(!(strcmp(temp_id,Car[i].id)))
return i;
}
return NUM;
}
//判断是否是闰年
uchar IsLeapYear(int year)
{
if((year%4 == 0 && year%100 == 0) || (year%400 == 0))
return 1;
else
return 0;
}
//把日期换算成秒
uint32_t Time(int year,int month,int day,int hour,int minute,int second)
{
uint32_t time;
uchar i;
for(i=20;i<year;i++)
{
if(IsLeapYear(2000+year))
time += 366*24*60*60;
else
time += 365*24*60*60;
}
month -= 1;
for(i=0;i<month;i++)
{
time += (uint32_t) mon_table[i]*24*60*60;
if(IsLeapYear(2000+year)&&i==1)
time += 24*60*60;
}
time += (uint32_t) (day-1)*24*60*60;
time += (uint32_t) hour*60*60;
time += (uint32_t) minute*60;
time += (uint32_t) second;
return time;
}
//计算停车时间
uint Count_Time(const char *temp_time, uchar temp)
{
Date tempdate,date;
uint32_t Ptime,Ctime;
uint Stime;
sscanf(temp_time, "%2d%2d%2d%2d%2d%2d",\
&tempdate.year,&tempdate.month,&tempdate.day,&tempdate.hour,&tempdate.minute,&tempdate.second);
sscanf(Car[temp].time, "%2d%2d%2d%2d%2d%2d",\
&date.year,&date.month,&date.day,&date.hour,&date.minute,&date.second);
Ctime = Time(tempdate.year,tempdate.month,tempdate.day,tempdate.hour,tempdate.minute,tempdate.second);
Ptime = Time(date.year,date.month,date.day,date.hour,date.minute,date.second);
if(Ctime<=Ptime)
return 0;
else
{
Stime = (Ctime-Ptime)/3600;
if((Ctime-Ptime)%3600)
return Stime+1;
else
return Stime;
}
}
//计算停车费用
double Count_Fare(uint SumTime, uchar temp)
{
if(!strcmp(Car[temp].type,"CNBR"))
return SumTime*CNBR;
else
return SumTime*VNBR;
}
//寻找停车位置
uchar Find_Location(void)
{
uchar i;
for(i=0;i<NUM;i++)
{
if(!Car[i].flag)
break;
}
return i;
}
//处理串口发送的数据
void StringHandle(void)
{
if(arrow>0)
{
if(22 == arrow)
{
uchar temp = 0;
CarData TempCar;
sscanf(rxarr, "%4s:%4s:%12s",TempCar.type,TempCar.id,TempCar.time);
temp = Check_Car(TempCar.id);
if(temp<NUM) //车已入库
{
int SumTime;
if(!(strcmp(TempCar.type,Car[temp].type))) //检查入库车辆与出库车辆的类型是否相同
{
SumTime = Count_Time(TempCar.time, temp); //计算时间是否正确
if(SumTime)
{
printf("%4s:%4s:%d:%.2lf\r\n",Car[temp].type, Car[temp].id, SumTime, Count_Fare(SumTime, temp));
memset(&Car[temp],0,sizeof(Car[0])); //车辆出库,清除数据
num--;
if(!strcmp(TempCar.type,"CNBR"))
C--;
else if(!strcmp(TempCar.type,"VNBR"))
V--;
}
else
printf("Error\r\n");
}
else
printf("Error\r\n");
}
else //车没入库
{
if(num<NUM) //车库没满
{
uchar L;
L = Find_Location(); //寻找车位
if(!strcmp(TempCar.type,"CNBR"))
{
TempCar.flag = 1;
Car[L] = TempCar;
C++;
num++;
}
else if(!strcmp(TempCar.type,"VNBR"))
{
TempCar.flag = 1;
Car[L] = TempCar;
V++;
num++;
}
else
printf("Error\r\n");
}
else
printf("Error\r\n");
}
}
else
printf("Error\r\n");
}
arrow = 0;
memset(rxarr, 0, sizeof(rxarr));
}
void Data(void)
{
sprintf(text, " Data ");
LCD_Print(1, text);
sprintf(text, " CNBR:%d ",C);
LCD_Print(3, text);
sprintf(text, " VNBR:%d ",V);
LCD_Print(5, text);
sprintf(text, " IDLE:%d ",NUM-C-V);
LCD_Print(7, text);
}
void Para(void)
{
sprintf(text, " Para ");
LCD_Print(1, text);
sprintf(text, " CNBR:%.2f ",CNBR);
LCD_Print(3, text);
sprintf(text, " VNBR:%.2f ",VNBR);
LCD_Print(5, text);
}
show.h
#ifndef __SHOW_H
#define __SHOW_H
#include "main.h"
#include "lcd.h"
#define NUM 8
typedef struct{
char type[5]; //车类型
char id[5]; //车id
char time[13]; //入库时间
bool flag; //入库标志
}CarData;
typedef struct{
int year;
int month;
int day;
int hour;
int minute;
int second;
}Date;
void LCD_Print(uchar line, char *text);
uchar IsLeapYear(int year);
uint32_t Time(int year,int month,int day,int hour,int minute,int second);
uint Count_Time(const char *temp_time, uchar temp);
double Count_Fare(uint SumTime, uchar temp);
uchar Find_Location(void);
void StringHandle(void);
void Data(void);
void Para(void);
#endif
把停车时间都转化成秒钟,在判断是否合理,然后相减计算时间,不足一小时按一小时算。
timer.c
#include "timer.h"
uint8_t rxdata;
uchar arrow = 0;
char rxarr[23];
//中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(TIM3 == htim->Instance)
{
KEY_Scan();
}
}
//串口中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(USART1 == huart->Instance)
{
rxarr[arrow++] = rxdata;
HAL_UART_Receive_IT(&huart1, &rxdata, 1);
}
}
//判断串口有没有接受完数据
void UART_RX(void)
{
uchar temp = 0;
if(arrow != 0)
{
temp = arrow;
HAL_Delay(1);
if(temp == arrow)
StringHandle();
}
}
//重定义printf函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "main.h"
#include "usart.h"
#include "key.h"
#include "show.h"
void UART_RX(void);
#endif
以上就是我修改过的文件和新添加的文件。还有lcd模块,不过不需要我们自己写,官方有提供,直接复制过来就行,注意有三个文件,别只复制lcd.c和lcd.h。
好了,以上就是蓝桥杯嵌入式第十二届省赛的题目解析了,如果有什么问题和建议都欢迎在评论区提出来喔。