STM32HAL库【G431】--【蓝桥杯嵌入式第十三届省赛题】第一场

完整工程(百度网盘免费下载,提取码:0403)和演示视频在文章末尾,需要请移步至文章末尾。

第十三届第一场省赛题目

在这里插入图片描述

题目分析

个人观点:笔者读完题目,发现使用的模块涉及到短按按键LED串口LCD
大概注意的内容:

  1. 串口只做数据接收处理,不返回任何东西。
  2. 密码输入错误大于等于三次LED2以0.1s间隔闪烁5s后熄灭。
  3. 密码输入正确LED1点亮5s
  4. 串口接收固定7字符的内容。

涉及模块主要代码

主要变量与结构体

struct keys//按键结构体
{
	uchar judge;
	bool sta;
	bool single;
};
struct keys key[4]={0,0,0};

uint freq=0;//PWM输出的频率
uchar duty=0;//PWM输出的占空比
uint ntimer=0;//定时计数
uchar LED_timer=0;// LED0.1s闪烁计数
bool led_flicker=0;//LED闪烁标志位
bool over_fault=0;//次数超过3此标志位

uchar password[3] = {1,2,3};//密码初始化
uchar pass_in[3] = {0,0,0};//输入的密码
char lcd_buffer[30]={0};//LCD显示缓存

uchar fault_count =0;//密码错误次数计数

bool scene_flag=0;//0:输入密码界面 1:密码正确跳转的界面
bool a_flag=1;//界面显示@的标志位,默认显示,按下按键就置0,不显示

char Rxdata[10]={0};//串口接收数组
char Txdata[10]={0};//串口发送数组
char rxdat=0;//串口缓存
uchar rx_count=0;//串口接收计数
uchar rx_timer=0;//串口是否接收完毕时间计数
bool finish_rx_flag=0;//串口完成接收标志位

按键扫描与按键处理

由于此次赛题只涉及短按,所以相对简单。我们采用定时器每10ms进行一次按键扫描。代码如下:

void Key_Scan(void)//按键扫描
{
	key[0].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
	key[1].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	key[2].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
	key[3].sta = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
	
	for(uchar i=0; i<4; i++)
	{
		switch (key[i].judge)
		{
			case 0://状态1:判断按键是否按下
			{
				if(key[i].sta==0)key[i].judge=1;
			}break;
			case 1://状态2:按键消抖
			{
				if(key[i].sta==0)key[i].judge=2;
				else key[i].judge=0;
			}break;
			case 2://状态3:确认按键按下,标志位置1
			{
				if(key[i].sta==1)
				{
					key[i].single=1;
					key[i].judge=0;
				}
			}break;
		}
	}
}
void Key_process(void)//按键处理
{
	if(key[0].single==1 && scene_flag==0)
	{
		//利用C语言顺序执行的规则将a_flag做出的操作写到最后
		key[0].single=0;
		if(a1_flag==0)pass_in[0]++;
		pass_in[0]%=10;
		a1_flag=0;//按键开始输入,置0
	}
	else if(key[1].single==1 && scene_flag==0)
	{
		key[1].single=0;
		if(a2_flag==0)pass_in[1]++;
		pass_in[1]%=10;
		a2_flag=0;//按键开始输入,置0
	}
	else if(key[2].single==1 && scene_flag==0)
	{
		key[2].single=0;
		if(a3_flag==0)pass_in[2]++;
		pass_in[2]%=10;
		a3_flag=0;//按键开始输入,置0
	}
	else if(key[3].single==1)
	{
		key[3].single=0;
		key[2].single=0;
		key[1].single=0;
		key[0].single=0;
		if(password[0] == pass_in[0] &&password[1] == pass_in[1]
		&& password[2] == pass_in[2])//判断密码是否正确
		{
			scene_flag=1;//sta界面
			a1_flag=a2_flag=a3_flag=1;//初始界面显示@
			fault_count=0;//错误次数请0
			LCD_Clear(Black);
			TIM2->ARR=500-1;TIM2->CCR2=50;//设置PWM 2khz 10%
		}
		else 
		{
			a1_flag=a2_flag=a3_flag=1;
			fault_count++;
		}
		if(fault_count>=3)over_fault=1;
		memset(pass_in,0,sizeof(pass_in));
	}
}

LED处理

LED就是要注意,在密码输入正确后,对错误次数清零,其他的就是理清逻辑关系就好。

  1. 密码输入正确LED1点亮5s后熄灭
  2. 密码输入错误LED2以0.1间隔闪烁5s后熄灭
void LED_disp(uchar dis)//LED显示
{
	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(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);//LED锁存
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	
	HAL_GPIO_WritePin(GPIOC,dis <<8,GPIO_PIN_RESET);
	
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);//LED锁存
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_proc(void)//LED处理
{
	if(scene_flag==1)//密码输入成功后,换界面和LED
	{
		LED_disp(0x01);
		if(ntimer>=500)//5s结束,返回初始密码界面
		{
			LED_disp(0x00);
			ntimer=0;scene_flag=0;
			TIM2->ARR=1000-1;TIM2->CCR2=500;//PWM 1Khz 50%
		}
	}
	else
	{
		if(over_fault==1)//错误次数三次以上含三次
		{
			if(led_flicker)LED_disp(0x02);
			else LED_disp(0x00);
			if(ntimer>=500){ntimer=0;over_fault=0;}//清楚超过错误次数标志位
		}
		else LED_disp(0x00);
	}
}

LCD显示

