STM32CubeMX第八篇之DMA

前言

本文主要讲解自己实现DMA串口实验。

本实验主要实现以下功能:

  1. 按键0按下,则8000Byte数据通过由DMA控制的串口发出去
  2. 在LCD屏幕上显示数据发送的进度。
  3. LED0每1s闪烁一次,证明程序正确运行

本实验使用的HAL库的版本为:STM32Cube_FW_F4_V1.25.0
本实验使用的STM32CubeMX版本为:6.1.1

该工程的下载地址为:

本实验主要参考了正点原子的实验例程,关于正点原子程序的讲解,参考博客<STM32F429第二十七篇之DMA实验详解>。

关于 按键处理,LED灯,还有LCD屏相关知识,请参考之前的博客。

程序讲解

/**
  ******************************************************************************
  * @file    main.c
  * @author  zhy
  * @version 1.0
  * @date    2021-05-26
  * @brief   DMA实验
  ******************************************************************************
  */

#include "stm32f4xx_hal.h"
#include "sys.h"
#include "fmc.h"
#include "gpio.h"
#include "dma.h"
#include "lcd.h"
#include "key.h"
#include "led.h"
#include "usart.h"
#include "stdio.h"

#define LENGTH 8000
static uint8_t testTxDma[LENGTH] = {0}; //因为数组过长,所以放置在此处

int main()
{
    /* 1.变量初始化 */
    unsigned int i = 0;
    unsigned int j = 0;

    uint32_t timer = 0;
    uint32_t timerLed = 0;
    uint32_t timerKey = 0;

    uint8_t keyEdge = 0;

    const LcdCoordinate focusTips = {64, 0};
    const LcdCoordinate focusPercent = {80, 0};

    const uint8_t *temp = "hello, word!\n";

    DmaType flagDma = DMA_NONE;

    /* 2.硬件初始化 */
    HAL_Init();
    SystemClock_Config();
    SramInit();
    GpioInit();
    LcdInit();
    Uart1Init();
    KeyInit();
    LedInit();
    DmaInit();

    /* 3.发送内容初始化 */
    for (i = 0; i < LENGTH; i++)
    {
        if (temp[j] == 0)
        {
            j = 0;
        }
        testTxDma[i] = temp[j];
        j++;
    }

    /* 4.lcd显示初始化 */
    LcdSetColor(LCD_RED, LCD_WHITE);
    LcdShowString("Demo9 DMA\n");
    LcdShowString("Zhen Haiyang\n");
    LcdShowString("2021/05/27\n");
    LcdShowString("--KEY0:Start--\n");
    LcdSetColor(LCD_BLUE, LCD_WHITE);

    /* 5.while循环 */
    while (1)
    {
        timer = HAL_GetTick();

        /* key */
        keyEdge = 0; //将有效边沿清0
        if (timer - timerKey >= 10)
        {
            timerKey = timer;             //时间复位
            KeyScan();                    //按键扫描
            keyEdge = KeyGetEdgeActive(); //获得有效按键边沿
        }

        /* usart */
        if (keyEdge & KEY_0) //key0按下
        {
            DmaStart((uint32_t)&testTxDma, LENGTH);
            flagDma = DMA_ONGOING;
        }
        if (DMA2->HISR & DMA_HISR_TCIF7) //传输完成
        {
            flagDma = DMA_FINISH;
            DMA2->HIFCR |= DMA_HIFCR_CTCIF7;
        }

        /* lcd */
        if (flagDma == DMA_FINISH)
        {
            flagDma = DMA_NONE;
            LcdSetFocus(focusTips);
            LcdShowString("Trassmit Finish!");
            LcdSetFocus(focusPercent);
            LcdShowNumber(100, 3);
            LcdShowString("%");
        }
        else if (flagDma == DMA_ONGOING)
        {
            uint16_t temp = LENGTH - DMA2_Stream7->NDTR;
            temp = temp * 100 / LENGTH;
            LcdSetFocus(focusTips);
            LcdShowString("Trassmiting...  ");
            LcdSetFocus(focusPercent);
            LcdShowNumber(temp, 3);
            LcdShowString("%");
        }

        /* led */
        if (timer - timerLed >= 500) //500ms
        {
            timerLed = timer;
            set_led_0 = !set_led_0;
        }
        if (keyEdge & KEY_1)
        {
            set_led_1 = !set_led_1;
        }
    }
}

该程序初始化部分和之前讲解的基本一致。不再详述。

while循环部分,采用分块处理的方法:

  1. 尽量减少延时函数的使用
  2. 将每个模块集中处理,中间通信通过标志位实现。

此处唯一需要注意的是, DMA在每次传输完成,会自动关闭使能标志。所以每次重新启动DMA,都需要做以下几个工作:

  1. 重新指定内存发送的起始地址和发送数据的大小。
  2. 使能DMA。

具体通过以下函数实验:

/** 
 * @brief DMA
 * @note 无
 * @param {uint32_t} memory 内存的地址
 * @param {uint32_t} length 发送数据的长度
 * @retval 无
 */
