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

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

第十二届省赛题目

在这里插入图片描述

题目分析

个人观点:博主一眼扫过去,发现难点在串口,其他的LED按键界面显示PWM输出比较简单,因此在此只分析串口,代码重点讲解串口
串口:

  1. 车辆类型只有2种CNBR和VNBR
  2. 数据长度固定22字符
  3. 接收的字符存在逻辑错误格式不正确,串口发送Error,全部正确不做反应。
  4. 停车时间不足一小时,按一小时计算。
  5. 字符串的处理是个难点

涉及模块主要代码

主要变量与结构体

struct keys//按键结构体
{
	uchar jugde;
	bool single;
	bool sta;
};
struct cars//车辆结构体
{
	char type[5];//类型
	char id[5];//编号
	uchar year;//年
	uchar month;//月
	uchar day;//天
	uchar hour;//时
	uchar min;//分
	uchar sec;//秒
};
struct keys key[4]={0,0,0};
struct cars car[8]={"","",0,0,0,0,0,0};

float C_expense=3.5,V_expense=2.0;

uchar car_c=0,car_v=0,car_i=0;//分别为CNBR、VNBR、IDLE
uchar car_total=0;//停车场中车辆总数
uchar Rx_count=0;//接收的内容计数
uchar ctimer=0;//串口判断
uchar car_go=0;//cars 结构体中第几辆车要出停车场

char Rxdata[30];//串口接收的所有内容
char rxdat;//串口接收保存的变量
char Txdata[30];//串口发送的内容
char lcd_buffer[30];//lcd显示缓存
char type[5],id[5],time[13];//存储车辆的类型,编号,时间
char year[3],month[3],day[3],hour[3],min[3],sec[3];//用于分割字符串

bool uart_finish_flag=0;//串口接收完成标志位
bool scene_flag=0;//0:车位显示界面 1:费率设置界面
bool control_flag=0;//1:输出 PWM 2KHZ 20%  0:输出低电平
bool rx_flag=0;//串口接收数据成功标志  0:数据错误  1:数据有效
bool park_flag=0;//1:出停车场,0:进停车场
uchar cyear=0,cmonth=0,cday=0,chour=0,cmin=0,csec=0;//用于接收时间的转换

串口处理

注意:笔者对错误类型进行了相应返回赛题中只需要返回Error即可

HAL_UART_Transmit_IT()避坑

该函数不能连续使用!!!
例如…HAL_UART_Transmit_IT(…);HAL_UART_Transmit_IT(…)…;
原因:串口不能中断同时触发。
在这里插入图片描述

串口是否接收完成的简单处理

将串口接收的数据存到Rxdata数组里,并每30ms判断是否有新的内容更新,如果有就继续接收,如果没有就表示接收完成,然后对Rxdata进行相应的处理。
主要代码如下:

//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)//USB转串口对应的是串口1,PA9,PA10
	{
		uart_finish_flag=0;//完成标志位清0
		ctimer=0;//串口接收超时时间清0
		Rxdata[Rx_count++] = rxdat;
		
		//只能中断一次,所以接收完后需要再次打开,确保能连续接收
		HAL_UART_Receive_IT(&huart1,(uchar *)&rxdat,1);
	}
}
void Usart_rxproc(void)//对串口接收的内容进行处理
{
	if(Rx_count>0)
	{
		//这里写>=3条件宽一点,==3的话可能会卡死
		if(ctimer>=3)//30ms判断一次串口,定时器配置的是10ms中断一次
		{
			ctimer=0;
			//串口缓存数据30ms后等于前一次数据,则接收结束
			if(Rxdata[Rx_count-1] == rxdat)uart_finish_flag=1;//串口接收完成
		}
		if(uart_finish_flag==1)//串口接收完成的处理
		{
			uart_finish_flag=0;//清零
			if(Rx_count==22)//数据长度一致
			{
				Parking_equal_data_proc();//调用数据长度一致的处理
			}
			else HAL_UART_Transmit_IT(&huart1,"Error of length\r\n",sizeof("Error of length\r\n"));
			Rx_count=0;//清零
			//C 库函数 void *memset(void *str, int c, size_t n) 
			//复制字符 int c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
			memset(Rxdata,0,sizeof(Rxdata));//清零接收数组
		}
	}
}

