前提
- 在开发STM32过程中,芯片提供的串口引脚一般是不会发生变化的,所以为了方便移植,借助HAL提供的注册回调函数自定义,这边重新进行简要的封装
- 此工程开发是以Clion为开发的IDE,用keil只需将对应的文件进行移植即可.
- 文章末尾附带gitee工程地址
工程创建(以STM32F103C8T6为例)
- 参考Stm32开发环境从0搭建(Clion作为开发软件)
- 开启自定义注册串口硬件注册回调(方便后面工程移植,也可以使用HAL默认的硬件回调)
3. 打开工程并测试
添加自定义的项目路径(方便后期移植)
- 创建目录框架
2. 编写CMakeLists文件t和CMakeLists模板文件(此步骤是将自定义目录框架包含到项目中)
3. 创建同一的头文件去管理
统一串口宏定义
头文件 (bsp_serial_define.h)
#ifndef STM32_VET6_BSP_SERIAL_DEFINE_H
#define STM32_VET6_BSP_SERIAL_DEFINE_H#include "sys_driver_include.h"
// 串口只支持异步
/***********************************************************串口1相关宏定义*******************************************************/
#define USE_COM1_ENABLE (1)
#define USE_COM1_IRQ_ENABLE (0) // 串口1中断使能
#define USE_COM1_DMA_RX_ENABLE (0) // 串口1 DMA RX 使能
#define COM1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define COM1_TX_PORT GPIOA
#define COM1_TX_PIN GPIO_PIN_9
#define COM1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define COM1_RX_PORT GPIOA
#define COM1_RX_PIN GPIO_PIN_10
#define COM1_IRQ_Priority 5 // 抢占优先级
#define COM1_IRQ_SubPriority 0 // 响应优先级
#define COM1_DMA_RX_CHANNEL DMA1_Channel5
#define COM1_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE()
#define COM1_DMA_IRQ DMA1_Channel5_IRQn
#define COM1_IRQ_HANDLE DMA1_Channel5_IRQHandler
extern UART_HandleTypeDef com1_handle;
/***********************************************************串口2相关宏定义*******************************************************/
#define USE_COM2_ENABLE (0)
#define USE_COM2_IRQ_ENABLE (0) // 串口2中断使能
#define USE_COM2_DMA_RX_ENABLE (0) // 串口1 DMA RX 使能/**@details 引脚定义*/
#define COM2_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define COM2_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define COM2_TX_PIN GPIO_PIN_2
#define COM2_RX_PIN GPIO_PIN_3
#define COM2_TX_PORT GPIOA
#define COM2_RX_PORT GPIOA#define COM2_IRQ_Priority 5 // 抢占优先级
#define COM2_IRQ_SubPriority 0 // 响应优先级
#define COM2_DMA_RX_CHANNEL DMA1_Channel6
#define COM2_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE()
#define COM2_DMA_IRQ DMA1_Channel6_IRQn
#define COM2_IRQ_HANDLE DMA1_Channel6_IRQHandler
extern UART_HandleTypeDef com2_handle;
/***********************************************************串口3相关宏定义*******************************************************/
#define USE_COM3_ENABLE (0)
#define USE_COM3_IRQ_ENABLE (1) // 串口3中断使能
#define USE_COM3_DMA_RX_ENABLE (1) // 串口3 DMA RX 使能
#define COM3_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
#define COM3_TX_PORT GPIOD
#define COM3_TX_PIN GPIO_PIN_8
#define COM3_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
#define COM3_RX_PORT GPIOD
#define COM3_RX_PIN GPIO_PIN_9
#define COM3_IRQ_Priority 5 // 抢占优先级
#define COM3_IRQ_SubPriority 0 // 响应优先级
#define COM3_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE()
#define COM3_DMA_RX_CHANNEL DMA1_Channel3
#define COM3_DMA_IRQ DMA1_Channel3_IRQn
#define COM3_IRQ_HANDLE DMA1_Channel3_IRQHandler
extern UART_HandleTypeDef com3_handle;
/***********************************************************串口4相关宏定义*******************************************************/
#define USE_COM4_ENABLE (0)
#define USE_COM4_IRQ_ENABLE (1) // 串口4中断使能
#define USE_COM4_DMA_RX_ENABLE (1) // 串口4 DMA RX 使能
#define COM4_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define COM4_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define COM4_TX_PORT GPIOC
#define COM4_RX_PORT GPIOC
#define COM4_TX_PIN GPIO_PIN_10
#define COM4_RX_PIN GPIO_PIN_11
#define COM4_IRQ_Priority 5 // 抢占优先级
#define COM4_IRQ_SubPriority 0 // 响应优先级
#define COM4_DMA_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()
#define COM4_DMA_RX_CHANNEL DMA2_Channel3
#define COM4_DMA_IRQ DMA2_Channel3_IRQn
#define COM4_DMA_IRQ_HANDLE DMA2_Channel3_IRQHandler
extern UART_HandleTypeDef com4_handle;
#endif //STM32_VET6_BSP_SERIAL_DEFINE_H
串口核心文件
头文件(bsp_serial.h)
#ifndef STM32_VET6_BSP_SERIAL_H
#define STM32_VET6_BSP_SERIAL_H#include "bsp_serial_define.h"
#include "bsp_serial_com1.h"void Com_Init_01(USART_TypeDef *uart, uint32_t baud);void Com_Init(UART_HandleTypeDef *comHandle, UART_InitTypeDef *config);void Com_RegisterCallback(UART_HandleTypeDef *comHandle);void Com_Dma_Init(DMA_HandleTypeDef *dmaHandle, DMA_InitTypeDef *config);void UART_MspInit(UART_HandleTypeDef *comHandle);void UART_MspDeInit(UART_HandleTypeDef *comHandle);void UART_RxEventCallback(UART_HandleTypeDef *com_handle, uint16_t Pos);#endif //STM32_VET6_BSP_SERIAL_H
源文件(bsp_serial.c)
#include "bsp_serial.h"static UART_InitTypeDef init_cnf = {.OverSampling = UART_OVERSAMPLING_16,.Mode = UART_MODE_TX_RX,.HwFlowCtl = UART_HWCONTROL_NONE,.Parity = UART_PARITY_NONE,.StopBits = UART_STOPBITS_1,.WordLength = UART_WORDLENGTH_8B,.BaudRate = 9600
};
static DMA_InitTypeDef default_dma_config = {.Direction = DMA_PERIPH_TO_MEMORY,.PeriphInc = DMA_PINC_DISABLE,.MemInc = DMA_MINC_ENABLE,.PeriphDataAlignment = DMA_PDATAALIGN_BYTE,.MemDataAlignment =DMA_MDATAALIGN_BYTE,.Mode = DMA_NORMAL,.Priority = DMA_PRIORITY_LOW
};/*** 通用串口初始化* @param uart 串口* @param baud*/
void Com_Init_01(USART_TypeDef *uart, uint32_t baud) {init_cnf.BaudRate = baud;if (uart == NULL) {}
#if USE_COM1_ENABLEelse if (uart == USART1) {Com_Init(&com1_handle, &init_cnf);}
#endif
#if USE_COM2_ENABLEelse if (uart == USART2) {Sw_Com_Init(&com2_handle, &init_cnf);}
#endif
#if USE_COM3_ENABLEelse if (uart == USART3) {Sw_Com_Init(&com3_handle, &init_cnf);}
#endif
#if USE_COM4_ENABLEelse if (uart == UART4) {Sw_Com_Init(&com4_handle, &init_cnf);}
#endif
}/*** @brief 通用串口初始化* @param comHandle* @param config*/
void Com_Init(UART_HandleTypeDef *comHandle, UART_InitTypeDef *config) {
#if USE_HAL_UART_REGISTER_CALLBACKScomHandle->MspDeInitCallback = UART_MspDeInit;comHandle->MspInitCallback = UART_MspInit;
#endifUART_InitTypeDef *ptr = NULL;if (config != NULL) {ptr = config;} else {ptr = &init_cnf;}memcpy(&comHandle->Init, ptr, sizeof(UART_InitTypeDef));if (HAL_UART_Init(comHandle) != HAL_OK) {common_error_handle(__FILE__, __LINE__);}
}/*** @brief 注册接收事件回调* @param comHandle*/
void Com_RegisterCallback(UART_HandleTypeDef *comHandle) {
#if USE_HAL_UART_REGISTER_CALLBACKSHAL_UART_RegisterRxEventCallback(comHandle, UART_RxEventCallback);
#endif
}void UART_MspInit(UART_HandleTypeDef *comHandle) {
#if USE_COM1_ENABLEif (comHandle->Instance == USART1) {Com1_MspInit();}
#endif
#if USE_COM2_ENABLEif (comHandle->Instance == USART2) {Com2_MspInit();}
#endif
#if USE_COM3_ENABLEif (comHandle->Instance == USART3) {Com3_MspInit();}
#endif
#if USE_COM4_ENABLEif (comHandle->Instance == UART4) {Com4_MspInit();}
#endif
}void UART_MspDeInit(UART_HandleTypeDef *comHandle) {
#if USE_COM1_ENABLEif (comHandle->Instance == USART1) {Com1_MspDeInit();}
#endif
#if USE_COM2_ENABLEif (comHandle->Instance == USART2) {Com2_MspDeInit();}
#endif
#if USE_COM3_ENABLEif (comHandle->Instance == USART3) {Com3_MspDeInit();}
#endif
#if USE_COM4_ENABLEif (comHandle->Instance == UART4) {Com4_MspDeInit();}
#endif
}/*** @brief 串口接收事件* @param com_handle* @param Pos*/
void UART_RxEventCallback(UART_HandleTypeDef *com_handle, uint16_t Pos) {if (com_handle == NULL) {common_error_handle(__FILE__, __LINE__);}
#if (USE_COM1_ENABLE == 1)else if (com_handle->Instance == USART1) {Com1_RxEvent(Pos);}
#endif
#if (USE_COM2_ENABLE == 1)else if (comHandle->Instance == USART2) {Com2_RxEvent(Pos);}
#endif
#if (USE_COM3_ENABLE == 1)else if (comHandle->Instance == USART3) {Com3_RxEvent(Pos);}
#endif
#if (USE_COM4_ENABLE == 1)else if (comHandle->Instance == UART4) {Com4_RxEvent(Pos);}
#endif
}/*** 错误回调* @param comHandle 串口句柄*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *comHandle) {switch (comHandle->ErrorCode) {case HAL_UART_ERROR_PE:__HAL_UART_CLEAR_FLAG(comHandle, UART_FLAG_PE);break;case HAL_UART_ERROR_NE:__HAL_UART_CLEAR_FLAG(comHandle, UART_FLAG_NE);break;case HAL_UART_ERROR_FE:__HAL_UART_CLEAR_FLAG(comHandle, UART_FLAG_FE);break;case HAL_UART_ERROR_ORE:__HAL_UART_CLEAR_FLAG(comHandle, UART_FLAG_ORE);break;}if (comHandle == NULL) {common_error_handle(__FILE__, __LINE__);}
#if (USE_COM1_ENABLE == 1)else if (comHandle->Instance == USART1) {Com1_ErrEvent();}
#endif
#if (USE_COM2_ENABLE == 1)else if (comHandle->Instance == USART2) {Com2_ErrEvent();}
#endif
#if (USE_COM3_ENABLE == 1)else if (comHandle->Instance == USART3) {Com3_ErrEvent();}
#endif
#if (USE_COM4_ENABLE == 1)else if (comHandle->Instance == UART4) {Com4_ErrEvent();}
#endif
}/*** @brief 统一DMA接口初始化* @param dmaHandle* @param config*/
void Com_Dma_Init(DMA_HandleTypeDef *dmaHandle, DMA_InitTypeDef *config) {DMA_InitTypeDef *ptr;if (config != NULL) {ptr = config;} else {ptr = &default_dma_config;}memcpy(&dmaHandle->Init, ptr, sizeof(DMA_InitTypeDef));
}
串口1
头文件(bsp_serial_com1.h)
#ifndef STM32_VET6_BSP_SERIAL_COM1_H
#define STM32_VET6_BSP_SERIAL_COM1_H#include "bsp_serial_define.h"#if USE_COM1_ENABLEvoid Com1_DMA_Init(uint32_t PreemptPriority, uint32_t SubPriority, DMA_InitTypeDef *config);void Com1_Init(UART_InitTypeDef *config);void Com1_MspInit(void);void Com1_MspDeInit(void);void Com1_DMA_Rx_MspInit(void);void Com1_DMA_Rx_MspDeInit(void);void Com1_RxEvent(uint16_t pos);void Com1_ErrEvent(void);#endif#endif //STM32_VET6_BSP_SERIAL_COM1_H
基础源文件(bsp_serial_com1.c)
#include "bsp_serial.h"UART_HandleTypeDef com1_handle = {.Instance=USART1};/*** @brief Com1初始化* @param config*/
void Com1_Init(UART_InitTypeDef *config) {Com_Init(&com1_handle, config);// 使用自定义的中断回调Com_RegisterCallback(&com1_handle);
}void Com1_MspInit(void) {GPIO_InitTypeDef GPIO_InitStruct = {0};/* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();COM1_TX_GPIO_CLK_ENABLE();COM1_RX_GPIO_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9 ------> USART1_TXPA10 ------> USART1_RX*/GPIO_InitStruct.Pin = COM1_TX_PIN;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(COM1_TX_PORT, &GPIO_InitStruct);GPIO_InitStruct.Pin = COM1_RX_PIN;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(COM1_RX_PORT, &GPIO_InitStruct);
#if USE_COM1_DMA_RX_ENABLECom1_DMA_Rx_MspInit();
#endif
}void Com1_MspDeInit(void) {/* Peripheral clock disable */__HAL_RCC_USART1_CLK_DISABLE();HAL_GPIO_DeInit(COM1_TX_PORT, COM1_TX_PIN);HAL_GPIO_DeInit(COM1_RX_PORT, COM1_RX_PIN);
#if USE_COM1_DMA_RX_ENABLECom1_DMA_Rx_MspDeInit();
#endif
}void USART1_IRQHandler(void) {HAL_UART_IRQHandler(&com1_handle);
}
扩展串口接收dma(bsp_serial_dma_com1.c)
#include "bsp_serial.h"DMA_HandleTypeDef com1_dma_rx_handle={.Instance=COM1_DMA_RX_CHANNEL,
};void Com1_DMA_Init(uint32_t PreemptPriority, uint32_t SubPriority, DMA_InitTypeDef *config) {
/* DMA controller clock enable */COM1_DMA_CLK_ENABLE();/* DMA interrupt init */HAL_NVIC_SetPriority(COM1_DMA_IRQ, PreemptPriority, SubPriority);HAL_NVIC_EnableIRQ(COM1_DMA_IRQ);// com1初始化Com_Dma_Init(&com1_dma_rx_handle, config);
}void Com1_DMA_Rx_MspInit(void) {if (HAL_DMA_Init(&com1_dma_rx_handle) != HAL_OK) {common_error_handle(__FILE__, __LINE__);}__HAL_LINKDMA(&com1_handle, hdmarx, com1_dma_rx_handle);
#if USE_COM1_IRQ_ENABLEHAL_NVIC_SetPriority(USART1_IRQn, COM1_IRQ_Priority, COM1_IRQ_SubPriority);HAL_NVIC_EnableIRQ(USART1_IRQn);
#endif
}void Com1_DMA_Rx_MspDeInit(void) {/* DMA DeInit */HAL_DMA_DeInit(&com1_dma_rx_handle);/* interrupt Deinit */HAL_NVIC_DisableIRQ(COM1_DMA_IRQ);
}void COM1_IRQ_HANDLE(void) {/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */HAL_DMA_IRQHandler(&com1_dma_rx_handle);
}/*** @brief 参考 @ref HAL_UARTEx_RxEventCallback* @param pos*/
__weak void Com1_RxEvent(uint16_t pos) {UNUSED(pos);
}
/*** @brief 错误事件*/
__weak void Com1_ErrEvent(void) {}
示例
- 开启串口中断和DMA接收中断
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include <stdbool.h>
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "bsp_serial.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* 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 */
static uint8_t buffer[256];
// 接收数据标志位
static volatile bool rec_data_flag = false;
// 接收数据长度
static volatile uint16_t rec_len = 0;/*** @brief 错误事件*/
void Com1_ErrEvent(void) {rec_len = 0;rec_data_flag = false;// 重新开启DMA接收HAL_UARTEx_ReceiveToIdle_DMA(&com1_handle, buffer, 256);
}void Com1_RxEvent(uint16_t pos) {rec_len = pos;rec_data_flag = true;}static uint16_t com_rec(void *retBuf) {if (rec_data_flag) {rec_data_flag = false;memcpy(retBuf, buffer, rec_len);HAL_UARTEx_ReceiveToIdle_DMA(&com1_handle, buffer, 256);return rec_len;}return 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 */
#if 0/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */
#endifCom1_DMA_Init(5, 0, NULL);Com_Init_01(USART1, 9600);// 开启串口屏接收数据HAL_UARTEx_ReceiveToIdle_DMA(&com1_handle, buffer, 256);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */uint8_t read_buf[256];while (1) {/* USER CODE END WHILE *//* USER CODE BEGIN 3 */uint16_t data_len = com_rec(read_buf);if (data_len > 0) {// todo 解析数据 read_buf}}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;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_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {Error_Handler();}
}/* USER CODE BEGIN 4 *//* 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 */
项目gitee
https://gitee.com/scl_arm/serial_proj.git