蓝桥杯嵌入式第十三届第一场省赛真题

蓝桥杯嵌入式第十三届第一场省赛真题代码


前言

后面有时间的话,会不断更新各届真题,现在2022年的第一场省赛测试基本完成所有功能,注释掉的代码有解释,不看也没有影响。


提示:以下是本篇文章正文内容,下面案例可供参考

一、代码

进行按键扫描,置位相应标志位

1.按键扫描

代码如下(示例):

//按键标志位
uint8_t key1_flag = 0;
uint8_t key2_flag = 0;
uint8_t key3_flag = 0;
uint8_t key4_flag = 0;
//读取当前状态
uint8_t key1_state = 1;
uint8_t key2_state = 1;
uint8_t key3_state = 1;
uint8_t key4_state = 1;
//保存上一次状态
uint8_t key1_state_last = 1;
uint8_t key2_state_last = 1;
uint8_t key3_state_last = 1;
uint8_t key4_state_last = 1;
/*
 *@函 数: Key_Scan
 *@功 能: 进行按键扫描
 */
void Key_Scan(void)
{
    //读取当前电压
    key1_state =  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    key2_state =  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    key3_state =  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    key4_state =  HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    
    //检测按键按下1-->0 下降沿
    if(key1_state_last && !key1_state ) key1_flag = 1;
    if(key2_state_last && !key2_state ) key2_flag = 1;
    if(key3_state_last && !key3_state ) key3_flag = 1;
    if(key4_state_last && !key4_state ) key4_flag = 1;
    
    //保存当前状态用于下一次判断
    key1_state_last = key1_state ;
    key2_state_last = key2_state ;
    key3_state_last = key3_state ;
    key4_state_last = key4_state ;
    
}

2.LED显示

代码如下(示例):

/*
 *@函 数: LED_Disp
 *@功 能: LED显示
 */
