第十二届蓝桥杯嵌入式省赛试题以及题解

news2024/11/15 17:41:40

原题展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
    通读本试题后,可以知本试题所涉及到的模块有LCD显示LED指示按键切换串口收发定时器的PWM输出五个部分,试题的总体变化不大。在试题要求的所有功能中,串口这部分是侧重点,它既要负责收发数据,又要对数据进行验证与处理,可谓是试题所有功能中最难的部分。话不多说,下面小编就带大家一起来看看第十二届省赛试题吧!😍😍😍

题解

LED模块

CubeMx配置
在这里插入图片描述
代码样例

    在该试题要求比较单一,只要求有空闲车位时点亮LED1PA7输出2KHz,占空比为20%的脉冲时点亮LED2,其余情况所有的LED灯都熄灭,因此,我们的LED工作函数只需要两个:关闭所有的LED灯以及点亮某个LED灯。但是要注意的是:在初始化完成LCD后以及每次操作LED时都需要先熄灭所有的LED灯,否则开发板上的8个LED会出现工作紊乱的情况。

/*****************************************************
* 函数功能:改变所有LED的状态
* 函数参数:
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeAllLedByStateNumber(char LEDSTATE)
{
	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,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	//打开锁存器    准备写入数据
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	//关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

/*****************************************************
* 函数功能:根据LED的位置打开或者是关闭LED
* 函数参数:
*			uint16_t LEDLOCATION:需要操作LED的位置
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
{
	HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
	
}

按键模块

CubeMx配置
在这里插入图片描述

代码样例

    试题对于按键的要求比较简单,只要能够识别按键按下的位置即可,但是需要注意的是:按键需要消抖,通常使用的消抖方式有:延时消抖状态机,由于G431开发板的按键数量较少,小编采用的是延时消抖。值得一提的是,小编在按键扫描函数中加入锁机制也是为了消抖,防止二次触发。😉😉😉
    代码逻辑也比较简单,核心思想就是:在按键锁打开的前提下,间隔地读取两次按键的值,如果两次值都显示按键已经按下,那么按键就确实是按下;否则,就存在抖动。

/*********************************************
 * 函数功能:按键扫描 含按键消抖 无长按短按设计
 * 函数参数:无
 * 函数返回值:按键的位置
 *            返回值说明:B1-1 B2-2 B3-3 B4-4
*********************************************/
unsigned char scanKey(void)
{
	//按键锁
	static unsigned char keyLock = 1;
    //记录按键消抖时间
    // static uint16_t keyCount = 0;

	//按键按下
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
      || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET) 
      && keyLock == 1){
        //给按键上锁 避免多次触发按键
        keyLock = 0;
        
        //按键消抖 这里最好不要使用延时函数进行消抖 会影响系统的实时性
        // if(++keyCount % 10 < 5) return 0;
        // if(HAL_GetTick()%15 < 10) return 0;
        HAL_Delay(10);

        //按键B1
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
            return 1;
        }
        //按键B2
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
            return 2;
        }
        //按键B3
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
            return 3;
        }
        //按键B4
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
            return 4;
        }
    }
    //按键松开
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
      && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET) 
      && keyLock == 0){
        //开锁
        keyLock = 1;
    }
    return 0;
}

LCD模块

    LCD显示模块是所有模块中初始化最简单的啦!由于官方提供了源代码,咱只需要会使用即可,初始化都可以不使用CubeMX,直接使用源码初始化就好啦

LCD初始化样例

    下面会将LCD显示屏初始化成背景色为黑色、字体颜色为白色的屏幕。LCD显示屏支持的颜色有: 白色(White)、黑色(Black) 、灰色(Grey)、蓝色(Blue)、蓝色2( Blue2)、红色(Red) 、品红色(Magenta)、绿色(Green)、青色(Cyan) 、黄色(Yellow)。

/******************************************************************************
* 函数功能:LCD初始化
* 函数参数:无
* 函数返回值:无
*******************************************************************************/
void lcdInit(void)
{
	//HAL库的初始化
	LCD_Init();
	//设置LCD的背景色
	LCD_Clear(Black);
	//设置LCD字体颜色
	LCD_SetTextColor(White);
	//设置LCD字体的背景色
	LCD_SetBackColor(Black);
}

定时器的PWM输出

CubeMx配置
在这里插入图片描述
代码样例

    在本次试题中,PWM的输出要有两种即可,其一是频率为2KHz、占空比为20%的脉冲信号,另一种是持续输出低电平,那么持续输出低电平是不是可以理解为输出频率2KHz、占空比为0%的脉冲信号呢?😂😂😂答案是当然可以啦,题目要求是输出低电平,那我占空比为0的脉冲信号肯定也是符合要求的呀!!!😜😜😜
    由此,只要按键B4按下,使用函数_pwmWorkByFre()切换PWM工作的占空比即可。