串口接收的数据是固定22长度的处理

数据的有的有效性进行判断,如果有效,判断是进停车场还是出停车场
主要代码如下:

void Parking_equal_data_proc(void)//对接受的数据进行处理和判断
{
	uchar i=0;
	sscanf(Rxdata,"%4s:%4s:%12s",type,id,time);
	/*对等长数据进行判断是否正确*/

	if(strcmp("CNBR",type)==0 || strcmp("VNBR",type)==0)
	{
		for(i=0; i<12; i++)//判断数据中时间是否正确
			if(*(time+i) <'0' && *(time+i) >'9')break;
	
		if(i==12)	
		{
			/*对接收的时间进行转换*/
			sscanf(time,"%2s%2s%2s%2s%2s%2s",year,month,day,hour,min,sec);
			cyear = atoi(year);cmonth = atoi(month);
			cday  = atoi(day);chour = atoi(hour);
			cmin = atoi(min);csec = atoi(sec);
			if(cyear<=99 && cmonth <=12 && cday<=31 &&
				 chour <= 23 && cmin <=59 && csec <=59 )//时间数据是否有效
			{
				rx_flag=1;//数据有效
				HAL_UART_Transmit_IT(&huart1,"Correct\r\n",sizeof("Correct\r\n"));
				while(huart1.gState != HAL_UART_STATE_READY);
			}
			else
			{
				rx_flag=0;//数据无效
				HAL_UART_Transmit_IT(&huart1,"Error of time\r\n",sizeof("Error of time\r\n"));
				while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
			}
		}
		else 
		{
			rx_flag=0;数据无效
			HAL_UART_Transmit_IT(&huart1,"Error of time\r\n",sizeof("Error of time\r\n"));
			while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
		}
	}
	else
	{
		rx_flag=0;数据无效
		HAL_UART_Transmit_IT(&huart1,"Error of type\r\n",sizeof("Error of type\r\n"));
		while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
	}
	if(rx_flag==1)//数据有效才进行的操作
	{
		Parking_jugde();//判断车辆是进停车场还是出停车场
		//park_flag=0: 进停车场 park_flag=1: 出停车场
		if(park_flag ==0)Parking_in();//进停车场
		else Parking_out();//出停车场并计算费用
		rx_flag=0;

	}	
}

进停车场(及保存接收的数据)

void Parking_in(void)//进停车场处理
{
	/*strcmp()会依次毕竟字符串中的每个字符的ASCII码,
				如果全部都相等返回0 ,否则返回>0或者<0*/
	if(car_i!=0)
	{
		if(strcmp("CNBR",type)==0)//保存数据
		{
			strcpy(car[car_total].type,type);
			strcpy(car[car_total].id,id);
			car[car_total].year = cyear;
			car[car_total].month = cmonth;
			car[car_total].day = cday;
			car[car_total].hour = chour;
			car[car_total].min = cmin;
			car[car_total].sec = csec;
			car_c++;//CNBR类型加1
			car_total++;//总停车车辆加1
		}
		else if(strcmp("VNBR",type)==0)//保存数据
		{
			strcpy(car[car_total].type,type);
			strcpy(car[car_total].id,id);
			car[car_total].year = cyear;
			car[car_total].month = cmonth;
			car[car_total].day = cday;
			car[car_total].hour = chour;
			car[car_total].min = cmin;
			car[car_total].sec = csec;
			car_v++;//VNBR类型加1
			car_total++;//总停车车辆加1
		}
	}
	else 
	{
		HAL_UART_Transmit_IT(&huart1,"Error of Full parking space\r\n",
				sizeof("Error of Full parking space\r\n\r\n"));
		while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
	}
}


出停车场(及删除对应数据和计算停车费)

