Ⅰ、概述
上一篇文章讲述的内容是:三通道逐次转换(单次、单通道软件触发),也就是说3条通道要三次软件触发才能完成转换,而且是通过软件读取转换数值。
本文讲述三通道DMA连续转换(3通道、软件单次触发),也就是说3条通道只需要一次软件触发就能完成转换,使用DMA保存在数值。
上一篇文章实例是使用间断模式单次触发单条通道采集,本文是使用DMA模式单次触发三条通道采集。使用DMA传输的好处就是效率很高,我们直接读取转换的结果就是了,比如想做的示波器实例就是要求效率很高。
实例实验效果:
本文的实验效果和上一篇文章一样,只是实现的方式不一样。
通道1接地、通道2接1.5V电源、通道3接VCC
上一篇文章内容:
【ADC三通道逐次转换(单次、单通道软件触发)】
本文讲述的知识点相对较多,若初次学习STM32的ADC转换功能,可以参考我另外一篇相对简单一点的文章:
STM32F10x_ADC1单通道单次采集
关于本文的更多详情请往下看。
Ⅱ、实例工程下载
笔者针对于初学者提供的例程都是去掉了许多不必要的功能,精简了官方的代码,对初学者一看就明白,以简单明了的工程供大家学习。
笔者提供的实例工程都是在板子上经过多次测试并没有问题才上传至360云盘,欢迎下载测试、参照学习。
提供下载的软件工程是基于Keil(MDK-ARM) V5版本、STM32F103ZE芯片,但F1其他型号也适用(适用F1其他型号: 关注微信,回复“修改型号”,或阅读原文)。
STM32F10x_ADC三通道DMA连续转换(3通道、软件单次触发)实例源代码工程:
https://yunpan.cn/cBCmnZ58mI3Pp访问密码 f2ff
STM32F1资料:
https://yunpan.cn/crBUdUGdYKam2访问密码 ca90
Ⅲ、关于ADC
关于ADC的介绍及功能,请下载参考手册查看,笔者这里讲述几点重要的知识:
1.12位分辨率
在STM32所有系列芯片中只有少部分是16位的,如:F373芯片。
12位分辨率意味着我们采集电压的精度可以达到:Vref /4096。
采集电压 = Vref * ADC_DR / 4096;
Vref:参考电压
ADC_DR:读取到ADC数据寄存器的值
由于寄存器是32位的,在配置的时候分左对齐和右对齐,一般我们使用右对齐,也就是对低12位数据为有效数据。
2.转换模式
A.单次和连续转换
单次:单通道单次转换、多通道单次(分多次)转换;
连续:单通道连续转换、多通道连续(循环)转换;
B.双ADC模式
也就是使用到了两个ADC,比如:ADC1和ADC2同时使用也就是双ADC模式。在该模式下可以配置为如下一些模式:同步规则模式、同步注入模式、独立模式等。
3.触发源
触发源就是触发ADC转换的来源,有外部中断线、定时器、软件等触发源。我们初学者常用软件触发,也就是需要转换一次,我们软件启动一次(本文提供实例也是软件触发)。
Ⅳ、本文实例描述
本文实例中关于ADC部分的配置及知识点,针对初学者相对比较多、理解起来也相对比较难一点。
根据题目“ADC三通道逐次转换(单次、单通道软件触发)”我们不难理解其转换的过程,但如何实现是一个难点。
1、三通道:我们定义了3条通道ADC1的ADC_Channel_1、ADC_Channel_2、ADC_Channel_3.
2.逐次转换:我们使用的是间断模式(规则组),也就是在规则组中定义了触发转换的序列。
3.单次:我们是每触发一次转换一次。
4.单通道:每次触发只转换一条通道。
以简单的示意图来说明其原理:
实例总共有三条通道通道1、通道2、通道3,分别对应顺序,是1、2、3。我们是通过软件来定义的顺序:
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
所以,我们转换保存的顺序:ADC_Buf[0]是通道1的数据、ADC_Buf[1]是通道2的数据、ADC_Buf[2]是通道3的数据
也是按照上面来的。当然,也可以更改顺序。
Ⅴ、源代码分析
笔者以F1标准外设库(同时也建议初学者使用官方的标准外设库)为基础建立的工程,主要以库的方式来讲述(若您的F1芯片与提供工程不一样,可微信回复“修改型号”)。
下面将讲述ADC重要的几点:
1.输入引脚配置
该函数位于adc.c文件下面;
引脚与通道的对应关系请参看你使用芯片的数据手册。
注意:
为什么是“ADC123_IN1”?而不是ADC1_IN1,或者ADC2_IN1?
原因是ADC1、ADC2和ADC3共用这些引脚。
2.DMA配置
该函数位于adc.c文件下面;
1.外设地址:DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
我们使用ADC数据寄存器地址作为DMA的外设地址;
2.内存地址:DMA_MemoryBaseAddr = (uint32_t)ADC_Buf;
这里就是我们定义保存采集值数组的地址;
3.传输方向:DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_DIR_PeripheralSRC:外设 -> 内存
DMA_DIR_PeripheralDST:内存 -> 外设
4.传输长度:DMA_BufferSize = ADC_BUF_SIZE;
ADC_BUF_SIZE是一个宏定义,等于3; 也就是说我们需要转换并保存3组数据(3条通道的值)。
5.外设地址增长:DMA_PeripheralInc = DMA_PeripheralInc_Disable;
由于外设的地址都是ADC数据寄存器,没有改变,所以不需要增长地址;
6.内存地址增长:DMA_MemoryInc = DMA_MemoryInc_Enable;
由于我们定义了一个数字,里面需要保存3个数值,所以需要增长;
【根据传输长度和循环模式,可以循环传输数据】
7.外设数据长度:DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_PeripheralDataSize_Byte:8位数据
DMA_PeripheralDataSize_HalfWord:16位数据
DMA_PeripheralDataSize_Word:32位数据
由于我们使用16位的数据,所以使用DMA_PeripheralDataSize_HalfWord;
8.内存数据长度:DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
和“外设数据长度”类似;
9.循环模式:DMA_Mode = DMA_Mode_Circular;
这里说的循环就是我们循环采集3组数据(更加传输长度来确定);
10.优先级:DMA_Priority = DMA_Priority_VeryHigh;
优先级应该都明白他的意思,我们只使用一组DMA这个优先级可高可低;
11.优先级:DMA_M2M = DMA_M2M_Disable;
内存传输到内存:否
注意:
为什么我们是使用DMA1_Channel1?
我们使用DMA通道是有要求的,需按照手册提供的规则来(如下图);请参看手册:
3.ADC配置
该函数位于adc.c文件下面;
这个函数是本文的重点,下面依次来讲述源代码内容的意思;
A.初始化基本参数:
工作模式:ADC_Mode = ADC_Mode_Independent;
总共有10种,主要都是针对双ADC下使用。针对初学者这里不多描述,感兴趣的朋友可以先自行研究一下各个模式的使用。
浏览模式:ADC_ScanConvMode = ENABLE;
主要是针对多条通道而言,也就是说你是否有多条通道。
多通道:ENABLE;
单通道:DISABLE;
转换模式:ADC_ContinuousConvMode = DISABLE;
这里是配置是否需要连续转换。
连续转换ENABLE:也就是只需要启动(触发)转换一次,后面就不用再次启动(触发)就可以连续工作了。
单次转换DISABLE:也就是根据一次转换完后需要再次启动(触发)才能工作。
触发方式:ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
触发方式也就是使用什么方法触发ADC转换。哟定时器、外部触发、软件触发,一般常用软件触发。这里有很多种触发方式,详情可以参考其参数。
对其方式:ADC_DataAlign = ADC_DataAlign_Right;
右对其:低12位数据为有效位(常用);
左对其:高12为数据为有效位;
通道数:ADC_NbrOfChannel = 3;
这个参数比较简单,我们定义工作的通道数量。
B.设置规则组通道:
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
我们定义通道1的转换顺序为第1、通道2的转换顺序为第2、通道3的转换顺序为第3;
C.校验:
ADC_ResetCalibration(ADC1); //校验复位
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位完成
ADC_StartCalibration(ADC1); //开始ADC1校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校验完成
建议每次上电校正一次。
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。