/****************************************
* 函数功能:修改PWM的占空比
* 函数参数:
*			unsigned int compareDate:PWM的比较值
* 函数返回值:无
****************************************/
void _pwmWorkByFre(unsigned int compareDate)
{
	__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,compareDate);
}

串口收发模块

CubeMX配置
在这里插入图片描述
代码样例

    在试题的要求中,单纯的数据传输还是蛮简单的,只要满足能够接收串口发送的数据以及当串口数据出现不符合要求时发送错误信息即可。

使用中断接收PC发送的数据

    为了防止系统未处理完成上一次PC发送的数据,再次接收本次PC发送的数据带来的影响,在串口接收与数据处理中特意加了锁机制。每次系统接收PC数据后会关闭锁,避免多次接收数据;每次数据处理完成后,就会打开锁,系统才会开始接收PC数据。

/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){
		// 重新使能中断
		if(!iRxFlag)
		{
			HAL_UART_Receive_IT(huart,(uint8_t *)&ucRxbuff,sizeof(ucRxbuff)); 
			iRxFlag = 1;
		}
	}
}

串口发送错误信息

    由于题目要求发送信息比较简单,因此,只需要使用HAL库提供的串口发送函数即可。

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t 
*pData, uint16_t Size, uint32_t Timeout);
/*
/*函数解析*/
UART_HandleTypeDef *huart:串口编号
const uint8_t *pData:发送的数据
uint16_t Size:发送数据的大小
uint32_t Timeout:超时时间,需要注意的时其为发送数据的最大时间,如果时间到了,但是数据未发送完,也不会再发送数据了
*/

串口数据处理

    串口数据处理时需要注意以下五点:

  • 数据长度是否符合要求;
  • 数据中的停车类型是否符合要求;
  • 数据中的时间是否符合要求;
  • 停车场中是否还有空余车位给新来的车停放;
  • 车辆出库时的停车类型是否跟车辆入库时停车类型一致;

    只要上述五点中有一点不满足,本次接收数据处理完成后都应该返回Error。
样例代码

    函数逻辑:

  • 步骤一:判断数据长度是否符合要求,如果不符合要求,发送错误信息后返回,否则,进入步骤二;
  • 步骤二:判断该车辆是否是新车入库,如果是,则新增一个存储车辆信息的节点到链表中,函数返回;否则,进入步骤三;
  • 步骤三:车辆出库,先查找车辆入库时间,再判断出库时间以及车辆停车类型是否合理,如果合理,将入库出库时间转换成秒求差后,计算相差的时间间隔,最后进入步骤四;否则发送错误信息后,函数返回;
  • 步骤四:根据车辆停车类型计算停车费,并且通过串口发送到PC,最后删除车辆信息后返回。
