STM32f10x学习----ADC和DMA功能 后附具体操作及使用过程中遇到的问题

2023-10-27

学习某一个东西,我们首先要了解这个东西的定义是什么,用来干什么的,怎么用,用的过程中有什么注意事项,这些都OK了,那么我们就算是基本掌握他了。

0 前言

ADC,Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器。

模拟信号转换为数字信号,一般分为4个步骤进行,即采样、保持、量化和编码。前2个步骤在采样——保持电路中完成,后两步骤则在ADC中完成。ADC是把经过与标准量比较处理后的模拟量转换成以二进制数值表示的离散信号的转换器。

DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

这里为什么将ADC和DMA放在一起来讲呢,因为通常使用到ADC就会使用到DMA来将ADC转换的值直接通过DMA传输到缓存中去,减少了CPU的参与,提高程序运行的效率。

1.ADC和DMA

1.1概述

1.1.1 ADC概述

在STM32F10X中对ADC的描述

在这里插入图片描述

12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

ADC的功能框架图如下:

在这里插入图片描述

​ 从框架图可以看出,ADC不仅支持GPIO口输入和外部中断触发转换还支持定时器触发转换,在转换结束,注入转换结束,发生模拟看门狗事件的时候会产生中断或者DMA请求。除此之外ADC不仅可以使用单通道采集还可以使用双重通道采集来提高精度,采样的间隔也可以通过通道编程,ADC转换时间:最大转换速率 1us(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到)

1.1.2 DMA概述

在STM32F10X中对DMA的描述

在这里插入图片描述

​ 直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

DMA的原理:

​ DMA 与cortex m3 共享数据总线,通常用到外设与存储器,当CPU和DMA同时访问相同目标时,(RAM或外设),MDA会请求暂停CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽,所以不用担心DMA霸占资源。

DMA的特点:

将CPU从数据传输中解放出来,实现数据传输的独立性

直接通过DMA实现外部内存与主存之间的数据传输

DMA的数据传输:

DMA支持数据从外设到存储器传输,存储器到外设传输,存储器到存储器传输,支持循环传输和单次传输

1)从外设到存储器

以ADC采集为例,DMA外部寄存器地址对应ADC数据寄存器地址,DMA存储器地址是我们自定义的变量的地址。方向设置为源地址。

2)存储器到外设

存储器到外设传输以串口向电脑端发送为例,DMA 外设寄存器的地址对应的就是串口数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。

3)存储器到存储器

当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。

DMA外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把

4)传输模式

传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCR寄存器的 CIRC 循环模式位控制。

DMA的功能框图

在这里插入图片描述

从上图可以看出DMA的数据传输流程:

1.外设对DMA控制器发出请求。

2.DMA控制器收到请求,触发DMA工作。

3.DMA控制器从外设获取数据存储到DMA通道中

4.DMA控制器的DMA总线与总线矩阵协调,使用数据总线把外设的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与

2.使用示例

2.1ADC和DMA配置

使用固件库函数配置ADC两个通道采集并用DMA传输数据

.c文件内容

#include "ADC_DMA.h"
#define ADC1_DR_Address    ((uint32_t)0x4001244C)
#define     M       2

volatile uint16_t adc_value[2]; //DMA缓存


/**
 * @brief  配置ADC的GPIO口
 * @param  None
 * @retval None
 */
static void adc1_gpio_config(void)
{

    GPIO_InitTypeDef GPIO_InitStructure;
    
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
    GPIO_InitStructure.GPIO_Pin =   GPIO_Pin_3|GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	 
}

/**
 * @brief  配置ADC和DMA的相关寄存器
 * @param  None
 * @retval None
 */
static void adc1_dma_config(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    
	
    /* DMA1 channel1 configuration ----------------------------------------------*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel1);
	
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adc_value[0];
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;   //外设到SRAM      
    DMA_InitStructure.DMA_BufferSize = M;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_disable; //外设地址不递增 
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//半字
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环传送
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
 
  
	/* Enable DMA1 channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);
	
    /* ADC1 configuration ------------------------------------------------------*/
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//正常模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;   //工作在多通道
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换使能
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //软件控制
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 2;//两个通道    
    ADC_Init(ADC1, &ADC_InitStructure);
	
  
	
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1,1, ADC_SampleTime_239Cycles5);		//温度传感器1
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2,2, ADC_SampleTime_239Cycles5);     //温度传感器2

    /* Enable ADC1 DMA */
    ADC_DMACmd(ADC1, ENABLE);//允许DMA请求

    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);

    /* Enable ADC1 reset calibration register */   
    ADC_ResetCalibration(ADC1);
    /* Check the end of ADC1 reset calibration register */
    while(ADC_GetResetCalibrationStatus(ADC1));

    /* Start ADC1 calibration */
    ADC_StartCalibration(ADC1);
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1));
     
    /* Start ADC1 Software Conversion */ 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //启动ADC 

}

/**
 * @brief  初始化ADC和DMA
 * @param  None
 * @retval None
 */
void adc1_init(void)
{
 //   rcc_config();
    adc1_gpio_config();
    adc1_dma_config();
	
}
/**
 * @brief  从DMA缓存中读取ADC采集过来的数据
 * @param  times :次数    ADCchannel_numb :ADC通道
 * @retval None
 */
uint32_t Get_Adc_Average(uint8_t times,uint8_t ADCchannel_numb)  //平均滤波
{
    u32 temp_val=0;
    uint8_t t;
	if(ADCchannel_numb==0) //第一个温度传感器的
	{
		 for(t=0;t<times;t++)
			{ 
					temp_val += adc_value[0];
			}
			return temp_val/times;
	}			 
	else  //第二个温度传感器
	{
		 for(t=0;t<times;t++)
			{ 
					temp_val += adc_value[1];
			}
			return temp_val/times;
	}		
}

.h文件内容

#ifndef __ADC_DMA_H
#define __ADC_DMA_H

extern volatile uint16_t adc_value[2];
void adc1_init(void);
uint32_t Get_Adc_Average(uint8_t times,uint8_t ADCchannel_numb)  //平均滤波
    
#endif

3.问题总结

1.ADC的分辨率,可选的分辨率有12位、10位、8位和6位。分辨率越高,AD转换数据精度越高,转换时间也越长;分辨率越低,AD转换数据精度越低,转换时间也越短。默认是12位的分辨率,数据左对齐需要除以16。

2.ADC时钟是有PCLK2分频而来,分频系数决定ADC时钟频率,可选的分频系数为2、4、6和8。ADC最大时钟配置为36MHz。

3.ADC的采样周期跟与采样的目标相关,采集有频率的目标需要目标频率设置采样周期,采样周期越长,精度越高,但是时间越长。

4.使用ADC_RegularChannelConfig函数可以配置通道采集的先后顺序,配合DMA的缓存数组可以确定哪一个通道存放在哪一个内存。

5.DMA_BufferSize 表示的是有几个缓存,如果DMA_PeripheralDataSize 和DMA_MemoryDataSize都设置的是半字,那1个DMA_BufferSize 就表示1个半字缓存,注意 DMA_PeripheralDataSize 和DMA_MemoryDataSize的配置必须一致。

6.扫描模式用于多通道采集时,一轮采集为根据通道设定的顺序依次采集每个开启的通道。但要注意的是每个通道采集到的值都是存放在寄存器ADCx-> DR中,而只有当一轮采集完即所有设定的通道都采集完后采集完成标志才会被置位,所以不使用DMA传输方式的时候会出现采集到的数据被覆盖的现象。

aSize 和DMA_MemoryDataSize都设置的是半字,那1个DMA_BufferSize 就表示1个半字缓存,注意 DMA_PeripheralDataSize 和DMA_MemoryDataSize的配置必须一致。

6.扫描模式用于多通道采集时,一轮采集为根据通道设定的顺序依次采集每个开启的通道。但要注意的是每个通道采集到的值都是存放在寄存器ADCx-> DR中,而只有当一轮采集完即所有设定的通道都采集完后采集完成标志才会被置位,所以不使用DMA传输方式的时候会出现采集到的数据被覆盖的现象。

7.规则通道组中可以安排最多16个通道,而注入通道组可以安排最多4个通道。规则组是程序按照配置规则转换,注入组是根据外部触发中断来转换,在执行规则通道组扫描转换时,如有例外处理则可启用注入通道组的转换。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32f10x学习----ADC和DMA功能 后附具体操作及使用过程中遇到的问题 的相关文章

  • C语言 -- 动态数组&链表

    目录 动态数组 动态数组的实现 用户test 链表 目的 链表的结构体 链表的实现 初始化链表 插入节点 遍历链表 删除节点 清空链表 销毁链表 用户回调函数 给用户提供接口获取链表长度 用户test 动态数组 将数组开辟到堆区 实现动态扩
  • ubuntu 16.04安装 jdk 1.9

    1 命令行输入javac 提示java尚未安装 选择提示中的包进行安装 2 执行 sudo apt install 安装jdk的版本号 根据提示确认安装 3 配置jdk路径 执行 sudo gedit etc profile 输入密码打开文
  • c++ STL vector知识点

    c 知识点 vector基本概念及定义 vector函数的构造 vector函数实现 vector基本概念及定义 头文件 include
  • VTK入门001——HelloVTK

    include VTKPlatform h vtkConeSource 是 VTK 库中的一个类 用于生成一个圆锥体的几何形状 include
  • 网站数据分析报告怎么写?

    网站数据分析报告怎么写 1 目标 Objective 是前提 网站分析报告的起点不是从现象开始的 而是从网站的目标 objective 开始的 我相信大多数网站的目标不应该超过1个 那些各种目标都应该归结为一个最终的最根本的目标 如果你的老

