STM32HAL库【G431】--【蓝桥杯嵌入式第十二届省赛题】

2024-02-15 18:40

本文主要是介绍STM32HAL库【G431】--【蓝桥杯嵌入式第十二届省赛题】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

完整工程(百度网盘免费下载,提取码:0403)和演示视频在文章末尾,需要请移步至文章末尾。

目录

  • 第十二届省赛题目
  • 题目分析
  • 涉及模块主要代码
    • 主要变量与结构体
    • 串口处理
      • HAL_UART_Transmit_IT()避坑
      • 串口是否接收完成的简单处理
      • 串口接收的数据是固定22长度的处理
    • 进停车场(及保存接收的数据)
    • 出停车场(及删除对应数据和计算停车费)
    • 判断车辆是进停车场还是出停车场
    • LED和PWM
    • 按键扫描
  • 完整功能演示视频
  • 完整工程文件
  • 总结

第十二届省赛题目

在这里插入图片描述

题目分析

个人观点:博主一眼扫过去,发现难点在串口,其他的LED按键界面显示PWM输出比较简单,因此在此只分析串口,代码重点讲解串口
串口:

  1. 车辆类型只有2种CNBR和VNBR
  2. 数据长度固定22字符
  3. 接收的字符存在逻辑错误格式不正确,串口发送Error,全部正确不做反应。
  4. 停车时间不足一小时,按一小时计算。
  5. 字符串的处理是个难点

涉及模块主要代码

主要变量与结构体

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】--【蓝桥杯嵌入式第十二届省赛题】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/712221

相关文章

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

嵌入式方向的毕业生,找工作很迷茫

一个应届硕士生的问题: 虽然我明白想成为技术大牛需要日积月累的磨练,但我总感觉自己学习方法或者哪些方面有问题,时间一天天过去,自己也每天不停学习,但总感觉自己没有想象中那样进步,总感觉找不到一个很清晰的学习规划……眼看 9 月份就要参加秋招了,我想毕业了去大城市磨练几年,涨涨见识,拓开眼界多学点东西。但是感觉自己的实力还是很不够,内心慌得不行,总怕浪费了这人生唯一的校招机会,当然我也明白,毕业

深入探索嵌入式 Linux

摘要:本文深入探究嵌入式 Linux。首先回顾其发展历程,从早期尝试到克服诸多困难逐渐成熟。接着阐述其体系结构,涵盖硬件、内核、文件系统和应用层。开发环境方面包括交叉编译工具链、调试工具和集成开发环境。在应用领域,广泛应用于消费电子、工业控制、汽车电子和智能家居等领域。关键技术有内核裁剪与优化、设备驱动程序开发、实时性增强和电源管理等。最后展望其未来发展趋势,如与物联网融合、人工智能应用、安全性与

C语言蓝桥杯

一、语言基础 竞赛常用库函数 最值查询 min_element和max_element在vector(迭代器的使用) nth_element函数的使用 例题lanqiao OJ 497成绩分析 第一种用min_element和max_element函数的写法 第二种用min和max的写法 二分查找 二分查找只能对数组操作 binary_s

嵌入式技术的核心技术有哪些?请详细列举并解释每项技术的主要功能和应用场景。

嵌入式技术的核心技术包括处理器技术、IC技术和设计/验证技术。 1. 处理器技术    通用处理器:这类处理器适用于不同类型的应用,其主要特征是存储程序和通用的数据路径,使其能够处理各种计算任务。例如,在智能家居中,通用处理器可以用于控制和管理家庭设备,如灯光、空调和安全系统。    单用途处理器:这些处理器执行特定程序,如JPEG编解码器,专门用于视频信息的压缩或解压。在数字相机中,单用途

基于微信小程序与嵌入式系统的智能小车开发(详细流程)

一、项目概述 本项目旨在开发一款智能小车,结合微信小程序与嵌入式系统,提供实时图像处理与控制功能。用户可以通过微信小程序远程操控小车,并实时接收摄像头采集的图像。该项目解决了传统遥控小车在图像反馈和控制延迟方面的问题,提升了小车的智能化水平,适用于教育、科研和娱乐等多个领域。 二、系统架构 1. 系统架构设计 本项目的系统架构主要分为以下几个部分: 微信小程序:负责用户界面、控制指令的

嵌入式面试经典30问:二

1. 嵌入式系统中,如何选择合适的微控制器或微处理器? 在嵌入式系统中选择合适的微控制器(MCU)或微处理器(MPU)时,需要考虑多个因素以确保所选组件能够满足项目的具体需求。以下是一些关键步骤和考虑因素: 1.1 确定项目需求 性能要求:根据项目的复杂度、处理速度和数据吞吐量等要求,确定所需的处理器性能。功耗:评估系统的功耗需求,选择低功耗的MCU或MPU以延长电池寿命或减少能源消耗。成本

嵌入式Linux C基本知识点总结

学习这块知识点有好久了,最近借一本书的框架来对基本的知识点进行总结,参考《嵌入式Linux上的C语言编程实践》进行总结记录之。 一、 基本开发环境 Linux下C语言开发环境 使用工具:程序生成工具GCC、程序调试工具GDB、工程管理工具为make和Makefile。 开发流程:使用编辑工具编写C语言源文件,然后编译生成机器代码为主的二进制可执行程序。 编译流程:C语言经过编译-汇