void Parking_out(void)//已经停的车辆,出停车场
{
	int parking_time=0;//停车时间
	double parking_cost;//停车费用
	
	parking_time = (cyear - car[car_go].year)*365*24+
							   (cmonth - car[car_go].month)*30*24+
								 (cday - car[car_go].day)*24+
								 (chour - car[car_go].hour);//取出小时及以前的数据进行换算
	
	/*parking_time处理*/
	if(parking_time<0)
	{
		HAL_UART_Transmit_IT(&huart1,"Error of time illegal\r\n",
									sizeof("Error of time illegal\r\n"));
		while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
	}
	
	else if(parking_time==0)
	{
		if(((cmin - car[car_go].min)*60 +csec - car[car_go].sec) < 0)//数据错误
		{
			HAL_UART_Transmit_IT(&huart1,"Error of time illegal\r\n",
									sizeof("Error of time illegal\r\n"));
			while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
		}
		else parking_time=1;//不满1小时,按一小时处理
	}
	else if(parking_time>0)
	{
		if(((cmin - car[car_go].min)*60 +csec - car[car_go].sec) > 0)
			parking_time++;//不满1小时,按一小时处理
	}
	
	if(parking_time>0)//时间处理完成进行费用等其他运算
	{
		if(strcmp(car[car_go].type,"CNBR")==0 &&
			 strcmp(car[car_go].id,id)==0)
		{
			car_c--;parking_cost = parking_time * C_expense;
			sprintf(Txdata,"%s:%s:%d:%.2f",car[car_go].type,car[car_go].id,parking_time,parking_cost);
		}
		else if(strcmp(car[car_go].type,"VNBR")==0 &&
				 strcmp(car[car_go].id,id)==0)
		{
			car_v--;parking_cost = parking_time * V_expense;
			sprintf(Txdata,"%s:%s:%d:%.2f\r\n",car[car_go].type,car[car_go].id,parking_time,parking_cost);
		}
		HAL_UART_Transmit_IT(&huart1,(uchar *)Txdata,sizeof(Txdata));
		parking_time=0;//使用完后清零
		parking_cost=0;//使用完后清零
		for(uchar i=car_go+1; i<8; i++)//加1防止越界
			car[i-1] = car[i];//从car_go开始,让后面覆盖前面,就完成了出停车场操作
		car_total--;
	}		
	
}

判断车辆是进停车场还是出停车场

car结构体进行遍历,查找是否有对应车辆

void Parking_jugde(void)//查找是否有这个车辆
{
	uchar i=0;
	for(i=0;i<8;i++)
	{
		if(strcmp(car[i].type,type)==0 &&
			 strcmp(car[i].id,id)==0)
		{car_go=i;break;}//找到了该车辆
	}
	if(i==8)park_flag=0;//0:进停车场
	else park_flag=1;//1:出停车场
}

LED和PWM

由于LED和PWM相对简单,且在以前的赛题文章中已经写到,在此就不多赘述。
主要代码:

void PWM_generation_LED(void)//PWM开启与关闭,和LED显示
{
	if(control_flag==1)//开PWM输出并且点亮相应LED
	{
		HAL_TIM_PWM_Start_IT(&htim17,TIM_CHANNEL_1);
		if(car_i>0)LED_disp(0x03);
		else LED_disp(0x02);
	}
	else //关PWM输出并且熄灭相应LED
	{
		HAL_TIM_PWM_Stop_IT(&htim17,TIM_CHANNEL_1);
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET);
		if(car_i>0)LED_disp(0x01);
		else LED_disp(0x00);
	}
}

按键扫描

前面文章多次提到短按,在此不多赘述。
主要代码:

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].jugde)
		{
			case 0://状态1:判断按键是否按下
			{
				if(key[i].sta==0)key[i].jugde=1;
			}break;
			case 1://状态2:按键消抖
			{
				if(key[i].sta==0)key[i].jugde=2;
				else key[i].jugde=0;
			}break;
			case 2://状态3:确认按下,标志位置1
			{
				if(key[i].sta==1)
				{
					key[i].single=1;
					key[i].jugde=0;
				}
			}break;	
		}
	}
}

完整功能演示视频

完整工程文件

总结

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

  • 10
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值