void LED_Disp(uint8_t ucLed)
{
    HAL_GPIO_WritePin(GPIOC,0xff<<8,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    
    HAL_GPIO_WritePin(GPIOC,ucLed<<8,GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

3.逻辑代码实现

1、主函数及相关变量

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

#include "lcd.h"
#include <stdio.h>
#include <stdbool.h>

void SystemClock_Config(void);

uint8_t UART_Receive_Buff;//串口每次中断接收变量存储
uint8_t Receive_Password[7];//将接收的七次的数据存储
uint8_t Index = 0;

//LED 变量
uint8_t ucled = 0;

//延时 变量
uint32_t uwTick_Key_Wait_Time = 0;//按键扫描延时

uint32_t uwTick_Page_Wait_Time = 0;//输出界面2切换延时

uint32_t uwTick_LED_Wait_Time = 0;//密码错误 LED 0.1s闪烁延时

//LCD 变量
uint8_t LCD_Buff[20];
uint8_t Page = 1;//界面切换变量

//定时器变量
float PA1_Fre = 0;
float PA1_Duty = 0;

//储存正确密码
uint8_t Right_Password[3] = {1,2,3};
//密码结构体
typedef struct
{
    int8_t Word1;//第一位密码
    int8_t Word2;//第二位密码
    int8_t Word3;//第三位密码
    uint8_t Input_Error_Cnt;//记录输入错误次数
    bool NoDisp_Flag[3];//B1 B2 B3 不显示密码标志
    bool Input_Right_Flag;//输入正确标志
}Password;

Password PSD = {-1,-1,-1,0,{1,1,1},0};

//函数声明
void KEY_Process(void);
void LED_Process(void);
void LCD_UI(void);
//主函数
int main(void)
{
  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  
  //关闭所有LED
  LED_Disp(0x00);
  
  //LCD 初始化
  LCD_Init();
  LCD_SetBackColor(Black);
  LCD_SetTextColor(White);
  LCD_Clear(Black);
  
  //定时器初始化
  __HAL_TIM_SET_AUTORELOAD(&htim2,999);//默认1KHz 1M/1000 = 1K
  __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,500);//占空比 500/(999+1)
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
  
  //开启串口
  HAL_UART_Receive_IT(&huart1,&UART_Receive_Buff,1);
  
  //默认上电不显示密码
  PSD.NoDisp_Flag[0] = 1;//B1不显示
  PSD.NoDisp_Flag[1] = 1;//B2不显示
  PSD.NoDisp_Flag[2] = 1;//B3不显示
  //主循环
  while (1)
  {
      KEY_Process();//按键处理
      LED_Process();//led处理
      LCD_UI();//显示界面
  }
}

2、按键处理函数

/*
 *@函 数:  KEY_Process
 *@功 能: 按键处理
 */
void KEY_Process(void)
{
    if((uwTick - uwTick_Key_Wait_Time)<50) return;
    uwTick_Key_Wait_Time = uwTick;
    
    //按键扫描,只有在输入界面才允许按键扫描
    if(Page==1)  { Key_Scan();}
    
    if(key1_flag)
    {
        key1_flag = 0;
        PSD.NoDisp_Flag[0] = 0;//允许显示输入的密码
        PSD.Word1 ++;
        if(PSD.Word1>=10) { PSD.Word1=0; }//限幅
    }
    else if(key2_flag)
    {
        key2_flag = 0;
        PSD.NoDisp_Flag[1] = 0;//允许显示输入的密码
        PSD.Word2 ++;
        if(PSD.Word2>=10) { PSD.Word2=0; }
    }
    else if(key3_flag)
    {
        key3_flag = 0;
        PSD.NoDisp_Flag[2] = 0;//允许显示输入的密码
        PSD.Word3 ++;
        if(PSD.Word3>=10) { PSD.Word3=0; }
    }
    else if(key4_flag)
    {
        key4_flag = 0;
        //开始比对密码
        if((PSD.Word1 == Right_Password[0]) && (PSD.Word2 == Right_Password[1]) &&
             (PSD.Word3 == Right_Password[2]))
        {
            //修改PWM参数
            __HAL_TIM_SET_AUTORELOAD(&htim2,499);//2KHz 1M/500 = 2K
            __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,50);//占空比 50/(499+1)= 10%
            HAL_TIM_PWM_Init(&htim2);
            HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
            if(__HAL_TIM_GET_AUTORELOAD(&htim2))
            PA1_Fre  = 1000000.0f/(__HAL_TIM_GET_AUTORELOAD(&htim2)+1);//2000Hz
            if(__HAL_TIM_GET_COMPARE(&htim2,TIM_CHANNEL_2))
            PA1_Duty = __HAL_TIM_GET_COMPARE(&htim2,TIM_CHANNEL_2)*100.0f/(__HAL_TIM_GET_AUTORELOAD(&htim2)+1);
            
            PSD.Input_Right_Flag = 1;//密码输入正确
            Page = 2;//切换显示界面
            uwTick_Page_Wait_Time = uwTick;//输出显示界面延时
            
            ucled = 0x01 ;//密码输入正确,LED1亮
        }else{
            
            Page = 1;//仍然处于输入界面
            //密码输入错误
            PSD.Input_Error_Cnt ++;
            //显示界面显示@
            PSD.NoDisp_Flag[0] = 1;//隐藏第一位密码
            PSD.NoDisp_Flag[1] = 1;//隐藏第二位密码
            PSD.NoDisp_Flag[2] = 1;//隐藏第三位密码
            //密码设置初始化,每按一次加一,从-1开始
            PSD.Word1 = -1; PSD.Word2 = -1; PSD.Word3 = -1; 
        }
    }  
}

3、LED处理函数

/*
 *@函 数:  LED_Process
 *@功 能: LED处理
 */
void LED_Process(void)
{
    static uint8_t led_cnt = 0;
    if(PSD.Input_Error_Cnt>=3)//错误次数达到三次时
    {
        if((uwTick - uwTick_LED_Wait_Time)>=100)//控制100ms闪烁
        {
            uwTick_LED_Wait_Time = uwTick;
            led_cnt ++;//0.1s*(51-1) = 0.1*50 = 5s 
            ucled ^= 0x02;
        }
        if(led_cnt>=51)
        {//清除标志位
            led_cnt = 0;
            ucled = 0x00;
            uwTick_LED_Wait_Time = 0;
            PSD.Input_Error_Cnt = 0;
        }
    }
    
    LED_Disp(ucled);
}

4、UI界面

/*
 *@函 数:  LCD_UI
 *@功 能: UI界面
 */
void LCD_UI(void)
{
    switch(Page)
    {
        case 1://输入界面
           LCD_DisplayStringLine(Line1,(uint8_t*)"        PSD  ");
        
            if(PSD.NoDisp_Flag[0]==1)//密码隐藏则显示@
            {
                sprintf((char*)LCD_Buff,"     B1:%c        ",'@');
                LCD_DisplayStringLine(Line3,LCD_Buff);
            }else{
                sprintf((char*)LCD_Buff,"     B1:%d        ",PSD.Word1);
                LCD_DisplayStringLine(Line3,LCD_Buff);
            }
            
            if(PSD.NoDisp_Flag[1]==1)//密码隐藏则显示@
            {
                sprintf((char*)LCD_Buff,"     B2:%c        ",'@');
                LCD_DisplayStringLine(Line4,LCD_Buff);
            }else{
                sprintf((char*)LCD_Buff,"     B2:%d        ",PSD.Word2);
                LCD_DisplayStringLine(Line4,LCD_Buff);
            }
            
             if(PSD.NoDisp_Flag[2]==1)//密码隐藏则显示@
            {
                sprintf((char*)LCD_Buff,"     B3:%c        ",'@');
                LCD_DisplayStringLine(Line5,LCD_Buff);
            }else{
                sprintf((char*)LCD_Buff,"     B3:%d        ",PSD.Word3);
                LCD_DisplayStringLine(Line5,LCD_Buff);
            } 
        break;
            
        case 2://输出界面
            LCD_DisplayStringLine(Line1,(uint8_t*)"        STA  ");
            sprintf((char*)LCD_Buff,"     F:%.0fHz        ",PA1_Fre);
            LCD_DisplayStringLine(Line3,LCD_Buff);
            sprintf((char*)LCD_Buff,"     D:%.0f%%        ",PA1_Duty);
            LCD_DisplayStringLine(Line4,LCD_Buff);
            LCD_ClearLine(Line5);
        break;   
    }
    
    //处于界面2
    if(Page==2)
    {
        if((uwTick - uwTick_Page_Wait_Time)>5000)//5s后
        {
            ucled = 0x00;//5s后 关闭LED
            //这句话很重要,如果直接使用下面频率和占空比的配置会出问题
            MX_TIM2_Init(); //配置后 ,默认的输出就是1KHz,50%,因此下面   的操作都可以不用,除非需要改到其他值
           
            //寄存器和库函数操作用的话 两者选其一,不用没用影响,MX_TIM2_Init已经配置好了
            //寄存器操作
            //htim2.Instance->ARR = 999;//默认1KHz 1M/1000 = 1K
            //htim2.Init.Period = 999;  //占空比 500/(999+1)=50%
            //htim2.Instance->CCR2 = 300; //操作寄存器修改比较值
            
            //库函数操作
            //__HAL_TIM_SET_AUTORELOAD(&htim2,999);//默认1KHz 1M/1000 = 1K
            //__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,500);//占空比 500/(999+1)=50%
            
            HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
            Page = 1;//切换界面
            //显示界面显示@
            PSD.NoDisp_Flag[0] = 1;//隐藏第一位密码
            PSD.NoDisp_Flag[1] = 1;//隐藏第二位密码
            PSD.NoDisp_Flag[2] = 1;//隐藏第三位密码
            //密码设置初始化
            PSD.Word1 = -1; PSD.Word2 = -1; PSD.Word3 = -1;
        }            
    }
}

5、串口接收中断回调函数

/*
 *@函 数:  HAL_UART_RxCpltCallback
 *@功 能: 串口接收中断回调函数
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    Receive_Password[Index++] = UART_Receive_Buff;
    if(Index==7)//接收到七个字符后
    {
        Index = 0;
        Receive_Password[0] = Receive_Password[0] - '0';
        Receive_Password[1] = Receive_Password[1] - '0';
        Receive_Password[2] = Receive_Password[2] - '0';
        if((Right_Password[0] == Receive_Password[0]) && (Right_Password[1] == Receive_Password[1]) &&
                      (Right_Password[2] == Receive_Password[2]))//密码正确修改密码
        {//更新密码
            Right_Password[0] = Receive_Password[4] - '0';
            Right_Password[1] = Receive_Password[5] - '0';
            Right_Password[2] = Receive_Password[6] - '0';
        }
    }
   HAL_UART_Receive_IT(&huart1,&UART_Receive_Buff,1);//进行下一次接收
}

总结

代码链接就放在下面了,需要的可以自己提取:
https://www.aliyundrive.com/s/yt8cDeNM1y6
提取码: hq40

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

积分兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值