STM32-笔记35-DMA(直接存储器访问)

news2025/2/28 20:17:51

一、什么叫DMA?

        DMA(Direct Memory Access,直接存储器访问)提供在外设与内存、存储器和存储器之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来说就无法使用。

简单来说,就是一个数据的搬运工

而搬运的路径有三种方式:如下所示

  • 存储器→存储器(例如:复制某特别大的数据buf)
  • 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
  • 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)

        存储器我们知道,就是内存,存储器到存储器之间的搬运,也就是从内存中一地方搬运到内存中另一个地方。

        那么这里的外设是指什么呢?

        外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,而这里的存储器包括 自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的

具体在参考手册中DMA框图中可以看见外设有哪些

 二、DMA存在的意义?

        代替 CPU 搬运数据,为 CPU 减负。

  • 1. 数据搬运的工作比较耗时间;
  • 2. 数据搬运工作时效要求高(有数据来就要搬走);
  • 3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。

三、搬运的流程

存储器→存储器

存储器→外设

外设→存储器

四、DMA控制器-通道

STM32F103 有 2 个 DMA 控制器,DMA1 有 7 个通道,DMA 2 有 5 个通道。
一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
STM32F103C8T6 只有 DMA1 !
DMA1有7个通道:

每个通道传输特定外设的数据

五、DMA优先级管理

优先级管理采用软件+硬件:
        软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级
最高级>高级>中级>低级
        硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
        比如:如果软件优先级相同,通道2优先于通道4

六、DMA传输方式

 

七、DMA寄存器及库函数介绍
 

八、小实验1:DMA内存到内存数据搬运

实验目的

使用DMA将一个大数组的数据搬运到另一个位置。

复制项目文件19-串口打印功能

重命名为40-DMA实验(内存到内存)

新建文件夹dma dma.c  dma.h

打开项目文件

加载文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();//初始化led灯
    uart1_init(115200);
    dma_init();
    printf("hello word!\r\n");
    dma_transmit();
    
//    led1_ON();
//    led1_OFF();
    while(1)
    { 
        led1_ON();
        led2_OFF();
        delay_ms(500);
        led1_OFF();
        led2_ON();
        delay_ms(500);
    }
}

dma.c

#include "dma.h"
#include "stdio.h"
#define BUF_SIZE 16

uint32_t src_buf[BUF_SIZE] = {
    0x00000000,0x11111111,0x22222222,0x33333333,
    0x44444444,0x55555555,0x66666666,0x77777777,
    0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
    0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
uint32_t dst_buf[BUF_SIZE] = {0};

DMA_HandleTypeDef dma_handle = {0};
//初始化dma函数
void dma_init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
    dma_handle.Instance = DMA1_Channel1;//选择DMA1通道1(由于是内存传递内存所以不管哪一个通道都可以)
    dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;//方向:内存到内存
    //内存的设置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动
    //外设的设置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
    dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;//外设增量模式启动
    
    dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
    
    HAL_DMA_Init(&dma_handle);
}
//准备数据传输
void dma_transmit(void)
{
    //启动DMA传输
    HAL_DMA_Start(&dma_handle,(uint32_t)src_buf,(uint32_t)dst_buf,sizeof(uint32_t) * BUF_SIZE);
    //查询DMA传输状态
    while(__HAL_DMA_GET_FLAG(&dma_handle,DMA_FLAG_TC1) == RESET);
    //打印数据
    int i;
    for(i=0;i<BUF_SIZE;i++)
    {
        printf("buf[%d]:%X\r\n",i,dst_buf[i]);
    }
    
}

dma.h

#ifndef __DMA_H__
#define __DMA_H__

#include "sys.h"

void dma_init(void);
void dma_transmit(void);
    
#endif

九、小实验2:DMA内存到外设数据搬运

实验目的

使用DMA将一个大数组的数据通过串口1发送 。

内存通过DMA将数据搬运到外设中

USART1-TX负责将内存中的数据按照指定的格式和波特率发送到外部设备‌

复制项目文件40-DMA实验(内存到内存)

重命名项目文件41-DMA实验(内存到外设)

打开项目文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"

uint8_t send_buf[1000] = {0};
extern UART_HandleTypeDef uart1_handle;

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();//初始化led灯
    uart1_init(115200);
    dma_init();
    //printf("hello word!\r\n");
    
    int i = 0;
    for(i=0;i<1000;i++)
    {
        send_buf[i] = 'A';
    }
    //串口句柄,发送的数据,发送的个数
    HAL_UART_Transmit_DMA(&uart1_handle,send_buf,1000);//以DMA的方式发送数据
    while(1)
    { 
        led1_ON();
        led2_OFF();
        delay_ms(500);
        led1_OFF();
        led2_ON();
        delay_ms(500);
    }
}

dma.c

#include "dma.h"
#include "stdio.h"

extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef dma_handle = {0};
//初始化dma函数
void dma_init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
    dma_handle.Instance = DMA1_Channel4;//选择DMA1通道4,内存到外设通道4-以DMA的方式发送(tx)给串口1数据
    dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;//方向:内存到外设
    //内存的设置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增
    //外设的设置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增
    
    dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
    
    HAL_DMA_Init(&dma_handle);
    //链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄
    __HAL_LINKDMA(&uart1_handle,hdmatx,dma_handle);
}

dma.h

#ifndef __DMA_H__
#define __DMA_H__

#include "sys.h"

void dma_init(void);
    
#endif

结果:

十、小实验3:DMA外设到内存数据搬运

实验目的

使用DMA接收串口1发的数据。

串口通过DMA将数据搬运到内存中

USART1-RX负责将外部设备‌中的数据按照指定的格式和波特率发送到内存,

也就是内存接收外部设备中的数据

所以这里的USART1-RX和USART1-TX是针对内存来讲(暂时先这么记)

复制项目文件41-DMA实验(内存到外设)

重命名项目文件42-DMA实验(外设到内存)

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();//初始化led灯
    uart1_init(115200);
    dma_init();
    printf("hello word!\r\n");
    
    while(1)
    { 
        led1_ON();
        led2_OFF();
        delay_ms(500);
        led1_OFF();
        led2_ON();
        delay_ms(500);
    }
}

dma.c

#include "dma.h"
#include "stdio.h"
#include "uart1.h"

extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef dma_handle = {0};
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 */
                                               

//初始化dma函数
void dma_init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
    dma_handle.Instance = DMA1_Channel5;//选择DMA1通道5,外设到内存通道5-以DMA的方式发送(rx)给串口1数据
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//方向:外设到内存
    //内存的设置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增
    //外设的设置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增
    
    dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
    
    HAL_DMA_Init(&dma_handle);
    //链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄
    __HAL_LINKDMA(&uart1_handle,hdmarx,dma_handle);
    //串口通过DMA接收
    HAL_UART_Receive_DMA(&uart1_handle,uart1_rx_buf,UART1_RX_BUF_SIZE);//将数据接收到uart1_rx_buf缓冲区里面长度是UART1_RX_BUF_SIZE这么长
}

dma.h

#ifndef __DMA_H__
#define __DMA_H__

#include "sys.h"

void dma_init(void);
    
#endif

结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2273668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【踩坑】SparkSQL union/unionAll 函数的去重问题

【踩坑】SparkSQL union/unionAll 函数的去重问题 测试数据 case class Employee(first_name:String)val employeeDF1 spark.createDataset(Seq( Employee("Mary"), Employee("Mandy"),Employee("Kurt") )) val employeeDF2 spark.createDat…

allure报告修改默认语言为中文

1、项目根目录创建.py文件&#xff0c;把代码复制进去 import os from pathlib import Pathdef create_settings_js_file(directory"../pytest_mytt/reports/allures/", filenamesettings.js):# 创建或确认目录存在Path(directory).mkdir(parentsTrue, exist_okTrue…

使用frp实现本地内网穿透

环境&#xff1a;linux &#xff08;具有公网ip的线上服务器&#xff09;、windows&#xff08;本地&#xff09;、frp Releases fatedier/frphttps://github.com/fatedier/frp/releases 首先下载下来下面两个文件 概览 | frp一些概述&#xff0c;便于您快速的了解 frp。http…

Cursor无限续杯——解决Too many free trials.

前情提要 我们都知道Cursor对新用户是有14天且500条免费限制的。 一般情况下&#xff0c;当14天过期&#xff0c;是可以注销账户再重新注册&#xff0c;这样就可以继续拥有14天的体验时长。 但是&#xff01;&#xff01;如果使用超过500次&#xff0c;Cusor就会把你的电脑I…

cursor试用出现:Too many free trial accounts used on this machine 的解决方法

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…

攻防世界 Web_php_wrong_nginx_config

​ 打开题目地址&#xff0c;显示为登录页面。尝试用御剑扫描一下&#xff0c;发现了admin页面&#xff0c;点进去显示如下 点开控制台&#xff0c;发现如下 isLogin参数为0。尝试抓包并该islogin参数为1&#xff0c;返回依旧不变。 再扫描&#xff0c;发现robots.txt&#xff…

WordPress静态缓存插件WP Super Cache与 WP Fastest Cache

引言 WordPress是一款开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;最初作为博客平台开发&#xff0c;现已发展成为一个功能强大的建站工具&#xff0c;支持创建各种类型的网站&#xff0c;包括企业网站、在线商店、个人博客等。它具有用户友好的界面、丰富的插…

多模态大模型初探索:通过ollama部署多模态大模型

文章目录 前言模型下载 前言 今天和同事聊天&#xff0c;聊到多模态大模型&#xff0c;感觉可以作为2025年的一个新的探索方向。希望和大家一起学习&#xff0c;一起进步。 今天也是尝试了我能想到的最基本最快速地本地部署多模态大模型的方式&#xff0c;那便是使用ollama。…