/****************************************
* 函数功能:处理接收串口信息
* 函数参数:无
* 函数返回值:无
****************************************/
void _usartMsgProcess(void)
{
	char temp[19];
	//串口未收到数据该函数应该直接返回
	if(!iRxFlag) return ;
	
	struct node*msg = NULL;
	//串口发送的数据长度不对
	if(strlen((char*)ucRxbuff)!= 22)
	{
		goto MYERROR;
	}
	//获取本次车辆的车牌 用于查找该车是否已经入库
	getStringRxBuffDataByLocation((char*)ucRxbuff,temp,5,9);
	msg = searchListNode(pcarMessage,temp);
	
	//车辆出库
	if(msg != NULL)
	{
		uint32_t longTime[3] = {0,0,0};
		//记录停车时间 单位为h
		double dStopTime = 0;
		//记录停车费用 单位为元
		double dStopMoney = 0;
		//记录开始停车时间 结束停车时间
		struct node*eTime = (struct node*)malloc(sizeof(struct node));
		getStringRxBuffDataByLocation((char*)ucRxbuff,eTime->ucType,0,4);
		eTime->year = getIntRxBuffDataByLocation((char*)ucRxbuff,10,12);
		eTime->month = getIntRxBuffDataByLocation((char*)ucRxbuff,12,14);
		eTime->day = getIntRxBuffDataByLocation((char*)ucRxbuff,14,16);
		eTime->hour = getIntRxBuffDataByLocation((char*)ucRxbuff,16,18);
		eTime->minute = getIntRxBuffDataByLocation((char*)ucRxbuff,18,20);
		eTime->second = getIntRxBuffDataByLocation((char*)ucRxbuff,20,22);
		//判断数据是否合理 不合理直接返回打印错误信息
		if(checkData(msg->ucType,eTime->year,eTime->month,eTime->day,eTime->hour,eTime->minute,eTime->second)==0 || strcmp(eTime->ucType,msg->ucType)!=0)
		{
			goto MYERROR;
		}
		else
		{
			//时间转换
			longTime[0] = myMktime(2000+msg->year,msg->month,msg->day,msg->hour,msg->minute,msg->second);
			longTime[1] = myMktime(2000+eTime->year,eTime->month,eTime->day,eTime->hour,eTime->minute,eTime->second);
			longTime[2] = longTime[1]-longTime[0];
			//计算停留时间
			dStopTime = ceil((double)(longTime[2]*1.0/3600));		
			//计算停车费
			if(msg->ucType[0] == 'C')
				dStopMoney = dStopTime*dCnbrPrice;
			else
				dStopMoney = dStopTime*dVnbrPrice;
			//发送信息到PC
			sprintf(temp,"%s:%s:%.0f,%.2f",msg->ucType,msg->ucCode,dStopTime,dStopMoney);
			HAL_UART_Transmit(&huart1,(uint8_t*)temp,sizeof(temp),150);
			//车辆出库
			deleteListNode(pcarMessage,msg->ucCode);
		}
	}
	// 新车入库
	else
	{
		//新车入库 需要新增一个节点存储车辆信息
		struct node* node = (struct node*)malloc(sizeof(struct node));
		node->pNext = NULL;
		getStringRxBuffDataByLocation((char*)ucRxbuff,node->ucType,0,4);
		getStringRxBuffDataByLocation((char*)ucRxbuff,node->ucCode,5,9);
		node->year = getIntRxBuffDataByLocation((char*)ucRxbuff,10,12);
		node->month = getIntRxBuffDataByLocation((char*)ucRxbuff,12,14);
		node->day = getIntRxBuffDataByLocation((char*)ucRxbuff,14,16);
		node->hour = getIntRxBuffDataByLocation((char*)ucRxbuff,16,18);
		node->minute = getIntRxBuffDataByLocation((char*)ucRxbuff,18,20);
		node->second = getIntRxBuffDataByLocation((char*)ucRxbuff,20,22);
		//判断数据是否合理 不合理直接返回打印错误信息
		if(checkData(node->ucType,node->year,node->month,node->day,node->hour,node->minute,node->second)==0 || pcarMessage->uiIdleCount-1<0)
			goto MYERROR;
		//数据无误  添加车辆信息到存储链表中
		else
			addListNode(pcarMessage,node);	
	}	
	
	//清除本次串口接收到的数据 避免影响后续数据
	memset(ucRxbuff,0,sizeof(ucRxbuff));
	//处理完本次串口接收到的数据后清除标志位
	iRxFlag = 0;
	return ;
	
	//接收数据出现问题时 发送错误信息到PC
	MYERROR:	
		HAL_UART_Transmit(&huart1,(uint8_t*)"Error\r\n",sizeof("Error\r\n"),50);
		//清除本次串口接收到的数据 避免影响后续数据
		memset(ucRxbuff,0,sizeof(ucRxbuff));
		//处理完本次串口接收到的数据后清除标志位
		iRxFlag = 0;
}

辅助数据处理的函数

获取字符串指定位置的内容

    在C语言中,对字符串赋值操作并不是特别舒服,因此,写了两个字符串赋值函数,一个函数是将字符串指定位置的值转换成整型数据返回,另一个函数是获取字符串指定位置为值,以字符串返回。

/****************************************
* 函数功能:将指定位置字符串转换成整型数字
* 函数参数:
* 			char*s:传入的字符串数字
*			int iStart:起始位置
*			int iEnd:终止位置
* 函数返回值:
*			res:返回的整型数字
****************************************/
int getIntRxBuffDataByLocation(char*s,int iStart,int iEnd)
{
	int res = 0;
	while(iStart < iEnd)
	{
		res = res*10 + s[iStart] - '0';
		iStart++;
	}
	
	return res;
}

/****************************************
* 函数功能:获取指定位置字符串
* 函数参数:
* 			char*s:传入的字符串
*			int iStart:起始位置
*			int iEnd:终止位置
* 函数返回值:
*			res:返回的字符串
****************************************/
void getStringRxBuffDataByLocation(char*s,char*res,int iStart,int iEnd)
{
	int j=0;
	while(iStart < iEnd)
	{
		res[j++] = s[iStart++];
	}
	res[j++] = '\0';
}

时间处理

    考虑到串口接收数据的多样性——只同年的时间、同年同月的时间、同年同月同天的时间等等,小编特意写了时间转换函数,其能够将年月日时分秒的时间转换成秒,这样就不用再考虑停车起始时间是否同年、同月、同天等情况了。)在获取停车时间时,只需要先将两个时间转换成秒,再计算时间间隔,最后除一小时时间间隔(60*60s)向上取整就可以得到停车时间。

