【蓝桥杯嵌入式】STM32G431省赛真题及代码详解

文章目录

  • 前言
  • 一、题目介绍
  • 二、相关模块介绍和重难点分析
  • 1.相关模块
  • 2.重难点分析
  • 三.题解
  • 1、代码相关定义与函数声明:
  • 2、主函数代码:
  • 3、按键扫描函数:
  • 4、按键处理函数:
  • 5、界面和PWM输出处理函数:
  • 6、LED处理函数:
  • 7、串口处理函数:
  • 8、判断串口信息格式是否有误以及修改密码函数
  • 总结:
  • 源码:

  • 前言

    本次分享的内容是关于第十三届蓝桥杯嵌入式省赛的解题思路和经验总结。这也是我自上月以来的首次更新,希望能够为大家提供一些有用的参考和指导。

    一、题目介绍





    二、相关模块介绍和重难点分析

    1.相关模块

    第十三届比赛主要有:LED、KEY、LCD、TIM、USART。

    2.重难点分析

    这届的目的是做一个密码锁,主要是对按键和屏幕处理的考查。通过按键输入密码,并且通过是否正确输入密码来改变PA1脉冲的输出状态和LED的闪烁情况,以及通过串口发送正确的指令来改变原有的密码。
    **难点1:**按键从ASCII码@变成ASCII码0然后在依次增加变成9在变回0如此循环。
    **难点2:**对LED1灯持续亮五秒和PWM脉冲持续输出2KHZ五秒再变回1KHZ的处理,这可能是本届最易出错的点。

    三.题解

    1、代码相关定义与函数声明:

    char view;//LCD显示界面 0是输入密码界面 1是PWM输出界面
    char temp[20];//LCD显示参数数组
    char mark='@';//ASCII@变量
    char secret_one,secret_sec,secret_thir;//定义3位输入参数变量
    uint8_t judge;//判断串口数据是否合法
    uint8_t uled;//LED显示参数
    uint8_t pwm;//PWM输出状态界面 0是PWM输出1KHZ 1是PWM输出2KHZ
    uint8_t flag;//输入密码超过三次报警标志位
    uint8_t wordnum=0;//密码输入次数
    uint8_t keynum=0;//按键值
    uint8_t arr[7];//接受串口数据数组
    uint8_t word[3]={'1','2','3'};//初始化密码,储存密码数组
    extern struct Chword New_word;
    extern char Cword;
    __IO uint32_t LED_tick;//LED计时时间
    __IO uint32_t PWM_tick;//PWM计时时间
    
    ......函数声明......
    void pro_view(void);//界面处理函数
    void key_pro(uint8_t keyword);安检处
    void dis_chword(void);
    void LED_dis(void);
    

    2、主函数代码:

    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_USART1_UART_Init();
      MX_TIM2_Init();
      /* USER CODE BEGIN 2 */
      	secret_one=mark;//初始化输入密码界面的参数
    	secret_sec=mark;
    	secret_thir=mark;
      	LCD_Init();      
    	LCD_Clear(Black);
    	LCD_SetBackColor(Black);
    	LCD_SetTextColor(White);
    	LED_pro(0x00);//使LED全部熄灭
    	HAL_UART_Receive_IT(&huart1,arr,7);//开启串口接收中断
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
    	 
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  keynum=Key_Read();
    	  key_pro(keynum);
    	  dis_chword();
    	  pro_view();
    	  LED_dis();
      }
      /* USER CODE END 3 */
    }
    

    3、按键扫描函数:

    eg:这里需要注意题目中给出的避免多次触发,最好给按键上锁。

    uint8_t Key_Read(void)
    {
    	//定义按键锁变量
    	static uint8_t key_lock=1;
    	
    	if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==RESET||HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==RESET||
    	   HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==RESET||HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==RESET)
    	   &&key_lock==1)
    	{
    		//给按键上锁,避免多次触发
    		key_lock=0;
    		//消抖
    		HAL_Delay(10);
    		
    		//判断B1是否按下
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==RESET)
    		{
    			return 1;
    		}
    		
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==RESET)
    		{
    			return 2;
    		}
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==RESET)
    		{
    			return 3;
    		}
    		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==RESET)
    		{
    			return 4;
    		}
    
    	}
    	//按键松手
    	if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==SET&&HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==SET&&
    	    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==SET&&HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==SET)
    	    &&key_lock==0)
    	   {
    		   //开锁
    		 key_lock=1;
    	   }
    	
    	   return 0;
    }
    
    

    4、按键处理函数:

    eg:这里就是我上面提到的第一个难点和第二个LED1难点,第一个难点写代码容易写成从’@'变为0,然后9直接变为1了,而没有变成0。解决方法是单独用if单独判断为’@‘时就变为0。至于第二个难点解决方法用系统时钟uwtick变量计时,计时完全五秒不会影响程序的运行。(或者用定时器计时来解决)

    void key_pro(uint8_t keyword)
    {
    	
    	if(keyword==1)
    	{
    		if(secret_one==mark){
    		secret_one='0';
    		}
    		else{
    				secret_one=secret_one+1;
    				if(secret_one>'9'){
    				secret_one='0';
    			}
    		}
    
    	}
    	if(keyword==2)
    	{
    		if(secret_sec==mark){
    		secret_sec='0';
    		}
    		else{
    			secret_sec=secret_sec+1;
    			if(secret_sec>'9'){
    			secret_sec='0';
    			}
    		}	
    	}
    	if(keyword==3)
    	{
    		if(secret_thir==mark){
    		secret_thir='0';
    		}
    		else{
    		   	secret_thir=secret_thir+1;
    			if(secret_thir>'9'){
    			secret_thir='0';
    			}
    		}
    	}
    	if(keyword==4){
    		//判断密码是否正确
    		if(secret_one==word[0] &&secret_sec==word[1] &&secret_thir==word[2]){
    			//点亮LED1
    			uled |= 0x01;
    			LED_tick=uwTick;//开始计时
    			wordnum=0;//密码输入次数清零
    			pwm=1;//转变PWM波状态
    			PWM_tick=uwTick;//PWM时间开始计时
    			view=1;//切换成PWm输出界面
    			LCD_Clear(Black);//清屏
    		}
    		else{
    			wordnum++;//错误密码输入次数增加
    			if(wordnum>=3){
    				LED_tick=uwTick;
    				wordnum=0;
    				flag=1;//报警
    			}
    			secret_one=mark;//错误三次以上密码参数恢复'@'状态
    			secret_sec=mark;
    			secret_thir=mark;
    		}
    		
    
    	}
    }
    

    5、界面和PWM输出处理函数:

    eg:这里的PWM输出时间处理,跟上面一样就行。

    void pro_view(void)
    {	
    	//配置PWM初始状态下的输出和占空比
    	if(pwm==0){
    		__HAL_TIM_SetAutoreload(&htim2,1999);
    		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,100);
    	}
    	//配置PWM在密码正确时的输出和占空比
    	if(pwm==1){
    		__HAL_TIM_SetAutoreload(&htim2,999);
    		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,100);
    	}
    	//输入密码界面
    	if(view==0)
    	{
    		sprintf(temp,"      PSD");
    		LCD_DisplayStringLine(Line2,(uint8_t*)temp);
    		sprintf(temp,"    B1:%c",secret_one);
    		LCD_DisplayStringLine(Line4,(uint8_t*)temp);
    		sprintf(temp,"    B2:%c",secret_sec);
    		LCD_DisplayStringLine(Line5,(uint8_t*)temp);
    		sprintf(temp,"    B3:%c",secret_thir);
    		LCD_DisplayStringLine(Line6,(uint8_t*)temp);
    	}
    
    	if(view==1)//PWM输出状态界面
    	{
    		if(uwTick-PWM_tick>5000){
    			pwm=0;
    		}
    		if(pwm==0){
    			sprintf(temp,"      STD");
    			LCD_DisplayStringLine(Line2,(uint8_t*)temp);
    			sprintf(temp,"    F:1000HZ");
    			LCD_DisplayStringLine(Line4,(uint8_t*)temp);
    			sprintf(temp,"    D:10%%");
    			LCD_DisplayStringLine(Line5,(uint8_t*)temp);			
    		}
    		else{
    			sprintf(temp,"      STD");
    			LCD_DisplayStringLine(Line2,(uint8_t*)temp);
    			sprintf(temp,"    F:2000HZ");
    			LCD_DisplayStringLine(Line4,(uint8_t*)temp);
    			sprintf(temp,"    D:10%%");
    			LCD_DisplayStringLine(Line5,(uint8_t*)temp);
    		
    		}
    	}
    }
    
    

    6、LED处理函数:

    void LED_dis(void)
    {
    	static __IO uint32_t led_tick;//减速变量
    	if(uwTick-led_tick<100){//以0.1秒闪烁
    		return;
    	}
    	led_tick=uwTick;
    	//五秒后LED1、2熄灭
    	if(uwTick-LED_tick>5000){
    		uled=0x00;
    		flag=0;
    	}
    	//报警,点亮LED2
    	if(flag==1){
    		uled^=0x02;
    	}
    	LED_pro(uled);
    }
    
    

    7、串口处理函数:

    struct Chword
    {
    	uint8_t Pri[3];
    	uint8_t Sec;
    	uint8_t Fin[3];
    };
    struct Chword New_word;//记录串口信息结构体
    char Cword=0;//接收数据成功标志位
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	//记录串口信息
    	New_word.Pri[0]=arr[0];
    	New_word.Pri[1]=arr[1];
    	New_word.Pri[2]=arr[2];
    	New_word.Sec=arr[3];
    	New_word.Fin[0]=arr[4];
    	New_word.Fin[1]=arr[5];
    	New_word.Fin[2]=arr[6];
    	
    	Cword=1;
    	//清除原有数组数据,为下次接收数据做准备
    	memset(arr,0,7);
    }
    

    8、判断串口信息格式是否有误以及修改密码函数

    uint8_t judge_word(void)
    {
    	uint8_t i=0;
    	if(Cword==1)
    	{
    	//验证串口信息
    		for(i=0;i<3;i++){
    			if(New_word.Fin[i]>'0' && New_word.Fin[i]<'9' &&
    				New_word.Pri[i]>'0' && New_word.Pri[i]<'9' &&
    				 New_word.Sec=='-'
    				 ){
    				return 2;//数据合格
    			}
    		}
    	}
    		return 1;//若接收失败也不合格
    }
    //修改密码函数
    void dis_chword(void)
    {
    	judge=judge_word();//判断密码是否合格
    	//修改密码
    	if(judge==2){
    		if(New_word.Pri[0]==word[0] && New_word.Pri[1]==word[1] && New_word.Pri[2]== word[2]){
    			word[0]=New_word.Fin[0];
    			word[1]=New_word.Fin[1];
    			word[2]=New_word.Fin[2];
    		}	
    	}
    
    	
    }
    

    总结:

    通过本届试题的分析,我觉得这次串口模块的考察相对上届更简单了,其他模块也没有太多创新,整体难度比上届稍微容易一些。解题过程也没有太大困难,只要注意细节就能拿到满分。和以往一样,完整的源代码如下。
    

    源码:

    源码

    作者:嵌入式-小博

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【蓝桥杯嵌入式】STM32G431省赛真题及代码详解

    发表评论