关于LCD要注意:

  1. 上电默认密码输入界面且B1、B2、B3显示@,按下对应按键后才是0~9循环
    在这里插入图片描述
  2. LCD显示界面切换时,可以不用刷新屏幕或者清理某一行,可以在显示相应字符串时,后面多加几个空格覆盖就行。
  3. 密码输入正确后进入STA界面,5s后返回PSD界面时,也要显示@
    主要代码如下:
void LCD_scene(void)//LCD显示
{
	if(scene_flag==0)//密码输入界面
	{
		LCD_DisplayStringLine(Line1,"       PSD");
		
		if(a1_flag)LCD_DisplayStringLine(Line3,"    B1:@      ");//初始状态,没有按下按键
		else 
		{
			sprintf(lcd_buffer,"    B1:%d       ",pass_in[0]);
			LCD_DisplayStringLine(Line3,(uchar *)lcd_buffer);
		}

		if(a2_flag)LCD_DisplayStringLine(Line4,"    B2:@      ");//初始状态,没有按下按键
		else
		{
			sprintf(lcd_buffer,"    B2:%d       ",pass_in[1]);
			LCD_DisplayStringLine(Line4,(uchar *)lcd_buffer);
			
		}
		if(a3_flag)LCD_DisplayStringLine(Line5,"    B3:@      ");//初始状态,没有按下按键
		else 
		{
			sprintf(lcd_buffer,"    B3:%d       ",pass_in[2]);
			LCD_DisplayStringLine(Line5,(uchar *)lcd_buffer);
		}
	}
	else//密码输入正确跳转的界面
	{
		freq = 80000000/(TIM2->PSC+1)/(TIM2->ARR+1);//计算频率
		duty = 100*TIM2->CCR2/(TIM2->ARR+1);//计算占空比
		
		LCD_DisplayStringLine(Line1,"       STA");
		
		sprintf(lcd_buffer,"    F:%dHz   ",freq);
		LCD_DisplayStringLine(Line3,(uchar *)lcd_buffer);
		sprintf(lcd_buffer,"    D:%d%%   ",duty);
		LCD_DisplayStringLine(Line4,(uchar *)lcd_buffer);
	}
	LED_proc();//调用LED处理函数
}

简单讲解一下LCD中的这段代码:

freq = 80000000/(TIM2->PSC+1)/(TIM2->ARR+1);//计算频率
duty = 100*TIM2->CCR2/(TIM2->ARR+1);//计算占空比

上述代码中:

  1. TIM2->PSC是Cudemx中的Prescaler(就是预分频系数,80-1是因为从0开始的,所以到79就是80次了)。
  2. TIM2->ARR是Cudemx中的Counter Period(就是自动重装值,也就是定时的溢出值,溢出时产生一次中断,1000-1与80-1同理)。
  3. TIM2->CCR2是Cudemx中的Pluse(就是定时器2的捕获/比较寄存器的值,因为是PWMmode1,CH polarity是High,所以当计数器的值<Pulse时,对应输出高电平计数器的值>Pulse时,对应输出低电平)。
    在这里插入图片描述

串口处理

因为本题中串口接收的字符串相对较少,在处理串口字符串的时候就直接上条件了。没过多处理,
注意:

  1. 题目中不要求串口返回什么信息,但是我在这里返回了,方便判断是否起作用。
  2. HAL_UART_Transmit_IT()不能连续使用,在十二届赛题中详细讲到,这里就不多讲啦。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口回调函数
{
	if(huart->Instance==USART1)//USB转串口对应串口1
	{
		rx_timer=0;
		finish_rx_flag=0;
		Rxdata[rx_count++] = rxdat;
		HAL_UART_Receive_IT(&huart1,(uchar *)&rxdat,1);//只能中断一次,连续接收再次打开
	}
}
void Usart_proc(void)//串口处理
{
	if(rx_count>0)
	{
		if(rx_timer>=5)//50ms判断串口是不是还在接收
		{
			rx_timer=0;
			if(Rxdata[rx_count-1] == rxdat)finish_rx_flag=1;//接收完成
		}
		if(finish_rx_flag)
		{
			finish_rx_flag=0;
			if(rx_count==7)
			{
				if(Rxdata[3] == '-'&& 
					 Rxdata[4] >='0' && Rxdata[4] <='9' && 
					 Rxdata[5] >='0' && Rxdata[5] <='9' && 
				   Rxdata[6] >='0' && Rxdata[6] <='9' &&
					 Rxdata[0]-'0'==password[0] &&
					 Rxdata[1]-'0'==password[1] &&
				   Rxdata[2]-'0'==password[2])//密码条件判断
				{
					password[0]= Rxdata[4] - '0';
					password[1]= Rxdata[5] - '0';
					password[2]= Rxdata[6] - '0';
					HAL_UART_Transmit_IT(&huart1,"Successfully!\r\n",
					sizeof("Successfully!\r\n"));
					while(huart1.gState != HAL_UART_STATE_READY);
				}
				else
				{
					HAL_UART_Transmit_IT(&huart1,"Error!\r\n",sizeof("Error!\r\n"));
					while(huart1.gState != HAL_UART_STATE_READY);
				}	
			}
			else 
			{
				HAL_UART_Transmit_IT(&huart1,"Error!\r\n",sizeof("Error!\r\n"));
				while(huart1.gState != HAL_UART_STATE_READY);
			}
			rx_count=0;
			memset(Rxdata,0,sizeof(Rxdata));
		}
	}
}

完整功能演示

完整工程文件

总结

自己写的代码,不喜勿喷,欢迎参考呀
以上就是本次的全部内容了,笔者水平有限,仅供参考。
想联系笔者请私信就好。

  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值