C++ STM32 F4xx USART LL库 DMA + IDLE ISR 驱动裸机 +FreeRTOS 任务通知
写的一般,大佬可以帮我看看
头文件
/**
******************************************************************************
* @file : usart_driver.hpp
* @brief : usart_driver program head
******************************************************************************
* @attention
******************************************************************************
*/
#ifndef __USART_DRIVER_H
#define __USART_DRIVER_H
#include "stm32f4xx_hal.h"
#define COM1 USART1
#define COM2 USART2
#define COM3 USART3
#define COM4 UART4
#define COM5 UART5
#define COM6 USART6
#define USE_RTOS_FOR_USART
#ifdef USE_RTOS_FOR_USART
#include "FreeRTOS.h"
#include "task.h"
#endif
class serial_device {
public:
explicit serial_device(USART_TypeDef* usartPort, uint32_t baudRate, uint16_t nvicPriority)
: m_serial_port(usartPort),
m_baudrate(baudRate),
m_nvic_priority(nvicPriority) {
mp_buffer_for_dma_rx = mp_rx_bufferA;
mp_buffer_for_user_rx = mp_buffer_for_dma_rx;
m_is_rx_complete = 0;
m_rx_data_size = 0;
m_is_tx_complete = 0;
#if USE_FREERTOS
mp_task_handle = NULL; //接收任务通知
#endif
}
void init(); // serial_init USART
void send(const uint8_t* data, uint16_t size); // Send data
void handle_isr_recv(); // Interrupt receive processing function
#ifdef USE_RTOS_FOR_USART
void set_notify_task(TaskHandle_t task_handle); //设置任务通知的task handle
uint32_t task_notify_take();
#endif
void reset_rx_complete_flag();
void reset_tx_complete_flag();
const uint8_t* get_recv_data();
uint16_t get_recv_data_size();
uint8_t is_recv_ready();
uint8_t is_send_ready();
private:
USART_TypeDef* m_serial_port;
DMA_TypeDef* m_dma_port;
uint32_t m_dma_rx_stream;
uint32_t m_dma_tx_stream;
#ifdef USE_RTOS_FOR_USART
TaskHandle_t mp_task_handle;
BaseType_t mpx_task_woken;
#endif
uint32_t m_baudrate;
uint16_t m_nvic_priority;
uint16_t m_rx_data_size;
static constexpr uint32_t RECEIVE_BUFFER_SIZE = 32; // Size of received data buffer
uint8_t m_is_rx_complete;
uint8_t m_is_tx_complete;
uint8_t* mp_buffer_for_dma_rx; // Pointer to ready-to-receive buffer
uint8_t* mp_buffer_for_user_rx; // Pointer to user-accessible receive buffer
uint8_t mp_rx_bufferA[RECEIVE_BUFFER_SIZE * 2]; // Receive buffer A
uint8_t mp_rx_bufferB[RECEIVE_BUFFER_SIZE * 2]; // Receive buffer B
serial_device(const serial_device&) = delete;
serial_device& operator=(const serial_device&) = delete; // Disable copy and move constructors
serial_device(serial_device&&) = delete;
serial_device& operator=(serial_device&&) = delete;
};
#endif
源文件
/**
******************************************************************************
* @file : usart_driver.c
* @brief : usart program body
******************************************************************************
* @attention
******************************************************************************
*/
#include "usart_driver.h"
#include "string.h"
#include "stm32f4xx_ll_usart.h"
#include "stm32f4xx_ll_dma.h"
#include "stm32f4xx_ll_gpio.h"
#include "stm32f4xx_ll_rcc.h"
#include "stm32f4xx_ll_bus.h"
/**
* @brief 初始化串口
*
* 此函数用于初始化指定的串口,包括GPIO配置、DMA配置、中断配置和串口配置。
*
* @param 无
*
* @return 无
*/
void serial_device::init(){
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
IRQn_Type COMIRQn;
IRQn_Type DMARxIRQn;
IRQn_Type DMATxIRQn;
/* Peripheral clock enable */
if(this->m_serial_port == COM1){
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_9|LL_GPIO_PIN_10;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
m_dma_port = DMA2;
m_dma_rx_stream = LL_DMA_STREAM_2;
m_dma_tx_stream = LL_DMA_STREAM_7;
COMIRQn = USART1_IRQn;
DMARxIRQn = DMA2_Stream2_IRQn;
DMATxIRQn = DMA2_Stream7_IRQn;
} else if(this->m_serial_port == COM2){
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2|LL_GPIO_PIN_3;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
m_dma_port = DMA1;
m_dma_rx_stream = LL_DMA_STREAM_5;
m_dma_tx_stream = LL_DMA_STREAM_6;
COMIRQn = USART2_IRQn;
DMARxIRQn = DMA1_Stream5_IRQn;
DMATxIRQn = DMA1_Stream6_IRQn;
} else if(this->m_serial_port == COM3){
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_10|LL_GPIO_PIN_11;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
m_dma_port = DMA1;
m_dma_rx_stream = LL_DMA_STREAM_1;
m_dma_tx_stream = LL_DMA_STREAM_3;
COMIRQn = USART2_IRQn;
DMARxIRQn = DMA1_Stream1_IRQn;
DMATxIRQn = DMA1_Stream3_IRQn;
} else if(this->m_serial_port == COM4){
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_UART4);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**UART4 GPIO Configuration
PA0-WKUP ------> UART4_TX
PA1 ------> UART4_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
m_dma_port = DMA1;
m_dma_rx_stream = LL_DMA_STREAM_2;
m_dma_tx_stream = LL_DMA_STREAM_4;
COMIRQn = UART4_IRQn;
DMARxIRQn = DMA1_Stream2_IRQn;
DMATxIRQn = DMA1_Stream4_IRQn;
}else if(this->m_serial_port == COM5){
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_UART5);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
/**UART5 GPIO Configuration
PC12 ------> UART5_TX
PD2 ------> UART5_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_12;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
m_dma_port = DMA1;
m_dma_rx_stream = LL_DMA_STREAM_0;
m_dma_tx_stream = LL_DMA_STREAM_7;
COMIRQn = UART5_IRQn;
DMARxIRQn = DMA1_Stream0_IRQn;
DMATxIRQn = DMA1_Stream7_IRQn;
}else if(this->m_serial_port == COM6){
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART6);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
/**USART6 GPIO Configuration
PC6 ------> USART6_TX
PC7 ------> USART6_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
this->m_dma_port = DMA2;
this->m_dma_rx_stream = LL_DMA_STREAM_1;
this->m_dma_tx_stream = LL_DMA_STREAM_6;
COMIRQn = UART5_IRQn;
DMARxIRQn = DMA2_Stream1_IRQn;
DMATxIRQn = DMA2_Stream6_IRQn;
}
/* DMA Init */
/* RX Init */
LL_DMA_SetChannelSelection(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_CHANNEL_4);
LL_DMA_SetDataTransferDirection(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(this->m_dma_port, this->m_dma_rx_stream, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(this->m_dma_port, this->m_dma_rx_stream);
/* TX Init */
LL_DMA_SetChannelSelection(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_CHANNEL_4);
LL_DMA_SetDataTransferDirection(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(this->m_dma_port, this->m_dma_tx_stream, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(this->m_dma_port, this->m_dma_tx_stream);
/* Interrupt Init */
NVIC_SetPriority(COMIRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),this->m_nvic_priority, 0));
NVIC_SetPriority(DMARxIRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),this->m_nvic_priority, 0));
NVIC_SetPriority(DMATxIRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),this->m_nvic_priority, 0));
NVIC_EnableIRQ(COMIRQn);
NVIC_EnableIRQ(DMARxIRQn);
NVIC_EnableIRQ(DMATxIRQn);
/* UART init */
USART_InitStruct.BaudRate = this->m_baudrate;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(this->m_serial_port, &USART_InitStruct);
LL_USART_ConfigAsyncMode(this->m_serial_port);
LL_USART_Enable(this->m_serial_port);
LL_USART_EnableIT_IDLE(this->m_serial_port);//使用空闲中断,必须使能空闲中断
LL_DMA_EnableIT_TC(this->m_dma_port,this->m_dma_tx_stream);
LL_DMA_SetMemoryAddress(this->m_dma_port, m_dma_rx_stream,(uint32_t)(this->mp_buffer_for_dma_rx));
LL_DMA_SetPeriphAddress(this->m_dma_port,this->m_dma_rx_stream, LL_USART_DMA_GetRegAddr(this->m_serial_port));
LL_DMA_SetDataTransferDirection(this->m_dma_port,this->m_dma_rx_stream,LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(this->m_dma_port,this->m_dma_rx_stream, RECEIVE_BUFFER_SIZE);
LL_DMA_EnableStream(this->m_dma_port,this->m_dma_rx_stream);//使能DMA流
LL_USART_EnableDMAReq_RX(this->m_serial_port);
LL_USART_EnableDMAReq_TX(this->m_serial_port);
}
/**
* @brief 发送数据到串口
*
* 通过DMA(直接内存访问)方式将指定大小的数据发送到串口。
*
* @param data 要发送的数据指针
* @param size 要发送的数据大小(以字节为单位)
*/
void serial_device::send(const uint8_t *data, uint16_t size) {
// 重置传输完成标志
reset_tx_complete_flag();
LL_DMA_SetMemoryAddress(this->m_dma_port, m_dma_tx_stream,(uint32_t)(data));
LL_DMA_SetPeriphAddress(this->m_dma_port,this->m_dma_tx_stream, LL_USART_DMA_GetRegAddr(this->m_serial_port));
LL_DMA_SetDataTransferDirection(this->m_dma_port,this->m_dma_tx_stream,LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetDataLength(this->m_dma_port, m_dma_tx_stream, size);
LL_DMA_EnableStream(this->m_dma_port, m_dma_tx_stream);//使能DMA流
}
/**
* @brief UART 中断处理函数
*
* 该函数处理UART的中断,包括空闲中断和发送完成中断。
*
* 当检测到空闲中断时,会清除空闲中断标志位,关闭DMA,清除DMA搬运完成标志,
* 获取接收数据大小,更新接收缓冲区指针,并重新配置DMA以继续接收数据。
*
* 当检测到发送完成中断时,会清除发送完成标志,清除DMA搬运完成中断,并禁用DMA流。
*/
void serial_device::handle_isr_recv(){
if((LL_USART_IsEnabledIT_IDLE(this->m_serial_port) != RESET) && (LL_USART_IsActiveFlag_IDLE(this->m_serial_port) != RESET))//空闲中断
{
//清除空闲中断标志位
LL_USART_ClearFlag_IDLE(this->m_serial_port);
//读取数据前将DMA关闭
LL_DMA_DisableStream(this->m_dma_port,this->m_dma_rx_stream);
//清除DMA搬运完成标志
if(this->m_dma_rx_stream == LL_DMA_STREAM_0){LL_DMA_ClearFlag_TC0(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_1){LL_DMA_ClearFlag_TC1(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_2){LL_DMA_ClearFlag_TC2(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_3){LL_DMA_ClearFlag_TC3(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_4){LL_DMA_ClearFlag_TC4(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_5){LL_DMA_ClearFlag_TC5(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_6){LL_DMA_ClearFlag_TC6(m_dma_port);}
else if(this->m_dma_rx_stream == LL_DMA_STREAM_7){LL_DMA_ClearFlag_TC7(m_dma_port);}
//获取接收数据大小
m_rx_data_size = RECEIVE_BUFFER_SIZE - LL_DMA_GetDataLength(m_dma_port, m_dma_rx_stream);//得到串口收到的数据长度
this->m_is_rx_complete = 1;
#ifdef USE_RTOS_FOR_USART
this->mpx_task_woken = pdFALSE;
vTaskNotifyGiveFromISR(this->mp_task_handle,&mpx_task_woken); //发送通知
#endif
mp_buffer_for_user_rx = mp_buffer_for_dma_rx;
//this->send(mp_buffer_for_user_rx,m_rx_data_size);
if(this->mp_buffer_for_dma_rx == this->mp_rx_bufferA){ //切换缓冲区
this->mp_buffer_for_dma_rx = this->mp_rx_bufferB;
}else{
this->mp_buffer_for_dma_rx = this->mp_rx_bufferA;
}
//重新开始接收,设置DMA的地址和数据大小
LL_DMA_ConfigAddresses(this->m_dma_port,this->m_dma_rx_stream, LL_USART_DMA_GetRegAddr(this->m_serial_port), (uint32_t)(this->mp_buffer_for_dma_rx), LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(this->m_dma_port,this->m_dma_rx_stream, RECEIVE_BUFFER_SIZE);
LL_DMA_EnableStream(this->m_dma_port,this->m_dma_rx_stream);//使能DMA流
#ifdef USE_RTOS_FOR_USART
portYIELD_FROM_ISR(this->mpx_task_woken);
#endif
}
}
#ifdef USE_RTOS_FOR_USART
void serial_device::set_notify_task(TaskHandle_t task_handle){
this->mp_task_handle = task_handle;
}
uint32_t serial_device::task_notify_take(){
return ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
#endif
/**
* @brief 从接收缓冲区中读取数据指针
*
* 获取接收缓冲区的数据指针,以便读取串口接收到的数据。
*
* @return 返回接收缓冲区的指针
*/
const uint8_t * serial_device::get_recv_data(){
return this->mp_buffer_for_user_rx;
}
/**
* @brief 读取接收到的数据大小
*
* 获取当前接收到的数据大小。
*
* @return uint16_t 接收到的数据大小
*/
uint16_t serial_device::get_recv_data_size(){
return this->m_rx_data_size;
}
/**
* @brief 检查串口接收是否准备好
*
* 检查串口接收是否完成,如果完成则返回true,否则返回false。
*
* @return 如果接收完成,返回true;否则返回false。
*/
uint8_t serial_device::is_recv_ready(){
return this->m_is_rx_complete;
}
/**
* @brief 判断串口传输是否准备完成
*
* 判断串口传输是否已经完成准备,可以开始传输数据。
*
* @return 如果传输准备完成,返回非零值;否则返回0。
*/
uint8_t serial_device::is_send_ready(){
return this->m_is_tx_complete;
}
/**
* @brief 重置接收完成标志位
*
* 将接收完成标志位 `RecvCpltFlag` 重置为 0。
*/
void serial_device::reset_rx_complete_flag(){
this->m_is_rx_complete = 0;
}
/**
* @brief 重置传输完成标志
*
* 该函数用于将传输完成标志重置为0。
*/
void serial_device::reset_tx_complete_flag(){
this->m_is_tx_complete = 0;
}