STM32--DMA

文章目录

  • DMA简介
    • DMA特性
  • DMA框图
  • DMA基本结构
  • DMA请求
  • 数据宽度对齐
  • DMA数据转运工程
  • DMA+ADC多通道

DMA简介

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。

DMA特性

拥有12个独立可配置通道:DMA1(7个通道),DMA2(5个通道)

STM32F103C8T6 DMA资源:DMA1(7个通道)

在这里插入图片描述

每个通道都直接连接专用的硬件DMA请求,也就是硬件触发,也支持软件触发

对于有多个请求的同时,可以利用软件编程设置优先级的先后顺序,优先级相等的情况下由硬件决定

对于DMA来说,需要传输的源头和传输的目的地,传输的大小称为传输宽度,一般有字节(8bit)、半字(16bit)、字(32bit)。

在这里插入图片描述

每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
在这里插入图片描述

DMA框图

在这里插入图片描述
先看左上角,DMA与Cortex™-M3核心共享系统数据总线,通过总数据库,可以直接执行存储器的数据传输(Flash、SRAM、外设寄存器);当DMA与Cortex™-M3同时访问相同的目标时,会通过一个仲裁器,来给它们两个循环调度访问目标,而CPU最终会得到至少一半的带宽,也就访问权限。

接着往下看,AHB从设备连接着AHB总线,可以控制总裁器和通道的选择。相当于AHB放长线,放出一个设备来管理DMA。

总裁器是可以根据通道请求的优先级来启动外设/存储器的访问。
可以通过软件编程设置4个不同的等级:最高优先级、高优先级、中等优先级、低优先级。
相同等级的情况下,则较低编号的通道比较高编号的通道有较高的优
先权。举个例子,通道2优先于通道4。

右边都是一些存储器和外设寄存器,通过DMA的请求,就可以直接对数据进行传输了。
在这里插入图片描述
这是存储器对应的起始地址和作用。

DMA基本结构

在这里插入图片描述

DMA的处理:在发生一个事件后,外设向DMA控制器发送一个请求信号。 DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时, DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求, DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。

我们首先会从要传输的外设寄存器或者是储存器获取它的起始地址和传输的数据宽度,然后再存数据到外设寄存器或者存储器指示的存储器地址,并确定数据宽度;

首先这里要明确DMA的数据转运是用复制去传输,也就是原位置的数据是不会改变的。地址自增是考虑到一般给出的地址只是首元素地址或者说是起始地址,要对下一个数据进行传输,就需要对地址进行增加,才能获取到下一个数据

传输计数器是来统计要传输多少个数据宽度的,一开始先赋予传输计数器一个初始值
在没有启动自动重装器的情况下,这个初始值会逐渐递减,当计数器的值变为0时,就停止传输。
如果启动了自动重装器,当初始值逐渐递减为0时,会重新恢复到初始值,而对应的传输地址,也会恢复到初始地址的状态;

M2M是决定使用软件触发或者硬件触发的寄存器

DMA请求

在这里插入图片描述
这是DMA1的请求映像,对于DMA请求来说,不同的外设是有要求不同对应通道的,没有由我们所决定哪个硬件外设对应哪条通道;而软件触发的则每条通道都允许。

数据宽度对齐

在这里插入图片描述

大小端讲解连接入口

这里是小端存储。
对以上的总结:
当两端宽度相等时:那么传输数据不变;
当源端宽度小于目标宽度时:目标宽度高位补0;
当源端宽度大于目标宽度时:对源端宽度进行截断,保留低位的数据

DMA数据转运工程

OLED代码连接入口

连接方式:
在这里插入图片描述
在这里插入图片描述
通过DMA的数据搬运,将一个数组中的内容搬运到另一个数组中,并且原数组会随时间不断增加,DMA也不断的进行数据搬运;

