文章目录
- 前言
- 一、CubeMX配置(第十二届省赛完整版)
- 二、代码相关定义、声明
-
- 三、主要函数
- 1.按键扫描
- 2.串口接收中断、定时器中断(接收)
- 3.数据解析
- 4.判定数据正误
- 5.数据更新
- 6.结算
- 7.Main函数
- 四、实验结果
-
- 五、源码(转载请注明出处)
- 总结
前言
相关说明:
开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第十二届蓝桥杯嵌入式省赛
题目难点:停车管理系统逻辑编写;数据接收,解析,判定,更新。
代码思路:(使用usart1时需要修改引脚为PA8 PA9 PA10)串口接收到数据后,先判定数据接收长度是否正确,即每接收到一个字节都重新开启定时器,最后一字节数据接收完且进入定时器中断后判断接收数据长度,准确无误则进行数据解析;解析时将数据分段保存:车类型,车牌号,时间;保存好后再对数据的合法性进行判定,车类型是否为规定类型之一,类型、车牌号数据长度是否为四位。时间是否合法(月份对应天数,时分秒对应进制);最后是存储数据的更新,车牌号是否已经存在?不存在的话判断是否还有空余车位?有则将类型、车牌号、时间等数据存储在数组中;存在的话考虑现在接收时间是否大于到达时间?时间合法则对存储在数组组中的数据进行计算和输出。
CubeMX配置、主要函数代码及说明:
一、CubeMX配置(第十二届省赛完整版)
1.使能外部高速时钟:
2.配置时钟树:
3.GPIO:
4.TIM3(PWM):
5.TIM6(串口在接收到最后一字节数据5us后进入定时器中断函数):
6.USART1:
7.NVIC(中断配置):
二、代码相关定义、声明
1.函数声明
main.c
void Car_Change(char *type,char *carNum,time_t *time,char *str);
uint8_t Dat_Check(char *type,char *carNum);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void Settle_Accounts(struct car outCar);
void Switch_RecBuff(char *type,char *carNum,time_t *time,char *timStr);
void LCD_Init_Show();
void LCD_Refresh(uint8_t page);
void LED_Change();
gpio.h
void KEY_Scan(void);
void LED_AllClose(uint8_t *LCD_Close);
2.宏定义
#define LED_GPIO_PORT GPIOC
#define LED1_GPIO_PIN GPIO_PIN_8
#define LED2_GPIO_PIN GPIO_PIN_9
#define LED3_GPIO_PIN GPIO_PIN_10
#define LED4_GPIO_PIN GPIO_PIN_11
#define LED5_GPIO_PIN GPIO_PIN_12
#define LED6_GPIO_PIN GPIO_PIN_13
#define LED7_GPIO_PIN GPIO_PIN_14
#define LED8_GPIO_PIN GPIO_PIN_15
#define ON GPIO_PIN_RESET
#define OFF GPIO_PIN_SET
#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)
#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)
#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)
#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)
#define LED5(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED5_GPIO_PIN,a)
#define LED6(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED6_GPIO_PIN,a)
#define LED7(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED7_GPIO_PIN,a)
#define LED8(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED8_GPIO_PIN,a)
#define KEY1_GPIO_PORT GPIOB
#define KEY1_GPIO_PIN GPIO_PIN_0
#define KEY2_GPIO_PORT GPIOB
#define KEY2_GPIO_PIN GPIO_PIN_1
#define KEY3_GPIO_PORT GPIOB
#define KEY3_GPIO_PIN GPIO_PIN_2
#define KEY4_GPIO_PORT GPIOA
#define KEY4_GPIO_PIN GPIO_PIN_0
3.变量定义
main.c
uint8_t CNBR=0;
uint8_t VNBR=0;
uint8_t IDLE=8;
double CNBR_Price=3.5;
double VNBR_Price=2.0;
char str[30];
uint8_t LED_Close[3]={1,0,1};
uint8_t recDatBuff[3][20]={0,0,0,0};
uint8_t recDex=0;
uint8_t recNum=0;
uint8_t recDat;
uint32_t recLong=0;
uint8_t firstByte=1;
uint8_t switch_flag=0;
int Error;
int recYear;
int recMon;
int recDay;
int recHour;
int recMin;
int recSec;
struct car
{
char num[10];
char type[10];
int dftime;
double EndPrice;
double type_price;
char reach_time[60];
char leave_time[60];
int reach;
int leave;
};
struct car car[9];
uint8_t car_dex=0;
三、主要函数
首先是按键按下对数据以及输出PWM的更改;更改PWM输出时,按键按下后,先判断LED_Close[2]存储的状态,为灭则使LED2亮,开启PWM,输出1KHz信号。为亮则使LED2灭,暂停PWM,输出持续的低电平。(PWM在MX的配置为1KHz的输出。)
1.按键扫描
gpio.c
void Data_Change(uint8_t mode)
{
switch(mode)
{
case ADD:
CNBR_Price+=Price_step;
VNBR_Price+=Price_step;
break;
case SUB:
CNBR_Price-=Price_step;
VNBR_Price-=Price_step;
break;
}
}
void Setting_Mode()
{
uint8_t delay=0;
while(1)
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
LCD_Refresh(1);
break;
}
}
else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);
Data_Change(ADD);
LCD_Refresh(2);
}
}
else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);
Data_Change(SUB);
LCD_Refresh(2);
}
}
}
}
void KEY_Scan()
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
LCD_Refresh(2);
Setting_Mode();
}
}
else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);
if(LED_Close[2]==1)
{
LED_Close[2]=0;
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
}
else
{
LED_Close[2]=1;
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);
}
}
}
}
接下来是数据的处理,分别为接收,解析,判定和更新。
2.串口接收中断、定时器中断(接收)
串口在接收到一个字节数据时进入串口中断函数,每次进入串口中断函数都需要重新开启判定,在最后一字节数据接收完5us后进入定时器中断函数,在定时器中断函数中判定接收数据长度是否符合要求,不符合则返回Error,符合要求则按照设定好的规则进行保存。我使用的是二维数组,遇到冒号就换行,最后根据数据长度来设定接收结束的标志(接收结束后将开始解析)即可。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_Base_Stop_IT(&htim6);
HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));
if(recLong!=22)
{
Error=1;
}
else
{
switch_flag=1;
}
recLong=0;
recNum=0;
recDex=0;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_TIM_Base_Stop_IT(&htim6);
recLong++;
recDatBuff[recNum][recDex++]=recDat;
if(recDat==':')
{
recNum++;
recDex=0;
}
TIM6->CNT=0;
HAL_TIM_Base_Start_IT(&htim6);
HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));
}
3.数据解析
将保存在二维数组中的数据进行字符串组合,数组中第0行数据为车的类型+冒号,第1行数据为车牌号+冒号,第2行数据为时间。
这里用到time.h中的函数对时间进行转换,先将时间进行类型转换(字符型转整型),转换后根据mktime函数规则进行调整(年份减1900,月份减1),再将调整后的结果赋值给时间结构体,再调用mktime函数进行时间转换,转换后的时间为自1970年1月1日以来持续时间的秒数 (为什么用一个错误的时间测试,mktime不会返回-1,欢迎懂的大佬留言)
void Switch_RecBuff(char *type,char *carNum,time_t *time,char *timStr)
{
struct tm timeTem;
sprintf(type,"%c%c%c%c",recDatBuff[0][0],recDatBuff[0][1],recDatBuff[0][2],recDatBuff[0][3]);
sprintf(carNum,"%c%c%c%c",recDatBuff[1][0],recDatBuff[1][1],recDatBuff[1][2],recDatBuff[1][3]);
sprintf(timStr,"%c%c%c%c%c%c%c%c%c%c%c%c",recDatBuff[2][0],recDatBuff[2][1],recDatBuff[2][2],recDatBuff[2][3],recDatBuff[2][4],recDatBuff[2][5],recDatBuff[2][6],recDatBuff[2][7],recDatBuff[2][8],recDatBuff[2][9],recDatBuff[2][10],recDatBuff[2][11]);
recYear=2000+(recDatBuff[2][0]-48)*10+(recDatBuff[2][1]-48)-1900;
recMon=(recDatBuff[2][2]-48)*10+(recDatBuff[2][3]-48)-1;
recDay=(recDatBuff[2][4]-48)*10+(recDatBuff[2][5]-48);
recHour=(recDatBuff[2][6]-48)*10+(recDatBuff[2][7]-48);
recMin=(recDatBuff[2][8]-48)*10+(recDatBuff[2][9]-48);
recSec=(recDatBuff[2][10]-48)*10+(recDatBuff[2][11]-48);
timeTem.tm_year=recYear;
timeTem.tm_mon=recMon;
timeTem.tm_mday=recDay;
timeTem.tm_hour=recHour;
timeTem.tm_min=recMin;
timeTem.tm_sec=recSec;
*time=mktime(&timeTem);
}
4.判定数据正误
车类型是否为规定类型之一,类型、车牌号数据长度是否为四位(这里用冒号的位置进行判断)。时间是否合法(月份对应天数是否准确,,2月份还需考虑闰年;时分秒对应进制有误);
uint8_t Dat_Check(char *type,char *carNum)
{
recMon+=1;
recYear-=100;
if(strcmp(type,"CNBR")!=0 && strcmp(type,"VNBR")!=0)
{
return 1;
}
if(recDatBuff[0][4]!=':' || recDatBuff[1][4]!=':')
{
return 1;
}
if(recMon>12 || recMon<0)
{
return 1;
}
else if(recMon==2)
{
if(recYear%4==0)
{
if(recDay>28 ||recDay<0)
{
return 1;
}
}
else
{
if(recDay>29 ||recDay<0)
{
return 1;
}
}
}
else if(recMon==1 || recMon==3 || recMon==5 || recMon==7 || recMon==8 || recMon==10 || recMon==12)
{
if(recDay>31 ||recDay<0)
{
return 1;
}
}
else if(recMon==4 || recMon==6 || recMon==9 || recMon==11)
{
if(recDay>30 ||recDay<0)
{
return 1;
}
}
if(recHour>23 || recHour<0)
{
return 1;
}
if(recMin>59 || recMin<0)
{
return 1;
}
if(recSec>59 || recSec<0)
{
return 1;
}
return 0;
}
5.数据更新
接收数据判定无误后进入数据更新步骤。首先判断车牌号是否已经存在,如不存在则为进入,进入时需要判断是否有空余车位,有则将接收数据保存在数组中,并更新车位信息;如果车牌号存在则为离开,离开需判断时间是否大于车辆到达时间,若合法则将离开车辆信息传递给结算函数进行结算并更新车位信息,不合法返回Error。
void Car_Change(char *type,char *carNum,time_t *time,char *str)
{
uint8_t i;
uint8_t dir=1;
uint8_t outcar_dex;
for(i=0;i<9;i++)
{
if(strcmp(carNum,car[i].num)==0)
{
dir=0;
outcar_dex=i;
if(*time<car[outcar_dex].reach)
{
printf("Error\n");
return;
}
break;
}
}
if(dir==1)
{
if(IDLE==0)
{
return;
}
if(strcmp(type,"CNBR")==0)
{
CNBR++;
IDLE--;
car[car_dex].type_price=CNBR_Price;
}
else if(strcmp(type,"VNBR")==0)
{
VNBR++;
IDLE--;
car[car_dex].type_price=VNBR_Price;
}
strcpy(car[car_dex].type,type);
strcpy(car[car_dex].num,carNum);
strcpy(car[car_dex].reach_time,str);
car[car_dex].reach=*time;
car_dex++;
}
else
{
if(strcmp(type,"CNBR")==0)
{
CNBR--;
IDLE++;
}
else if(strcmp(type,"VNBR")==0)
{
VNBR--;
IDLE++;
}
strcpy(car[outcar_dex].leave_time,str);
car[outcar_dex].leave=*time;
Settle_Accounts(car[outcar_dex]);
for(i=outcar_dex;i<car_dex;i++)
{
car[i]=car[i+1];
}
car_dex--;
}
}
6.结算
打印到达和离开信息,并用difftime函数计算时间差,单位为秒,再将时间单位化为小时,最后计算费用并将时间和费用信息进行打印。
void Settle_Accounts(struct car outCar)
{
printf("%s:%s:%s\n",outCar.type,outCar.num,outCar.reach_time);
printf("%s:%s:%s\n",outCar.type,outCar.num,outCar.leave_time);
outCar.dftime=difftime(outCar.leave,outCar.reach)/60/60;
if(outCar.dftime==0)outCar.dftime=1;
outCar.EndPrice=outCar.dftime*outCar.type_price;
printf("%s:%s:%d:%.2f\n\n",outCar.type,outCar.num,outCar.dftime,outCar.EndPrice);
}
7.Main函数
在主循环之前需要做好初始化工作。
1.要先重置定时器更新标志位(TIMX->SR=0),否则程序运行后将立刻进入定时器中断函数。
2.开启串口接收中断
3.LCD初始化显示
4.PWM初始化
主循环逻辑大体就是实时更新LCD和LED,并检测是否需要对数据进行转换,转换完后判断数据是否合法,合法的话是车辆进入还是车辆离开。
int main(void)
{
time_t time;
char type[10];
char carNum[20];
char timStr[60];
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_TIM6_Init();
LCD_Init();
LCD_Init_Show();
HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));
TIM6->SR=0;
LED_Close[2]=1;
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);
while (1)
{
KEY_Scan();
LCD_Refresh(1);
if(switch_flag==1 && !Error)
{
switch_flag=0;
Switch_RecBuff(type,carNum,&time,timStr);
Error=Dat_Check(type,carNum);
if(!Error)
{
Car_Change(type,carNum,&time,timStr);
}
else
{
Error=0;
printf("Error\n");
}
LED_Change();
}
else if(Error)
{
Error=0;
printf("Error\n");
}
LED_AllClose(LED_Close);
}
}
四、实验结果
1.数据长度有误
a.数据过长
b.数据过短
c.返回
2.数据不合法
a.类型错误
b.时间不合法
c.离开时间小于到达时间
d.返回
3.数据正常
a.输入车辆到达信息
b.输入车辆离开信息
c.返回
五、源码(转载请注明出处)
总结
以上就是全部内容,如有错误请批评指正。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)