本文主要是介绍STM32HAL库【G431】--【蓝桥杯嵌入式第十二届省赛题】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
完整工程(百度网盘免费下载,提取码:0403)和演示视频在文章末尾,需要请移步至文章末尾。
目录
- 第十二届省赛题目
- 题目分析
- 涉及模块主要代码
- 主要变量与结构体
- 串口处理
- HAL_UART_Transmit_IT()避坑
- 串口是否接收完成的简单处理
- 串口接收的数据是固定22长度的处理
- 进停车场(及保存接收的数据)
- 出停车场(及删除对应数据和计算停车费)
- 判断车辆是进停车场还是出停车场
- LED和PWM
- 按键扫描
- 完整功能演示视频
- 完整工程文件
- 总结
第十二届省赛题目
题目分析
个人观点:博主一眼扫过去,发现难点在串口,其他的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;//完成标志位清0ctimer=0;//串口接收超时时间清0Rxdata[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类型加1car_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类型加1car_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; }}
}
完整功能演示视频
完整工程文件
总结
自己写的代码,不喜勿喷,欢迎参考呀
以上就是本次的全部内容了,笔者水平有限,仅供参考。
想联系笔者请私信就好。
这篇关于STM32HAL库【G431】--【蓝桥杯嵌入式第十二届省赛题】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!