【深度学习】布匹寻边【附完整链接】

布匹寻边 项目简介 布匹寻边是指布料裁剪过程中&#xff0c;通过AI寻边技术自动识别布匹的边缘&#xff0c;将检测到的边缘信息输出&#xff0c;确保裁剪的准确性&#xff0c;减少浪费&#xff0c;并提高生产效率。 项目需求 将打满针眼的布匹边缘裁剪掉&#xff0c;且误差小…

scanf:数据之舟的摆渡人,静卧输入港湾的诗意守候

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。* 这一节我们主要来学习scanf的基本用法&#xff0c;了解scanf返回值&#xff0c;懂得scanf占位符和赋值…

vue数据请求通用方案:axios的options都有哪些值

Axios 是一个基于 promise 的 HTTP 库&#xff0c;可以用在浏览器和 Node.js 中。 在使用 Axios 发送请求时&#xff0c;可以通过传递一个配置对象来指定请求的各种选项。 以下是一些常用的 Axios 配置选项及其说明&#xff1a; 1.url: &#xff08;必需&#xff09;请求的 …

阿里mod_asr3.0集成webrtc静音算法

alibabacloud-nls-cpp-sdk-master 先到阿里官网下载nls库的源代码&#xff0c;编译生成对应的库文件和头文件。 我编译的放到了以下目录。 /home/jp/2025/alibabacloud-nls-cpp-sdk-master/build/install/NlsSdk3.X_LINUX/include/ /home/jp/2025/alibabacloud-nls-cpp-sdk-…

【大模型】百度千帆大模型对接LangChain使用详解

目录 一、前言 二、LangChain架构与核心组件 2.1 LangChain 核心架构 2.2 LangChain 核心组件 三、环境准备 3.1 前置准备 3.1.1 创建应用并获取apikey 3.1.2 开通付费功能 3.2 获取LangChain文档 3.3 安装LangChain依赖包 四、百度千帆大模型对接 LangChain 4.1 LL…

【51单片机零基础-chapter6:LCD1602调试工具】

实验0-用显示屏LCD验证自己的猜想 如同c的cout,前端的console.log() #include <REGX52.H> #include <INTRINS.H> #include "LCD1602.h" int var0; void main() {LCD_Init();LCD_ShowNum(1,1,var211,5);while(1){;} }实验1-编写LCD1602液晶显示屏驱动函…

Mysql--基础篇--事务(ACID特征及实现原理,事务管理模式,隔离级别,并发问题,锁机制,行级锁,表级锁,意向锁,共享锁,排他锁,死锁,MVCC)

在MySQL中&#xff0c;事务&#xff08;Transaction&#xff09;是一组SQL语句的集合&#xff0c;这些语句一起被视为一个单一的工作单元。事务具有ACID特性&#xff0c;确保数据的一致性和完整性。通过事务&#xff0c;可以保证多个操作要么全部成功执行&#xff0c;要么全部不…

使用VUE3创建个人静态主页

使用VUE3创建个人静态主页 &#x1f31f; 前言&#x1f60e;体验&#x1f528; 具体实现✨ 核心功能&#x1f3d7;️ 项目结构&#x1f680; 用这个项目部署 Git Page &#x1f4d6; 参考 &#x1f31f; 前言 作为开发者或者内容创作者&#xff0c;我们经常需要创建静态网页&a…

鸿蒙APP之从开发到发布的一点心得

引言&#xff1a; 做鸿蒙开发大概有1年左右时间了&#xff0c;从最开始的看官方文档、看B站视频&#xff0c;到后来成功发布两款个人APP&#xff08;房贷计算极简版、时简时钟 轻喷&#xff0c;谢谢&#xff09;。简单描述一下里边遇到的坑以及一些经历吧。 学习鸿蒙开发 个…

【HTML+CSS+JS+VUE】web前端教程-3-标题标签

标题介绍与应用 标题是通过<h1>-<h6>标签进行定义的 <h1>定义最大的标题 <h6>定义最小的标题<h1>一级标题</h1> <h2>二级标题</h2> <h3>三级标题</h3> <h4>四级标题</h4> <h5>五级标题</h5…

Web应用安全-漏洞扫描器设计与实现

摘 要 随着Web2.0、社交网络、微博等一系列新型的互联网产品的诞生&#xff0c;基于Web环境的互联网应用越来越广泛&#xff0c;企业信息化的过程中各种应用都架设在Web平台上。Web应用的迅速发展也引起黑客们的强烈关注&#xff0c;接踵而至的就是Web安全威胁的凸显&#xff…

SpringBootWeb案例-1(day10)

准备工作 需求 & 环境搭建 需求说明 环境搭建 步骤&#xff1a; 准备数据库表(dept、emp)创建 springboot 工程&#xff0c;引入对应的起步依赖&#xff08;web、mybatis、mysql 驱动、lombok&#xff09;配置文件 application.properties 中引入 mybatis 的配置信息&…