您尚未登录。

楼主 #1 2020-09-01 18:08:12

vip888888
会员
注册时间: 2020-07-16
已发帖子: 141
积分: 138

STM32 HAL DMA串口接收不定长度实现

1.使用STM32Cube 直接先配置串口1的基础信息。
1.png

2.然后配置DMA传输
2.png

3.就是启动了,在串口初始化之后默认是没有开启串口DMA接收的。所以增加几行代码如下图所示
3.png

4.写串口1中断函数:

这个函数就是实现不等长的精髓所在了。这里用到了类似环形队列的方法,至于什么是环形队列这个不知道童鞋自行Google了。

void USART1_IRQHandler(void)
{
    if(__HAL_UART_GET_FLAG(&systemUart, UART_FLAG_IDLE) != RESET)
    {
        __HAL_UART_FLUSH_DRREGISTER(&systemUart); //清除 IDLE中断
    }
  HAL_UART_IRQHandler(&systemUart);
}

首先将中断函数写出来,如上所示了。这时候帧中断的时候会进这个中断,数据用DMA接收。

第二步建立对应变量

#define UART1_DMA_DATA_LEN 100//数据最大长度

static UART_HandleTypeDef systemUart;
DMA_HandleTypeDef hdma_usart1_rx;
static uint8_t _uartDmaDataBuffer[UART1_DMA_DATA_LEN]; //缓冲区就是初始化的时候用到的
static int dmaDataTail = 0;       //将DMA数据传输Buffer 虚拟成一个队列缓冲区
static int dmaDataHead = 0;
static int dmaDataLenght = 0;

第三步实现类似环形队列功能

void USART1_IRQHandler(void)
{
    if(__HAL_UART_GET_FLAG(&systemUart, UART_FLAG_IDLE) != RESET)
    {
        __HAL_UART_FLUSH_DRREGISTER(&systemUart); //清除 IDLE中断
       
        dmaDataTail  = hdma_usart1_rx.Instance->NDTR;
        dmaDataTail  = UART1_DMA_DATA_LEN - dmaDataTail;
        dmaDataLenght = (dmaDataTail - dmaDataHead+UART1_DMA_DATA_LEN) % UART1_DMA_DATA_LEN; //求数据长度
    }
  HAL_UART_IRQHandler(&systemUart);
}
注意里面 hdma_usart1_rx.Instance->NDTR的NDTR不同的型号可能不同可以在DMA的结构体里找到对应的名字,如下图所示
4.png

这个NDTR的值是从UART1_DMA_DATA_LEN开始递减的,因为启动DMA的时候设置了传输长度为UART1_DMA_DATA_LEN

所以我们接收到当前的数据下标就是UART1_DMA_DATA_LEN - hdma_usart1_rx.Instance->NDTR这个。这个其实就是当前接收到数据的结束下标。有了数据结束下标,我们还需要数据起始下标,以及数据长度。

①数据起始下标: 等于上一次的dmaDataTail 所以每次处理完数据都有dmaDataHead = dmaDataTail;

②数据长度:这个利用环形队列求长度的做法:

dmaDataLenght = (dmaDataTail - dmaDataHead+UART1_DMA_DATA_LEN) % UART1_DMA_DATA_LEN;

现在我们有了数据起始下标 数据长度那么我们就可以访问我们的数据了。当然方法也和环形队列差不多

完成的中断函数就是这样的。可以接收什么返回什么了,要想自己处理可以在这里增加缓冲区或者调用处理函数了。

void USART1_IRQHandler(void)
{
    if(__HAL_UART_GET_FLAG(&systemUart, UART_FLAG_IDLE) != RESET)
    {
        __HAL_UART_FLUSH_DRREGISTER(&systemUart); //清除 IDLE中断
        dmaDataTail  = hdma_usart1_rx.Instance->NDTR;
        dmaDataTail  = UART1_DMA_DATA_LEN - dmaDataTail;
        dmaDataLenght = (dmaDataTail - dmaDataHead+UART1_DMA_DATA_LEN) % UART1_DMA_DATA_LEN; //求数据长度
        for(int i = 0; i < dmaDataLenght; i++)
        {
            int index = (dmaDataHead+i)%UART1_DMA_DATA_LEN;
            printf("%c",_uartDmaDataBuffer[index]);
        }

        printf("\r\n");
        dmaDataHead = dmaDataTail;
    }
  HAL_UART_IRQHandler(&systemUart);
}

最后来张测试结果图分别是一顿乱操作发不同的数据长度和数据
5.png

最后注意一点的就是这个程序如果你一下子发超过100个字节那估计就不行了。因为超了缓冲区长度数据被覆盖了,当然增加长度就可以解决了。

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn