版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zouleideboke/article/details/75112224

ADC简介:

ADC(Analog-to-Digital Converter,模/ 数转换器)。也就是将模拟信号转换为数字信号进行处理,在存储或传输时,模数转换器几乎必不可少。

STM32在片上集成的ADC外设非常强大,我使用的奋斗开发板是STM32F103VET6,属于增强型的CPU,它有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次,连续,扫描或间断模式执行,ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

ADC工作过程分析:

我们以ADC规则通道转换过程来分析,如上图,所有的器件都是围绕中间的模拟至数字转换器部分展开的。它的左端VREF+,VREF- 等ADC参考电压,ADCx_IN0 ~ ADCx_IN15为ADC的输入信号通道,即某些GPIO引脚。输入信号经过这些通道被送到ADC器件,ADC器件需要收到触发信号才开始进行转换,如EXTI外部触发,定时器触发,也可以使用软件触发。ADC部件接受到触发信号后,在ADCCLK时钟的驱动下对输入通道的信号进行采样,并进行模数转换,其中ADCCLK是来自ADC预分频器。

ADC部件转换后的数值被保存到一个16位的规则通道数据寄存器(或注入通道数据寄存器)中,我们可以通过CPU指令或DMA把它读到内存(变量),模数转换之后,可以出发DMA请求或者触发ADC转换结束事件,如果配置了模拟看门狗,并且采集的电压大于阈值,会触发看门狗中断。

其实对于ADC采样,软件编程主要就是ADC的配置,当然我是基于DMA方式的,所以DMA的配置也是关键!话不多说看代码!

主函数:main.c

  1. #include "printf.h"
  2. #include "adc.h"
  3. #include "stm32f10x.h"
  4.  
  5. extern __IO uint16_t ADC_ConvertedValue;
  6. float ADC_ConvertedValueLocal;
  7. void Delay(__IO uint32_t nCount)
  8. {
  9. for(;nCount !=0;nCount--);
  10. }
  11. int main(void)
  12. {
  13. printf_init();
  14. adc_init();
  15. printf("******This is a ADC test******\n");
  16.  
  17. while(1)
  18. {
  19. ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;
  20. printf("The current AD value =0x%04X\n",ADC_ConvertedValue);
  21. printf("The current AD value =%f V\n",ADC_ConvertedValueLocal);
  22.  
  23. Delay(0xffffee);
  24. }
  25. return 0;
  26. }

注意ADC_ConvertedValueLocal保存了由转换值计算出来的电压值,计算公式是:实际电压值=ADC转换值 x LSB ,这里由于我的板子VREF+接的参考电压为3.3V,所以LSB=3.3/4096,STM32的ADC的精度为12位。

ADC与DMA配置:adc.c

  1. #include "adc.h"
  2. volatile uint16_t ADC_ConvertedValue;
  3. void adc_init()
  4. {
  5. GPIO_InitTypeDef GPIO_InitStructure;
  6. ADC_InitTypeDef ADC_InitStructure;
  7. DMA_InitTypeDef DMA_InitStructure;
  8.  
  9. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);
  11.  
  12. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
  13. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
  14. GPIO_Init(GPIOC,&GPIO_InitStructure);
  15. DMA_DeInit(DMA1_Channel1);
  16. DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//ADC地址
  17. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址
  18. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
  19. DMA_InitStructure.DMA_BufferSize = 1; //传输内容的大小
  20. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
  21. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定
  22. DMA_InitStructure.DMA_PeripheralDataSize =
  23. DMA_PeripheralDataSize_HalfWord ; //外设数据单位
  24. DMA_InitStructure.DMA_MemoryDataSize =
  25. DMA_MemoryDataSize_HalfWord ; //内存数据单位
  26. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循环传输
  27. DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
  28. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
  29.  
  30. DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1的4通道
  31. DMA_Cmd(DMA1_Channel1,ENABLE);
  32.  
  33. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
  34. ADC_InitStructure.ADC_ScanConvMode = DISABLE; //禁止扫描方式
  35. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换模式
  36. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发转换
  37. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
  38. ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
  39. ADC_Init(ADC1, &ADC_InitStructure);
  40.  
  41. RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟,为PCLK2的8分频,即9Hz
  42. ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);//配置ADC1通道11为55.5个采样周期
  43.  
  44. ADC_DMACmd(ADC1,ENABLE);
  45. ADC_Cmd(ADC1,ENABLE);
  46.  
  47. ADC_ResetCalibration(ADC1);//复位校准寄存器
  48. while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成
  49.  
  50. ADC_StartCalibration(ADC1);//ADC校准
  51. while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
  52.  
  53. ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于没有采用外部触发,所以使用软件触发ADC转换
  54. }