//存储每月的天数
int monthTable[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
/****************************************
* 函数功能:将一个年月日时分秒的时间转换成秒
* 函数参数:
* 			const unsigned int year0:转换时间的年
*			const unsigned int mon0:转换时间的月
*			const unsigned int day:转换时间的天
*			const unsigned int hour:转换时间的小时
*			const unsigned int min:转换时间的分钟
*			const unsigned int sec:转换时间的秒
* 函数返回值:
*			res:返回转换成秒后的时间
****************************************/
unsigned long myMktime(const unsigned int year0, const unsigned int mon0,const unsigned int day,
	const unsigned int hour,const unsigned int min,const unsigned int sec)
{
    unsigned int mon = mon0, year = year0;

    /* 1..12 -> 11,12,1..10 */
    if (0 >= (int) (mon -= 2)) {
        mon += 12;    /* Puts Feb last since it has leap day */
        year -= 1;
    }

    return ((((unsigned long)
          (year/4 - year/100 + year/400 + 367*mon/12 + day) +
          year*365 - 719499
        )*24 + hour /* now have hours */
      )*60 + min /* now have minutes */
    )*60+sec; /* finally seconds */
}

链表处理

  • 链表头以及节点的结构体包含的内容。
//链表头结点
struct head{
	//指向存储辆车的信息
	struct node*pNext;
	//记录空余车位数量
	int uiIdleCount;
	//CNBR类型车辆数量
	int uiCnbrCount;
	//VNBR类型车辆的数量
	int uiCnbrCount;
};

//车辆信息结构体
struct node{
	//记录停车类型
	char ucType[5];
	//记录车牌号
	char ucCode[5];
	//记录进入时间
	int year;
	int month;
	int day;
	int hour;
	int minute;
	int second;
	//指向下一辆车信息
	struct node*pNext;
};
  • 增加链表节点

    判断链表中是否含有节点,如果没有,就直接添加到头结点中;否则就先移动到链表末端,再添加节点;

  • 删除节点

    判断删除的是否是头结点,如果是头结点,那么就返回除头结点外的所有节点;否则就先移动到待删除节点的前,将其指向待删除节点的后一个节点,最后返回即可。

  • 查找节点

    查找节点比较简单,直接遍历链表,挨个节点判断是否是目标节点,如果是目标节点就返回目标节点,否则就返回NULL;

  • 获取节点数量

    由于每次添加节点时都会增加uiCnbrCount的值或uiVnbrCount的值,因此只要返回uiCnbrCount+uiVnbrCount即可。

/*******************************************
* 函数功能:给链表添加节点
* 参数:
*		struct head*head:链表的头结点
*		LISTNODETYPE*newNode:链表新增的节点
* 返回值:无
********************************************/
void addListNode(struct head*head,struct node*newNode)
{
	struct node*list = head->pNext;
	//判断是否没有任何节点
	if(list)
	{
		//移动到待添加位置的前一个位置
		while(list->pNext){
			list = list->pNext;
		}
		//添加节点并且给链表长度加1
		list->pNext = newNode;
	}
	else
	{
		head->pNext = newNode; 
	}
	//判断节点的类型
	if(newNode->ucType[0] == 'C')
	{
		head->uiCnbrCount++;
		head->uiIdleCount--;
	}
	else if(newNode->ucType[0] == 'V')
	{
		head->uiVnbrCount++;
		head->uiIdleCount--;
	}
}


/*******************************************
* 函数功能:给链表删除节点
* 参数:
*		struct head*head:链表的头结点
*		unsigned char*target:目标值
* 返回值:无
********************************************/
void deleteListNode(struct head*head,char*target)
{
	struct node*p,*q;
	if(!head->pNext) return ;
	p = head->pNext;
	//判断头结点是否是目标值
	if(strcmp((char*)p->ucCode,(char*)target))
	{
		//遍历出头节点外的所有节点
		while(p->pNext && strcmp((char*)p->pNext->ucCode,(char*)target))
		{
			p = p->pNext;
		}
		q = p->pNext;
		p->pNext = p->pNext->pNext;
	}
	else
	{
		//删除头结点
		q = head->pNext;
		head->pNext = head->pNext->pNext;
	}
	//判断目标值的类型
	if(q->ucType[0] == 'C')
	{
		head->uiCnbrCount--;
		head->uiIdleCount++;
	}
	else if(q->ucType[0] == 'V')
	{
		head->uiVnbrCount--;
		head->uiIdleCount++;
	}
}

/*******************************************
* 函数功能:判断链表是否为空
* 参数:
*		struct head*head:链表的头结点
* 返回值:
*		链表为空返回0 否则返回链表长度
********************************************/
unsigned int isEmptyListNode(struct head*head)
{
	return head->uiCnbrCount+head->uiVnbrCount;
}

/*******************************************
* 函数功能:查找链表
* 参数:
*		struct head*head:链表的头结点
*		
* 返回值:
*		链表无值返回0 否则返回1
********************************************/
struct node* searchListNode(struct head*head,char*target)
{
	struct node*p = head->pNext;
	//遍历链表
	while(p)
	{
		//判断是否应该删除
		if(!strcmp(p->ucCode,target))
		{
			return p;
		}
		p = p->pNext;
	}
	return NULL;
}

完整的系统配置文件

  • 使用说明
        sysInit()函数是在CubeMx配置完成后新增的初始化函数,sysWork()是系统的工作逻辑函数,其余函数都是一些小型配置函数,均被sysInit()或sysWork函数调用过了。
#include "config.h"


/* 存储串口1接收的数据 
** 数据样例:CNBR: A392: 2002021 20000  停车类型:车辆编号:进入/退出时间(YYMMDDHmmSS)
** 数据样例解释:表示停车类型CNBR,编号为A392的车辆,进入停车场时间为2020年2月2日12时整。
**/
uint8_t ucRxbuff[22];
//记录是否收到信息
int iRxFlag = 0;

//频率测量
u32 crrl_t,frd;
u32 oldFrd = 1;

//记录定时器7触发次数
uint16_t uiTime7Count = 0;
//记录按键的值
uint8_t ucKeyNumber = 0;
//记录车辆信息的头指针
struct head*pcarMessage;


//VNBR类型停车单价
double dVnbrPrice = 2.0;
//CNBR类型停车单价
double dCnbrPrice = 3.5;
//记录显示界面
int iDisplayMod = 1;
//记录PWM输出的模式
int iPwmMode = 0;

/***********************************************
* 函数功能:自定义的系统初始化
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysInit(void)
{
	//LCD初始化
	lcdInit();
	//关闭所有的LED
	changeAllLedByStateNumber(0);
	//打开串口的中断接收功能
	HAL_UART_Receive_IT(&huart1,(uint8_t *)&ucRxbuff,sizeof(ucRxbuff)); 
	//打开定时器17通道1的PWM输出功能
	HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
	_pwmWorkByFre(0);
	
	//为汽车存储申请空间
	pcarMessage = (struct head*)malloc(sizeof(struct head));
	pcarMessage->uiIdleCount = 8;
	pcarMessage->uiCnbrCount = 0;
	pcarMessage->uiVnbrCount = 0;
}


/***********************************************
* 函数功能:系统工作逻辑函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void sysWork(void)
{
	//按键处理
	_keyPro();
	
	//串口数据处理
	_usartMsgProcess();
	
	//显示停车场车辆信息
	if(iDisplayMod)
		_dataMessageDisplay();
	//显示单价信息
	else
		_paraMessageDisplay();
	//LED工作函数
	_LEDDisplay();
}

/***********************************************
* 函数功能:按键工作逻辑函数
* 函数参数:无
* 函数返回值:无
***********************************************/
void _keyPro(void)
{
	ucKeyNumber = scanKey();
	switch(ucKeyNumber)
	{
		//按键B1 切换显示界面
		case 1: 
			iDisplayMod ^= 1;
			break;
		//按键B2 增加费率
		case 2: 
			if(!iDisplayMod)
			{
				dVnbrPrice += 0.5;
				dCnbrPrice += 0.5;
			}
			break;
		//按键B3 减少费率
		case 3: 
			if(!iDisplayMod)
			{
				dVnbrPrice -= 0.5;
				dCnbrPrice -= 0.5;
			}
			break;
		//按键B4 切换PA7的PWM输出
		case 4: 
			iPwmMode ^= 1;
			//持续输出低电平
			if(iPwmMode)
				_pwmWorkByFre(20);
			//输出占空比为20的PWM
			else
				_pwmWorkByFre(0);
			break;
		default:return ;
	}
	ucKeyNumber = 0;
}

/****************************************
* 函数功能:汽车信息显示界面
* 函数参数:无
* 函数返回值:无
****************************************/
void _dataMessageDisplay(void)
{
	char temp[20];
	LCD_DisplayStringLine(Line1,(uint8_t*)"       Data");
	sprintf(temp,"   CNBR:%3d  ",pcarMessage->uiCnbrCount);
	LCD_DisplayStringLine(Line3,(uint8_t*)temp);
	sprintf(temp,"   VNBR:%3d  ",pcarMessage->uiVnbrCount);
	LCD_DisplayStringLine(Line5,(uint8_t*)temp);
	sprintf(temp,"   IDLE:%3d",pcarMessage->uiIdleCount);
	LCD_DisplayStringLine(Line7,(uint8_t*)temp);
}

/****************************************
* 函数功能:汽车信息显示界面
* 函数参数:无
* 函数返回值:无
****************************************/
void _paraMessageDisplay(void)
{
	char temp[20];
	LCD_DisplayStringLine(Line1,(uint8_t*)"       Para");
	sprintf(temp,"   CNBR:%.2f",dCnbrPrice);
	LCD_DisplayStringLine(Line3,(uint8_t*)temp);
	sprintf(temp,"   VNBR:%.2f",dVnbrPrice);
	LCD_DisplayStringLine(Line5,(uint8_t*)temp);
	LCD_ClearLine(Line7);
}

/****************************************
* 函数功能:修改PWM的占空比
* 函数参数:
*			unsigned int compareDate:PWM的比较值
* 函数返回值:无
****************************************/
void _pwmWorkByFre(unsigned int compareDate)
{
	__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,compareDate);
}

