031 - STM32学习笔记 - ADC(五) 三重ADC交替模式采集

发布时间:2023年12月20日

031 - STM32学习笔记 - ADC(五) 三重ADC交替模式采集

在这里插入图片描述

在这节内容开始前,先测试一下上节内容中红茶加巧克力提出的问题。

在这里插入图片描述

问题总结一下有两点:

1、影响DMA数据传输顺序的因素?

2、假如定义存储采集结果的数组为通道数的N倍,且在DMA中设置存储器地址递增,DMA是否会传输N组数据存储到数组中,并且顺序每组数据也为通道转换顺序?

这里我做了测试,发现在独立模式下除了变更通道顺序会影响数据传输数据外,无其他因素影响DMA传输顺序,网上查了一下,看到有人提到转换周期也会影响阐述顺序,但是实测了一下,将光敏的转换周期更改为3个周期,电位器和悬空保持为3个周期,并未发现出现顺序错乱的情况。

第二个问题同样也是创建了个通道N倍的数组,用来接收转换数据,发现不管采集时间多久,并没有存储多组数据,只有通道数个单元被填充,那么想存储多组数据就只能采集到一组数据就保存一组数据,直到达到需要的数据量位置。

以上两点就是关于红茶加巧克力提出的问题的测试,如果哪位大佬发现还有其他情况的,不妨也提出来大家一起探讨。

OK,继续新的内容。

-----------------------------------------------------------这是分割线----------------------------------------------------------------

ADC转换分为两个阶段,采样和转换,其中采样阶段是对通道模拟数据进行采集,转换阶段将采集到的数据转换为数字量输出,在采集阶段通道模拟量的变化并不会影响转换结果(即使采集时间极短的,频率极快)。但是独立模式的ADC采集采集下一个通道的数据前,必须先完成上一个通道的采集并转换完成后。因此从速度上来说,独立模式相对来说还是慢一些。

如果需要提高转换速度,可以采用双重或者三重ADC,同时采集多个不同通道的数据,或者可以使用多个ADC交叉采集同一个通道的数据,这样可以提高速度。

这里以三重转换为里,三重交替采集模式顾名思义,就是通过三个ADC交叉对同一通道进行采集,假如我们使用三重模式对电位器进行模拟量采集,当ADC1在采集完成后进行数据转换时,ADC2接替进行采集,ADC2采集完成后惊醒数据转换时,ADC3再接替进行采集,依次向后类推,因此三重模式就是利用通道转换的时间差来进行采集,尽量减少转换时间对采集频率的影响以提高转换速度。

三重转换可以通过如下图进行理解:

在这里插入图片描述

前面对于ADC的以及DMA的使用相信大家已经特别熟悉了,这节内容主要时针对如何实现三重模式采集单通道进行讲解,采集通道仍然是采集电位器的模拟量来实现。

总结下这节的编程步骤:

  1. 配置ADC引脚PC3为模拟输入模式,并初始化(记得使能时钟哈!);
  2. 使能ADC1、ADC2、ADC3以及DMA时钟;
  3. 配置DMA将ADC通用规则数据寄存器数据转存到指定存储区;
  4. 配置通用ADC为三重ADC交替模式,采样4分频,采用DMA模式2(ADC只能使用DMA2);
  5. 设置ADC1/2/3分辨率为12位,单通道禁用扫描,连续转换,不需要外部触发;
  6. 设置ADC1/2/3转换通道顺序及采样时间(采样周期位3个周期,顺序为ADC1 -> ADC2 -> ADC3);
  7. 使能ADC1的DMA请求,ADC转换完成后自动请求DMA进行数据传输;
  8. 启动ADC1/2/3转换;
  9. 使能软件触发ADC转换。

这节的内容是采用三重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 <stdio.h>

__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]);   
    }
}


在这里插入图片描述

文章来源:https://blog.csdn.net/guanxiaozhi/article/details/135097663
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。