ADC配置还是比较简单的,毕竟只配置了单通道,还是分析一下吧!这里我是把ADC1的通道11使用的GPIO引脚PC1配置成模拟输入模式,在作为ADC的输入时,必须使用模拟输入。对于ADC通道,每个ADC通道对应一个GPIO引脚端口,GPIO的引脚在设为模拟输入模式后可用于模拟电压的输入。STM32F103VET6有三个ADC,这三个ADC公用16个外部通道。
DMA的整体配置为:使用DMA1的通道1,数据从ADC外设的数据寄存器(ADC1_DR_Address)转移到内存(ADC_ConvertedValue变量),内存外设地址都固定,每次传输的大小为半字(16位),使用DMA循环传输模式。

DMA传输的外设地址,也就是ADC1的地址为0x40012400+0x4c,这个地址可查STM32 datasheet获得,如图;

要特别注意ADC转换时间配置,由于ADC时钟频率越高,转换速度越快,那是不是就把ADC的时钟频率设的越大越好呢?其实不然,根据ADC时钟图可知,ADC时钟有上限值,即不能超过14MHz,如图:

这里ADC预分频器的输入为高速外设时钟(PCLK2),使用RCC_ADCCLKConfig()库函数来设置ADC预分频的分频值,PCLK2常用时钟为72MHz,而ADCCLK必须小于14MHz,所以这里ADCCLK为PCLK2的6分频,即12MHz,而我的程序中只是随便设为8分频,9MHz,若希望ADC以最高频率14MHz运行,可以把PCLK2设置为56MHz,然后再4分频得到ACCLK。

ADC的转换时间不仅与ADC的时钟有关,还与采样周期有关。每个ADC通道可以设置为不同的采样周期。STM32的ADC采样时间计算公式为:

T=采样周期+12.5个周期

公式中的采样周期就是函数中配置的 ADC_SampleTime,而后边加上的12.5个周期为固定值,则ADC1通道11的转换时间为T=(55.5+12.5) x 1/9=7.56us。

补充:在adc.c文件中定义了ADC_ConvertedValue变量,要注意这个变量是由关键字volatile修饰的,volatile的作用是让编译器不要去优化这个变量,这样每次用到这个变量时都要回到相应变量的内存中去取值,而如果不使用volatile进行修饰的话,ADC_ConvertedValue变量在被访问的时候可能会直接从CPU的寄存器中取出(因为之前该变量被访问过,也就是说之前就从内存中取出ADC_ConvertedValue的值保存到某个CPU寄存器中),之所以直接从寄存器中去取值而不去内存中取值,这是编译器优化代码的结果(访问CPU寄存器比访问内存快得多)。这里的CPU寄存器指R0,R1等CPU通用寄存器,用于CPU运算及暂存数据,不是指外设中的寄存器。

因为ADC_ConvertedValue这个变量值随时都会被DMA控制器改变的,所以用volatile来修饰它,确保每次读取到的都是实时ADC转换值。

adc.h:

  1. #ifndef _adc_H
  2. #define _adc_H
  3. #include "stm32f10x.h"
  4. #include "stm32f10x_dma.h"
  5. #include "stm32f10x_adc.h"
  6. #define ADC1_DR_Address ((uint32_t)0x4001244c);
  7.  
  8. void adc_init(void);
  9.  
  10. #endif

效果图:

由于我的开发板没有滑动变阻器,所以我就将电压的输入端接入通用IO口的3V引脚。如图:

