不含stm32 底层的代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
MyStm32Code/DOC/05_DMA.md

7.7 KiB

DMA

F103ZET usart2 DMA

RX引脚配置为上拉 Pull-up

CubeMX配置DMA

dma.h dma.c 初始化

void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 2, 2);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);

  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 2, 2);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);

}

usart.c

定义DMA 结构体 DMA_HandleTypeDef hdma_usart2_rx; DMA_HandleTypeDef hdma_usart2_tx;

对应串口 HAL_UART_MspInit

    /* USART2 DMA Init */
    /* USART2_RX Init */
    hdma_usart2_rx.Instance = DMA1_Channel6;
    hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_rx.Init.Mode = DMA_NORMAL;
    hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);

    /* USART2_TX Init */
    hdma_usart2_tx.Instance = DMA1_Channel7;
    hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_tx.Init.Mode = DMA_NORMAL;
    hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);

对应的 HAL_UART_MspDeInit HAL_DMA_DeInit(uartHandle->hdmarx); HAL_DMA_DeInit(uartHandle->hdmatx);

stm32f1xx_it.h .c

extern DMA_HandleTypeDef 引入DMA定义 void DMA1_Channel6_IRQHandler(void); void DMA1_Channel7_IRQHandler(void);

void DMA1_Channel6_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart2_rx); }

void DMA1_Channel7_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart2_tx); }

main.h main.c

#include "dma.h" MX_DMA_Init(); 在串口之前初始化,否则不能输出

usart dma 发送接收

HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收

HAL_UART_Receive_DMA(&huart2, Rxbuf, sizeof(Rxbuf)); UART一旦开启DMA之后,DMA收发中断都是强制开启的,所以DMA收发函数也可以编写回调函数。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{
    if(huart->Instance==USART2) 
    {
      HAL_UART_Transmit_DMA(&huart2, ACKbuf, sizeof(ACKbuf));
      // HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf));  // 重新使用串口接收中断
    }
}

DMA 相关函数

__HAL_DMA_GET_COUNTER 获取DMA剩余未接收数据

HAL_UART_Transmit 串口阻塞方式发送函数

HAL_UART_Transmit_IT 串口中断方式发送函数

HAL_UART_Receive_IT 串口中断方式接收数据

HAL_UART_Transmit_DMA 串口DMA方式发送函数

HAL_UART_Receive_DMA 串口DMA方式接收函数

HAL_UART_TxCpltCallback 串口发送完成回调函数

HAL_UART_RxCpltCallback 串口接收完成回调函数-- 和中断模式函数一样

HAL_UART_RxHalfCpltCallback 串口接收过半回调函数

DMA 模式

普通模式:当传输数据量为0时,需要程序中手动将传输数据量重新设置满才能开启下一次的DMA数据传输。 循环模式:则当传输数据量为0时,会自动将传输数据量设置为满的,这样数据就能不断的传输。双缓冲

但有时为了安全,当我们设置的DMA缓冲区大小大于一次完整的数据时,当接收完一次数据后,Counter值还没有变为0,这时不会自动指向另一个缓冲区,所以这时候我们就需要手动更改指向下一个缓冲区。(也就是当第一帧数据传输完成后,Counter值不会自动填满,且内存区域还是指向Memory0,然后我们将剩余数据量保存下来,再将Counter值填满,接着把DMA指向Memory1,最后通过判断剩余数据量来决定是否对数据进行处理)

双缓冲的数据存储格式 uint8_t sbus_rx_buffer[2][18u]; DMA_InitStruct.DMA_BufferSize = 18; DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&sbus_rx_buffer[0][0]; DMA_DoubleBufferModeConfig(DMA1_Stream1,(uint32_t)&sbus_rx_buffer[1][0],DMA_Memory_0); //first used memory configuration DMA_DoubleBufferModeCmd(DMA1_Stream1, ENABLE);

空闲中断计算 长度,及中间数据处理方法参考

uint32_t temp;
    if(USART1 == huart1.Instance)//判断是否为串口1中断
    {
        if(RESET != __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))//如果为串口1空闲
        {
            __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除中断标志
            HAL_UART_DMAStop(&huart1);//停止DMA接收
            temp  = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//获取DMA当前还有多少未填充
            Rx_len_1 =  BUFFERSIZE_1 - temp; //计算串口接收到的数据个数
            HAL_UART_Transmit_DMA(&huart1,ReceiveBuff_1,Rx_len_1);//发送数据
            Rx_len_1=0;//接收数据长度清零
            HAL_UART_Receive_DMA(&huart1,ReceiveBuff_1,BUFFERSIZE_1);//开启下一次接收
        }    
    }

DMA 调试

监控半满,满状态。监控满后,指针是否重回开头接收 可以串口监控 huart.Init.BaudRate huart->RxXferSize 为Rx需要接收的大小 __HAL_DMA_GET_COUNTER(huart2.hdmarx) 可以获取剩余的空间

const uint8_t pTxBuffPtr; /!< Pointer to UART Tx transfer Buffer / uint16_t TxXferSize; /!< UART Tx Transfer size / __IO uint16_t TxXferCount; /!< UART Tx Transfer Counter */ uint8_t pRxBuffPtr; /!< Pointer to UART Rx transfer Buffer / uint16_t RxXferSize; /!< UART Rx Transfer size / __IO uint16_t RxXferCount; /!< UART Rx Transfer Counter */

__HAL_DMA_GET_COUNTER(huart2.hdmarx) 可以获取剩余的空间 获得 dma-init-CNDTR值

定义接收数据的结构

typedef struct
{
uint8_t RxBuffer[RCV_BUF_LENGTH];    /* 接收缓冲区 */
uint16_t RxEndIndex;                    /* 尾索引值 */
FlagStatus RxEndFlag ;                  /* 接收结束标识 */
}Usart_BufTypeDef;

半满中断标志:
标准库 :DMA_ITConfig(DMA1_Channel4, DMA_IT_HT, ENABLE); HAL库: __HAL_UART_ENABLE_IT(&huart2, DMA_IT_HT); 可以查DMA中断位置,中断函数要清除半满标记

开启半满中断后,可以看到半满的LED 翻转 如何解决 后续出现的错误?? 获得空余数

   // __HAL_DMA_CLEAR_FLAG(&hdma_usart2_rx, DMA_IT_HT);
  __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_HT );
  __HAL_UART_CLEAR_FLAG(&huart2, DMA_IT_HT);

解决串口发送DMA只能发送一次的问题 注意,在DMA传输过程中,会将串口从READY状态改为发送BUSY状态(软件修改):huart->gState = HAL_UART_STATE_BUSY_TX;但是在DMA完成本次传输工作以后,并没有将串口从发送BUSY状态改回READY状态

DMA 中断函数可以如下处理一些错误

hdma->XferHalfCpltCallback

if (hdma->XferErrorCallback != NULL)
{
  /* Transfer error callback */
  hdma->XferErrorCallback(hdma);
}

思路:

DMA 采集, 采集立刻复制数据, 轮询处理数据, 判断是否收集完成