飞行的UPS 发表于 2017-7-26 14:36:18

STM32评测之最强3ADC交替采样+DMA(5.4MSa/s)

一通道3ADC交替采样+DMA传输数据速度高达 5.4MSa/s
NUCLEO-F767ZI自带3个ADC ,在系统时钟216MHZ 的情况下 ADC时钟 4分频 的到27MHZ的时钟ADCCLK=PCLK2/4=108/4=27MHZ
交替模式下 2个ADC 转换之间的延迟是5个时钟周期
总体的转化周期是15/3=5个周期
ADC 时钟是27MHZ
所以采样率是27/5=5.4MSa/s

F767手册上介绍

F767是带有3个12位的ADC,它有高达19多路复用的通道,使它可以测量来自16个外部源的信号,两个内部来源。
下面介绍一下一通道的3ADC交替采样的原理

在多重 ADC 模式下,通过 ADC1 主器件到 ADC2 和 ADC3 从器件的交替触发或同时触发来
启动转换,具体取决于 ADC_CCR 寄存器中的 MULTI 位所选的模式。
注: 在多重 ADC模式下,配置外部事件触发转换时,应用必须设置为仅主器件触发而禁止从器件
触发,以防止出现意外触发而启动不需要的从转换。
可实现以下四种模式:
注入同步模式
常规同步模式
交替模式
交替触发模式
也可按以下方式组合使用上述模式:
注入同步模式 + 常规同步模式
常规同步模式 + 交替触发模式

三重 ADC模式
出现外部触发之后:
ADC1 立即启动
经过几个 ADC 时钟周期延迟后 ADC2 启动
在 ADC2 转换经过几个 ADC 时钟周期的延迟后 ADC3 启动
交替模式下 2 个转换之间的最小延迟通过 ADC_CCR 寄存器中的 DELAY 位进行配置。但是,
如果某个 ADC 的互补 ADC 仍在对其输入进行采样,则该 ADC 无法启动转换(在给定时间
内,只有一个 ADC 能够对输入信号采样)。在这种情况下,延迟时间为采样时间 + 2 个 ADC
时钟周期。例如,如果这三个 ADC 的 DELAY = 5 个时钟周期,且采样时间为 15 个时钟周
期,则 ADC1、ADC2 和 ADC3 之间的转换延迟为 17 个时钟周期。
如果 ADC1、ADC2 和 ADC3 上的 CONT 位均置 1,则这些 ADC 所选常规通道会连续进行
转换。
注: 如果转换序列中断(例如 DMA传输结束时),则必须首先通过在独立模式下进行配置来将多
重 ADC定序器复位(位 DUAL = 00000 ),然后才可以对交替模式进行编程。
在此模式下,每当出现 2 个可用数据项时,就会生成一个 DMA 传输请求(如果 ADC_CCR
寄存器中的 DMA 位等于 0b10)。此请求首先会将存储在 ADC_CDR 32 位寄存器低位
半字中的第一批转换数据传输到 SRAM,然后将存储在 ADC_CDR 高位半字中的第二批转换
数据传输到 SRAM。具体顺序如下:
第 1 个请求:ADC_CDR = ADC2_DR | ADC1_DR
第 2 个请求:ADC_CDR = ADC1_DR | ADC3_DR
第 3 个请求:ADC_CDR = ADC3_DR | ADC2_DR
第 4 个请求:ADC_CDR = ADC2_DR | ADC1_DR

简要:
AD 转换包括采样阶段和转换阶段,在采样阶段才对通道数据进行采集;而在转换阶
段只是将采集到的数据进行转换为数字量输出,此刻通道数据变化不会改变转换结果。独
立模式的 ADC采集需要在一个通道采集并且转换完成后才会进行下一个通道的采集。双
重或者三重 ADC的机制使用两个或以上 ADC同时采样两个或以上不同通道的数据或者使
用两个或以上 ADC交叉采集同一通道的数据。双重或者三重 ADC模式较独立模式一个最
大的优势就是转换速度快。
       介绍三重 ADC交替模式,三重 ADC交替模式是针对同一通道的使用三个 ADC
