蓝桥杯嵌入式——第十二届蓝桥杯嵌入式第一场省赛

蓝桥杯嵌入式——第十二届蓝桥杯嵌入式第一场省赛

一、赛题分析

这是刚刚过去的第十二届第一场嵌入式的省赛,也是我刚刚参加的。由于我用的是旧版,我们那个考场只测试了新版的环境,而旧版的软件环境有问题,和新版的存在冲突,耽搁了很久的时间,有点搞人心态。但是最终还是基本上把赛题的功能全部实现完了的,仅仅串口有一个小bug,就是第8辆车进去过后不能出来,其他的都没有什么问题,比赛的时候时间也做的比较久了,实在是不想再调试了,这个bug就没有管了,最后还有点担心进不了国赛,但是成绩下来那一刻,进国赛了,更加没想到还是全省前三,还是不错了。

比赛的时候,拿到赛题,就发现串口部分不是那么简单。所以,就先一步一步的把其他的模块先实现了,除了串口,其他的模块都很简单,非常中规中矩。接下来就搞串口部分,串口其实说难也不是很难,主要是比较麻烦。

我觉得在我们拿到赛题的时候,应该分模块来实现,首先看看用到了哪些模块, 再去看每一个模块,赛题的要求是什么。比如说输出PWM, 题目要求就是PA7输出2kHz,占空比20%,,只用到了一个通道,并且占空比和频率是固定的,所以直接就想到用定时器的PWM模式就行了,就不需要再去用什么OCTOGGLE模式浪费时间了。

我个人觉得,进国赛是比较简单的,只要选择题不要错的太多,就拿这一届来说,串口部分比较难,所以其他部分必须全部实现了,串口实现一部分,不要有太大的逻辑错误, 影响系统运行,应该进国赛都稳了吧。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

二、问题总结

其他的模块应该都不会遇到什么大的问题,主要就是串口。我串口使用的是IDLE+RXNE来实现串口的不定长数据的接收的。

在实现串口的部分,首先要实现能够正确识别的进入以及出去,然后再注意一些细节的逻辑错误;

  1. 接收到正确信息的长度为22个字符,冒号的位置等等。
  2. 车型只有CNBR和VNBR两种
  3. 时间格式必须正确,并且不能出现时间的逻辑错误,如年份大于99,小时大于23等。
  4. 进入车辆信息和出去车辆信息要匹配,如进入和出去的车辆的ID相同,但是他们的车型不同是不应该的,因为对于同一个车的ID,它的信息一定全都是相同的。
  5. 出去的时间一定要晚于进入的时间。
  6. 整数,单位为小时, 不足 1 小时,按 1 小时统计

下面附上串口部分的关键代码:

void substr(uint8_t* d_str,uint8_t* s_str,uint8_t locate,uint8_t length)
{
    
    
	uint8_t i = 0;
	for(i = 0; i < length; i++)
	{
    
    
		d_str[i] = s_str[locate + i];
	}
	d_str[length] = '\0';
}

uint8_t findLocate(void)	// 找一个空闲的位置
{
    
    
	uint8_t i = 0;
	for(i = 0; i < 8; i++)
	{
    
    
		if(!car[i].notEmpty)	// 如果是空闲
			return i;
	}
	return 0xFF;
}

uint8_t isExist(uint8_t* str)		// 判断车辆是否存在
{
    
    
	uint8_t i = 0;
	for(i = 0; i < 8; i++)
	{
    
    
		if(strcmp((const char*)str,(const char*)car[i].id) == 0)
		{
    
    
			return i;			// 如果这辆车存在,则返回这辆车所在的车位i
		}
	}
	return 0xFF;			// 返回0xFF表示这辆车不存在
}

_Bool checkCmd(uint8_t* str)
{
    
    
	// VNBR:D583:200202120000
	// 0123456789012345678901
	if(RxCounter != 22)
		return 0;
	if((str[0] == 'C' || str[0] == 'V') && str[1] == 'N' && str[2] == 'B' && str[3] == 'R' && str[4] == ':' && str[9] == ':')
	{
    
    
		uint8_t i;
		for(i = 10; i < 22; i++)
		{
    
    
			if(str[i] > '9' || str[i] < '0')
				return 0;
		}
	}
	return 1;
}

