--- title: mcu-stm32-cube-06-配置DMA date: 2020-05-31 16:39:05 categories: tags: - stm32 - cubeMx - dma - serial ---

知识

DMA(Direct Memory Access,直接存储器访问) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。

可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。

DMA参数

传输模式:数据从哪里搬到哪里。三种可能的传输方向:存储器到外设、外设到存储器或存储器到存储器。

通道选择:数据传输的是走哪条道路。

仲裁器:多个DMA传输时,优先级高的优先传输。

数据长度:每次传输的数据长度,可以1个字节,2个字节(半字),4个字节(字)

指针递增:使能此模式以后,则下次传输的地址将是前一次传输的地址递增 1(对于字节)、2(对于半字)或4(对于字)。

CubeMX 配置 DMA(以 USART2 为例)

配置

添加串口以及中断

Pinout & Configuration页中的Connectivity,选择USART2

  • Mode : Asynchronous(异步); Hardware Flow Control(硬件流控) 选择 Disable
  • Configuration - Parameter Settings中 (任意设置都可以,但通讯双方要匹配)
  • Baud Rate : 波特率,一般使用 115200
  • Word Length : 字长 8
  • Parity: 校验
  • Stop Bits : 停止位
  • Configuration - NVIC Settings中 : 勾选 Enabled (开启中断)

配置DMA

  • Configuration - DMA Settings中,点击ADD分别将USART2_RXUSART_TX添加到DMA Request表中:
  • Priority:设置优先级为Low
  • DMA Request Settings-Mode
  • Normal:只传送一次(选择此项)
  • Circular:不停地传送(循环模式)
  • Data Width:选择Byte

生产中断初始化函数

Pinout & Configuration中,System Core,选择NVIC

  • Configuration - Parameter Settings中 ,确认UsartX global interrupt Enable是勾选的
  • Configuration - Code generation中,确认UsartX global interrupt DMA1 channel 7 global interruptDMA1 channel 6 global interruptSelect for init sequence ordering是勾选的。

收尾工作

填写有关的项目属性、点击右上角的GENERATE CODE

添加代码

stm32f1xx_it.h

添加全局变量

实际上,这些变量可以封装成函数,但是为了减少教程的篇幅,这里没有这么做

/* USER CODE BEGIN EC */
/* 以下变量与DMA接收流程有关 */
volatile uint8_t rx_len = 0; // 获取收到的数据长度
volatile uint8_t recv_end_flag = 0;
uint8_t rx_buffer[200] ;
/* USER CODE END EC */

stm32f1xx_it.c

修改中断处理函数

void USART2_IRQHandler(void)
{
    /* USAR CODE BEGIN USART2_IRQn 0 */
    uint32_t tmp_flag = 0;
    uint32_t temp;
    tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); //获取IDLE标志位
    if((tmp_flag != RESET))//idle标志被置位
    { 
        __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除标志位
        temp = huart2.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
        temp = huart2.Instance->DR; //读取数据寄存器中的数据
        HAL_UART_DMAStop(&huart2); //
        temp  = hdma_usart2_rx.Instance->CNDTR;// 获取DMA中未传输的数据个数,CNDTR寄存器分析见下面
        rx_len =  sizeof(rx_buffer) - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
        recv_end_flag = 1;	// 接受完成标志位置1	
    }
    /* USAR CODE END USART2_IRQn 0 */
    HAL_UART_IRQHandler(&huart2);
	/* USAR CODE BEGIN USART2_IRQn 1 */
    /* USAR CODE END USART2_IRQn 1 */
}

main.c

引入全局变量

/* USER CODE BEGIN PV */

/* 以下变量与DMA接收流程有关 */
extern volatile uint8_t rx_len; // 获取收到的数据长度
extern  volatile uint8_t recv_end_flag;
extern  uint8_t rx_buffer[200] ;

/* USER CODE END PV */

添加printf重定向

/* USER CODE BEGIN 0 */

#include "stdio.h"

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
/* 重定向printf */
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart2, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    return ch;
}
/* 重定向scanf */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart2, &ch, 1, 0xffff);
  return ch;
}

/* USER CODE END 0 */

添加DMA初始化

/* USER CODE BEGIN 2 */

// 在 MX_USART2_UART_Init 之后添加这2行
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能idle中断
HAL_UART_Receive_DMA(&huart2,rx_buffer, sizeof(rx_buffer)); //打开DMA接收,数据会存入rx_buffer数组中。(删掉会导致第一次DMA异常)

/* USER CODE END 2 */

使用(在需要调用的地方,添加以下函数,以main中的while(1)为例):

/* USER CODE BEGIN WHILE */
while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    if(recv_end_flag ==1)
    {
        //打印接收长度、数据
        printf("rx_len=%d\r\n",rx_len);
        printf("%s\r\n",rx_buffer);
        for(uint8_t i=0;i<rx_len;i++)
        {
            rx_buffer[i]=0;//清接收缓存
        }
        rx_len=0;//清除计数
        recv_end_flag=0;//清除接收结束标志位
    }
    HAL_UART_Receive_DMA(&huart2,rx_buffer,sizeof(rx_buffer));//重新打开DMA接收
}
/* USER CODE END 3 */

附录:

typedef struct
{
    __IO uint32_t CCR;     /*!< DMA stream x configuration register      */
    __IO uint32_t CNDTR;   /*!< DMA stream x **number of data register**     */
    __IO uint32_t CPAR;    /*!< DMA stream x peripheral address register */
    __IO uint32_t CMAR;   /*!< DMA stream x memory address register   */
} DMA_Channel_TypeDef;
如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。