交叉采集,就是在 ADC1 采样完等几个时钟周期后 ADC2 开始采样,此时 ADC1处在转换
阶段,当 ADC2 采样完成再等几个时钟周期后 ADC3就进行采样此时 ADC1 和 ADC2 处在
转换阶段,如果 ADC3 采样完成并且 ADC1已经转换完成那么就可以准备下一轮的循环,
这样充分利用转换阶段时间达到增快采样速度的效果。AD 转换过程见图 30-6,利用 ADC
的转换阶段时间另外一个 ADC 进行采样,而不用像独立模式必须等待采样和转换结束后
才进行下一次采样及转换。
-----------------------------------多重 ADC 模式下的 DMA 请求(我选用DMA模式2)-------------------------------
DMA模式 2:每发送一个 DMA 请求(两个数据项可用),就会以字的形式传输表
示两个 ADC 转换数据项的两个半字。
在双重 ADC 模式下,发出第一个请求时会传输 ADC2 和 ADC1 的数据(ADC2 数
据占用高位半字,ADC1 数据占用低位半字),依此类推。
在三重 ADC 模式下,将生成三个 DMA 请求:发出第一个请求时,会传输 ADC2 和
ADC1 的数据(ADC2 数据占用高位半字,ADC1 数据占用低位半字)。发出第二
个请求时,会传输 ADC1 和 ADC3 的数据(ADC1 数据占用高位半字,ADC3 数
据占用地位半字)。发出第三个请求时,会传输 ADC3 和 ADC2 的数据(ADC3
占用高位半字,ADC2 数据占用地位半字),依此类推。
DMA 模式 2 用于交替模式和常规同步模式(仅适用于双重 ADC 模式)。
示例:
a)双重交替模式:每当有 2 个数据项可用时,就会生成一个 DMA 请求:
第 1 个请求:ADC_CDR = ADC2_DR | ADC1_DR
第 2 个请求:ADC_CDR = ADC2_DR | ADC1_DR
b)三重交替模式:每当有 2 个数据项可用时,就会生成一个 DMA 请求
第 1 个请求:ADC_CDR = ADC2_DR | ADC1_DR
第 2 个请求:ADC_CDR = ADC1_DR | ADC3_DR
第 3 个请求:ADC_CDR = ADC3_DR | ADC2_DR
第 4 个请求:ADC_CDR = ADC2_DR | ADC1_DR

在F767的datasheetz中介绍 GPIO PC2 是ADC1 ADC2 ADC3的第12通道
这里我选用GPIO PC2口

DMA2数据流0 通道 0



下面开始配置了
代码:
ADC配置:#include "sys.h"
#include "adc.h"

      ADC_HandleTypeDef ADC1_Handler ;
      ADC_HandleTypeDef ADC2_Handler ;
      ADC_HandleTypeDef ADC3_Handler ;

extern uint32_t ADC123_Buff;

int i=0;
//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
   GPIO_InitTypeDef   GPIO_InitStruct;
   DMA_HandleTypeDefhdma_adc;
__HAL_RCC_GPIOC_CLK_ENABLE();//开启GPIOC的时钟
__HAL_RCC_ADC1_CLK_ENABLE();//开启ADC 1 2 3 的时钟
__HAL_RCC_ADC2_CLK_ENABLE();
__HAL_RCC_ADC3_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE(); //开启DMA的时钟

//设置GPIOC2 模拟输入
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;//不上拉也不下拉
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
      

   //配置DMA
hdma_adc.Instance = DMA2_Stream0;//   数据流 0 通道 0
hdma_adc.Init.Channel = DMA_CHANNEL_0;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到寄存器
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;//外设地址非增量
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;//内存地址递增
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;// 32位
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //32位
hdma_adc.Init.Mode = DMA_CIRCULAR; //循环模式
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH; //优先级高
hdma_adc.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //关闭FIFO
hdma_adc.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_adc.Init.MemBurst = DMA_MBURST_SINGLE; //单次突发
hdma_adc.Init.PeriphBurst = DMA_PBURST_SINGLE; //单次突发

HAL_DMA_Init(&hdma_adc);
      
__HAL_LINKDMA(hadc, DMA_Handle, hdma_adc);

/* NVIC配置DMA传输完成中断 */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);      
}




void ADC123_Config(void)
{
ADC_ChannelConfTypeDef sConfig; //ADC 通道的
ADC_MultiModeTypeDef   mode; //ADC 模式的选择

/*##-1- Configure the ADC2 peripheral ######################################*/
ADC3_Handler.Instance          = ADC3;
ADC3_Handler.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;// 108/4 = 27 MHZ
ADC3_Handler.Init.ScanConvMode = DISABLE; //关闭扫描模式
ADC3_Handler.Init.ContinuousConvMode = ENABLE; //使能连续转换模式
ADC3_Handler.Init.DiscontinuousConvMode = DISABLE;
ADC3_Handler.Init.NbrOfDiscConversion = 0; //第0个转换 通道0 的
ADC3_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //用软件触发
ADC3_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START ; //软件触发
ADC3_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //数据右对齐
ADC3_Handler.Init.NbrOfConversion = 1; //连续采样 通道数为 1
ADC3_Handler.Init.DMAContinuousRequests = DISABLE;
ADC3_Handler.Init.EOCSelection = DISABLE;

if (HAL_ADC_Init(&ADC3_Handler) != HAL_OK)
{
    /* Initialization Error */
    Error_Handler();
}

/*##-2- Configure ADC3 regular channel #####################################*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfig.Offset = 0;

if(HAL_ADC_ConfigChannel(&ADC3_Handler, &sConfig) != HAL_OK)
{
    /* Channel Configuration Error */
    Error_Handler();
}

