前言: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.第十二届题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