STM32之ADC实例(基于DMA方式)的更多相关文章

  1. GD32F330 | ADC实例 基于DMA方式

    GD32F330 | ADC实例 基于DMA方式 简单记录一下 ADC多通道转换 DMA搬运 的使用,以 GD32F330G8U6 为例: 一.ADC 基础知识 12位ADC是一种采用逐次逼近方式的模 ...

  2. 【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第46章       STM32H7的ADC应用之DMA方式多 ...

  3. 案例 stm32单片机,adc的双通道+dma 内部温度

    可以这样理解 先配置adc :有几个通道就配置几个通道. 然后配置dma,dma是针对adc的,而不是针对通道的. 一开始我以为一个adc通道对应一个dma通道.(这里是错的,其实是我想复杂了) 一个 ...

  4. stm32之ADC应用实例(单通道、多通道、基于DMA)

    文本仅做记录.. 硬件:STM32F103VCT6 开发工具:Keil uVision4 下载调试工具:ARM仿真器 网上资料很多,这里做一个详细的整合.(也不是很详细,但很通俗).  所用的芯片内嵌 ...

  5. stm32之ADC应用实例(单通道、多通道、基于DMA)-转载精华帖,最后一部分的代码是精华

    硬件:STM32F103VCT6    开发工具:Keil uVision4    下载调试工具:ARM仿真器网上资料很多,这里做一个详细的整合.(也不是很详细,但很通俗).所用的芯片内嵌3个12位的 ...

  6. STM32 串口DMA方式接收(转)

    STM32 是一款基于ARM Cortex-M3内核的32位MCU,主频最高可达72M.最近因为要在车机上集成TPMS功能, 便开始着手STM32的开发工作,STM32F10x系列共有5个串口(USA ...

  7. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  8. 关于Stm32定时器+ADC+DMA进行AD采样的实现

    Stm32的ADC有DMA功能这都毋庸置疑,也是我们用的最多的!然而,如果我们要对一个信号(比如脉搏信号)进行定时采样(也就是隔一段时间,比如说2ms),有三种方法: 1.使用定时器中断每隔一定时间进 ...

  9. STM32应用实例十五:STM32的ADC通道间干扰的问题

    最近我们在开发一个项目时,用到了MCU自带的ADC,在调试过程中发现通道之间村在相互干扰的问题.以前其实也用过好几次,但要求都不高所以没有太关注,此次因为物理量的量程较大,所以看到了变化. 首先来说明 ...

随机推荐

  1. 微信小程序侧边栏滑动特效(左右滑动)

    侧边栏滑动是很常见的功能,但是小程序出来不久,很多特效还没有成熟案例,只能原生重写,所以今天为大家带来4个漂亮的侧边栏特效~~ 侧边栏特效一 先看效果: wxml: <!--page/one/i ...

  2. postman测试API

    首先创建环境变量 再次在请求参数中,可以应用环境变量,只需要在地址中引用环境变量即可 将返回的参数设置到环境变量中 如已经设置好环境变量,在认证中,选择Bearer Token,然后设置Token为环 ...

  3. 数据库连接池配置(案例及排查指南) 原创: 有赞技术 有赞coder 4天前

    数据库连接池配置(案例及排查指南) 原创: 有赞技术 有赞coder 4天前

  4. VisualStudio版本号

    VisualStudio的工程文件,后面的数字对应的VS的版本号, 71表示的VS2003, 80表示VS2005, 90表示VS2008, 10表示VS2010等.

  5. hue集成mysql找不到 libmysqlclient.so.16问题解决

    首先我的配置文件如下,这个是没有问题的 但是在重启hue连接mysql时,却发生了如下问题: 这个错误的意思就是没有找到libmysqlclient_r.so.16这个文件,可能是我安装的mysql有 ...

  6. Qt编写自定义控件58-直方对称图

    一.前言 本控件也非原创控件,是参考网上的代码而来的,对称顾名思义就是将画布平均成上下两部分,将设置的值自动按照画布高度的一半作为参照高度进行绘制,然后增加动态过渡效果,有点类似于声音播放时候的频谱效 ...

  7. PAT 甲级 1036 Boys vs Girls (25 分)(简单题)

    1036 Boys vs Girls (25 分)   This time you are asked to tell the difference between the lowest grade ...

  8. (三)Centos之安装Xshell

    Xshell就是一个远程控制Centos的软件:(用XShell比较方便,试用的都知道,界面也人性化) 详细介绍请看 百度百科 我随便百度下载了一个中文版的 给下地址  http://pan.baid ...

  9. javascript——URI的编解码方法

    有效的URI(统一资源标示符)是不能包含某些字符的,如空格,所以需要进行编码,编码方法有:encodeURI()和encodeURIComponent(), 对编的码进行解码方法有:decodeURI ...

  10. Python3之类和实例

    面向对象的重要概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如学生类Student,而实例是根据类创建出来的一个个具体的对象,每个对象都拥有相同的方法,单各自的数据可能 ...