/*##-3- Configure the ADC2 peripheral ######################################*/
ADC2_Handler.Instance          = ADC2;

ADC2_Handler.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
ADC2_Handler.Init.ScanConvMode = DISABLE;
ADC2_Handler.Init.ContinuousConvMode = ENABLE;
ADC2_Handler.Init.DiscontinuousConvMode = DISABLE;
ADC2_Handler.Init.NbrOfDiscConversion = 0;
ADC2_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
ADC2_Handler.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
ADC2_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;
ADC2_Handler.Init.NbrOfConversion = 1;
ADC2_Handler.Init.DMAContinuousRequests = DISABLE;
ADC2_Handler.Init.EOCSelection = DISABLE;

if (HAL_ADC_Init(&ADC2_Handler) != HAL_OK)
{
    /* Initialization Error */
    Error_Handler();
}

/*##-4- Configure ADC2 regular channel #####################################*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfig.Offset = 0;

if (HAL_ADC_ConfigChannel(&ADC2_Handler, &sConfig) != HAL_OK)
{
    /* Channel Configuration Error */
    Error_Handler();
}

/*##-5- Configure the ADC1 peripheral ######################################*/
ADC1_Handler.Instance          = ADC1;

ADC1_Handler.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
ADC1_Handler.Init.ScanConvMode = DISABLE;
ADC1_Handler.Init.ContinuousConvMode = ENABLE;
ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;
ADC1_Handler.Init.NbrOfDiscConversion = 0;
ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
ADC1_Handler.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;
ADC1_Handler.Init.NbrOfConversion = 1;
ADC1_Handler.Init.DMAContinuousRequests = ENABLE;
ADC1_Handler.Init.EOCSelection = DISABLE;


if (HAL_ADC_Init(&ADC1_Handler) != HAL_OK)
{
    /* Initialization Error */
    Error_Handler();
}

/*##-6- Configure ADC1 regular channel #####################################*/
if (HAL_ADC_ConfigChannel(&ADC1_Handler, &sConfig) != HAL_OK)
{
    /* Channel Configuration Error */
    Error_Handler();
}

/*##-7- Configure Multimode ################################################*/
mode.Mode = ADC_TRIPLEMODE_INTERL; //三重交替模式
mode.DMAAccessMode = ADC_DMAACCESSMODE_2; //DMA模式2
mode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES; //两个ADC采样的间隔是5时钟间隔
if (HAL_ADCEx_MultiModeConfigChannel(&ADC1_Handler, &mode) != HAL_OK)
{
    /* Multimode Configuration Error */
    Error_Handler();
}      
}

void DMA2_Stream0_IRQHandler (void)
{
if(DMA2->LISR & 0x00000010) //完成中断
{
   DMA2->LIFCR |= 0x00000010;
         for(i=0;i<1024;i++)
                {
                        printf("ADC:%f V   ",(float)(uint16_t)ADC123_Buff*(3.3/4096));
                }
}      
} main:
#include "sys.h"
#include "led.h"
#include "uart.h"
#include "adc.h"
extern UART_HandleTypeDef UART3_Handler;
extern ADC_HandleTypeDef ADC1_Handler ;
extern ADC_HandleTypeDef ADC2_Handler ;
extern ADC_HandleTypeDef ADC3_Handler ;
uint32_t ADC123_Buff;
int main(void)
{
      double data;
int len,i=0;
MPU_Config();
CPU_CACHE_Enable();
HAL_Init();
SystemClock_Config();
      Led_Init();
uart_init(9600);
      ADC123_Config();
HAL_ADC_Start(&ADC3_Handler);
      HAL_ADC_Start(&ADC2_Handler);
      HAL_ADCEx_MultiModeStart_DMA(&ADC1_Handler, (uint32_t *)ADC123_Buff, 1024);
while (1)
{      
}
}
下面是串口输出的:

一通道3ADC交替采样+DMA传输数据 速度高达 5.4MSa/s

wolfgang2015 发表于 2017-7-26 15:58:11

谢谢分享~~~~

队长shiwo 发表于 2017-7-26 18:04:30

感谢分享正在学习这个多通道ADC

dsjsjf 发表于 2017-7-27 13:35:46

学习了
页: [1]
查看完整版本: STM32评测之最强3ADC交替采样+DMA(5.4MSa/s)