stm32 DMA(在uart中的应用)

DMA 初始化

DMA初始化结构体如下

    //index:0 USART1_TX
    {
        .Instance = DMA2_Stream0,
        .Init = 
        {
            .Channel = DMA_CHANNEL_4,
            .Direction = DMA_MEMORY_TO_PERIPH,/*内存到外设*/
            .PeriphInc = DMA_PINC_DISABLE,/*外设地址不自增*/
            .MemInc = DMA_MINC_ENABLE,/*内存地址自增*/
            .PeriphDataAlignment = DMA_PDATAALIGN_BYTE,/*外设数据宽度*/
            .MemDataAlignment = DMA_MDATAALIGN_BYTE,/*内存数据宽度*/
            .Mode =DMA_NORMAL,
            .Priority = DMA_PRIORITY_LOW,
            .FIFOMode = DMA_FIFOMODE_DISABLE,
            .FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL,
            .MemBurst = DMA_MBURST_SINGLE,
            .PeriphBurst = DMA_PBURST_SINGLE,
        },
        .Lock = HAL_UNLOCKED,
        .State = HAL_DMA_STATE_RESET,
        .Parent = NULL,
        .XferCpltCallback = NULL,/*!< DMA transfer complete callback         */
        .XferHalfCpltCallback = NULL,/*!< DMA Half transfer complete callback    */
        .XferM1CpltCallback = NULL,/*!< DMA transfer complete Memory1 callback */
        .XferM1HalfCpltCallback = NULL,/*!< DMA transfer Half complete Memory1 callback */
        .XferErrorCallback = NULL,/*!< DMA transfer error callback            */
        .XferAbortCallback = NULL,/*!< DMA transfer Abort callback            */
        .ErrorCode = HAL_DMA_ERROR_NONE,
        .StreamBaseAddress = 0u/*后面会赋值*/
        .StreamIndex = 0u,/*后面会赋值*/
    }

.Instance .Channel Instance决定选择哪个数据流,哪个通道,也就是下面这两张图,根据外设选择数据流和通道。
一共有7个数据流,数据流会按照优先级依次传输数据(如果遇到同时触发DMA),每个数据流能够通过寄存器选择一个通道(只能选一个)
这里要注意,如果是内存到内存的dma,只能选择DMA2,DMA1不支持该功能,DMA2随便选择一个数据流和通道就可以了


.Direction 决定数据的方向:外设到内存,内存到外设,内存到内存
PeriphInc 外设地址自增,后面会设置传输的项数以及传输的宽度,也就是说,开启这个外设地址自增之后,传输每一个项完成了以后,地址就会按照设置的传输的宽度增加
MemInc 内存地址自增,后面会设置传输的项数以及传输的宽度,也就是说,开启这个内存地址自增之后,传输每一个项完成了以后,地址就会按照设置的传输的宽度增加
PerphDataAlignment 设置外设数据的宽度,byte,half word,或者word,在直接模式下(也就是不使用fifo的情况下)应该与内存数据宽度一致
MemDataAlignment 同上
Mode 这个与两个功能相关:循环模式是否开启,外设流控制选择什么,看这两个位的解释就能懂了
外设流控制器的意思就是DMA传输到什么时候停止,选择0则是到达数量了就停下来,选择1则是到达到硬件触发的条件才停止(手册上说,只有SDIO能够发送结束信号,所以只有这个外设才能够使用该功能。这个还得深入探究,到底怎么能够触发,怎么设置?现在先放一边


.Priority不同数据流同时想要发送数据的时候就需要仲裁了,仲裁方式就是依靠这里设置的Priority以及数据流的序号大小(序号越低越先发送)
.FIFOModefifo经常用在当接收dma宽度和发送的宽度不一致的时候,FIFO可以暂存数据,等到数据到达相应的位宽的时候再传输。fifo的大小位4words,阈值可以设置为1/4、 1/2、 3/4 或满
FIFOThreshold上面说到的阈值
MemBurs;PeriphBurstBurst即突发,也就是burst的时候会占据AHB总线,以最快的速度将本包发完。burst经常与fifo一起配合使用,如下图。
可以参考该文章

https://blog.csdn.net/xianyulajiao/article/details/128287672

uart DMA发送

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
该函数流程如下

  1. 在句柄中存入data,size,状态位,回调函数等
  2. 调用HAL_DMA_Start_IT,其中源地址为pData,目标地址为uart的数据寄存器
    1. 设置源地址,目标地址
    2. 清除中断标志位
    3. 使能中断:
      • TCIE:传输完成中断使能
      • TEIE:传输错误中断使能
      • DMEIE:直接模式错误中断使能
      • HTIE:半传输中断使能
      • FEIE: FIFO 错误中断使能
  3. 清除标志位:TCIFx:数据流 x 传输完成中断标志
  4. 使能DMA 传输

uart DMA接收

如果是定长的uart数据,使能dma非常好用,手册中如下:

也就是说,再uart的接收过程中,完全不需要mcu的介入,接收到的数据自动由dma转移到对应存储当中,当结束了才产生中断。
但是缺点是一定要是定长的数据才行了,不定长的数据要使用dma的化,不过就是把每一帧产生中断中推入fifo的数据改成使用dma传入对应存储区,等到对uart的速度有提升要求或者对mcu的使用率很严苛的时候再搞吧。下面是HAL库函数的流程
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

  1. 在句柄中存入data,size,状态位,回调函数等
  2. 调用HAL_DMA_Start_IT,其中源地址为uart的数据寄存器,目标地址为pData
  3. 使能错误中断
  4. 使能DMAR位开启uart的DMA传输
posted @ 2024-03-17 21:59  kefa  阅读(144)  评论(0)    收藏  举报