2022年蓝桥杯第十三届:嵌入式技术剖析

        此前也发了关于蓝桥杯的题目,昨天刚比完赛,放松了一下,没来得及整理,早上我就把我所有的思路整理了一下,发出来,希望能帮到大家。当然也有很多不足的地方,希望大家能提出,我们一起讨论。

首先,关于题目的功能,我基本全部实现,一开始发题目,花了10分钟读题整理逻辑,记得是在2小时左右,我基本上都编出来了,就是led那里出了点小bug,后面也是顺利找出来了,然后再把代码完善了一下,后面也是顺利交卷。

今年的题目不算太难,考的模块涉及主要是串口,定时器。附上框图

cubemx配置如下:

由于配置都很简单我就不做多讲,但是由于我用的是G431新板子,必须要配置PD2为输出,保证led的功能正常运行,详细的可以去看我上一篇停车场的介绍,那里有关于这个引脚的详细说明

我将从4个部分进行讲解:led_key–LCD–PWM–串口(uart)

详细的设计要求我就不发出来了,大家可以去官网找,13届蓝桥杯试题

目录

整体代码

key和led

LCD

PWM

串口(uart


先附上main函数整体代码

整体代码


#include "main.h"
#include "lcd\lcd.h"
#include "ledkey\bsp_ledkey.h"
#include "tim\bsp_tim.h"
#include "uart\bsp_uart.h"
#include "stdio.h"
#include "string.h"





uint8_t   B1='@',B2='@',B3='@';//密码初始化
uint8_t   time=0,i=0;//控制报警灯参数
uint8_t   mauce[30];//lcd显示数组
uint8_t   ucled=0x00;
uint8_t   lcd_land=0;//屏幕标志位
uint8_t   vuale,key_up,key_down,key_old;//按键检测相关变量
uint16_t    freqq_HZ,Duty;
__IO uint32_t uwTick_key=0,uwTick_pwm=0,uwTick_lcd=0,uwTick_led=0,uwTick_uart=0;//减速变量

uint8_t    buffer;//缓冲接收字符
uint8_t    scree=0;//密码是否正确
uint8_t    buff[10];//接收的字符放在这里面
uint8_t    cout=0;//计数变量
uint8_t   yuan_mi[3]={1,2,3};
void SystemClock_Config(void);

void keyledjuel_project(void);
void lcd_project(void);
void uart_rx(void);

int main(void)
{

  HAL_Init();
  SystemClock_Config();
  ledkey_Init();
  TIM2_Init();
  UART_Init();
  LCD_Init();
  led_paly(0x00);
  LCD_SetTextColor(White);
  LCD_SetBackColor(Black);
  LCD_Clear(Black);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
  UART_Start_Receive_IT(&huart1, (uint8_t*)&buffer, 1);
	
	
	while (1)
  {

     keyledjuel_project();
		 lcd_project();
		 uart_rx();
  }

}



//报警执行5s,led2以0.1s间隔闪烁
void led_start(){

if(uwTick-uwTick_led<100)
	uwTick_led=uwTick;
if(i<50){
ucled^=0x02;
i++;
}




}

void uart_rx(){

	if(uwTick-uwTick_uart<100)return;
	uwTick_uart=uwTick;
//判断是否有7个字符传进来了
	if(cout==7){
		//判断格式是否合格
	if( buff[3]=='-'&&buff[2]<='9'&&buff[2]>='0'
	&&buff[1]<='9'&&buff[1]>='0'
	&&buff[0]<='9'&&buff[0]>='0'
	&&buff[4]<='9'&&buff[4]>='0'
	&&buff[5]<='9'&&buff[5]>='0'
	&&buff[6]<='9'&&buff[6]>='0'
	)
	{
	
		//将字符转化成为数字
		buff[0]-=0x30;buff[1]-=0x30;buff[2]-=0x30;
		//判断串口给的原密码是否符合
		if(buff[0]==yuan_mi[0]&&buff[1]==yuan_mi[1]&&buff[2]==yuan_mi[2])//验证成功
		{
			//修改密码
		yuan_mi[0]=buff[4]-0x30;
		yuan_mi[1]=buff[5]-0x30;
		yuan_mi[2]=buff[6]-0x30;
	
		}
		
	cout=0;
	}

	else
	cout=0;

}
	

}

void keyledjuel_project(){

	if(uwTick-uwTick_key<50)return;//减速
	uwTick_key=uwTick;

    vuale=key_paly();	
	key_down=vuale&(vuale^key_old);
	key_up=~vuale&(vuale^key_old);
	key_old=vuale;


switch(key_down){

	case 1:
		
	if(lcd_land==0){

	if(B1=='@')
	B1=0;
	if(B1++==9)
	B1=0;
	
}
		break;
	case 2:

	if(lcd_land==0){

	if(B2=='@')
	B2=0;
	if(B2++==9)
	B2=0;
	
}
		break;

	case 3:

	if(lcd_land==0){

	if(B3=='@')
	B3=0;
	if(B3++==9)
	B3=0;
	
}
		break;


	case 4:

	if(B1==yuan_mi[0]&&B2==yuan_mi[1]&&B3==yuan_mi[2])
	scree=1; 
	time++;//每进来一次代表,验证密码的次数,当超过三次或等于三次,进行0.1s报警

	if(scree==1)//验证正确
	{
	time=0;
	uwTick_pwm=uwTick;
	}
	
	break;


}


if(scree==1){
		if(uwTick-uwTick_pwm<5000){
	    ucled|=0x01;
		__HAL_TIM_SET_AUTORELOAD(&htim2,499);
	    __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,50);
			
		if(lcd_land!=1)
	    LCD_Clear(Black);
	    lcd_land=1;
		}
		
		
	else{
		ucled&=~0x01;
	    __HAL_TIM_SET_AUTORELOAD(&htim2,999);
	    __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,0);
		if(lcd_land!=0)
		LCD_Clear(Black);
		
		lcd_land=0;
		B1='@';
		B2='@';
		B3='@';
		__HAL_TIM_SET_AUTORELOAD(&htim2,999);
		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,500);
	    scree=0;
		
	}
	
	
}	
	//判断当输入密码是否连续错了三次及以上
	if(time>2){
	led_start();
		if(i==50)
		{
			time--;
			i=0;
		}
	}
	
	//执行led工作