void usart_proc(void)
{
    
    
	if(RxFlag)
	{
    
    
		RxFlag = 0;
		
// 将接收到的字符串显示到LCD上面
//		memset(lcd_str,0,sizeof(lcd_str));
//		sprintf((char*)lcd_str,"%-20.20s",RxBuffer);
//		LCD_DisplayStringLine(Line9,lcd_str);
		
		// VNBR:D583:200202120000
		// 0123456789012345678901
		if(checkCmd(RxBuffer))	// 接收到的标准信息应该是22个字符,并且只有在有空闲车位的时候有效
		{
    
    
			uint8_t car_id[5];
			uint8_t car_type[5];
			uint8_t locate = 0xFF;
			uint8_t year_temp,month_temp,day_temp,hour_temp,min_temp,sec_temp;
			// 将字符串中时间的信息提取出来
			year_temp = (RxBuffer[10] - '0') * 10 + (RxBuffer[11] - '0');
			month_temp = (RxBuffer[12] - '0') * 10 + (RxBuffer[13] - '0');
			day_temp = (RxBuffer[14] - '0') * 10 + (RxBuffer[15] - '0');
			hour_temp = (RxBuffer[16] - '0') * 10 + (RxBuffer[17] - '0');
			min_temp = (RxBuffer[18] - '0') * 10 + (RxBuffer[19] - '0');
			sec_temp = (RxBuffer[20] - '0') * 10 + (RxBuffer[21] - '0');
			if(year_temp > 99 || month_temp > 12 || day_temp > 31 || hour_temp > 23 || min_temp > 59 || sec_temp > 59)
			{
    
    
//				printf("shi jian ge shi error!\r\n");
				goto SEND_ERROR;
			}
			substr(car_id,RxBuffer,5,4);		// 将车辆的编号信息提取到car_id
			substr(car_type,RxBuffer,0,4);	// 将车辆的类型信息提取到car_type
			locate = isExist(car_id);				// 查询这辆车是否在车库从存在
			
			if(locate != 0xFF)	// 这辆车在车库中存在
			{
    
    
				int time_val;
				printf("locate:%d,type:%s,id:%s\r\n",locate,car[locate].type,car[locate].id);
				if(strcmp((const char *)car_type,(const char *)car[locate].type))		// 如果车辆的id和车辆的类型不同,则表示信息错误
				{
    
    
//					printf("id and type pi pei error!\r\n");
					goto SEND_ERROR;
				}
				// 假设一年365天,一个月30天,秒为单位
				time_val = (year_temp - car[locate].year_in) * 365 * 24 * 3600 + (month_temp - car[locate].month_in) * 30 * 24 * 3600 + (day_temp - car[locate].day_in) * 24 * 3600 + \
									 (hour_temp - car[locate].hour_in) * 3600 + (min_temp - car[locate].min_in) * 60 + (sec_temp - car[locate].sec_in);
				if(time_val < 0)
				{
    
    
//					printf("time_val error!\r\n");
					goto SEND_ERROR;
				}
				time_val = (time_val + 3599) / 3600;	// 换算成小时,并且不足一个小时按一个小时算
				
				// 输出计费信息
				printf("%s:%s:%d:%.2f\r\n",car[locate].type,car[locate].id,time_val,time_val / 10.0 * (RxBuffer[0] == 'C'?CNBR_fee:VNBR_fee));
				
				if(RxBuffer[0] == 'C')
					CNBR_cnt--;
				else if(RxBuffer[0] == 'V')
					VNBR_cnt--;
				
				memset(&car[locate],0,sizeof(car[locate]));	// 将当前结构体清空
			}
			else		// 这辆车在车库中不存在
			{
    
    
				uint8_t locate = findLocate();	// 找一个空闲的车位
				
				if(locate == 0xFF)	// 没有找到空闲车位
				{
    
    
					goto SEND_ERROR;
				}
//				printf("locate:%d,type:%s\r\n",locate,car[locate].type);
				// 保存车辆信息
				substr(car[locate].type,RxBuffer,0,4);
				substr(car[locate].id,RxBuffer,5,4);
				car[locate].year_in = year_temp;
				car[locate].month_in = month_temp;
				car[locate].day_in = day_temp;
				car[locate].hour_in = hour_temp;
				car[locate].min_in = min_temp;
				car[locate].sec_in = sec_temp;
				car[locate].notEmpty = 1;			// 标记为非空闲
				if(RxBuffer[0] == 'C')
					CNBR_cnt++;
				else if(RxBuffer[0] == 'V')
					VNBR_cnt++;
			}
			goto CMD_YES;
		}
SEND_ERROR:printf("ERROR\r\n");
CMD_YES:		memset(RxBuffer,0,sizeof(RxBuffer));
		RxCounter = 0;
	}
}

/*	测试数据,用来测试逻辑是否有错误
1. *
VNBR:D583:200202120000
VNBR:D583:200202213205

2. *
CNBR:D593:200202120000
CNBR:D593:200203213205

3. *
VNBR:D883:200202120000
VNBR:D883:200202223205

4. *
CNBR:D588:200202120000
CNBR:D588:200202313205	会提示时间设置错误
CNBR:D588:200202223205	时间设置正确

5. *
CNBR:D580:200202120000
CNBR:D580:200202215205

6. *
VNBR:D58S:200202120000
VNBR:D58S:200203213205

7. *
CNBR:D58E:200202120000
CNBR:D58E:200204213205

8. *
CNBR:D58B:200202120000
CNBR:D58B:200205213205

9. 
CNBR:D555:200202120000
CNBR:D555:200205213205
*/

三、代码(完整代码,功能全部实现,附有注释)

码云

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/116451070