DMA.c

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
    MyDMA_Size=Size;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    //DMA初始化
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_BufferSize=Size; //指定通道缓冲区大小
    DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //指定外设是源或者目标
    DMA_InitStructure.DMA_M2M=DMA_M2M_Enable; //指定是否为软件触发运送
    DMA_InitStructure.DMA_MemoryBaseAddr=AddrB; //指定内存基地址
    DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; //内存数据宽度
    DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //指定内存地址是否自增
    DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //指定DMA通道工作模式
    DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA; //指定外设基地址
    DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //外设数据宽度
    DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable; //指定内存地址是否自增
    DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //指定DMA通道优先级
    DMA_Init(DMA1_Channel1,&DMA_InitStructure);

    //DMA是否启动
    DMA_Cmd(DMA1_Channel1,DISABLE);
}
//数据转运函数
void MyDMA_Transfer()
{
    DMA_Cmd(DMA1_Channel1,DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); //设计DMA通道数据单元数
    DMA_Cmd(DMA1_Channel1,ENABLE);
    
    //检查DMA通道上的标志位,运送完成标志
    while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET); 
    //需要手动清除标志位
    DMA_ClearFlag(DMA1_FLAG_TC1);
}

DMA.h

#ifndef __DMA_H__
#define __DMA_H__

void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer();

#endif

size表示传输的个数,大小由源端数据宽度决定,地址自增将会使数组下标进行移动;
规定软件触发不能启动自动重装器,也就是DMA的模式
数据转运函数:由于需要不断的数据转运,而没有启动自动重装器,所以需要我们自己编程来进行重装,也就是将Size恢复为初始值,地址会随着Size的变化进行变化。
手动将Size重装之前,需要先对运行控制禁用,全部操作设置完才可以启用运行控制

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "DMA.h"
#include "OLED.h"

uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
int main()
{
	OLED_Init();
	MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);

    OLED_ShowString(1,1,"DataA");
    OLED_ShowString(3,1,"DataB");
    OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
    OLED_ShowHexNum(3,8,(uint32_t)DataB,8);

    while(1)
    {
        DataA[0]++;
        DataA[1]++;
        DataA[2]++;
        DataA[3]++;
        OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
        OLED_ShowHexNum(2,1,DataA[0],2);
        OLED_ShowHexNum(2,4,DataA[1],2);
        OLED_ShowHexNum(2,7,DataA[2],2);
        OLED_ShowHexNum(2,10,DataA[3],2);
        OLED_ShowHexNum(4,1,DataB[0],2);
        OLED_ShowHexNum(4,4,DataB[1],2);
        OLED_ShowHexNum(4,7,DataB[2],2);
        OLED_ShowHexNum(4,10,DataB[3],2);
        
        Delay_ms(1000);
        
        MyDMA_Transfer();
        
        OLED_ShowHexNum(2,1,DataA[0],2);
        OLED_ShowHexNum(2,4,DataA[1],2);
        OLED_ShowHexNum(2,7,DataA[2],2);
        OLED_ShowHexNum(2,10,DataA[3],2);
        OLED_ShowHexNum(4,1,DataB[0],2);
        OLED_ShowHexNum(4,4,DataB[1],2);
        OLED_ShowHexNum(4,7,DataB[2],2);
        OLED_ShowHexNum(4,10,DataB[3],2);
        
        Delay_ms(1000);
    }
}

DMA+ADC多通道

接线方式:
在这里插入图片描述
在这里插入图片描述
ADC的规则通道会将多通道结果放在规则通道寄存器上,每当扫描到一个通道,结果就放在ADC_DR上,结果会覆盖掉上一个储存的数据,DMA就会将结果转运到DMA存储器地址上。所以对于ADC_DR不用实现地址自增,而DMA储存器需要实现地址自增。

AD.c

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void AD_Init()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    
    //配置ADC时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 72M/6=12MHz

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    
    //为所选ADC规则通道配置其序列器对应等级和采样时间
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
    
    
    //ADC结构体成员
    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_ContinuousConvMode=ENABLE; //指定通道模式为连续转换或者单转换
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐方式
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //启动规则通道模拟电压到数字转换的外部触发器
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //配置ADC为独立模式或者双模式
    ADC_InitStructure.ADC_NbrOfChannel=4;
    ADC_InitStructure.ADC_ScanConvMode=ENABLE; //选择是否为扫描模式
    ADC_Init(ADC1,&ADC_InitStructure);
    
    //DMA结构体成员
    DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 4;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    //ADC运行控制
    DMA_Cmd(DMA1_Channel1,ENABLE);
    ADC_DMACmd(ADC1,ENABLE);
    ADC_Cmd(ADC1,ENABLE);
    
    
    //重置所选ADC校准寄存器
    ADC_ResetCalibration(ADC1);
    //获取ADC复位状态,复位后为0
    while(ADC_GetResetCalibrationStatus(ADC1));
    //开始校准
    ADC_StartCalibration(ADC1);
    //获取ADC所选标准位状态,校准需要时间,校准好后置0
    while(ADC_GetCalibrationStatus(ADC1));

    //启动ADC软件转换
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);

}