/****************************************
* 函数功能:LED工作函数
* 函数参数:无
* 函数返回值:无
****************************************/
void _LEDDisplay(void)
{
	//关闭所有LED灯
	changeAllLedByStateNumber(0);
	//有空余 LED1点亮
	if(pcarMessage->uiIdleCount > 0)
		changeLedStateByLocation(LED1,1);
	//PWM占空比为20 LED2点亮
	if(iPwmMode)
		changeLedStateByLocation(LED2,1);
}

/****************************************
* 函数功能:处理接收串口信息
* 函数参数:无
* 函数返回值:无
****************************************/
void _usartMsgProcess(void)
{
	char temp[19];
	//串口未收到数据该函数应该直接返回
	if(!iRxFlag) return ;
	
	struct node*msg = NULL;
	//串口发送的数据长度不对
	if(strlen((char*)ucRxbuff)!= 22)
	{
		goto MYERROR;
	}
	//获取本次车辆的车牌 用于查找该车是否已经入库
	getStringRxBuffDataByLocation((char*)ucRxbuff,temp,5,9);
	msg = searchListNode(pcarMessage,temp);
	
	//车辆出库
	if(msg != NULL)
	{
		uint32_t longTime[3] = {0,0,0};
		//记录停车时间 单位为h
		double dStopTime = 0;
		//记录停车费用 单位为元
		double dStopMoney = 0;
		//记录开始停车时间 结束停车时间
		struct node*eTime = (struct node*)malloc(sizeof(struct node));
		getStringRxBuffDataByLocation((char*)ucRxbuff,eTime->ucType,0,4);
		eTime->year = getIntRxBuffDataByLocation((char*)ucRxbuff,10,12);
		eTime->month = getIntRxBuffDataByLocation((char*)ucRxbuff,12,14);
		eTime->day = getIntRxBuffDataByLocation((char*)ucRxbuff,14,16);
		eTime->hour = getIntRxBuffDataByLocation((char*)ucRxbuff,16,18);
		eTime->minute = getIntRxBuffDataByLocation((char*)ucRxbuff,18,20);
		eTime->second = getIntRxBuffDataByLocation((char*)ucRxbuff,20,22);
		//判断数据是否合理 不合理直接返回打印错误信息
		if(checkData(msg->ucType,eTime->year,eTime->month,eTime->day,eTime->hour,eTime->minute,eTime->second)==0 || strcmp(eTime->ucType,msg->ucType)!=0)
		{
			goto MYERROR;
		}
		else
		{
			//时间转换
			longTime[0] = myMktime(2000+msg->year,msg->month,msg->day,msg->hour,msg->minute,msg->second);
			longTime[1] = myMktime(2000+eTime->year,eTime->month,eTime->day,eTime->hour,eTime->minute,eTime->second);
			longTime[2] = longTime[1]-longTime[0];
			//计算停留时间
			dStopTime = ceil((double)(longTime[2]*1.0/3600));		
			//计算停车费
			if(msg->ucType[0] == 'C')
				dStopMoney = dStopTime*dCnbrPrice;
			else
				dStopMoney = dStopTime*dVnbrPrice;
			//发送信息到PC
			sprintf(temp,"%s:%s:%.0f,%.2f\r\n",msg->ucType,msg->ucCode,dStopTime,dStopMoney);
			HAL_UART_Transmit(&huart1,(uint8_t*)temp,sizeof(temp),150);
			//车辆出库
			deleteListNode(pcarMessage,msg->ucCode);
		}
	}
	// 新车入库
	else
	{
		//新车入库 需要新增一个节点存储车辆信息
		struct node* node = (struct node*)malloc(sizeof(struct node));
		node->pNext = NULL;
		getStringRxBuffDataByLocation((char*)ucRxbuff,node->ucType,0,4);
		getStringRxBuffDataByLocation((char*)ucRxbuff,node->ucCode,5,9);
		node->year = getIntRxBuffDataByLocation((char*)ucRxbuff,10,12);
		node->month = getIntRxBuffDataByLocation((char*)ucRxbuff,12,14);
		node->day = getIntRxBuffDataByLocation((char*)ucRxbuff,14,16);
		node->hour = getIntRxBuffDataByLocation((char*)ucRxbuff,16,18);
		node->minute = getIntRxBuffDataByLocation((char*)ucRxbuff,18,20);
		node->second = getIntRxBuffDataByLocation((char*)ucRxbuff,20,22);
		//判断数据是否合理 不合理直接返回打印错误信息
		if(checkData(node->ucType,node->year,node->month,node->day,node->hour,node->minute,node->second)==0 || pcarMessage->uiIdleCount-1<0)
			goto MYERROR;
		//数据无误  添加车辆信息到存储链表中
		else
			addListNode(pcarMessage,node);	
	}	
	
	//清除本次串口接收到的数据 避免影响后续数据
	memset(ucRxbuff,0,sizeof(ucRxbuff));
	//处理完本次串口接收到的数据后清除标志位
	iRxFlag = 0;
	return ;
	
	//接收数据出现问题时 发送错误信息到PC
	MYERROR:	
		HAL_UART_Transmit(&huart1,(uint8_t*)"Error\r\n",sizeof("Error\r\n"),50);
		//清除本次串口接收到的数据 避免影响后续数据
		memset(ucRxbuff,0,sizeof(ucRxbuff));
		//处理完本次串口接收到的数据后清除标志位
		iRxFlag = 0;
}

