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 headeruint16_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 headeruint16_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=12MHzGPIO_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复位状态,复位后为0while(ADC_GetResetCalibrationStatus(ADC1));//开始校准ADC_StartCalibration(ADC1);//获取ADC所选标准位状态,校准需要时间,校准好后置0while(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.mzph.cn/news/45545.shtml

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

相关文章

PyTorch学习笔记(十七)——完整的模型验证(测试,demo)套路

完整代码: import torch import torchvision from PIL import Image from torch import nnimage_path "../imgs/dog.png" image Image.open(image_path) print(image)# 因为png格式是四个通道,除了RGB三通道外,还有一个透明度通…

字符设备驱动实例(ADC驱动)

四、ADC驱动 ADC是将模拟信号转换为数字信号的转换器,在 Exynos4412 上有一个ADC,其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz,转换速率最高为1MSPS (4)具有四路模拟输入,同一时…

差值结构的复合底部

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点,AB训练集各由6张二值化的图片组成,让A 中有3个点,B中有1个点,且不重合,统计迭代次数并排序。 其中有20组数据 让迭代次数与排斥能成反比,排…

第 7 章 排序算法(3)(选择排序)

7.6选择排序 7.6.1基本介绍 选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。 7.6.2选择排序思想: 选择排序(select sorting)也是一种简单的排序方法…

装饰器读取不到被装饰函数的参数-已解决

def write_case_log(func):def wrapper(*args, **kwargs):logger.info("{}开始执行".format(func.__name__))func(*args,**kwargs)logger.info("{}执行中".format(args))logger.info("{}执行结束",format(func.__name__))return wrapper被装饰函…

每天一道leetcode:剑指 Offer 34. 二叉树中和为某一值的路径(中等图论深度优先遍历递归)

今日份题目: 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例1 输入:root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSu…

PyCharm连接Docker中的容器(ubuntu)

一、为什么要用Pycharm链接Docker中的ubuntu 因为在进行深度学习的时候,基于windows系统在开发的过程中,老是出现很多问题,大多数是环境问题。 尽管安装了Conda,也不能很好的解决问题,使用ubuntu是最好的选择。 二、…

docker基础

本地安装 ①卸载旧版 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc 保证虚机无安装包冲突 ②在新主机上首次安装 Docker 引擎之前,您需…

运行flutter doctor命令窗口直接闪退

在cmd中输入flutter doctor后闪退了。 使用高速摄像机可以看到报错信息。 报错信息的意思是git的文件夹不能删掉,请保留flutter中git文件。

[机器学习]特征工程:主成分分析

目录 主成分分析 1、简介 2、帮助理解 3、API调用 4、案例 本文介绍主成分分析的概述以及python如何实现算法,关于主成分分析算法数学原理讲解的文章,请看这一篇: 探究主成分分析方法数学原理_逐梦苍穹的博客-CSDN博客https://blog.csdn.…

css 实现电梯导航

实现原理&#xff1a;利用css实现电梯导航很简单&#xff0c;基本原理就是通过a标签绑定跳转目标的id来实现的 html代码&#xff1a; <div class"body"><div class"top" id"top"></div><div class"con1" id"…

线性代数的学习和整理5: 矩阵的加减乘除及其几何意义(未完成,建设ing)

目录 1 矩阵加法 1.1 矩阵加法的定义 1.2 加法的属性 1.2.1 只有同类型&#xff0c;相同n*m的矩阵才可以相加 1.2.1 矩阵加法的可交换律&#xff1a; 1.2.2 矩阵加法的可结合律&#xff1a; 1.3矩阵加法的几何意义 2 矩阵的减法 2.1 矩阵减法定义和原理基本同 矩阵的…

Linux命令200例:tail用来显示文件的末尾内容(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

HCIP之VLAN实验

目录 一、实验题目 二、实验思路 三、实验步骤 3.1 将接口划入vlan&#xff0c;设置trunk干道 3.2 启动DHCP服务&#xff0c;下发地址 四、测试 一、实验题目 实验要求&#xff1a; 1&#xff0c;PC1/3的接口均为access模式&#xff0c;且属于vlan2&#xff0c;处于同一…

NLP中的RNN、Seq2Seq与attention注意力机制

目录 NLP自然语言处理 的RNN、Seq2Seq与attention注意力机制 RNN循环神经网络 前馈网络入门 前馈网络 循环网络 多层感知器架构示例 循环神经网络的运作原理 展开 RNN seq2seq模型 Attention&#xff08;注意力机制&#xff09; 总结 引用 NLP自然语言处理 的RNN、…

Ubuntu20.04搭建OpenGL环境(glfw+glad)

Ubuntu20.04搭建OpenGL环境(glfwglad) Linux环境搭建 本文在VMware安装Ubuntu20.04桌面版的环境下搭建OpenGL&#xff0c;按照本文搭建完成后可以执行LearnOpenGL网站上的demo。 关于VMware可自行到VMware Workstation Pro | CN下载 关于Ubuntu20.04桌面版可自行到官网或In…

轮腿机器人的PID控制

1 PID介绍 PID&#xff08;Proportional Integral Derivative&#xff09;控制系统。其实质是根据输入的偏差值&#xff0c;按比例、积分、微分的函数关系进行运算&#xff0c;运算结果用以输出进行控制。它是在长期的工程实践中总结出来的一套控制方法&#xff0c;实际运行经…

【C++】做一个飞机空战小游戏(十一)——游戏过关、通关、结束的设置

[导读]本系列博文内容链接如下&#xff1a; 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动【C】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动 【C】做一个飞…

STM32F4X USART串口使用

STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口&#xff0c;也是MCU在开发过程中的调试利器。串口通信有几个重要的参数&#xff0c;分别…

【Linux】进程信号篇Ⅰ:信号的产生(signal、kill、raise、abort、alarm)、信号的保存(core dump)

文章目录 一、 signal 函数&#xff1a;用户自定义捕捉信号二、信号的产生1. 通过中断按键产生信号2. 调用系统函数向进程发信号2.1 kill 函数&#xff1a;给任意进程发送任意信号2.2 raise 函数&#xff1a;给调用进程发送任意信号2.3 abort 函数&#xff1a;给调用进程发送 6…