STM32之ADC实例(基于DMA方式)
出处:https://blog.csdn.net/zouleideboke/article/details/75112224?locationNum=9&fps=1
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
- #include "printf.h"
- #include "adc.h"
- #include "stm32f10x.h"
- extern __IO uint16_t ADC_ConvertedValue;
- float ADC_ConvertedValueLocal;
- void Delay(__IO uint32_t nCount)
- {
- for(;nCount !=0;nCount--);
- }
- int main(void)
- {
- printf_init();
- adc_init();
- printf("******This is a ADC test******\n");
- while(1)
- {
- ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;
- printf("The current AD value =0x%04X\n",ADC_ConvertedValue);
- printf("The current AD value =%f V\n",ADC_ConvertedValueLocal);
- Delay(0xffffee);
- }
- return 0;
- }
注意ADC_ConvertedValueLocal保存了由转换值计算出来的电压值,计算公式是:实际电压值=ADC转换值 x LSB ,这里由于我的板子VREF+接的参考电压为3.3V,所以LSB=3.3/4096,STM32的ADC的精度为12位。
ADC与DMA配置:adc.c
- #include "adc.h"
- volatile uint16_t ADC_ConvertedValue;
- void adc_init()
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- ADC_InitTypeDef ADC_InitStructure;
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
- GPIO_Init(GPIOC,&GPIO_InitStructure);
- DMA_DeInit(DMA1_Channel1);
- DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//ADC地址
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
- DMA_InitStructure.DMA_BufferSize = 1; //传输内容的大小
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定
- DMA_InitStructure.DMA_PeripheralDataSize =
- DMA_PeripheralDataSize_HalfWord ; //外设数据单位
- DMA_InitStructure.DMA_MemoryDataSize =
- DMA_MemoryDataSize_HalfWord ; //内存数据单位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循环传输
- DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
- DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1的4通道
- DMA_Cmd(DMA1_Channel1,ENABLE);
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //禁止扫描方式
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换模式
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发转换
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
- ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
- ADC_Init(ADC1, &ADC_InitStructure);
- RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟,为PCLK2的8分频,即9Hz
- ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);//配置ADC1通道11为55.5个采样周期
- ADC_DMACmd(ADC1,ENABLE);
- ADC_Cmd(ADC1,ENABLE);
- ADC_ResetCalibration(ADC1);//复位校准寄存器
- while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成
- ADC_StartCalibration(ADC1);//ADC校准
- while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于没有采用外部触发,所以使用软件触发ADC转换
- }
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:
- #ifndef _adc_H
- #define _adc_H
- #include "stm32f10x.h"
- #include "stm32f10x_dma.h"
- #include "stm32f10x_adc.h"
- #define ADC1_DR_Address ((uint32_t)0x4001244c);
- void adc_init(void);
- #endif
效果图:
由于我的开发板没有滑动变阻器,所以我就将电压的输入端接入通用IO口的3V引脚。如图:
STM32之ADC实例(基于DMA方式)相关推荐
- STM32CubeIDE STM32H743实现软件触发ADC实现多通道DMA方式、过采样方式采集数据
在STM32CubeIDE 1.4.0环境下测试通过. 1. ADC设置 设置CPU频率:400MHz ADC输入时钟自动设置为:150MHz 2. DMA设置 3. NVIC设置 4. 代码 //d ...
- STM32单片机开发实例 基于STM32单片机的智能行李箱
一.系统设计 本次设计的是基于STM32F103C8T6单片机设计的一款智能行李箱,系统内使用HX711压力传感器模块采集行李箱内的重量数据,使用ULN2003电机驱动模块来驱动两个步进电机 以此来模 ...
- STM32单片机开发实例 基于STM32单片机的温室大棚监测系统
一.系统设计 通过STM32单片机进行主控,通过光敏电阻模块进行大棚内的光照强度的采集,通过DHT11数字温湿度传感器进行温湿度的采集,通过气压模块进行大棚气压强度的采集,通过水滴模块进行土壤湿度的采 ...
- STM32单片机开发实例 基于STM32单片机的智能血压计
一.系统设计 通过单片机32作为主控制器,完成对人体血压的测量,主要方式是通过气压模块来模拟完成对血压的采集,同时将采集到的血压数据在显示屏上进显示.同时可以通过按键对整个血压上下限值进行设定,一旦超 ...
- stm32之ADC应用实例(单通道、多通道、基于DMA)
硬件:STM32F103VCT6 开发工具:Keil uVision4 下载调试工具:ARM仿真器 网上资料很多,这里做一个详细的整合.(也不是很详细,但很通俗). 所用的芯片内嵌3个12位的模拟/数 ...
- STM32——多通道ADC的DMA方式采集方法
前言: 最近在调试STM32F205芯片ADC多通道DMA方式采集数据,总结下STM32多通道ADC的DMA方式采集的使用方法. 硬件平台:STM32F205 软件平台:keil v5 函数库:标准库 ...
- 基于STM32的ADC电压采集(DMA传输)
我最近在写stm32的adc电压采集过程中遇到了一些问题,这些问题或许对stm32初学者入手adc有所帮助. 对adc采集到的数据进行均值滤波处理,新建一个数组ADC_ConvertedValue_1 ...
- 【STM32】定时器TIM触发ADC采样,DMA搬运到内存(超详细讲解)
TIM+ADC+DMA原理 一般情况下,当我们需要进行采样的时候,需要用到ADC.例如:需要对某个信号进行定时采样(也就是隔一段时间,比如说2ms). 本文提供的解决方案是:使用ADC的定时器触发AD ...
- 基于uFUN开发板的心率计(一)DMA方式获取传感器数据
前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...
最新文章
- powerdesigner 同步mysql 报错_PowerDesigner技巧小结
- 梯度下降法及其Python实现
- Leetcode-121. 买卖股票的最佳时机
- 对Javascript 类、原型链、继承的理解
- Go gin运行原理
- 使用application log 分析navigation target解析错误
- 堆叠自编码器中的微调解释_25种深刻漫画中的编码解释
- pict 手册_zbrush中文手册.pdf
- 自动化测试--testNG
- 安装you-get和ffmpeg for Mac
- 计算机多媒体最新参考文献,计算机多媒体论文
- linux指纹识别,基于Linux的指纹识别系统的设计
- GAMES101-现代计算机图形学入门-闫令琪 - lecture7 着色(Shading) - 课后笔记
- 测试三极管的口诀[转]
- netopeer-manager手册翻译
- TikTok在国内运营一个月,20万粉丝蛮简单的,绝对避免抖音国际版零播放问题
- VC Socket编程源码
- 号称下一代监控系统,到底有多牛逼!
- 整理全网最全大屏,可视化大屏,可视化方案,可视化参考,报表,大屏设计,大屏资源,大屏学习,高保真大屏
- python 计算结果 nan_python中的nan是什么意思