小结

    总的来说,本届试题难度不大,知识点也比较常规,但是存储车辆信息的方式以及处理串口数据是试题中比较难的部分, 需要各位多花心思。


    最后,小编在此处附上获取源码的链接蓝桥杯嵌入式源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/131167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用C++播放音频

编译环境为codeblocks 20.03&#xff0c;编译器为mingw64非自带的版本&#xff08;版本号多少忘记了&#xff09; 头文件 #include<dsound.h> #include <mmsystem.h> #pragma comment ( lib, "winmm.lib" ) 播放音频代码 int play_sound() {mciSendStr…

linux搭建服务器的准备工作

一&#xff0c;操作环境 创建完虚拟机&#xff0c;安装命令行界面的linux操作系统&#xff08;centos8,rocky8,rhel8) 详细过程请看rhcsa第一天作业 二&#xff0c;为服务器配置白名单和安全组 1&#xff0c; 查看selinux的工作模式 使用命令getenforce查看 工作模式有以下三种…

JavaSE学习(一)

1.java的特点 一次编译&#xff0c;到处运行 2.转义字符 \t 制表符 \\ 单个\ \ 单引号 \" 双引号 \n 换行 \r 回车3.注释 1.单行注释 // 2.多行注释 /* */ 3.文档注释 注释内容可以被JDK提供的工具javadoc所解析&#xff0c;生成一套以网页文件形式体现的该程序的说…

