蓝桥杯嵌入式组第十二届省赛题目解析+STM32G431RBT6实现源码
STM32G431RBT6实现嵌入式组第十二届题目解析+源码。文章末尾有第十二届题目。
文章目录
前言:STM32G431RBT6实现嵌入式组第十二届题目解析+源码,本文默认读者具备基础的stm32知识。文章末尾有第十二届题目。
1.题目解析
第十二届虽说题目长,难度集中体现在uart接收的数据处理上。
1.1 分而治之,藕断丝连
还是那句话,将不同模块进行封装,通过变量进行模块间的合作。
函数将模块分而治之,变量使模块间藕断丝连。
1.2 模块化思维导图
下图根据题目梳理。还是使用思维导图。
1.3 模块解析
1.3.1 KEY模块
还是控制按一次处理一次。老朋友了我们就不多说了,题目限制了按键消抖和单次处理,所以我们要加上消抖,和第十一届的处理一模一样。
具体实现看源码
1.3.2 LED模块
ld1:有空闲车位亮,否则灭
ld2:PWM占空比20%输出亮,输出低电平灭
解决办法,设置一个标志位代表ld1~ld8,改变对应位的的值,再将标志位写入ODR寄存器中来控制led的亮灭。
具体实现看源码
1.3.3 LCD模块
lcd显示两个界面,注意首次切换的时候得清屏。
根据B1界面1和界面2切换;
状态0:参数界面;
状态1:费用设置界面;
具体实现看源码
1.3.4 TIM模块
TIM2产生1s时基。PSC:16999,ARR:9999;
TIM17通道1产生2kHzPWM。PSC:16,ARR:4999;
PSC和ARR计算公式(计算周期就是频率的倒数):
/* 定时开启uart接收中断1s */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_UARTEx_ReceiveToIdle_IT(&huart1, uart_rx_data, 24);
}
/* pa7pwm输出 */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(pa7_pwm_ctrl == 1) TIM17->CCR1 = 999;
else TIM17->CCR1 = 0;
}
1.3.5 UART模块
12届题目的难度就在uart的数据处理上。
1.单片机接收来自电脑固定格式的数据,我们就需要数据限制条件来写解析接收的数据,数据长度,停车类别,车牌格式,时间格式。
2.如果格式错误返回Error,判断是停车还是取车。
2.1 应该先判断取车,这大家都能理解。如果是取车,计算收费,时间,返回给电脑。
2.2 如果是存车,先要判断是否有空闲车位,如果有存车,如果没有不做处理。
具体其他涉及函数代码请看源码
1.3.5.1 uart数据解析
我们可以使用指针加for单个字符判断,也可以使用string.h库中的字符处理函数,strcmp(), strcpy(),strncmp(), strncpy()等函数,将数据先切段,再通过各段的限制条件进行格式判断。
//解析串口数据
uint8_t analyze_rx_data()
{
char copy_data[23] = {0};
char *p = copy_data;
for(uint8_t i=0; uart_rx_data[i] != '\0'; i++) copy_data[i] = uart_rx_data[i]; //拷贝数据
if(strlen(copy_data) == 22){
//前五位“*NBR:”
if(copy_data[0] == 'C' || copy_data[0] == 'V' && !strncmp(copy_data+1, "NBR",3)) analyze_car.park_type = copy_data[0];
else return 1;
if(*(p+4) != ':') return 1;
p+=5;
uint8_t j=0;
//后五位"****:"
for(;*p!=':';p++){
if(*(p) < 0 || *(p) > 128) return 1;
analyze_car.car_num[j] = *(p);
j++;
if(j>4) return 1;
}
analyze_car.car_num[4] = '\0';
//判断是否是出库
for(uint8_t i=0;i<8;i++){
if(!strncmp(car[i].car_num, analyze_car.car_num, 4)){
analyze_car.car_state = i+1;
break;
}
}
//判断第二个":"
if(*p==':'){
sscanf(p, ":%2d%2d%2d%2d%2d%2d", &analyze_car.time.year, &analyze_car.time.date, &analyze_car.time.day,
&analyze_car.time.hour, &analyze_car.time.minute, &analyze_car.time.second);
//判断时间格式
if(analyze_car.time.date>12 || analyze_car.time.day >30 || analyze_car.time.hour > 23 ||
analyze_car.time.minute>59 || analyze_car.time.second >59) return 1;
return 0;
}
}
return 1;
}
2.源码
我所有的实现都在main.c文件中。
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 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 "stdio.h"
#include "lcd.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 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 */
typedef enum{ //按键的四种状态
key_released = 0U,
key_reduction,
key_pressed,
key_wait
}KEY_STATE;
KEY_STATE key_state[4] = {0}; //按键状态
uint8_t key_volt[4] = {0}; //按键电平
uint32_t key_redu = 0; //消抖时间戳
typedef struct{ //存储串口解析数据
uint8_t car_state; //停车或出库
char park_type; //V或C
char car_num[5]; //车牌号
struct{ //时间记录
int year;
int date;
int day;
int hour;
int minute;
int second;
}time;
}ANALYZE_RX_DATA;
ANALYZE_RX_DATA car[8] = {0}, analyze_car = {0};
uint8_t CVI[3] = {0, 0, 8}; //界面1三数据
//串口接收和发送数据
char uart_tx_data[30] = {0};
uint8_t uart_rx_data[23] = {0};
void gain_key_state() //获取按键状态
{
key_volt[0] = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key_volt[1] = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key_volt[2] = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key_volt[3] = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
for(uint8_t i=0;i<4;i++){
if(!key_volt[i]){
switch(key_state[i]){
case key_released:
key_redu = uwTick;
key_state[i] = key_reduction;
break;
case key_reduction:
if(uwTick - key_redu >= 10){
key_state[i] = key_pressed;
}
break;
case key_pressed:
key_state[i] = key_wait;
break;
case key_wait:
break;
}
}
else {
switch(key_state[i]){
case key_reduction:
if(uwTick - key_redu >= 10){
key_state[i] = key_released;
}
break;
case key_released:
break;
default:
key_redu = uwTick;
key_state[i] = key_reduction;
break;
}
}
}
}
//lcd标志位,pwm标志位
uint8_t lcd_conv_flag = 0, pwm_conv_flag = 0;
float CV_NBR[2] = {3.5,2.0}; //停车收费
void key_process() //按键处理
{
if(key_state[0] == key_pressed){
lcd_conv_flag ^= 1;
}
else if(key_state[1] == key_pressed && lcd_conv_flag==1){
CV_NBR[0] += 0.5;
CV_NBR[1] += 0.5;
}
else if(key_state[2] == key_pressed && lcd_conv_flag==1){
CV_NBR[0] -= 0.5;
CV_NBR[1] -= 0.5;
if(CV_NBR[1] < 0.0){
CV_NBR[0] = 1.5;
CV_NBR[1] = 0.0;
}
}
else if(key_state[3] == key_pressed){
pwm_conv_flag ^= 1;
TIM3->CCR2 = pwm_conv_flag == 1 ? (uint32_t)TIM3->ARR*0.2 : 0;
}
}
uint8_t lcd_clear_flag = 0;
char lcd_str[21] = {0};
void lcd_process()
{
switch(lcd_conv_flag){
case 0:
if(lcd_clear_flag == 1){
lcd_clear_flag = 0;
LCD_Clear(Black);
}
LCD_DisplayStringLine(Line1, (uint8_t*)" Data ");
sprintf(lcd_str, " CNBR:%hhu ", CVI[0]);
LCD_DisplayStringLine(Line3, (uint8_t*)lcd_str);
sprintf(lcd_str, " VNBR:%hhu ", CVI[1]);
LCD_DisplayStringLine(Line5, (uint8_t*)lcd_str);
sprintf(lcd_str, " IDLE:%hhu ", CVI[2]);
LCD_DisplayStringLine(Line7, (uint8_t*)lcd_str);
break;
case 1:
if(lcd_clear_flag == 0){
lcd_clear_flag = 1;
LCD_Clear(Black);
}
LCD_DisplayStringLine(Line1, (uint8_t*)" Para ");
sprintf(lcd_str, " CNBR:%.2f ", CV_NBR[0]);
LCD_DisplayStringLine(Line3, (uint8_t*)lcd_str);
sprintf(lcd_str, " VNBR:%.2f ", CV_NBR[1]);
LCD_DisplayStringLine(Line5, (uint8_t*)lcd_str);
break;
}
}
uint8_t led_flag = 0;
void led_process()
{
if(CVI[2] > 0) led_flag = 1;
else led_flag = 0;
if(pwm_conv_flag == 1) led_flag += 1<< 1;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 1);
GPIOC->ODR = 0xffff ^ led_flag << 8;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 0);
}
//解析串口数据
uint8_t analyze_rx_data()
{
char copy_data[23] = {0};
char *p = copy_data;
for(uint8_t i=0; uart_rx_data[i] != '\0'; i++) copy_data[i] = uart_rx_data[i]; //拷贝数据
if(strlen(copy_data) == 22){
//前五位“*NBR:”
if(copy_data[0] == 'C' || copy_data[0] == 'V' && !strncmp(copy_data+1, "NBR",3)) analyze_car.park_type = copy_data[0];
else return 1;
if(*(p+4) != ':') return 1;
p+=5;
uint8_t j=0;
//后五位"****:"
for(;*p!=':';p++){
if(*(p) < 0 || *(p) > 128) return 1;
analyze_car.car_num[j] = *(p);
j++;
if(j>4) return 1;
}
analyze_car.car_num[4] = '\0';
//判断是否是出库
for(uint8_t i=0;i<8;i++){
if(!strncmp(car[i].car_num, analyze_car.car_num, 4)){
analyze_car.car_state = i+1;
break;
}
}
//判断第二个":"
if(*p==':'){
sscanf(p, ":%2d%2d%2d%2d%2d%2d", &analyze_car.time.year, &analyze_car.time.date, &analyze_car.time.day,
&analyze_car.time.hour, &analyze_car.time.minute, &analyze_car.time.second);
//判断时间格式
if(analyze_car.time.date>12 || analyze_car.time.day >30 || analyze_car.time.hour > 23 ||
analyze_car.time.minute>59 || analyze_car.time.second >59) return 1;
return 0;
}
}
return 1;
}
int caculate_park_tim(ANALYZE_RX_DATA *car1, ANALYZE_RX_DATA *car2)
{
int year = 0, date = 0, day = 0, hour = 0, minute = 0, second = 0;
year = (int)(car1->time.year - car2->time.year)*12*30*24;
date = (int)(car1->time.date - car2->time.date)*30*24;
day = (int)(car1->time.day - car2->time.day)*24;
hour = (int)(car1->time.hour - car2->time.hour);
minute = (int)(car1->time.minute - car2->time.minute);
second = (int)(car1->time.second - car2->time.second);
if(year < 0 || date < 0 ||day<0 ||hour<0||minute<0||second<0) return -1;
if(year+date+day+hour+minute+second<1)return 1; //不足一小时按一小时计
else return (year+date+day+hour+minute+second);
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
uart_rx_data[22] = '\0';
if(!analyze_rx_data()){ //数据正确
if(!analyze_car.car_state){ //停车
if(CVI[2]>0){ //停车前提IDLE > 0
for(uint8_t i=0;i<8;i++){
if(car[i].park_type == 0){
memcpy(&car[i], &analyze_car, sizeof(analyze_car));
break;
}
}
if(analyze_car.park_type == 'C') {CVI[0]++;CVI[2]--;}
else if(analyze_car.park_type == 'V') {CVI[1]++;CVI[2]--;}
}
}
else{ //取车
memset(uart_tx_data, 0,strlen(uart_tx_data));
int time = caculate_park_tim(&analyze_car, &car[analyze_car.car_state-1]);
if(time <0){ //时间逻辑错误,停车时间大于取车时间
HAL_UART_Transmit_IT(huart, (uint8_t*)"Error", 5);
goto end;
}
else if(analyze_car.park_type == 'C' && CVI[0]>0){
CVI[0]--;CVI[2]++;
sprintf(uart_tx_data, "CNBR:%s:%d:%.2f",analyze_car.car_num, time, time*CV_NBR[0]);
HAL_UART_Transmit_IT(huart, (uint8_t*)uart_tx_data, strlen(uart_tx_data));
memset(&car[analyze_car.car_state-1], 0, sizeof(car[analyze_car.car_state-1]));
}
else if(analyze_car.park_type == 'V' && CVI[1]>0){
CVI[1]--;CVI[2]++;
sprintf(uart_tx_data, "VNBR:%s:%d:%.2f",analyze_car.car_num, time, time*CV_NBR[1]);
HAL_UART_Transmit_IT(huart, (uint8_t*)uart_tx_data, strlen(uart_tx_data));
memset(&car[analyze_car.car_state-1], 0, sizeof(car[analyze_car.car_state-1]));
}
}
}
else{ //数据错误
HAL_UART_Transmit_IT(huart, (uint8_t*)"Error", 5);
}
end:
memset(&analyze_car, 0, sizeof(analyze_car));
}
/* 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 */
LCD_Init();
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LCD_Clear(Black);
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_2);
HAL_UARTEx_ReceiveToIdle_IT(&huart1, uart_rx_data, 22);
uint32_t period_start_IT = 0;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
gain_key_state();
key_process();
lcd_process();
led_process();
if(uwTick - period_start_IT >=10){
period_start_IT = uwTick;
HAL_UARTEx_ReceiveToIdle_IT(&huart1, uart_rx_data, 22);
}
}
/* 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 */
3.第十二届题目








更多推荐


所有评论(0)