在这节内容开始前,先测试一下上节内容中红茶加巧克力提出的问题。
问题总结一下有两点:
1、影响DMA数据传输顺序的因素?
2、假如定义存储采集结果的数组为通道数的N倍,且在DMA中设置存储器地址递增,DMA是否会传输N组数据存储到数组中,并且顺序每组数据也为通道转换顺序?
这里我做了测试,发现在独立模式下除了变更通道顺序会影响数据传输数据外,无其他因素影响DMA传输顺序,网上查了一下,看到有人提到转换周期也会影响阐述顺序,但是实测了一下,将光敏的转换周期更改为3个周期,电位器和悬空保持为3个周期,并未发现出现顺序错乱的情况。
第二个问题同样也是创建了个通道N倍的数组,用来接收转换数据,发现不管采集时间多久,并没有存储多组数据,只有通道数个单元被填充,那么想存储多组数据就只能采集到一组数据就保存一组数据,直到达到需要的数据量位置。
以上两点就是关于红茶加巧克力提出的问题的测试,如果哪位大佬发现还有其他情况的,不妨也提出来大家一起探讨。
OK,继续新的内容。
-----------------------------------------------------------这是分割线----------------------------------------------------------------
ADC转换分为两个阶段,采样和转换,其中采样阶段是对通道模拟数据进行采集,转换阶段将采集到的数据转换为数字量输出,在采集阶段通道模拟量的变化并不会影响转换结果(即使采集时间极短的,频率极快)。但是独立模式的ADC采集采集下一个通道的数据前,必须先完成上一个通道的采集并转换完成后。因此从速度上来说,独立模式相对来说还是慢一些。
如果需要提高转换速度,可以采用双重或者三重ADC,同时采集多个不同通道的数据,或者可以使用多个ADC交叉采集同一个通道的数据,这样可以提高速度。
这里以三重转换为里,三重交替采集模式顾名思义,就是通过三个ADC交叉对同一通道进行采集,假如我们使用三重模式对电位器进行模拟量采集,当ADC1在采集完成后进行数据转换时,ADC2接替进行采集,ADC2采集完成后惊醒数据转换时,ADC3再接替进行采集,依次向后类推,因此三重模式就是利用通道转换的时间差来进行采集,尽量减少转换时间对采集频率的影响以提高转换速度。
三重转换可以通过如下图进行理解:
前面对于ADC的以及DMA的使用相信大家已经特别熟悉了,这节内容主要时针对如何实现三重模式采集单通道进行讲解,采集通道仍然是采集电位器的模拟量来实现。
总结下这节的编程步骤:
这节的内容是采用三重ADC交叉对一个通道的ADC进行采集,所以跟《独立模式单通道DMA采集》的内容很类似,区别在于的之前的内容只使用到一个ADC,而这节是用三个通道,因此在硬件设计上仍然是对电位器通道进行模拟量采集。
本节程序在之前的程序上进行修改。
先看一下相关宏定义,这里需要注意的是,独立模式下ADC的数据寄存器我们使用的是ADC_DR寄存器,但是在双重或者三重模式下,需要使用ADC_CDR寄存器。
extern __IO uint16_t ADC_Value[3];
#define R_ADC_DMA_CLK RCC_AHB1Periph_DMA2
#define R_ADC_DMA_CHANNEL DMA_Channel_0
#define R_ADC_DMA_STREAM DMA2_Stream0
#define R_ADC_GPIO_PORT GPIOC
#define R_ADC_GPIO_PIN GPIO_Pin_3
#define R_ADC_GPIO_CLK RCC_AHB1Periph_GPIOC
#define R_ADC1 ADC1
#define R_ADC1_CLK RCC_APB2Periph_ADC1
#define R_ADC2 ADC2
#define R_ADC2_CLK RCC_APB2Periph_ADC2
#define R_ADC3 ADC3
#define R_ADC3_CLK RCC_APB2Periph_ADC3
#define R_ADC3_CHANNEL ADC_Channel_13
#define R_ADC_CDR_ADDR ((u32)ADC1+0x300+0x08)
void R_ADC_Init(void);
GPIO仍然使用的是电位器,因此跟之前的配置一样,GPIO初始化如下:
/** @brief 初始化ADC GPIO引脚
* @parm 无
* @retval 无
*/
static void R_ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(R_ADC_GPIO_CLK,ENABLE); //开启ADC外设引脚时钟
GPIO_InitStructure.GPIO_Pin = R_ADC_GPIO_PIN; //配置引脚位3引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //配置引脚为模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //配置为无上下拉
GPIO_Init(R_ADC_GPIO_PORT,&GPIO_InitStructure); //初始化引脚
}
下来就是对ADC及DMA进行相关配置了,代码如下:
/** @brief 配置ADC引脚工作模式及DMA
* @parm 无
* @retval 无
*/
static void R_ADC_Mode_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
//-----------------DMA Init结构体参数初始化-----------------//
RCC_AHB1PeriphClockCmd(R_ADC_DMA_CLK,ENABLE); //开启DMA时钟
DMA_InitStructure.DMA_PeripheralBaseAddr = R_ADC_CDR_ADDR; //设置ADC外设基地址,为ADC数据寄存器地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_Value; //存储器地址,地址为内部SRAM变量
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //配置数据传输方向为外设到存储器
DMA_InitStructure.DMA_BufferSize = 3; //配置缓冲区大小,大小取决于一次传输的量,这里使用到三重模式,则配置为3
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设寄存器只有一个,不用递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //三次采集,需要三个存储单位,因此需要递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //外设数据大小为字(4个字节)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //与外设相同
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环传输
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //传输优先级为高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //采用直连,不适用FIFO
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //FIFO禁止,下面不用配置
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_Channel = R_ADC_DMA_CHANNEL; //选择DMA通道
DMA_Init(R_ADC_DMA_STREAM,&DMA_InitStructure);
DMA_Cmd(R_ADC_DMA_STREAM,ENABLE);
RCC_APB2PeriphClockCmd(R_ADC1_CLK|R_ADC2_CLK|R_ADC3_CLK,ENABLE); //开启ADC时钟
//-----------------ADC Common结构体参数初始化--------------//
ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_Interl; //设置模式为三重ADC交替模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //设置为4分频
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2; //禁止DMA直接访问模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //设置采样间隔周期为10个周期
ADC_CommonInit(&ADC_CommonInitStructure);
//-----------------ADC Init结构体参数初始化--------------//
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC采样分辨率为12位
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //多通道下才会用到扫描模式,这里直接禁止
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //设置为连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁用外部边沿触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置为右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1; //转换通道只有1个
ADC_Init(R_ADC1,&ADC_InitStructure);
ADC_RegularChannelConfig(R_ADC1,R_ADC_CHANNEL,1,ADC_SampleTime_3Cycles); //配置ADC1通道转换顺序为1,第一个转换,才压根时间为56个时钟周期
ADC_Init(R_ADC2,&ADC_InitStructure);
ADC_RegularChannelConfig(R_ADC2,R_ADC_CHANNEL,1,ADC_SampleTime_3Cycles); //配置ADC2通道转换顺序为1,第一个转换,才压根时间为56个时钟周期
ADC_Init(R_ADC3,&ADC_InitStructure);
ADC_RegularChannelConfig(R_ADC3,R_ADC_CHANNEL,1,ADC_SampleTime_3Cycles); //配置ADC3通道转换顺序为1,第一个转换,才压根时间为56个时钟周期
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
ADC_DMACmd(R_ADC1,ENABLE); //使能ADC DMA
ADC_DMACmd(R_ADC2,ENABLE); //使能ADC DMA
ADC_DMACmd(R_ADC3,ENABLE); //使能ADC DMA
ADC_Cmd(R_ADC1,ENABLE); //使能ADC
ADC_Cmd(R_ADC2,ENABLE); //使能ADC
ADC_Cmd(R_ADC3,ENABLE); //使能ADC
ADC_SoftwareStartConv(R_ADC1); //开始ADC转换,由软件触发
ADC_SoftwareStartConv(R_ADC2); //开始ADC转换,由软件触发
ADC_SoftwareStartConv(R_ADC3); //开始ADC转换,由软件触发
}
主函数如下:
#include "stm32f4xx.h"
#include "bsp_usart_dma.h"
#include "bsp_systick.h"
#include "bsp_adc.h"
#include
__IO uint32_t ADC_Value[3] = {0};
float ADC_Vol[3] = {0};
int main(void)
{
DEBUG_USART1_Config();
R_ADC_Init();
SysTick_Init();
printf("\r\n---------------ADC实验(三重ADC交替模式)----------------\r\n");
while(1)
{
Delay_ms(1000);
ADC_Vol[0] =(float)((uint16_t)ADC_Value[0]*3.3/4096); // 读取转换的 ADC1 值
ADC_Vol[1] =(float)((uint16_t)ADC_Value[1]*3.3/4096); // 读取转换的 ADC1 值
ADC_Vol[2] =(float)((uint16_t)ADC_Value[2]*3.3/4096); // 读取转换的 ADC1 值
printf("\r\n ADC1 数据(未转换) = 0x%08X \r\n",ADC_Value[0]);
printf("\r\n ADC2 数据(未转换) = 0x%08X \r\n",ADC_Value[1]);
printf("\r\n ADC3 数据(未转换) = 0x%08X \r\n",ADC_Value[2]);
printf("\r\n ADC1 数据(已转换) = %.2f V \r\n",ADC_Vol[0]);
printf("\r\n ADC2 数据(已转换) = %.2f V \r\n",ADC_Vol[1]);
printf("\r\n ADC3 数据(已转换) = %.2f V \r\n",ADC_Vol[2]);
}
}