三、MySQL 数据库的基本操作

文章目录一、前置知识二、SQL 语言2.1 SQL 语言的兴起与语法标准2.2 SQL 是什么及 SQL 能做什么2.3 SQL 语言规范2.4 SQL 语句分类三、认识数据库3.1 数据库基本概念3.2 数据库常用对象3.3 系统数据库四、创建数据库五、查看数据库六、选择数据库七、修改数据库八、删除数据库九…

2023年国家留学基金委(CSC)有关国别申请、派出注意事项

国家留学基金委&#xff08;CSC&#xff09;已开始陆续公布2023年度的各公派项目实施办法&#xff0c;我们关注到&#xff1a;尽管选派流程及办法与往年相比没有明显变化&#xff0c;但由于各国对于接收CSC出国留学人员的签证、保险等会有不同的要求&#xff0c;所以CSC专门细化…

RHCE第一天之Linux例行性工作at、crontab详解

文章目录一、学习内容总结1、单一执行的例行性工作at2、循环执行的例行性工作crontab二、作业at和crontab的使用一、学习内容总结 1、单一执行的例行性工作at **概念&#xff1a;**指仅处理执行一次就结束了的工作。 要使用单一工作调度时&#xff0c;linux上面需要有负责这个…

电磁兼容大作业

目录 作业一 滤波器1的仿真 作业二 滤波器2的仿真 1. 初始滤波器 2. 仅加金属通孔 3. 仅加拓展枝节 4. 完整的发夹滤波器 作业三 屏蔽 1. 仿真未加屏蔽时的S11 S12 S21 S22 2. 尝试3.37GHz学号的仿真 作业一 滤波器1的仿真 仿真模型可以得到S参数&#xff0c;观察曲线…

