完整工程(百度网盘免费下载,提取码:0403)和演示视频在文章末尾,需要请移步至文章末尾。
目录
第十二届省赛题目
题目分析
个人观点:博主一眼扫过去,发现难点在串口,其他的LED,按键,界面显示,PWM输出都比较简单,因此在此只分析串口,代码也重点讲解串口。
串口:
- 车辆类型只有2种,CNBR和VNBR。
- 数据长度固定22字符。
- 接收的字符存在逻辑错误或格式不正确,串口发送Error,全部正确不做反应。
- 停车时间不足一小时,按一小时计算。
- 字符串的处理是个难点。
涉及模块主要代码
主要变量与结构体
struct keys//按键结构体
{
uchar jugde;
bool single;
bool sta;
};
struct cars//车辆结构体
{
char type[5];//类型
char id[5];//编号
uchar year;//年
uchar month;//月
uchar day;//天
uchar hour;//时
uchar min;//分
uchar sec;//秒
};
struct keys key[4]={0,0,0};
struct cars car[8]={"","",0,0,0,0,0,0};
float C_expense=3.5,V_expense=2.0;
uchar car_c=0,car_v=0,car_i=0;//分别为CNBR、VNBR、IDLE
uchar car_total=0;//停车场中车辆总数
uchar Rx_count=0;//接收的内容计数
uchar ctimer=0;//串口判断
uchar car_go=0;//cars 结构体中第几辆车要出停车场
char Rxdata[30];//串口接收的所有内容
char rxdat;//串口接收保存的变量
char Txdata[30];//串口发送的内容
char lcd_buffer[30];//lcd显示缓存
char type[5],id[5],time[13];//存储车辆的类型,编号,时间
char year[3],month[3],day[3],hour[3],min[3],sec[3];//用于分割字符串
bool uart_finish_flag=0;//串口接收完成标志位
bool scene_flag=0;//0:车位显示界面 1:费率设置界面
bool control_flag=0;//1:输出 PWM 2KHZ 20% 0:输出低电平
bool rx_flag=0;//串口接收数据成功标志 0:数据错误 1:数据有效
bool park_flag=0;//1:出停车场,0:进停车场
uchar cyear=0,cmonth=0,cday=0,chour=0,cmin=0,csec=0;//用于接收时间的转换
串口处理
注意:笔者对错误类型进行了相应返回,赛题中只需要返回Error即可
HAL_UART_Transmit_IT()避坑
该函数不能连续使用!!!
例如…HAL_UART_Transmit_IT(…);HAL_UART_Transmit_IT(…)…;
原因:串口不能中断同时触发。
串口是否接收完成的简单处理
将串口接收的数据存到Rxdata数组里,并每30ms判断是否有新的内容更新,如果有就继续接收,如果没有就表示接收完成,然后对Rxdata进行相应的处理。
主要代码如下:
//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)//USB转串口对应的是串口1,PA9,PA10
{
uart_finish_flag=0;//完成标志位清0
ctimer=0;//串口接收超时时间清0
Rxdata[Rx_count++] = rxdat;
//只能中断一次,所以接收完后需要再次打开,确保能连续接收
HAL_UART_Receive_IT(&huart1,(uchar *)&rxdat,1);
}
}
void Usart_rxproc(void)//对串口接收的内容进行处理
{
if(Rx_count>0)
{
//这里写>=3条件宽一点,==3的话可能会卡死
if(ctimer>=3)//30ms判断一次串口,定时器配置的是10ms中断一次
{
ctimer=0;
//串口缓存数据30ms后等于前一次数据,则接收结束
if(Rxdata[Rx_count-1] == rxdat)uart_finish_flag=1;//串口接收完成
}
if(uart_finish_flag==1)//串口接收完成的处理
{
uart_finish_flag=0;//清零
if(Rx_count==22)//数据长度一致
{
Parking_equal_data_proc();//调用数据长度一致的处理
}
else HAL_UART_Transmit_IT(&huart1,"Error of length\r\n",sizeof("Error of length\r\n"));
Rx_count=0;//清零
//C 库函数 void *memset(void *str, int c, size_t n)
//复制字符 int c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
memset(Rxdata,0,sizeof(Rxdata));//清零接收数组
}
}
}
串口接收的数据是固定22长度的处理
对数据的有的有效性进行判断,如果有效,判断是进停车场还是出停车场。
主要代码如下:
void Parking_equal_data_proc(void)//对接受的数据进行处理和判断
{
uchar i=0;
sscanf(Rxdata,"%4s:%4s:%12s",type,id,time);
/*对等长数据进行判断是否正确*/
if(strcmp("CNBR",type)==0 || strcmp("VNBR",type)==0)
{
for(i=0; i<12; i++)//判断数据中时间是否正确
if(*(time+i) <'0' && *(time+i) >'9')break;
if(i==12)
{
/*对接收的时间进行转换*/
sscanf(time,"%2s%2s%2s%2s%2s%2s",year,month,day,hour,min,sec);
cyear = atoi(year);cmonth = atoi(month);
cday = atoi(day);chour = atoi(hour);
cmin = atoi(min);csec = atoi(sec);
if(cyear<=99 && cmonth <=12 && cday<=31 &&
chour <= 23 && cmin <=59 && csec <=59 )//时间数据是否有效
{
rx_flag=1;//数据有效
HAL_UART_Transmit_IT(&huart1,"Correct\r\n",sizeof("Correct\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);
}
else
{
rx_flag=0;//数据无效
HAL_UART_Transmit_IT(&huart1,"Error of time\r\n",sizeof("Error of time\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
}
else
{
rx_flag=0;数据无效
HAL_UART_Transmit_IT(&huart1,"Error of time\r\n",sizeof("Error of time\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
}
else
{
rx_flag=0;数据无效
HAL_UART_Transmit_IT(&huart1,"Error of type\r\n",sizeof("Error of type\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
if(rx_flag==1)//数据有效才进行的操作
{
Parking_jugde();//判断车辆是进停车场还是出停车场
//park_flag=0: 进停车场 park_flag=1: 出停车场
if(park_flag ==0)Parking_in();//进停车场
else Parking_out();//出停车场并计算费用
rx_flag=0;
}
}
进停车场(及保存接收的数据)
void Parking_in(void)//进停车场处理
{
/*strcmp()会依次毕竟字符串中的每个字符的ASCII码,
如果全部都相等返回0 ,否则返回>0或者<0*/
if(car_i!=0)
{
if(strcmp("CNBR",type)==0)//保存数据
{
strcpy(car[car_total].type,type);
strcpy(car[car_total].id,id);
car[car_total].year = cyear;
car[car_total].month = cmonth;
car[car_total].day = cday;
car[car_total].hour = chour;
car[car_total].min = cmin;
car[car_total].sec = csec;
car_c++;//CNBR类型加1
car_total++;//总停车车辆加1
}
else if(strcmp("VNBR",type)==0)//保存数据
{
strcpy(car[car_total].type,type);
strcpy(car[car_total].id,id);
car[car_total].year = cyear;
car[car_total].month = cmonth;
car[car_total].day = cday;
car[car_total].hour = chour;
car[car_total].min = cmin;
car[car_total].sec = csec;
car_v++;//VNBR类型加1
car_total++;//总停车车辆加1
}
}
else
{
HAL_UART_Transmit_IT(&huart1,"Error of Full parking space\r\n",
sizeof("Error of Full parking space\r\n\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
}
出停车场(及删除对应数据和计算停车费)
void Parking_out(void)//已经停的车辆,出停车场
{
int parking_time=0;//停车时间
double parking_cost;//停车费用
parking_time = (cyear - car[car_go].year)*365*24+
(cmonth - car[car_go].month)*30*24+
(cday - car[car_go].day)*24+
(chour - car[car_go].hour);//取出小时及以前的数据进行换算
/*parking_time处理*/
if(parking_time<0)
{
HAL_UART_Transmit_IT(&huart1,"Error of time illegal\r\n",
sizeof("Error of time illegal\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
else if(parking_time==0)
{
if(((cmin - car[car_go].min)*60 +csec - car[car_go].sec) < 0)//数据错误
{
HAL_UART_Transmit_IT(&huart1,"Error of time illegal\r\n",
sizeof("Error of time illegal\r\n"));
while(huart1.gState != HAL_UART_STATE_READY);//确保上次中断发送完,再进行下次中断
}
else parking_time=1;//不满1小时,按一小时处理
}
else if(parking_time>0)
{
if(((cmin - car[car_go].min)*60 +csec - car[car_go].sec) > 0)
parking_time++;//不满1小时,按一小时处理
}
if(parking_time>0)//时间处理完成进行费用等其他运算
{
if(strcmp(car[car_go].type,"CNBR")==0 &&
strcmp(car[car_go].id,id)==0)
{
car_c--;parking_cost = parking_time * C_expense;
sprintf(Txdata,"%s:%s:%d:%.2f",car[car_go].type,car[car_go].id,parking_time,parking_cost);
}
else if(strcmp(car[car_go].type,"VNBR")==0 &&
strcmp(car[car_go].id,id)==0)
{
car_v--;parking_cost = parking_time * V_expense;
sprintf(Txdata,"%s:%s:%d:%.2f\r\n",car[car_go].type,car[car_go].id,parking_time,parking_cost);
}
HAL_UART_Transmit_IT(&huart1,(uchar *)Txdata,sizeof(Txdata));
parking_time=0;//使用完后清零
parking_cost=0;//使用完后清零
for(uchar i=car_go+1; i<8; i++)//加1防止越界
car[i-1] = car[i];//从car_go开始,让后面覆盖前面,就完成了出停车场操作
car_total--;
}
}
判断车辆是进停车场还是出停车场
对car结构体进行遍历,查找是否有对应车辆
void Parking_jugde(void)//查找是否有这个车辆
{
uchar i=0;
for(i=0;i<8;i++)
{
if(strcmp(car[i].type,type)==0 &&
strcmp(car[i].id,id)==0)
{car_go=i;break;}//找到了该车辆
}
if(i==8)park_flag=0;//0:进停车场
else park_flag=1;//1:出停车场
}
LED和PWM
由于LED和PWM相对简单,且在以前的赛题文章中已经写到,在此就不多赘述。
主要代码:
void PWM_generation_LED(void)//PWM开启与关闭,和LED显示
{
if(control_flag==1)//开PWM输出并且点亮相应LED
{
HAL_TIM_PWM_Start_IT(&htim17,TIM_CHANNEL_1);
if(car_i>0)LED_disp(0x03);
else LED_disp(0x02);
}
else //关PWM输出并且熄灭相应LED
{
HAL_TIM_PWM_Stop_IT(&htim17,TIM_CHANNEL_1);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET);
if(car_i>0)LED_disp(0x01);
else LED_disp(0x00);
}
}
按键扫描
前面文章多次提到短按,在此不多赘述。
主要代码:
void Key_Scan(void)//按键扫描
{
key[0].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].sta = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(uchar i=0; i< 4; i++)
{
switch(key[i].jugde)
{
case 0://状态1:判断按键是否按下
{
if(key[i].sta==0)key[i].jugde=1;
}break;
case 1://状态2:按键消抖
{
if(key[i].sta==0)key[i].jugde=2;
else key[i].jugde=0;
}break;
case 2://状态3:确认按下,标志位置1
{
if(key[i].sta==1)
{
key[i].single=1;
key[i].jugde=0;
}
}break;
}
}
}
完整功能演示视频
完整工程文件
总结
自己写的代码,不喜勿喷,欢迎参考呀
以上就是本次的全部内容了,笔者水平有限,仅供参考。
想联系笔者请私信就好。