void DmaStart(uint32_t memory, uint32_t length)
{
    DMA2_Stream7->M0AR = memory;
    DMA2_Stream7->NDTR = length;
    DMA2_Stream7->CR |= DMA_SxCR_EN;
}

Cube功能和上文讲解基本一致,不再详细讲解。

<think>好的,我需要帮助用户了解如何使用STM32CubeMX配置DMA串口通信。首先,我需要回忆STM32CubeMX的基本操作流程,以及DMA串口的相关知识。 用户提供的引用中提到了几个关键点:DMA控制器的功能、CubeMX配置串口DMA和FreeRTOS的结合使用,还有具体的硬件型号如STM32F103C8T6。我应该结合这些信息来构建步骤。 首先,用户需要创建一个新工程,选择正确的芯片型号。这对应引用[3]中的内容,选择芯片型号是第一步。然后配置时钟源,通常使用HSE或HSI,确保系统时钟正确。 接下来是配置USART。用户需要启用串口,并设置模式为异步,波特率等参数。这里可能需要参考引用[3]中的教程,详细说明参数设置。 然后是关键的DMA配置。需要添加DMA通道,设置方向为内存到外设或外设到内存,取决于传输方向。例如,发送数据可能需要内存到外设,而接收可能需要外设到内存。需要确保DMA模式是否正确,比如是否使用循环模式,或者普通模式。引用[1]提到DMA直接存储器访问的功能,这里要解释DMA如何减轻CPU负担。 生成代码后的部分,用户需要编写发送和接收的代码。可能需要使用HAL库函数,如HAL_UART_Transmit_DMA,并处理中断。引用[2]提到了空闲中断和FreeRTOS的结合,但用户的问题可能不需要操作系统部分,但可以提及如果需要更复杂处理,可以结合这些技术。 注意事项部分,时钟配置是否正确,DMA中断是否启用,数据对齐是否匹配,这些都可能影响功能。比如,如果数据对齐不匹配,可能导致传输错误。引用[2]中的配置可能涉及这些细节,需要提醒用户检查。 最后,生成相关问题时要考虑用户可能的后续问题,比如如何结合中断,如何调试,性能优化等。同时,确保引用正确标记,比如引用[1]关于DMA的基本功能,引用[3]关于CubeMX的操作步骤。 需要检查步骤是否清晰,是否覆盖了所有关键配置点,并确保LaTeX格式正确,行内公式用$...$,独立公式用$$...$$。但当前问题中没有数学公式,可能不需要使用,但系统指令要求必须遵守,所以需要确认是否有需要公式的地方,比如波特率计算,但可能不需要,所以可能不需要添加。 最后,确保使用中文回答,结构清晰,分步骤说明,代码示例正确,引用正确标注。</think>### 使用STM32CubeMX配置DMA串口通信的步骤 以下是基于STM32F103C8T6核心板的配置流程,结合DMA实现高效串口数据传输: --- #### **1. 创建新工程并配置时钟** 1. 打开STM32CubeMX,选择芯片型号(如STM32F103C8T6)[^3]。 2. 配置系统时钟源(如HSE或HSI),并设置时钟树到目标频率(例如72MHz)。 --- #### **2. 配置USART串口** 1. **启用USART**:在`Connectivity`中选择需要使用的串口(如USART1)。 2. **设置参数**: - 工作模式:Asynchronous(异步模式) - 波特率:115200 - 数据位:8 bits - 校验位:None - 停止位:1 bit 3. **启用DMA**: - 在`DMA Settings`选项卡中,添加发送(TX)和接收(RX)的DMA通道。 - 方向选择: - 发送:Memory to Peripheral - 接收:Peripheral to Memory - 模式:Normal(单次传输)或 Circular(循环传输,适用于持续接收)[^1]。 --- #### **3. 生成代码并编写逻辑** 1. **生成代码**:点击`GENERATE CODE`生成Keil工程。 2. **发送数据(DMA模式)**: ```c // 发送数据 uint8_t tx_data[] = "Hello World!\r\n"; HAL_UART_Transmit_DMA(&huart1, tx_data, sizeof(tx_data)); ``` 3. **接收数据(DMA+空闲中断)**: - 在CubeMX中使能串口全局中断(NVIC Settings)。 - 在代码中启动DMA接收: ```c HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); ``` - 实现空闲中断回调函数(需手动添加): ```c void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 处理接收到的数据 process_data(rx_buffer); // 重新启动DMA接收 HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); } } ``` --- #### **4. 注意事项** - **时钟配置**:确保DMA和USART的时钟已使能(通过`__HAL_RCC_DMA1_CLK_ENABLE()`等函数)[^3]。 - **中断优先级**:若使用FreeRTOS,需调整DMA串口中断优先级,避免任务阻塞[^2]。 - **数据对齐**:DMA传输的源地址和目标地址需对齐(如内存地址需4字节对齐)。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值