????【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦????????????
????本系列专栏 - 蓝桥杯嵌入式_勾栏听曲_0的博客
????欢迎大家 ???? 点赞???? 评论???? 收藏⭐️
????个人主页 - 勾栏听曲_0的博客????
????希望本文能对你有所帮助,如有不足请指正,共同进步吧????
????君子欲讷于言而敏于行。????
题目
硬件框图
功能要求
功能概述
1)设计一个停车计费系统,能够完成费率设置、费用计算等功能。
2)使用串口获取车辆进、出停车场信息和时间,并能够输出计费信息。
3)使用按键完成费率设置、调整功能。
4)按照显示要求,通过LCD显示停车状态、费率参数。
5)通过PA7输出固定频率和占空比的脉冲信号或持续低电平。
6)使用LED指示灯完成相关指示功能。
性能要求
1)计费信息输出响应时间:≤0.1秒;
2)按键响应时间:≤0.2秒;
3)车位数量:8个。
LED显示界面
1)车位显示界面
在车位显示界面下,通过LCD显示界面名称(Data)、停车场内目前的停车数量和空闲车位,CNBR和VNBR代表两类不同的停车类型。
上图所示停车数量共6辆,CNBR类2辆,VNBR类4辆,空闲车位2个。
2)费率设置界面
在费率设置界面下,通过LCD显示界面名称(Para)、CNBR类型和VNBR类型停车的费率,单位为元/小时,保留小数点后2位有效数字。
上图所示CNBR类停车费率位3.50元/小时,VNBR类型停车费率位2.00元/小时。
3)LCD通用显示要求
显示背景色(BackColor):黑色显示前景色(TextColor):白色
请严格按照图示2、3要求设计各个信息项的名称(区分字母大小写)和行列位置。
按键功能
1)B1:定义为“界面切换”按键,切换LCD显示“车位显示界面”和“费率设置界面”。
2)B2:定义为“加”按键,每次按下B2按键,CNBR、VNBR费率增加0.5元。
3)B3:定义为“减”按键,每次按下B3按键,CNBR、VNBR费率减少0.5元。
4)B4:定义为“控制”按键,按下后,切换PA7端口输出状态(2KHz,20%占空比的脉冲信号或持续低电平),切换要求如下图所示。
5)通用按键设计要求
按键应进行有效的防抖处理,避免出现一次按下、多次触发等情形。
按键B2、B3仅在费率设置界面有效。
串口功能
1)使用竞赛平台上的USB转串口完成相关功能设计。
2)串口通信波特率设置为9600bps。
3)使用4个任意ASCII字符组成的字符串标识车辆,作为车辆编号。
4)串口接收车辆出入信息
入停车场
停车类型:车辆编号:进入时间(YYMMDDHHmmSs)举例:
CNBR:A392:200202120000
表示停车类型CNBR,编号为A392的车辆,进入停车场时间为2020年2月2日12时整。
出停车场
停车类型:车辆编号:退出时间举例:
VNBR: D583:200202132500
表示停车类型 VNBR,编号为D583的车辆,退出停车场时间为2020年2月2日13时25分。
5)串口输出计费信息
停车类型:车辆编号:停车时长:费用举例:
串口接收车辆入停车场信息 VNBR: D583:200202120000
串口接收车辆出停车场信息 VNBR: D583:200202213205
串口输出计费信息 VNBR:D583:10:20.00
表示停车类型VNBR,编号为D583的车辆,停车时长为10小时,停车费用为20.00元。
6)说明
车辆出入信息通过“资源数据包”中提供的串口助手向竞赛平台发送字符串,格式需要严格按照示例要求。
停车时长:整数,单位为小时,不足1小时,按1小时统计。
停车费用:以元为单位,按小时计费,保留小数点后2位有效数字。
系统收到入停车场信息后,不需要回复;接收到出停车场信息后,解析、计算并通过串口回复计费信息。
当接收到的字符串格式不正确或存在逻辑错误,系统通过串口输出固定提示信息字符串 Error 。
LED指示灯功能
1)若停车场内存在空闲车位,指示灯LD1点亮,否则熄灭。
2)PA7输出2KHz,20%占空比脉冲信号期间,指示灯LD2点亮,否则熄灭。
初始状态说明
1)上电默认PA7处于低电平状态。
2)上电默认处于车位显示界面。
3)上电默认参数,CNBR费率3.50元/小时,VNBR费率2.00元/小时。
4)每次重新上电后,默认空闲车位为8个。
真题讲解系列文章重点关注顶层逻辑代码编写,各模块的代码编写大家可点击蓝桥杯嵌入式专题,里面有各个模块的详细解析与代码编写
赛题分析
拿到赛题,第一件事就是看硬件框图,因为可以看出本届赛题的重点考点。除了老三样(LED,按键,LCD)以外,就是我们的重点考点了。很显然,本次赛题的考点是串口通信与脉冲频率的设置。
然后我们再来理解具体要实现的功能。看到功能概述,可以知道串口通信对应着停车计费系统,而脉冲频率对应着改变PA7的占空比。其他细节方面我们通过代码来进一步了解。
代码实现
串口模块
最重要的功能就是完整接收串口传来的出入车库的车辆信息。然后判断是接收的信息车牌是否已经存在,已经存在就代表是出库 ,不存在就代表入库,出库就要进行计费计算,并通过串口将结果按赛题要求打印出来。期间出入库是,改变相应的变量值,如空闲车位等。
void uart_rx_proc()
{
if(rx_pointer>0)
{
if(rx_pointer==22 && i < 8) //如果接收到的是22个字符
{
sscanf(rxdata,"%4s:%4s:%12s",car_type,car_data,car_time); //将字符串拆分
int j = 0;
for( j=0;j < 8;j++)
{
if ((strcmp(car_data, Car[j].car_data) == 0) && (strcmp(car_type, Car[j].car_type) == 0)) //如果与之前数据的DATA相同,则代表这次是出库,计算费用,并通过串口发送
{
char tyear[4],tm[2],td[2],th[2],tf[2],ts[2], tyear1[4],tm1[2],td1[2],th1[2],tf1[2],ts1[2];
int year,m,d,h,f,s,year1,m1,d1,h1,f1,s1;
int time = 0;
double fee;
sscanf(car_time,"%2s%2s%2s%2s%2s%2s",tyear,tm,td,th,tf,ts); //将表示时间的字符串分割
sscanf(Car[j].car_time,"%2s%2s%2s%2s%2s%2s",tyear1,tm1,td1,th1,tf1,ts1);
year=atoi(tyear); //将字符串转为整形变量
m = atoi(tm);
d = atoi(td);
h = atoi(th);
f = atoi(tf);
s = atoi(ts);
year1=atoi(tyear1);
m1 = atoi(tm1);
d1 = atoi(td1);
h1 = atoi(th1);
f1 = atoi(tf1);
s1 = atoi(ts1);
if(year1 == year) //计算时间
{
if(m == m1)
{
time = ceil( ( ((d*24+h)*60+f)*60+s - ((d1*24+h1)*60+f1)*60+s1 ) / 3600); //ceil向下取整,计算出停车多少小时
}
}
if(strcmp(car_type,"CNBR") == 0) //判断车型
{
fee = time * CNBR_fee;
CNBR_sum--;
}
else
{
fee = time * VNBR_fee;
VNBR_sum--;
}
char temp[20];
sprintf(temp,"%s:%s:%d:%.2f",car_type,car_data,time,fee);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50); //串口发送计费信息
if(i != j) //如果匹配的不是已入库的最后一个,就将最后一个入库的信息复制到这个位置上
{
sprintf(Car[j].car_type,"%s",Car[i].car_type); //将最后入库的信息复制到当前出库的位置
sprintf(Car[j].car_data,"%s",Car[i].car_data);
sprintf(Car[j].car_time,"%s",Car[i].car_time);
}
i--; //i--后,下次有新入库的车辆就直接覆盖这次最后入库的信息
break;
}
else
{
if(j >= 7) //说明未匹配成功
{
sscanf(rxdata,"%4s:%4s:%12s",Car[i].car_type,Car[i].car_data,Car[i].car_time); //将字符串拆分
char temp[20];
sprintf(temp,"%s:%s:%s",Car[i].car_type,Car[i].car_data,Car[i].car_time);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50); //串口发送入库信息
i++;
if(strcmp(Car[i].car_type,"VNBR") == 0)
{
VNBR_sum++;
}
else
{
CNBR_sum++;
}
}
}
}
}
else
{
char temp[20];
sprintf(temp,"Error");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50); //串口发送一个错误提示
}
rx_pointer=0;
memset(rxdata,0,30);
LDLE_sum = 8-i;
if(i == 8)
{
TurnOff_LED(1);
}
else
{
TurnOn_LED(1);
}
}
}
在串口调试工具中的实现效果 (第一次黑色信息为串口发送给开发板的让车库信息,第二次为出车库信息)
按键模块
按键模块就是简单的界面切换,费率的加减。要注意的是第四个按键被设置为改变PA7频率的按键。
void key_proc()
{
if(key[0].key_flag == 1)
{
view++;
if(view==2) view=0;
LCD_Clear(Black); //清屏
key[0].key_flag = 0;
}
if(view==1 && key[1].key_flag == 1)
{
CNBR_fee += 0.5;
VNBR_fee += 0.5;
key[1].key_flag = 0;
LCD_Clear(Black);
}
else if(view==1 && key[2].key_flag == 1)
{
CNBR_fee -= 0.5;
VNBR_fee -= 0.5;
key[2].key_flag = 0;
LCD_Clear(Black);
}
if(key[3].key_flag == 1)
{
if(pa7_duty == 0)
{
pa7_duty = 20;
TurnOn_LED(2);
}
else
{
pa7_duty = 0;
TurnOff_LED(2);
}
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,pa7_duty); //设置占空比pa7_duty
key[2].key_flag = 0;
}
}
LCD模块
LCD就是按赛题要求显示相应内容即可。注意:从第11届开始,已经要求LCD显示必须完全按照赛题上的格式来显示,每一行每一列显示什么一定要注意。
void disp_proc()
{
if(view==0)
{
char text[30];
sprintf(text," Data ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text," CNBR:%d",CNBR_sum);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text," VNBR:%d",VNBR_sum);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text," IDLE:%d",LDLE_sum);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
}
else if(view==1)
{
char text[30];
sprintf(text," Para ");
LCD_DisplayStringLine(Line2, (uint8_t *)text);
sprintf(text," CNBR:%.2f",CNBR_fee);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text," VNBR:%.2f",VNBR_fee);
LCD_DisplayStringLine(Line6, (uint8_t *)text);
}
}