AD.h

#ifndef __AD_H__
#define __AD_H__

extern uint16_t AD_Value[4];

void AD_Init();


#endif

这里采用的是连续转换+扫描模式。这样就可以直接实现数据转换的自动化。
这里需要加上ADC_DMACmd函数,因为上面DMA请求中通道一的硬件触发有3个选择,这里表示选择ADC1.
ADC为软件触发方式,DMA为硬件触发方式。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"


int main()
{
	OLED_Init();
	AD_Init();
    OLED_ShowString(1,1,"AD0:");
    OLED_ShowString(2,1,"AD1:");
    OLED_ShowString(3,1,"AD2:");
    OLED_ShowString(4,1,"AD3:");
    while(1)
    {
        OLED_ShowNum(1,5,AD_Value[0],4);
        OLED_ShowNum(2,5,AD_Value[1],4);
        OLED_ShowNum(3,5,AD_Value[2],4);
        OLED_ShowNum(4,5,AD_Value[3],4);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Debezium日常分享系列之:临时阻塞快照Ad-hoc blocking snapshots

Debezium日常分享系列之:临时阻塞快照Ad-hoc blocking snapshots 一、认识临时阻塞快照二、临时阻塞快照信号格式三、临时阻塞快照效果 一、认识临时阻塞快照 增量快照于大约两年前在 Debezium 1.6 中首次引入,并且在社区中仍然非常受欢迎,用…

Python:逢七拍腿游戏

场景模拟: 通过在 for 循环中使用 continue 语句实现计算拍腿次数,即计算从1到100(不包括100),一共有多少个尾数为7或7的倍数这样的游戏,代码如下: total 99 # 记…

使用swoole实现实时消息推送给客户端

一. 测试服务端 //测试服务端public function testServer(){$server new Server(192.168.0.144, 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP);$server->on(request, function ($request, $response) {$response->header(Content-Type, text/plain);$response->end("He…

小白的Node.js学习笔记大全---不定期更新

Node.js是什么 Node. js 是一个基于 Chrome v8 引擎的服务器端 JavaScript 运行环境Node. js 是一个事件驱动、非阻塞式I/O 的模型,轻量而又高效Node. js 的包管理器 npm 是全球最大的开源库生态系统 特性 单一线程 Node.js 沿用了 JavaScript 单一线程的执行特…

Collada .dae文件格式简明教程【3D】

当你从互联网下载 3D 模型时,可能会在格式列表中看到 .dae 格式。 它是什么? 推荐:用 NSDT编辑器 快速搭建可编程3D场景。 1、Collada DAE概述 COLLADA是COLLAborative Design Activity(中文:协作设计活动&#xff09…

高效解决Anaconda Prompt报错Did not find VSINSTALLDIR这类问题

文章目录 回忆问题解决问题step1step2 回忆问题 类似于划红线部分然后还有很多行的报错信息,最后一行肯定是红色划线部分 解决问题 step1 找到 D:\Anaconda\envs\pytorch\etc\conda\activate.d在这个文件夹内会有两个文件,删除 vs2017_compiler_v…

vue3生命周期

原理 vue3也提供了Composition API形式的生命周期钩子,与vue2.x中钩子对应关系如下: beforeCreate setup() created setup() beforeMountonBeforeMount mountedonMounted beforeUpdateonBeforeUpdate updat…

AI百度文心一言大语言模型接入使用(中国版ChatGPT)

百度文心一言接入使用(中国版ChatGPT) 一、百度文心一言API二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、百度文心一言API 基于百度文心一言语言大模型…

【MaxKey对接一】对接gitlab的oauth登录

MaxKey的Oauth过程 引导进入 GET http://{{maxKey_host}}/sign/authz/oauth/v20/authorize?client_idYOUR_CLIENT_ID&response_typecode&redirect_uriYOUR_REGISTERED_REDIRECT_URI 登录后回调地址 YOUR_REGISTERED_REDIRECT_URI/?code{{code}} 换取Access Token GET…

React 调试开发插件 React devtools 的使用

可以在谷歌扩展应用商店里获取这个插件。如果不能访问谷歌应用商店,可以点此下载最新版 安装插件后,控制台出现 “Components” 跟 “Profiler” 菜单选项。 查看版本,步骤: 下面介绍 react devtools 的使用方式。 在 Component…

安装jenkins-cli

1、要在 Linux 操作系统上安装 jcli curl -L https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz|tar xzv sudo mv jcli /usr/local/bin/ 在用户根目录下,增加 jcli 的配置文件: jcli config gen -ifalse …

如何快速制作解决方案PPT

如何快速制作解决方案PPT 理解客户的需求 在开始制作解决方案PPT之前,需要对客户的需求进行深入了解和分析。这包括客户需要解决的问题、目标、预算和时间限制等。 需求分析 客户需要解决的问题客户的目标预算限制时间限制 确定解决方案 基于客户的需求&#x…

小程序-基于vant的Picker组件实现省市区选择

一、原因 因vant/area-data部分的市/区数据跟后台使用的高德/腾讯省市区有所出入,故须保持跟后台用同一份数据,所以考虑以下几个组件 1、Area 2、Cascader 3、Picker 因为使用的是高德地图的省市区json文件,用area的话修改结构代价太大&…

2023年Java核心技术面试第五篇(篇篇万字精讲)

目录 十 . HashMap,ConcurrentHashMap源码解析 10.1 HashMap 的源码解析: 10.1.1数据结构: 10.1.2哈希算法: 10.1.3解决哈希冲突: 10.1.4扩容机制: 10.1.5如何使用 HashMap: 10.2 HashMap 关注…

小程序 CSS-in-JS 和原子化的另一种选择

小程序 CSS-in-JS 和原子化的另一种选择 小程序 CSS-in-JS 和原子化的另一种选择 介绍快速开始 pandacss 安装和配置 0. 安装和初始化 pandacss1. 配置 postcss2. 检查你的 panda.config.ts3. 修改 package.json 脚本4. 全局 css 注册 pandacss5. 配置的优化与别名 weapp-pand…

数学建模的概念和学习方法(什么是数学建模)

一、初步认识数学建模 数学建模是将数学方法和技巧应用于实际问题的过程。它涉及使用数学模型来描述和分析现实世界中的现象、系统或过程,并通过数学分析和计算来预测、优化或解决问题。数学建模可以应用于各种领域,包括自然科学、工程、经济学、环境科学…

微服务—远程调用(RestTemplate)

在微服务的所有框架中,SpringCloud脱颖而出,它是目前国内使用的最广泛的微服务框架 (官网地址),它集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱…

【激光雕刻与DIY Arduino SCARA机器人】

【激光雕刻与DIY Arduino SCARA机器人】 1. 项目概况2. 设计和3D模型3. 安装激光模块4. SCARA机器人激光雕刻机电路图5. 完成装配6. 马林固件,用于使用 SCARA 机器人进行激光雕刻7. 配置 Marlin 固件8. 控制软件 – 主机9. 使用 SCARA 机器人进行激光雕刻10. 生成用于激光雕刻…

wkhtmltopdf 与 .Net Core

wkhtmltopdf 是使用webkit引擎转化为pdf的开源小插件. 其有.NET CORE版本的组件,DinkToPdf,但该控件对跨平台支持有限 。 是由于各系统平台会产生不同的编译结果,故windows上使用.dll,而Linux上的动态链接库是.so 所以你需要在Linux系统上安装相关wkhtmltox软件。 我这里准备了…

【Linux命令详解 | wget命令】 wget命令用于从网络下载文件,支持HTTP、HTTPS和FTP协议

文章标题 简介一,参数列表二,使用介绍1. 基本文件下载2. 递归下载整个网站3. 限制下载速率4. 防止SSL证书校验5. 断点续传6. 指定保存目录7. 自定义保存文件名8. 增量下载9. 使用HTTP代理10. 后台下载 总结 简介 在编程世界中,处理网络资源是…
最新文章