当前位置: 首页 > article >正文

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;
}

http://www.kler.cn/a/454201.html

相关文章:

  • (六)循环神经网络_基本的RNN
  • Wireshark软件下载安装及基础
  • Nginx界的天花板-Oracle 中间件OHS 11g服务器环境搭建
  • JVM内存模型、垃圾回收机制及简单调优方式
  • 重温设计模式--10、单例模式
  • kubernetes存储架构之PV controller源码解读
  • 穿山甲等广告联盟依据哪些维度给APP、小程序结算广告变现收益
  • 【小菜鸟之—CEPH文件集群部署实践】
  • 【Redis】 数据淘汰策略
  • java中list和map区别
  • 国际版JAVA同城服务美容美发到店服务系统源码支持Android+IOS+H5
  • 计算机毕业设计Python+Spark知识图谱医生推荐系统 医生门诊预测系统 医生数据分析 医生可视化 医疗数据分析 医生爬虫 大数据毕业设计 机器学习
  • 《机器学习》从入门到实战(1)
  • 【05-数据库面试】
  • 【gym】理解gym并测试gym小游戏CartPole (一)
  • (附源码)基于springboot的酒店餐饮预定管理系统 用户前台管理后台 P10062
  • 【ETCD】【实操篇(十三)】ETCD Cluster体检指南:健康状态一键诊断,全方位解析!
  • 力扣第118题:杨辉三角 - C语言解法
  • 前端安全 常见的攻击类型及防御措施
  • HotpotQA多跳问答数据集;Kaggle在线数据科学和机器学习竞赛平台