STM32hal库学习(F1)-DMA

DMA简介

DMA:

直接存储器访问

DMA传输:

将数据从一个地址空间复制到另一个地址空间

DMA作用:

DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和回复现场过程

而是通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高

其实简言之:就是为CPU减负

DMA路径

内存-》外设

外设-》内存

内存-》内存

DMA框图

结构框图

①DMA请求:DMA传输数据,先向DMA控制器发送请求

                       多个请求通过逻辑或输入到DMA控制器,只能由一个请求有效

②DMA通道:DMA1有七个通道,DMA2有5个通道

③DMA优先级:优先级管理分软件和硬件,由仲裁器管理

                          软件阶段 在DMA_CCRx寄存器中设置,有四个等级 最高 高 中 低

                          硬件阶段 较低编号的通道有更高的优先级 

DMA处理过程

DMA通道映射框图

DMA1

DMA相关寄存器

DMA_CCRx (DMA通道x配置寄存器)

该寄存器用于配置DMA,数据传输方向 通道优先级 外设/存储器数据宽度

                                      外设/存储器增量模式 循环模式 中断使能 通道开启

DMA_ISR (DMA中断状态寄存器)

该寄存器主要用于查询DMA是否传输完成

DMA_IFCR(DMA中断标志清除寄存器)

该寄存器主要用于清除传输完成标志位

DMA_CNDTRx(DMA通道x传输数量寄存器)

该寄存器主要用于写入传输长度

这里注意在非循环模式情况下传输完成,要开启新的DMA传输,需要先关闭DMA通道,重新写入传输数量再开启通道

DMA_CPARx(DMA通道x外设地址寄存器)

该寄存器用于存放外设地址

DMA_CMARx(DMA通道x存储器地址寄存器)

该寄存器用于存放存储器地址

HAL库函数介绍

驱动函数

关联寄存器

功能描述

__HAL_RCC_DMAx_CLK_ENABLE()

RCC_AHBENR

使能DMAx时钟

HAL_DMA_Init()

DMA_CCR

初始化DMA

HAL_DMA_Start_IT()

DMA_CCR/CPAR/CMAR/CNDTR

开始DMA传输

__HAL_LINKDMA()

用来连接DMA和外设句柄

HAL_UART_Transmit_DMA()

CCR/CPAR/CMAR/CNDTR/

使能DMA发送,启动传输

__HAL_DMA_GET_FLAG()

DMA_ISR

查询DMA传输通道的状态

__HAL_DMA_ENABLE()

DMA_CCR(EN)

使能DMA外设

__HAL_DMA_DISABLE()

DMA_CCR(EN)

失能DMA外设

//相关结构体
typedef struct 
{
    DMA_Channel_TypeDef	*Instance
    DMA_InitTypeDef      Init
     ...
}DMA_HandleTypeDef;

typedef struct 
{
    uint32_t Direction			     /* DMA传输方向 */
    uint32_t PeriphInc			     /* 外设地址(非)增量 */
    uint32_t MemInc			         /* 存储器地址(非)增量*/
    uint32_t PeriphDataAlignment	 /* 外设数据宽度 */
    uint32_t MemDataAlignment	     /* 存储器数据宽度 */
    uint32_t Mode				     /* 操作模式 */
    uint32_t Priority	             /* DMA通道优先级 */
}DMA_InitTypeDef;

配置步骤

//DMA.c
#include "DMA.h"

DMA_HandleTypeDef DMA_Handler; // DMA初始化句柄

uint8_t old_buf[10] = {0x0a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
uint8_t new_buf[10] = {0};
uint8_t key;

void DMA_Init()
{
    __HAL_RCC_DMA1_CLK_ENABLE(); // 使能DMA1外设时钟

    DMA_Handler.Instance = DMA1_Channel1;                       // DMA1,通道1
    DMA_Handler.Init.Direction = DMA_MEMORY_TO_MEMORY;          // 存储器到存储器,这里是数组到数组
    DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // 存储器内存:字节
    DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                  // 存储器地址增量
    DMA_Handler.Init.Mode = DMA_NORMAL;                         // 不开启循环模式
    DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存(存储器):字节,这里也是数组
    DMA_Handler.Init.PeriphInc = DMA_PINC_ENABLE;               // 外设地址增量
    DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH;              // 优先级高
    HAL_DMA_Init(&DMA_Handler);                                 // DMA初始化
    HAL_DMA_Start(&DMA_Handler, (uint32_t)old_buf, (uint32_t)new_buf, 0);//DMA开启传输
}

void DMA_Transmit(uint16_t cndtr)
{
    __HAL_DMA_DISABLE(&DMA_Handler);//传输完后失能

    //    DMA1_Channel1->CNDTR = cndtr;
    DMA_Handler.Instance->CNDTR = cndtr;//写入传输数量

    __HAL_DMA_ENABLE(&DMA_Handler);//启动传输
}

void DMA_Transmit_Get()
{
    key = KEY_Scan(0); /* 得到键值 */
    if (key == KEY0_PRES)
    {
        memset(new_buf, 0, 10);//将数组清0
        DMA_Transmit(10);//传输10个数量
        while (1)
        {
            if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC1))//如果获取到传输完成标志位
            {
                __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC1);//清除传输完成标志位
                printf("传输完成 \r\n");//串口打印传输完成
            }
        }
    }
}
//DMA.h
#ifndef __DMA_H
#define __DMA_H

#include "sys.h"
#include "KEY.h"
#include <stdio.h>
#include <string.h>
void DMA_Init();
void DMA_Transmit(uint16_t cndtr);
void DMA_Transmit_Get();

