第一部分
1、整体理解与复述重难点
(1)主要考点
基础:led灯闪烁亮灭等功能,lcd界面显示信息,定时器实现按键,led灯闪烁时间计数等功能
重要:pwm方波输出并可改变,串口通信usart
(2)项目要求与初始化信息
2、各模块具体分析
(1)lcd界面显示
针对显示界面,主要分为了车位显示界面和费率设置界面,车位显示主要包括三种类型,分别是CNBR、VNBR和IDLE,因此只要在显示主函数中的车位显示界面中包括这三个功能即可;当处于费率设置界面时,只要写出对应的CNBR、VNBR所需要的数字即可,在初始化的设置过程中,需要设置初始值分别为3.50和2.00,并且保留两位小数。
(2)按键key功能要求
对于按键,首先需要通过定时器来控制对应按键的生效过程,对相应的按键进行设置定义,通过对B1,B2,B3,B4定义相应的功能,控制停车收费系统实现对应的停车收费功能和要求,再通过波形占空比的变化来保证车辆收费的有效性,在针对按键的使用过程中应注意防抖处理,进行消抖操作步骤。
(3)LED控制功能
通过书写LED通用的四段代码来完成基础的亮灭显示功能,当已知停车场内有空位时,LD灯点亮,此时对应的IDLE不为0,否则对应LD灯会熄灭。并且控制波形占空比输出,对应LD2灯被点亮。
(4)串口通信usart要求
该部分是车辆停车计费系统的关键,主要是通过对应串口的功能接收和发送相应车辆停车的时间、出库的时间、间隔时间长度来进行计算,最终获得相应的停车费用。
首先需要表示记录相应的车辆编号,识别出对应的车牌号格式,和当前对应日期、年月和时间的格式,通过串口实时记录出车辆入库的所有信息,并储存在相应的空间内,当车辆出库的时候再调出对应信息来完成停车时间的计算,完成收费并清除该条信息。
其次,需要针对于特定车辆类型输入相对应的车辆信息来完成停车收费系统的准确识别,包括入停车场的停车类型,车辆编号和进入时间存储在相应的存储器中来完成单个车辆的信息保留,在出停车场时系统会自动检索已存储的车辆信息来完成对应车辆的信息提取,最后会完成对应的收费操作。
在该串口的调用过程中,主要是理解串口的基本工作原理,正确使用串口接收发送的相关回调函数,对应使用串口调试工具时,设置好串口号(比如COM3),设置好波特率为9600bps,确保车辆数据可以准确,完整的被接收和发送,同时要注意考虑进行其他功能的一起串联。
第二部分:cubemx相应引脚配置
按键定时器tim4,基本配置,psc为80-1,计数周期(重装载值)为10000-1,频率为100HZ,则按键响应时间为10ms。
对于PA7的定时器tim17通道1,输出pwm方波,2000Hz,20%占空比
串口通信usart
第三部分:各模块具体代码实现
(1)对于按键的控制,主要通过定时器来进行定义,主要分析按键对应功能实现的逻辑
#include "interrupt.h"
struct keys key[4]={0,0,0,0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
key[0].key_sta=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key[1].key_sta=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key[2].key_sta=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key[3].key_sta=HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
/***长短按键合并使用,记住即可****/
for(int i=0;i<4;i++)
{
switch(key[i].judge_sta)
{
case 0:
{
if(key[i].key_sta==0)
{
key[i].judge_sta=1;
key[i].key_time=0;
}
}break;
case 1:
{
if(key[i].key_sta==0) {key[i].judge_sta=2;}
else {key[i].judge_sta=0;}
}break;
case 2:
{
if(key[i].key_sta==1)
{
if(key[i].key_time<70) key[i].sing_str=1;
key[i].judge_sta=0;
}
else
{
if(key[i].key_time>=70)
{
key[i].long_str=1;
if(key[i].key_time>70) key[i].key_time=70;
}
key[i].key_time++;
}
}break;
}
}
}
}
void led_Disp(uchar dsLED) //LED控制函数,均相同
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, dsLED<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
对应的interrupt.h文件内容为
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "main.h"
#include "stdbool.h"
//按键结构体,针对按键操作
struct keys{
uchar judge_sta;
bool key_sta;
bool sing_str;
bool long_str;
uint8_t key_time;
};
void HAL_TIM_PeriodElaspedCallback(TIM_HandleTypeDef *htim);
void led_Disp(uchar dsLED);
#endif
(2)串口通信进行配置,主要是通过回调函HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)来完成串口的基本接收和发送的功能,在发送数据的过程中,每次只能接受和发送一位数据。
#include "uart.h"
#include "usart.h"
/*****************X-Hoshino***************/
char rx_buf[30];//接收数据的数组长度
uint8_t rx;//接收数据
uchar rx_dat; //每次只能接受一位数据
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
rx_buf[rx_dat++]=rx; //将接收到的数据放进数组中,方便后续判断
HAL_UART_Receive_IT(&huart1,&rx,1);//重新开启串口中断
}
}
#ifndef _UART_H_
#define _UART_H_
#include "main.h"
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
#endif
(3)主函数main.c代码整体
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdbool.h"
#include "string.h"
#include <stdlib.h>
#include "interrupt.h"
#include "lcd.h"
#include "uart.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern struct keys key[];//按键导入
//串口信息
extern uint8_t rx;
extern char rx_buf[30];
extern unsigned char rx_dat;
char view=0; //界面
char text[30];
uchar c=0;//车辆信息
uchar v=0;
float cnbr=3.50;//收费信息
float vnbr=2.00;
float value=0.50;//额外收费值
struct cars //车辆信息及时间
{
char car_tapy[5];
char car_value[5];
char time_year[3];
char time_month[3];
char time_day[3];
char time_mintute[3];
char time_second[3];
};
char car_tapy_text[5]; //车辆类型
char car_value_text[5]; //车辆信息
char time_year_text[3]; //年
char time_month_text[3]; //月
char time_day_text[3]; //日
char time_mintute_text[3]; //分
char time_second_text[3]; //秒
//车辆进出时间初始化
int car_mintute=0;
int car_second=0;
int text_mintute=0;
int text_second=0;
int time_car=0;
float time_value=0;
uchar car_kong=8;//空闲车位
char car_for=0;
struct cars car[9]={0,0,0,0,0,0,0,0};//车辆信息结构体
uint8_t pa7_flag=0; //pa7输出波形标志位
uint8_t car_flag=0;//剩余车辆标志位
void lcd_proc(void);
void key_proc(void);
void Re_Receive(void);
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
led_Disp(0x00);
if((car_kong)!=0)//初始化车辆有空闲位置时,ld1常亮
{
led_Disp(0x01);
}
LCD_Init();
LCD_Clear(Black); //使用黑色清屏
LCD_SetBackColor(Black); //设置背景颜色为hei色
LCD_SetTextColor(White);
HAL_TIM_Base_Start_IT(&htim4);///开启定时器中断
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);//开启pwm波
HAL_UART_Receive_IT(&huart1,&rx,1);//开启串口接收中断
// char text[30]; //定义一个字符串数组
// sprintf(text, " lanqiao "); //将变量赋值到字符串数组中(需要调用stdio.h)
// LCD_DisplayStringLine(Line0, (uint8_t *)text); //显示该字符串数组
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
lcd_proc();
key_proc();
/**串口接收模块,得到串口发送的信息***/
if(rx_dat!=0)//当传入串口的数据不为空字符串时
{
char temp=rx_dat;//字符传入
HAL_Delay(1);//延时1ms保证传输完成
if(rx_dat==temp) Re_Receive();//传输数据和串口接收数据一致,串口函数被调用完成数据识别
}
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
/** Initializes the peripherals clocks
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void lcd_proc(void)
{
static __IO uint32_t lcd_tick=0; //控制界面响应时间,即刷新速度,10ms
if(uwTick-lcd_tick<10) return;
else lcd_tick=uwTick;
if(view==0)//界面为data
{
sprintf(text, " Data ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " CNBR:%d ",c);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text, " VNBR:%d ",v);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text, " IDLE:%d ",car_kong);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
}
else if(view==1)//界面为para
{
sprintf(text, " Para ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " CNBR:%.2f ",cnbr);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text, " VNBR:%.2f ",vnbr);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line7, (uint8_t *)text);
}
}
void key_proc(void)
{
if(key[0].sing_str==1)//B1
{
if(++view==2) view=0;//界面切换
key[0].sing_str=0;
}
if(key[1].sing_str==1)//B2
{
if(view==1) //当处于para时,费率增加0.5
{
cnbr=cnbr+value;
vnbr=vnbr+value;
}
key[1].sing_str=0;
}
if(key[2].sing_str==1)
{
if(view==1) //当处于para时,费率减少0.5
{
cnbr=cnbr-value;
vnbr=vnbr-value;
}
key[2].sing_str=0;
}
if(key[3].sing_str==1)//B4
{
if(++pa7_flag==2) pa7_flag=0;//当b4按键按下,切换波形,占空比为0
if(pa7_flag==0) //当波形不发生变化时
{
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,0);//设置pulse为0,占空比为0
if(car_kong!=0)
{
led_Disp(0x01);//当剩余车辆不等于0时,ld1常亮
}
else
{
led_Disp(0x00);//当剩余车辆为0时,ld1熄灭
}
}
else if(pa7_flag==1) //如果pwm输出波形发生变化时
{
if(car_kong!=0)
{
led_Disp(0x03);//当剩余车辆不等于0时,ld1和ld2常亮
}
else
{
led_Disp(0x02);//当剩余车辆等于0时,ld2常亮
}
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,100);//设置pulse为100,占空比为20%
}
key[3].sing_str=0;
}
}
void Re_Receive(void)//串口接收函数
{
if(rx_dat>0)//当传送数据不为空时
{
if(rx_dat==22)//如果传入数据数组大小为22位,即符合参数输入信息,包括车牌号,车辆信息,年月日进出时间,间隔时间等
{
sscanf(rx_buf,"%4s:%4s:%2s%2s%2s%2s%2s",car_tapy_text, car_value_text,
time_year_text, time_month_text, time_day_text, time_mintute_text, time_second_text);
for(car_for=0; car_for<8; car_for++) //判断停车数是否达到上限
{
if(strcmp(car_tapy_text,car[car_for].car_tapy)==0) //判断当前车辆是否和已保存的车辆类型相一致
{
if(strcmp(car_value_text,car[car_for].car_value)==0) //判断车牌号是否也相一致
{
car_mintute=atoi(car[car_for].time_mintute); //把相应的停车时间从字符串变成整数形式
car_second=atoi(car[car_for].time_second);
text_mintute=atoi(time_mintute_text);
text_second=atoi(time_second_text);
time_car=text_mintute - car_mintute;
if(text_second<car_second)
{
if(text_mintute==car_mintute)
{
time_car++;
}
}
else if(text_second>car_second)
{
time_car++;
}
if(strcmp(car_tapy_text, "CNBR")==0)
{
c--;
time_value=time_car*cnbr;
}
if(strcmp(car_tapy_text, "VNBR")==0)
{
v--;
time_value=time_car*vnbr;
}
sprintf(text,"%s:",car[car_for].car_tapy);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
sprintf(text,"%s:",car[car_for].car_value);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
sprintf(text,"%d:%.0f:00\r\n",time_car,time_value);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
memset(car[car_for].car_tapy,0,5);
memset(car[car_for].car_value,0,5);
memset(car[car_for].time_year,0,3);
memset(car[car_for].time_month,0,3);
memset(car[car_for].time_mintute,0,3);
memset(car[car_for].time_second,0,3);
car_kong++;break;
}
}
else if(strcmp(car_tapy_text,car[car_for].car_tapy)!=0)
{
if((strcmp(car_value_text,car[car_for].car_value)!=0)&&(car_kong!=0))
{
if(strcmp(car_tapy_text, "CNBR")==0)
{
c++;
}
if(strcmp(car_tapy_text, "VNBR")==0)
{
v++;
}
car_flag=1;break;
}
}
}
if((car_kong!=0)&&(car_flag==1))
{
car_flag=0;
sscanf(rx_buf,"%4s:%4s:%2s%2s%2s%2s%2s",car[car_for].car_tapy,car[car_for].car_value,car[car_for].time_year,car[car_for].time_month,car[car_for].time_day,car[car_for].time_mintute,car[car_for].time_second);
car_kong--;
if(car_kong==0)
{
led_Disp(0x00);
}
}
else if(car_kong==0)
{
if(car_kong<=1)
{
led_Disp(0x00);
}
sprintf(text,"No parking space!\r\n");
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
}
}
else //如果传入数据不为22位,则报错
{
sprintf(text,"Error!\r\n");
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50); //串口缓冲时间
}
rx_dat=0; //清空接收数据
memset(rx_buf,0,30);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
并且需要在main.h文件里对一些定义和库做出初始化
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32g4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdbool.h"
#include "stdlib.h"
#include "math.h"
#define uchar unsigned char
#define uint unsigned int
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
后续
本文写的代码都是自己结合b站up主:01stdio和其他各位大佬的文章,按照自己的理解和书写风格完成的,仅作为学习嵌入式知识来使用,有些代码写的不太清楚,本文也只是作为自己以后回顾学习而使用,不喜勿喷,欢迎大家来讨论并学习嵌入式相关知识。