一.赛题
本文的目的在于 记录下该次培训自己的经历以及习得的东西,顺便开源分享在网上,欢迎大家的阅读以及指点,该系列代码开源在git仓库中,如果出现找不到或者编译错误,无法下载的问题,欢迎评论区或者私信
二.思路分析
2.1 整体逻辑
- 利用一个车位结构体,将其变成结构体数组模拟停车场。
- 停车流程
- 按键1按下时,使能停车信号(只有该信号为1时才能停车)。
- 通过串口发送IDx给程序。程序解析这里的x,并检查x号停车位符不符合入库的要求(不符合的原因可能是,没有停车位了或者这个车位有车)
- 成功停车后,记录下该位置的时分秒 将该位设置为已被占用。
- 出车流程
- 按键2按下,使能出库信号(同上)
- 同停车流程第二步
- 成功停车后,计算该位置的钱 ( (当前小时-当前车辆的停车小时+1)* 费率 ),位置设置为未被占用
- 费率以及灯设置
- 串口接收到后,直接解析数据改变费率
- 停车位不为0时,LED1亮,为0时LED2亮
- 当没有停车位的时候key1又按下了,那么闪烁一下LED3
- LCD显示设置
- 当id不为0时,正常显示相应数据,id为0时,id显示x 时分秒默认为000
2.2 相关变量
- 时间部分 hour,sec,min不多说了
- parking_size停车场的10个车位 采用全局变量记录,这里没必要用线性表,麻烦了。
- 单个车位利用一个Parking_Space结构体表达,内置时分秒变量,以及该车位是否被使用的标志位,然后将其设置成10位的结构体数组即可。
- id变量,用来对应停车场结构体数组里的每一个具体车位。
- 入库使能信号和出库使能信号,在按键按下后生效,解析完数据后失能。
- rate费率,fee停车金额这些的也没什么特别的
示例
//车位结构体
struct Parking_Space{
int hour;
int min;
int sec;
int is_used;
};
//停车场
struct Parking_Space parking[10];
//剩余车位数
int parking_size = 10
//对应停车场里的车位
int id = 0;
2.3 相关函数
函数名就和他们的作用一样,处理各自对应的需求逻辑
void KEY_Control(void); //按键控制
void LCD_Control(void); //展示界面控制
void Led_Control(void); //LED逻辑控制
void TIME_Control(void); //时分秒的基础计时
void UART_Control(void); //串口逻辑控制
void In_Disp(void); //入车显示
void Out_Disp(void); //出车显示
void USART_Rate_Control(void);//串口费率模式控制
void USART_Car_Control(void); //串口出入库控制
//停车场出入库控制 1入库 2出库 返回值为1表示成功
int Car_Control(int mode,int index_id);
三.源码部分解读
3.1 按键控制
这里大家可能会有点不理解为什么要有key_flag,和id置0的操作,没事,按键代码完后续马上解释。
void KEY_Control(){
//按键1按下 有车位时
if(KEY_Scan()==1 && parking_size != 0)
{
//按键按下时,串口还没输入ID 所以这里提取置0,界面就会展示还没输ID时的情况
id = 0;
LCD_Clear(White);
key_flag = 1; //对应界面标志位
in_enable = 1; //入库使能
Uart_SendString("Appoint a in Id\r\n"); //串口发送提示
}
//按键1按下 无车位时
if(KEY_Scan()==1 && parking_size == 0)
{
LED_Control(LED3,1);
HAL_Delay(500);
LED_Control(LED3,0);
}
//按键2按下
if(KEY_Scan()==2)
{
id = 0;
LCD_Clear(White);
key_flag=2;
out_enable = 1; //这些基本同上
Uart_SendString("Appoint a out Id\r\n");
}
}
3.2 显示部分
为何要有id置0和key_flag的切换?大家看这边就知道了。
//入库显示
void In_Disp(){
LCD_DisplayStringLine(Line2," ID Code Time");
if(id!=0){
sprintf(disp," %d %02d:%02d:%02d",id,parking[id-1].hour,parking[id-1].min,parking[id-1].sec);
}else{
sprintf(disp," %s %02d:%02d:%02d","x",0,0,0); //id为0时的展示界面
}
LCD_DisplayStringLine(Line4,(u8 *)disp);
sprintf(disp," Rates:%d",rate);
LCD_DisplayStringLine(Line8,(u8 *)disp);
}
//出库显示
void Out_Disp(){
LCD_DisplayStringLine(Line2," ID Code Fee");
if(id!=0){
sprintf(disp," %d %2d",id,fee);
} else {
sprintf(disp," %s %2d","x",0);
}
LCD_DisplayStringLine(Line4,(u8 *)disp);
sprintf(disp," Rates:%d",rate);
LCD_DisplayStringLine(Line8,(u8 *)disp);
}
//根据标志位展示界面
void LCD_Control(){
if(key_flag==0){
sprintf(disp," Rates:%d",rate);
LCD_DisplayStringLine(Line8,(u8 *)disp);
}
if(key_flag==1){
In_Disp();
}
if(key_flag==2){
Out_Disp();
}
}
3.3串口部分
前面的两个使能信号 很好的在串口控制出入库的地方发挥了作用。以及需要讲解一下这里为什么需要一个临时变量temp_id,而不是直接给id赋值。
因为我们的id会直接影响lcd的显示,意味着这里的解析数据,如果直接给到id的话,屏幕就会更改。
可是这真的是我们想要的情况吗?不是的,因为这里解析出的数据不一定是正确数据。举个例子,1,3号库相继停入车辆,此时界面显示的是id为3的时候。
那如果这时我们又收到了1号库的停车请求,解析数据直接给id的话,那么显示界面就会显示id为1的车库了。可是这时我们的停车是失败的。
那么问题就出现了,停车失败,可是显示却按错误的id显示了。
所以解决办法就是拿一个临时变量temp_id接收串口解析的数据,如果我们正确停车,该变量赋值给id,正常显示。如果停车失败,id维持原样。
void USART_Car_Control(){
int temp_id;
if((UART1_Rx_Buf[3] - '0')== 0 && UART1_Rx_Buf[2] - '0'==1)
{
temp_id = 10;
}
else if(UART1_Rx_Buf[2] - '0' > 0 && UART1_Rx_Buf[3] - '0' <=9)
{
temp_id = UART1_Rx_Buf[2] - '0';
}
else{
temp_id = 0;
}
if(key_flag==1)
{
if(!in_enable)//如果还不是入库模式
{
Uart_SendString("In_Key not press\r\n");
return;
}
in_enable = 0; //失能入库
if(Car_Control(1,temp_id))//调用出入库控制函数
{
id = temp_id;//停车成功 改变当前显示的ID
sprintf(disp,"Park Success:ID%d\n",id);
Uart_SendString(disp);
}
else
{
sprintf(disp,"Park Fail:ID%d\n",temp_id);
Uart_SendString(disp);
}
}
if(key_flag==2) //基本同上
{
if(!out_enable)
{
Uart_SendString("Out_Key not press\r\n");
return;
}
out_enable = 0;
if(Car_Control(0,temp_id))
{
id = temp_id;
sprintf(disp,"Go Out Success:ID%d\n",id);
Uart_SendString(disp);
}
else
{
sprintf(disp,"Go Out Fail:ID%d\n",temp_id);
Uart_SendString(disp);
}
}
}
串口总控制部分 没有太多好说的。
void UART_Control(){
if(UART1_Rx_flg==1)
{
if(UART1_Rx_Buf[0]=='I' && UART1_Rx_Buf[1]=='D') //ID解析出入库部分
{
USART_Car_Control(); //调用出入库串口控制
}
else if(UART1_Rx_Buf[0]=='F' && UART1_Rx_Buf[1]=='x')
{
USART_Rate_Control(); //调用费率串口控制
}
else
{
Uart_SendString("Error Command\n");//命令错误
}
//清空缓冲区和串口标志
for(int i = 0;i<UART1_Rx_cnt;i++){
UART1_Rx_Buf[i] = '\0';
}
UART1_Rx_cnt = 0;
UART1_Rx_flg = 0;
}
}
3.4 出入库部分
看注释即可 存入位置是index_id-1是因为,ID1对应的存入的是数组的0号位,以此类推
int Car_Control(int mode,int index_id){
if(index_id < 1 || index_id > 10){
return 0;
}
if(mode)//入库模式
{
if(parking_size==0){//如果没有车位
return 0;
}
if(parking[index_id-1].is_used){//如果该车位已被使用
return 0;
}
parking[index_id-1].hour = hour;
parking[index_id-1].min = min ;
parking[index_id-1].sec = sec ;
parking[index_id-1].is_used = 1;//设置车位被使用
parking_size--; //车位剩余数--
return 1;
}
if(mode==0)//出库模式 基本同上
{
if(parking_size==10){
return 0;
}
if(!parking[index_id-1].is_used){
return 0;
}
fee = (1 + hour - parking[index_id-1].hour)*rate;//费用计算
parking[index_id-1].is_used=0;
parking_size++;
return 1;
}
return 0;
}
剩下的部分较为简单,不做细讲,大家可以直接阅读源码,都在Gitee仓库上。