突击蓝桥杯嵌入式(七)——第十三届省赛第一场真题
一、题目概览
二、思路梳理
我们直接在LCD例程的基础上,改需增加的外设如下:
LED灯(配置锁存器PD2),串口(波特率9600,带中断),按键4个,PWM输出。
题目较为简单,其逻辑部分如下:
上电之后,板子初始化,LCD显示为@@@,默认密码为123,PWM脉冲输出1KHZ的方波信号,然后开启串口接收中断(7个数据进入中断),然后中断里对于字符串进行处理,利用ssanf分离出输入的初始密码和新密码,然后对比密码是否正确,如果错误,则不改变原有密码值。
然后按键扫描部分:
按下B1B2B3分别对应密码的123位,其变化规律为0-9-0-9,然后B4确认,如果密码正确,LED1点亮五秒然后熄灭,LCD显示输出界面5s,且PWM输出2KHZ,占空比为百分之10的方波,之后显示三个@@@!!!如果错误,累计次数,超过两次,LED2以0.1s为间隔闪烁,持续五秒。
三、代码
uint8_t str[21];
uint8_t LCD_flag = 2;
/******************
LCD分为三个界面,分别为
复位页面(三个@@@)2
密码界面(主页面)0
数据页面(显示频率和占空比)1
******************/
int passward[3]; //现在输入的密码
uint8_t real_passward[3]; //真正的密码
uint8_t rx_passward[3]; //串口接收到的密码
uint8_t count; //
uint8_t right_flag; //密码正确标志
uint8_t count_led=0; //led计数器
uint8_t shock_flag=0; //led闪烁标志位
uint8_t shock; //闪烁种的led亮灭标志
uint8_t count_LCD; //数据页面LCD只能持续5s,所以用一个count计数
uint8_t key_value; //三行代码消抖,没啥好说的
uint8_t key_down;
uint8_t key_up;
uint8_t key_old;
uint8_t rx_buff[7]; //串口接收的消息
__IO uint32_t uwTickLCD_Speed; //滴答定时器参量,没啥好说的
__IO uint32_t uwTickKey_Speed;
__IO uint32_t uwTickLED_Speed;
LED灯
void LED_Disp(uint8_t ucled)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|
GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);//灭灯
HAL_GPIO_WritePin(GPIOC,ucled<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_Proc()
{
if(uwTick - uwTickLED_Speed < 100) return;
else uwTickLED_Speed = uwTick;
if(count_led<=50&&count_led>0) //如果触发了闪烁5s或亮5s的标志
{
if(shock_flag==1) //若是闪烁标志
{
if(shock==1) //led2闪烁5s
{
LED_Disp(0x02);
shock^=1;
}
else if(shock==0)
{
LED_Disp(0x00);
shock^=1;
}
count_led--; //每100ms 标志位-1
}
else //当不是闪烁标志
{
LED_Disp(0x01);
count_led--;
}
}
else if(count_led == 0){ //当计数位为0,也就是过了五秒时候,关闭所有LED和标志位
LED_Disp(0x00);
shock_flag=0;
}
}
LCD
void LCD_main_Dis() //主页面,显示密码
{
sprintf((char *)str," PSD ");
LCD_DisplayStringLine(Line1,(uint8_t *)str);
if(passward[0] == -1)
{
sprintf((char *)str," B1:@ ");
LCD_DisplayStringLine(Line3,(uint8_t *)str);
}
else
{
sprintf((char *)str," B1:%d ",passward[0]);
LCD_DisplayStringLine(Line3,(uint8_t *)str);
}
if(passward[1] == -1)
{
sprintf((char *)str," B2:@ ");
LCD_DisplayStringLine(Line4,(uint8_t *)str);
}
else
{
sprintf((char *)str," B2:%d ",passward[1]);
LCD_DisplayStringLine(Line4,(uint8_t *)str);
}
if(passward[2] == -1)
{
sprintf((char *)str," B3:@ ");
LCD_DisplayStringLine(Line5,(uint8_t *)str);
}
else
{
sprintf((char *)str," B3:%d ",passward[2]);
LCD_DisplayStringLine(Line5,(uint8_t *)str);
}
}
void LCD_data_Dis() //数据页面,密码正确时候显示
{
sprintf((char *)str," STA ");
LCD_DisplayStringLine(Line2,(uint8_t *)str);
sprintf((char *)str," F:2000HZ ");
LCD_DisplayStringLine(Line4,(uint8_t *)str);
sprintf((char *)str," D:10%% ");
LCD_DisplayStringLine(Line5,(uint8_t *)str);
sprintf((char *)str," ");
LCD_DisplayStringLine(Line6,(uint8_t *)str);
}
void LCD_Proc()
{
if(uwTick - uwTickLCD_Speed < 100) return;
else uwTickLCD_Speed = uwTick;
if(LCD_flag == 0)
{
LCD_main_Dis();
}
if(LCD_flag == 1) //如果密码正确,进入数据页面5s
{
LCD_data_Dis();
count_LCD++; //100ms +1
TIM2->ARR = 500; //改变频率和占空比
TIM2->CCR2 = 50;
if(count_LCD==50){ //当过了5s
count_LCD =0; //返回主页面,清除LCD计数
LCD_flag = 2;
TIM2->ARR = 1000; //复原占空比
TIM2->CCR2 = 500;
}
if(LCD_flag == 2)
{
passward[0]=-1;
passward[1]=-1;
passward[2]=-1;
}
}
Key按键
uint8_t Key_Scan() //扫描哪个键让按下去
{
static uint8_t value;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == GPIO_PIN_RESET)
{
value = 1;
}
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == GPIO_PIN_RESET)
{
value = 2;
}
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == GPIO_PIN_RESET)
{
value = 3;
}
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
{
value = 4;
}
else
{
value = 0;
}
return value;
}
void Key_Proc()
{
if(uwTick - uwTickKey_Speed < 100) return;
else uwTickKey_Speed = uwTick;
key_value = Key_Scan();
key_down = key_value&(key_value^key_old);
key_up = ~key_value&(key_value^key_old);
key_old = key_value;
switch(key_down)
{
case 1:
passward[0]++;
if(passward[0]>9){
passward[0] = 0;
}
LCD_flag = 0;
break;
case 2:
passward[1]++;
if(passward[1]>9){
passward[1] = 0;
}
LCD_flag = 0;
break;
case 3:
passward[2]++;
if(passward[2]>9){
passward[2] = 0;
}
LCD_flag = 0;
break;
case 4:
right_flag = check_passward();
if(right_flag){
count=0;
count_led=50;
LCD_flag = 1;
passward[0]=-1;
passward[1]=-1;
passward[2]=-1;
}
break;
}
}
uint8_t check_passward() //检查密码是否正确,正确就返回1,错误就计数,三次之后置1闪烁标志位
{
if((passward[0] == real_passward[0])&&(passward[1] == real_passward[1])&&(passward[2] == real_passward[2]))
{
count=0;
return 1;
}
else
{
count++;
if(count>2){
shock_flag=1;
count_led=50;
}
return 0;
}
}
串口
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint8_t cofirm_passward[3];
static uint8_t re_passward[3]; //sscanf从rx_buff读取密码,如果输入的旧密码正确,将修改的密码覆盖原来的密码
sscanf((char *)rx_buff,"%1d%1d%1d-%1d%1d%1d",&cofirm_passward[0],&cofirm_passward[1],
&cofirm_passward[2],&re_passward[0],&re_passward[1],&re_passward[2]);
LCD_DisplayStringLine(Line8,(uint8_t *)str);
if((cofirm_passward[0]==real_passward[0])&&(cofirm_passward[1]==real_passward[1])&&(cofirm_passward[2]==real_passward[2]))
{
for(int k=0;k<3;k++)
{
real_passward[k] = re_passward[k];
}
}
HAL_UART_Receive_IT(&huart1,rx_buff,7);
}
主函数,主要是一些初始化
HAL_TIM_PWM_Init(&htim2); //一些初始化,没啥好说的
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LED_Disp(0X00);
real_passward[0] = 1;
real_passward[1] = 2;
real_passward[2] = 3;
HAL_UART_Receive_IT(&huart1,rx_buff,7);
while (1)
{
LCD_Proc();
Key_Proc();
LED_Proc();
}