随机推荐

  • CTF show 萌新web-1

    首先看题目 代码整体逻辑是先通过GET请求传递参数id 如果id大于999 报错 如果小于999 则输出当前的执行的sql语句 如果根据id参数能在数据库中查到值 row 则将其打印出来 而flag则是id为1000的 row中的内容 这道
  • excel表格(.csv)保存到数据库--QT数据库

    刚学QT的菜鸟 无聊的时候总会找一点事情做 言归正传 将excel表格保存到数据库 是比较简单的 我的想法简单粗暴 从路径中获取文件 通过按键控制加载 将大象放进冰箱需要是三步 这个同样 1 获取路径 2 读取Excel文件 3 将其保存到
  • Domain-Specific Languages 23

    Domain Specific Languages Whenever you listen to a discussion by experts in any domain be it chess players kindergarten
  • 红日靶场(五)

    红日5 一 环境搭建 此次靶场虚拟机共用两个 一个外网一个内网 用来练习红队相关内容和方向 主要包括常规信息收集 Web攻防 代码审计 漏洞利用 内网渗透以及域渗透等相关内容学习 1 环境拓扑图 2 靶机下载地址 漏洞详情 3 我们设置VM
  • elasticsearch 集群配置

    主节点配置 集群名称 cluster name demo 节点名称 每个节点的名称不能重复 node name es node 1 ip 地址 每个节点的地址不能重复 network host 172 16 10 1 是不是有资格主节点 n
  • Python-schema的使用

    不管我们做什么应用 只要和用户输入打交道 就有一个原则 永远不要相信用户的输入数据 意味着我们要对用户输入进行严格的验证 web开发时一般输入数据都以JSON形式发送到后端API API要对输入数据做验证 一般我都是加很多判断 各种if 导
  • Python生成随机数,并将生成的随机数组成10道加减乘除的基本算术题目

    编写一个小学生算术能力测试题 提供10道加减乘除四种基本算术运算的题目 联系者根据显示的题目输入自己的答案 程序自动判断输入的答案是否正确并显示出相应的信息 生成一到一百的随机数 import random x random randint
  • SRM系统是什么?

    SRM全称Supplier Relationship Management 即供应商关系管理 SRM管理系统即供应商关系管理系统 供应商管理系统是采购管理系统的一个子系统 也是采购管理系统的一个重要模块 是用于改进企业与供应商关系的联系 完
  • Mysql主键约束和唯一约束

    Mysql约束 1 作用 约束定义为确保数据完整性必须遵循的规则 约束可以在创建表的过程中创建 也可以稍后再添加 在创建表后添加约束时 它将检查现有数据以确定其是否违背该约束 如果现有数据违背了将添加的约束 那么将不会向指定列施加该约束 2
  • 找不到文件、主类名和文件名不一致、缺少分号的解决方法

    1 找不到文件 解决方法 源文件名不存在或写错 或者当前路径错误 2 主类名和文件名不一致 解决方法 声明为public的主类应与文件名一致 否则编译失败 3 缺少分号 解决方法 编译失败 注意错误出现的行数 再到源代码中指定位置改错
  • shell实例流程控制&函数

    条件 if then elif then fi if的条件部分经常使用test EXPRESSION或 EXPRESSION 实现 test的用法可以参见test if 条件1 if 条件1 then then 执行语句1 elif 条件2
  • MetaMask安装使用指南

    前言 MetaMask是一个以太坊钱包插件 虽然只能在Chrome浏览器中使用 但作为以太坊钱包的metamask却很受以太坊开发者欢迎 MetaMask除了是一个简单的钱包 它主要卖点是让使用者可以很容易跟以太坊的智能合约互动 或者说说M
  • DLUT C++上机作业(实验六)

    注意 博客所有代码在VS上均能编译通过 codeblocks等编译器可能因为某些变量名无法识别而无法编译 我的VS上不能用end做变量名就很迷呀 2 有一个交通工具类vehicle 将它作为基类派生小车类car 卡车类truck和轮船类bo
  • Java面试必备,JVM核心知识点总结!

    JVM基础 程序计数器 Program Counter Register CPU中的寄存器 作用 记住下一条JVM指令 特点 线程私有 唯一一个不会出现内存溢出的区域 虚拟机栈 Java virtual mechine Stack 线程私有
  • 数据库查询: 列出表的所有字段,“*”符号,查询指定字段数据,DISTINCT查询,IN查询,BETWEEN AND查询,LIKE模糊查询,对查询结果排序,分组查询,统计分组查询

    数据库查询 列出表的所有字段 符号 查询指定字段数据 DISTINCT查询 IN查询 BETWEEN AND查询 LIKE模糊查询 对查询结果排序 分组查询 统计分组查询 列出表的所有字段 通过SQL语句SELECT列出表的所有字段 具体语
  • 软考-嵌入式系统设计师-笔记:嵌入式系统软件基础知识

    文章目录 嵌入式软件基础知识 嵌入式操作系统基础知识 任务调度 信号量 页面置换算法 嵌入式系统程序设计 嵌入式软件基础知识 嵌入式软件分类 系统软件 控制和管理嵌入式系统资源 为嵌入式应用提供支持的各种软件 如设备驱动程序 嵌入式操作系统
  • build中配置resource配置,来防止资源导出失败

  • 我最喜爱的十大技术文档写作工具

    转载 老实说 我爱死微软的Word了 Adobe FrameMaker也曾辉煌过 不过你懂的 这东西有时候会令人抓狂 过去5年来 我一直使用同一套写作工具 我也曾尝试过一些新的工具 可我最终还是很专情于我的老相好们 在这里我总结了一下我所用
  • MATLAB中GUI界面内数据的读取和存储操作

    要求GUI界面的输入数据为int16中频数据文件 输出数据也为int16中频数据文件 第一步 获取数据函数 uigetfile 先自己存储数据用于验证 将仿真数据以int16的格式存于txt文件中 分I O两路 I路代表实部 O路代表虚部
  • STM32f10x学习----ADC和DMA功能 后附具体操作及使用过程中遇到的问题

    学习某一个东西 我们首先要了解这个东西的定义是什么 用来干什么的 怎么用 用的过程中有什么注意事项 这些都OK了 那么我们就算是基本掌握他了 0 前言 ADC Analog to Digital Converter的缩写 指模 数转换器或者