一个测试人写在2022年的一些小感受

抓住2022年的尾巴&#xff0c;作为一名测试人&#xff0c;聊聊我的一些小感受&#xff0c;也给自己的2023年加加油&#xff1a; 这里写目录标题一、 不同的赛道&#xff0c;但是每个赛道都需要打比赛二、软件质量很重要&#xff0c;但是测试人却不重要&#xff1f;&#xff01;…

深度学习目标检测_YOLOV1超详细解读

文章目录YOLO背景介绍YOLO的发家史YOLO核心思想YOLO实现细节Pr(Object)Pr(Object)Pr(Object)的概率计算Pr(Classi∣Object)Pr(Class_i ∣Object)Pr(Classi​∣Object)的概率计算YOLO网络设计归一化30维向量含义预测框的定位再筛选bounding box损失函数由浅入深逐一解析损失函数…

MySQL数据库基础面试题

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java面试题…

python中的封装和继承

目录 一.面向对象的三大特性 私有成员 使用私有成员 封装小结 二.继承 pass关键字 多继承 多继承注意事项 小结 三.复写和使用父类成员 复写父类成员的语法 调用父类同名成员 一.面向对象的三大特性 面向对象编程&#xff0c;是许多编程语言都支持的一种编程思想。 简…

【机器学习】基于Pytorch和GoogleNet的海面舰船图像分类

文章目录基于Pytorch和GoogleNet的海面舰船图像分类1. 问题概述2. 实验环境、依赖库与代码结构3. GoogleNet网络架构3.1 Inception结构3.2 辅助分类器4. GoogleNet网络特征提取说明4.1 低层特征提取4.2 Inception特征提取4.3 分类器特征提取5. 具体参数设计6. 实际代码说明7. 网…

关于ElasticSearch新建文档的姿势

定义如下mapping&#xff0c;并且创建索引&#xff0c;索引包括四个字段 有三个分片 (number_of_shards)&#xff0c;每个分片有一个副本分片(number_of_replicas) PUT books {"mappings": {"properties": {"book_id": {"type": &qu…

[SUCTF 2018]MultiSQL(预处理)

打开界面发现注册admin已经有了这个用户&#xff0c;就注册了一个admin 然后功能都看一下&#xff0c;发现有一个id的get传参&#xff0c;所以我们可以传参数&#xff0c;实验一下是不是sql注入点 id1的时候显示了admin的用户&#xff0c;想到了水平越权的问题。但还是没啥&am…

计数指针 std::shared_ptr

shared_ptr 共享指针 shared_ptr 计数指针称为共享指针&#xff0c;可以共享数据。shared_ptr 创建了一个计数器与类对象所指的内存相关联Copy 之后计数器加一&#xff0c;销毁之后计数器减一。计数器 API 接口为 &#xff1a;use_count() 测试代码&#xff1a; #include <…

Python + Django开发在线考试管理系统(附源码)

本文最终实现一个Web在线考试管理系统&#xff0c;可作为Python Web&#xff0c;Django的练手项目&#xff0c;也可以作为计算机毕业设计参考项目。 文章目录系统功能需求分析系统设计及实现思路源码分享&系统实现过程系统展示系统功能需求分析 在线考试管理系统&#xff…

机器学习中的数学原理——二分类问题

今天是2022年的最后一天&#xff0c;提前祝大家新年快乐&#xff01;这个专栏主要是用来分享一下我在机器学习中的学习笔记及一些感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎私信或者评论区留言&#xff01;这一篇就更新一下《白话机器学习中的数…

shell流程控制之条件判断练习

1、ping主机测试,查看主机是否存活&#xff1b; #!/bin/bash ######################### #File name:ping.sh #Version:v1.0 #Email:admintest.com #Created time:2022-12-28 05:06:21 #Description: ######################### var$( ifconfig ens33 | grep inet | grep -v…

【JavaSE成神之路】Java面向对象(下)

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容还是Java的面向对象。 1. 构造方法 构造方法和new关键字是一对好拍档。 在之前GirlFriend的例子中&#xff0c;我们写了两个构造方法&#xff…

gl inet mt1300读写性能测试

为什么买这个路由器 1.最近需要一个路由器, 2.我需要在局域网存储一些东西方便手机电脑访问(微型nas), 3.另外还希望这个路由器是开源智能路由器(能装插件玩), 4.小,非常小,typec 供电(5v 1.5A),非常方便,支持tf卡 usb3.0 wifi 5GHz (1)不用nas的原因:我只是轻度使用nas,专…