led_paly(ucled);
	
}





void lcd_project(){


if(uwTick-uwTick_lcd<200)return;
	uwTick_lcd=uwTick;
	

	//算出频率
freqq_HZ = 1000000/(__HAL_TIM_GET_AUTORELOAD(&htim2)+1);
	//算出占空比
Duty=100*((float)__HAL_TIM_GET_COMPARE(&htim2,TIM_CHANNEL_2)/(__HAL_TIM_GET_AUTORELOAD(&htim2)+1));
	
	
	
if(lcd_land==0){
	
	
	
	sprintf((char*)mauce,"       PSD");
	LCD_DisplayStringLine(Line1,mauce);
	
	
  if(B1=='@')
	sprintf((char*)mauce,"    B1:%c",B1);
	else
	sprintf((char*)mauce,"    B1:%d",B1);
	LCD_DisplayStringLine(Line3,mauce);
	
	
	
	if(B2=='@')
	sprintf((char*)mauce,"    B2:%c",B2);
	else
	sprintf((char*)mauce,"    B2:%d",B2);
	LCD_DisplayStringLine(Line4,mauce);
	
	
  if(B3=='@')
	sprintf((char*)mauce,"    B3:%c",B3);
	else
	sprintf((char*)mauce,"    B3:%d",B3);
	LCD_DisplayStringLine(Line5,mauce);
	
	
}


if(lcd_land==1){

	sprintf((char*)mauce,"       STA");
	LCD_DisplayStringLine(Line1,mauce);

	sprintf((char*)mauce,"    F:%dHZ",freqq_HZ);
	LCD_DisplayStringLine(Line3,mauce);
	
	sprintf((char*)mauce,"    D:%d%%",Duty);
	LCD_DisplayStringLine(Line4,mauce);
	
}

}


//接收中断


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){


	
  buff[cout]=buffer;
  cout++;
	
	
  UART_Start_Receive_IT(&huart1, (uint8_t*)&buffer, 1);
	

}


key和led

驱动代码

这两部分代码是放在 bsp_ledkey.c里面的,没有在main.c里面,为了方便我直接在main中调用

void  led_paly(uint8_t  ucled1){


  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, 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, ucled1<<8, GPIO_PIN_RESET);
	
	 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
	

}


uint8_t key_paly(){

uint8_t vuale=0;
	
	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==0)
  vuale=1;
	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==0)
  vuale=2;
	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)==0)
  vuale=3;
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0)
  vuale=4;

		return vuale;

}

就是两段驱动代码,不做多讲,大家看看就会明白。

对于key和led的难点,想必就是,输入错误三次及三次以上进行led的报警,key没什么难度。

我们可以在按键四里面设置标志位time,每次进来都+1,但当密码正确就置0(连续已被打断)

后面就可以进行密码错误次数片段,这里面有个小细节,由于输入错误就要闪烁五秒,然后我通过设置i来进行50次的100ms间隔,后面我是将time又减了一次,让程序下一次就执行不到这里,除非密码又输错,然后time的状态应该是3-2-3-2-3-2…….

 闪烁程序

LCD

移植文件

lcd的话主要是'@'显示和数字显示的切换,其实还是比较简单的,只是注意当显示字符时,一定是%c,而不是%d。

PWM

开始产生PWM波

 pwm逻辑主要是集中在密码输入正确的页面也就是第二个页面

scree是判断密码是否正确密码的标志位,我们在判断正确后,开始保持输出页面5s,且设置2000hz的频率,和10的占空比(初始占空比大家随便设置,我一开始就设置的百分之50,但是最好不要设置百分之10),后面在回到初始界面(重新设置为初始值)

 大家可以看一看这段逻辑,

      __HAL_TIM_SET_AUTORELOAD(&htim2,499);
      __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,50);

关于重装载值的设置和比较值的设置,大家可以去HAL库tim.h中找

串口(uart)

 关于串口部分,逻辑不算难,就是:字符数量是否正确-判断字符合格-判断设置合格-设置密码

 我们开启中断接收一个字符的方式

 

将字符一个个送到buff里面

我们可以先进行判断字符是否进来7个,如果没有再将cout置0-重新接收,如果进来7个,我们再判断格式是否正确,这里也有个小细节,由于通过串口发送的是字符,我们要先转换为数字才能再进行判断串口给的原密码是否正确,然后修改密码。

好了,这大概就是整个工程的讲解,欢迎大家在评论区留言,期待我们一起交流学习。

物联沃分享整理
物联沃-IOTWORD物联网 » 2022年蓝桥杯第十三届:嵌入式技术剖析

发表回复