文章目录

  • 1 前言
  • 2 STM32H7实现
  • 2.1 关键步骤
  • 2.2 注意事项
  • 3 代码仓库


1 前言

  关于串口DMA收发实现,不同CPU其套路都是类似的,不同之处在于寄存器配置、依赖BSP库等差异。串口DMA收发详细实现技巧、流程、方法,参考文章“一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制”。



2 STM32H7实现

  H7已经不支持标准库,只支持HAL库,虽然HAL库臃肿,但借助CubeMX生成配置代码还是很香的,特别对于复杂的外设,如USB、Etherent、SDIO。某些外设支持LL库,在CubeMX生成工程时可以选择;LL库的套路比较像以前的标准库,效率比较高,个人建议首选LL库,虽然ST没有主推。这里的串口DMA收发,用LL库实现!



测试平台:

  • CPU:STM32H743XI
  • 主频:400 MHz
  • BSP库:HAL&LL
  • UART:UART5,1500000/8/n/1
  • DMA:DMA1 Stream0、DMA1 Stream1


2.1 关键步骤

  基于一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制里面工程,这里只需实现BSP初始和相关接口提供给“dev_uart”即可。



  • CubeMX配置

cpu时钟配置


STM32CUBEMX 串口dma接收不定长数据 stm32h7 串口dma接收_stm32


uart5配置


STM32CUBEMX 串口dma接收不定长数据 stm32h7 串口dma接收_物联网_02


uart5选择LL库



  • 补充初始代码
      CubeMX生成配置代码只是配置部分,并不能满足正常工作,需根据具体情况完善,如使能uart空闲中断、指定DMA收发内存、启动外设等。


  • 中断回调函数实体实现
      串口DMA收发需使用到串口空闲中断、DMA接收半满中断、DMA接收溢出中断、DMA发送完成中断。
/**
 * \brief           DMA1 stream0 interrupt handler for UART5 RX
 */
void DMA1_Stream0_IRQHandler(void) 
{
    if (LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_STREAM_0) && LL_DMA_IsActiveFlag_HT0(DMA1)) 
    {
        uart_dmarx_half_done_isr(DEV_UART1);
        LL_DMA_ClearFlag_HT0(DMA1);             
    }

    if (LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_STREAM_0) && LL_DMA_IsActiveFlag_TC0(DMA1)) 
    {
          uart_dmarx_done_isr(DEV_UART1);
          LL_DMA_ClearFlag_TC0(DMA1);          
    }
}

/**
 * \brief           DMA1 stream1 interrupt handler for UART5 TX
 */
void DMA1_Stream1_IRQHandler(void) 
{
    if (LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_STREAM_1) && LL_DMA_IsActiveFlag_TC1(DMA1)) 
    {
        uart_dmatx_done_isr(DEV_UART1);
        LL_DMA_ClearFlag_TC1(DMA1);             
    }
}

/**
  * @brief This function handles UART5 global interrupt.
  */
void UART5_IRQHandler(void)
{
   /* USER CODE BEGIN UART5_IRQn 0 */
    if (LL_USART_IsEnabledIT_IDLE(UART5) && LL_USART_IsActiveFlag_IDLE(UART5)) 
    {
        uart_dmarx_idle_isr(DEV_UART1);
        LL_USART_ClearFlag_IDLE(UART5);                              
    }
}



  • BSP接口支持

  Bsp初始化
  DMA发送使能接口
  DMA接收使能接口
  DMA接收buf剩余空间

void uart5_dma_init(uint8_t *mem_addr, uint32_t mem_size);
void bsp_uart5_dmatx_config(uint8_t *mem_addr, uint32_t mem_size);
void bsp_uart5_dmarx_config(uint8_t *mem_addr, uint32_t mem_size);
uint16_t bsp_uart5_get_dmarx_buf_remain_size(void);



2.2 注意事项

  H7与F1、F0系列稍有不同,以下注意事项避免踩坑。



  • 初始化过程

  DMA初始化必须置于uart初始化之前,否则通信异常

***************
    
    bsp_uart5_dmarx_config(mem_addr, mem_size);
    
    NVIC_SetPriority(DMA1_Stream0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 0));
    NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    NVIC_SetPriority(DMA1_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 1));
    NVIC_EnableIRQ(DMA1_Stream1_IRQn);
    
    NVIC_SetPriority(UART5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 2, 0));
    NVIC_EnableIRQ(UART5_IRQn);

    UART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
    UART_InitStruct.BaudRate = 1500000;
    UART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
    UART_InitStruct.StopBits = LL_USART_STOPBITS_1;
    UART_InitStruct.Parity = LL_USART_PARITY_NONE;
    UART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
    UART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
    UART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
    LL_USART_Init(UART5, &UART_InitStruct);
    
    ***************



  • DMA可访问内存

  H7内存区域分为几大块,TCM区、AXI SRAM区、SRAM1 SRAM2 SRAM3区、SRAM4区,不同内存区域的特性和使用者不同,如TCM区域用于CPU指令执行;SRAM3主要用于USB和Ethernet,详细内存分区特性自行查阅手册,各外设可访问内存区域如下图。对于DMA来说,只需知道TCM区不可访问,只能访问后三块区域,因此在使用DAM时,需指定内存区域。



STM32CUBEMX 串口dma接收不定长数据 stm32h7 串口dma接收_内存区域_03


H7 各外设可访问内存区域



  对于Keil来说,在编译器勾选RAM2即可。

STM32CUBEMX 串口dma接收不定长数据 stm32h7 串口dma接收_内存区域_04


定义DMA buf时指定内存绝对地址,推荐后者方法,可以适用于不同编译器。因为,不指定内存地址,将由编译时自由分配内存空间,如分配在TCM区域,DMA功能不能正常工作;如开启了DMA错误中断,则触发错误中断,可以看到错误码。

static uint8_t s_uart1_dmarx_buf[UART1_DMA_RX_BUF_SIZE] __attribute__((section(".ARM.__at_0x24000000")));
static uint8_t s_uart1_dmatx_buf[UART1_DMA_TX_BUF_SIZE] __attribute__((section(".ARM.__at_0x24000080")));



3 代码仓库

GitHub:https://github.com/Prry/stm32-uart-dma