#endif // !
//main.c
//本例程是按下按键KEY0 将一个数据转移到另一个数组,并在串口提示传输完成
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//	HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	uart_init(115200);				// 初始化串口
//  DMA_Init();

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "DMA.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	KEY_Init();						// 初始化按键
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
	DMA_Init();

	while (1)
	{
		DMA_Transmit_Get();
		LED0 = 1;
	}
}
//DMA.c
#include "DMA.h"

DMA_HandleTypeDef DMA_Handler;           // DMA初始化句柄
extern UART_HandleTypeDef UART1_Handler; // UART1初始化句柄

const uint8_t SEND[] = {"YMZ yy爱你"}; /* 要循环发送的字符串 */
uint8_t SEND_buf[SEND_BUF_SIZE];       /* 发送数据缓冲区 */

void DMA_Init(DMA_Channel_TypeDef *DMAx_CHx)
{

    if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7) /* 大于DMA1_Channel7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */
    }
    else
    {
        __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */
    }
    DMA_Handler.Instance = DMAx_CHx;
    DMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH;          // 从存储器到外设
    DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // 存储器内存:字节
    DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                  // 存储器地址增量
    DMA_Handler.Init.Mode = DMA_NORMAL;                         // 不循环模式
    DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存:字节
    DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;              // 外设地址不增量
    DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH;              // 高优先级
    HAL_DMA_Init(&DMA_Handler);                                 // DMA初始化

    __HAL_LINKDMA(&UART1_Handler, hdmatx, DMA_Handler); // 将DMA和UART1建立联系
}

uint16_t len= sizeof(SEND);// 字符串长度

void SEND_Transmit_buf()
{
    uint16_t i = 0;                     // 得到项
    uint16_t k = 0;                     // 传输项
    uint8_t mask = 0;                   // 换行标志
    for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
    {
        if (k >= len) // 入换行符,先判断回车,在判断换行
        {
            if (mask)
            {
                SEND_buf[i] = 0x0a; // 换行符
                k = 0;              // 下一行传输
            }
            else
            {
                SEND_buf[i] = 0x0d; // 回车
                mask++;             // 换行标志
            }
        }
        else /* 复制SEND语句 */
        {
            mask = 0;
            SEND_buf[i] = SEND[k]; // 将数据赋予得到项
            k++;                   // 传输下一个数据
        }
    }
}


float pro = 0; /* 进度 */
uint8_t key = 0;
void DMA_SHOW()
{
    key = KEY_Scan(0);
    if (key == KEY0_PRES) /* KEY0按下 */
    {
        printf("\r\nDMA DATA:\r\n");
        LCD_ShowString(30, 130, 200, 16, 16, "Start Transimit....");
        LCD_ShowString(30, 150, 200, 16, 16, "   %"); /* 显示百分号 */

        HAL_UART_Transmit_DMA(&UART1_Handler, SEND_buf, SEND_BUF_SIZE);
        /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯
         * 实际应用中,传输数据期间,可以执行另外的任务
         */
        while (1)
        {
            if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC4)) /* 等待 DMA1_Channel4 传输完成 */
            {
                __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC4); // 清除传输完成标志位
                HAL_UART_DMAStop(&UART1_Handler);                 /* 传输完成以后关闭串口DMA */
                break;
            }

            pro = DMA1_Channel4->CNDTR; /* 得到当前还剩余多少个数据 */
            len = SEND_BUF_SIZE;        /* 总长度 */
            pro = 1 - (pro / len);      /* 得到百分比 */
            pro *= 100;                 /* 扩大100倍 */
            LCD_ShowNum(30, 150, pro, 3, 16);
        }
        LCD_ShowNum(30, 150, 100, 3, 16);                            /* 显示100% */
        LCD_ShowString(30, 130, 200, 16, 16, "Transimit Finished!"); /* 提示传送完成 */
    }
}
//DMA.h
#ifndef __DMA_H
#define __DMA_H

#include "sys.h"
#include "usart.h"
#include "KEY.h"
#include "lcd.h"
#include <stdio.h>
#define SEND_BUF_SIZE (sizeof(SEND) + 2) * 200 // 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍.
                                               // 这里加了2是有一个\r\n

void DMA_Init(DMA_Channel_TypeDef *DMAx_CHx);
void SEND_Transmit_buf();
void DMA_SHOW();

#endif // !
//main.c
//本例程是按下按键KEY0 将串口数据传输到DMA,然后打印到串口上
//使用的是正点原子F1精英开发板

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "DMA.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	KEY_Init();						// 初始化按键
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");

	DMA_Init(DMA1_Channel4);
	SEND_Transmit_buf();
	while (1)
	{
		DMA_SHOW();
		LED0 = 1;
	}
}

  • 36
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32F1 HAL DMA UART是指在STM32F1系列微控制器上使用HAL库函数和DMA来实现UART串口通信。引用\[1\]中的代码片段展示了如何配置DMA和UART句柄以实现DMA传输数据到UART。首先,需要使能DMA时钟并配置DMA句柄。然后,通过HAL_DMA_Init函数初始化DMA句柄,并通过__HAL_LINKDMA函数将DMA句柄与UART句柄连接起来。这样,在执行UART传输函数之后,DMA将会自动将数据从内存传输到UART外设。引用\[2\]提供了关于UART串口通信过采样数据和利用DMA实现不定长数据接收的更多详细信息。引用\[3\]中的描述说明了在DMA传输完成后,UART状态会从"tx busy"变为"ready"。 #### 引用[.reference_title] - *1* [【STM32HAL库 SPI DMA UART驱动开发](https://blog.csdn.net/zDavid_2018/article/details/107988636)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【stm32HAL库】uart dma收发驱动(含实例)](https://blog.csdn.net/qq_24629659/article/details/129473515)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

油门子z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值