2022年蓝桥杯第十三届:嵌入式技术剖析
此前也发了关于蓝桥杯的题目,昨天刚比完赛,放松了一下,没来得及整理,早上我就把我所有的思路整理了一下,发出来,希望能帮到大家。当然也有很多不足的地方,希望大家能提出,我们一起讨论。
首先,关于题目的功能,我基本全部实现,一开始发题目,花了10分钟读题整理逻辑,记得是在2小时左右,我基本上都编出来了,就是led那里出了点小bug,后面也是顺利找出来了,然后再把代码完善了一下,后面也是顺利交卷。
今年的题目不算太难,考的模块涉及主要是串口,定时器。附上框图
cubemx配置如下:
由于配置都很简单我就不做多讲,但是由于我用的是G431新板子,必须要配置PD2为输出,保证led的功能正常运行,详细的可以去看我上一篇停车场的介绍,那里有关于这个引脚的详细说明
我将从4个部分进行讲解:led_key–LCD–PWM–串口(uart)
详细的设计要求我就不发出来了,大家可以去官网找,13届蓝桥杯试题
目录
先附上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
#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);
}
驱动代码
这两部分代码是放在 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个,我们再判断格式是否正确,这里也有个小细节,由于通过串口发送的是字符,我们要先转换为数字才能再进行判断串口给的原密码是否正确,然后修改密码。
好了,这大概就是整个工程的讲解,欢迎大家在评论区留言,期待我们一起交流学习。