[原创] 【NUCLEO-F767ZI】最强3ADC交替采样+DMA(5.4MSa/s)

ihalin   2016-8-5 12:27 楼主
一通道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

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

在多重 ADC 模式下,通过 ADC1 主器件到 ADC2 和 ADC3 从器件的交替触发或同时触发来
启动转换,具体取决于 ADC_CCR 寄存器中的 MULTI[4:0] 位所选的模式
注: 在多重 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[4:0] = 00000 ),然后才可以对交替模式进行编程。
在此模式下,每当出现 2 个可用数据项时,就会生成一个 DMA 传输请求(如果 ADC_CCR
寄存器中的 DMA[1:0] 位等于 0b10)。此请求首先会将存储在 ADC_CDR 32 位寄存器低位
半字中的第一批转换数据传输到 SRAM,然后将存储在 ADC_CDR 高位半字中的第二批转换
数据传输到 SRAM。具体顺序如下:
第 1 个请求:ADC_CDR[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]
第 2 个请求:ADC_CDR[31:0] = ADC1_DR[15:0] | ADC3_DR[15:0]
第 3 个请求:ADC_CDR[31:0] = ADC3_DR[15:0] | ADC2_DR[15:0]
第 4 个请求:ADC_CDR[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]
QQ截图20160805104904.png
简要:
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[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]
第 2 个请求:ADC_CDR[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]
b)  三重交替模式:每当有 2 个数据项可用时,就会生成一个 DMA 请求
第 1 个请求:ADC_CDR[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]
第 2 个请求:ADC_CDR[31:0] = ADC1_DR[15:0] | ADC3_DR[15:0]
第 3 个请求:ADC_CDR[31:0] = ADC3_DR[15:0] | ADC2_DR[15:0]
第 4 个请求:ADC_CDR[31:0] = ADC2_DR[15:0] | ADC1_DR[15:0]

在F767的datasheetz中介绍 GPIO PC2 是ADC1 ADC2 ADC3的第12通道
这里我选用GPIO PC2口
QQ截图20160805104639.png
DMA2数据流0 通道 0
QQ截图20160805104543.png
QQ截图20160805104602.png

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

  3.         ADC_HandleTypeDef ADC1_Handler ;
  4.         ADC_HandleTypeDef ADC2_Handler ;
  5.         ADC_HandleTypeDef ADC3_Handler ;

  6. extern uint32_t ADC123_Buff[3];

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

  10. void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  11. {
  12.    GPIO_InitTypeDef   GPIO_InitStruct;
  13.    DMA_HandleTypeDef  hdma_adc;
  14.   __HAL_RCC_GPIOC_CLK_ENABLE();//开启GPIOC的时钟
  15.   __HAL_RCC_ADC1_CLK_ENABLE();//开启ADC 1 2 3 的时钟
  16.   __HAL_RCC_ADC2_CLK_ENABLE();
  17.   __HAL_RCC_ADC3_CLK_ENABLE();
  18.   __HAL_RCC_DMA2_CLK_ENABLE(); //开启DMA的时钟

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

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

  39.   HAL_DMA_Init(&hdma_adc);
  40.         
  41.   __HAL_LINKDMA(hadc, DMA_Handle, hdma_adc);

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




  46. void ADC123_Config(void)
  47. {
  48.   ADC_ChannelConfTypeDef sConfig; //ADC 通道的
  49.   ADC_MultiModeTypeDef   mode; //ADC 模式的选择

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

  63.   if (HAL_ADC_Init(&ADC3_Handler) != HAL_OK)
  64.   {
  65.     /* Initialization Error */
  66.     Error_Handler();
  67.   }

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

  73.   if(HAL_ADC_ConfigChannel(&ADC3_Handler, &sConfig) != HAL_OK)
  74.   {
  75.     /* Channel Configuration Error */
  76.     Error_Handler();
  77.   }

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

  80.   ADC2_Handler.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
  81.   ADC2_Handler.Init.ScanConvMode = DISABLE;
  82.   ADC2_Handler.Init.ContinuousConvMode = ENABLE;
  83.   ADC2_Handler.Init.DiscontinuousConvMode = DISABLE;
  84.   ADC2_Handler.Init.NbrOfDiscConversion = 0;
  85.   ADC2_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  86.   ADC2_Handler.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
  87.   ADC2_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  88.   ADC2_Handler.Init.NbrOfConversion = 1;
  89.   ADC2_Handler.Init.DMAContinuousRequests = DISABLE;
  90.   ADC2_Handler.Init.EOCSelection = DISABLE;

  91.   if (HAL_ADC_Init(&ADC2_Handler) != HAL_OK)
  92.   {
  93.     /* Initialization Error */
  94.     Error_Handler();
  95.   }

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

  101.   if (HAL_ADC_ConfigChannel(&ADC2_Handler, &sConfig) != HAL_OK)
  102.   {
  103.     /* Channel Configuration Error */
  104.     Error_Handler();
  105.   }

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

  108.   ADC1_Handler.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
  109.   ADC1_Handler.Init.ScanConvMode = DISABLE;
  110.   ADC1_Handler.Init.ContinuousConvMode = ENABLE;
  111.   ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;
  112.   ADC1_Handler.Init.NbrOfDiscConversion = 0;
  113.   ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  114.   ADC1_Handler.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
  115.   ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  116.   ADC1_Handler.Init.NbrOfConversion = 1;
  117.   ADC1_Handler.Init.DMAContinuousRequests = ENABLE;
  118.   ADC1_Handler.Init.EOCSelection = DISABLE;
  119.   

  120.   if (HAL_ADC_Init(&ADC1_Handler) != HAL_OK)
  121.   {
  122.     /* Initialization Error */
  123.     Error_Handler();
  124.   }

  125.   /*##-6- Configure ADC1 regular channel #####################################*/
  126.   if (HAL_ADC_ConfigChannel(&ADC1_Handler, &sConfig) != HAL_OK)
  127.   {
  128.     /* Channel Configuration Error */
  129.     Error_Handler();
  130.   }

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

  141. void DMA2_Stream0_IRQHandler (void)  
  142. {  
  143.   if(DMA2->LISR & 0x00000010) //完成中断
  144.   {  
  145.      DMA2->LIFCR |= 0x00000010;  
  146.          for(i=0;i<1024;i++)
  147.                 {
  148.                         printf("ADC:%f V   ",(float)(uint16_t)ADC123_Buff[i]*(3.3/4096));
  149.                 }
  150.   }        
  151. }  


main:
  1. #include "sys.h"
  2. #include "led.h"
  3. #include "uart.h"
  4. #include "adc.h"
  5. extern UART_HandleTypeDef UART3_Handler;
  6. extern ADC_HandleTypeDef ADC1_Handler ;
  7. extern ADC_HandleTypeDef ADC2_Handler ;
  8. extern ADC_HandleTypeDef ADC3_Handler ;
  9. uint32_t ADC123_Buff[1024];
  10. int main(void)
  11. {
  12.         double data;
  13.   int len,i=0;
  14.   MPU_Config();
  15.   CPU_CACHE_Enable();
  16.   HAL_Init();
  17.   SystemClock_Config();
  18.         Led_Init();
  19.   uart_init(9600);
  20.         ADC123_Config();
  21.   HAL_ADC_Start(&ADC3_Handler);
  22.         HAL_ADC_Start(&ADC2_Handler);
  23.         HAL_ADCEx_MultiModeStart_DMA(&ADC1_Handler, (uint32_t *)ADC123_Buff, 1024);
  24.   while (1)
  25.   {        
  26. }
  27. }



下面是串口输出的:
20160805095808.png


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



回复评论 (9)

占楼  
点赞  2016-8-5 12:28
做小示波器挺好。
点赞  2016-8-5 12:51
可以解调中波广播试试?
点赞  2016-8-5 13:01
天地庄周马;江湖范蠡船。 个性签名还是放QQ号吧,2060347305,添加说明EEworld好友
点赞  2016-8-6 10:02
这个很有用,我先学习学习。
点赞  2016-8-8 10:05
看来用F767还是不错啊。
点赞  2016-8-8 15:46
您好,看了您的帖子很受启发,我想请教一下,ADC三重模式是否可以在F1系列上工作?
点赞  2018-1-6 18:19
厉害
每个人心中都一片待开发的天空!
点赞  2018-1-7 17:20

你好,我使用你的代码但是没有进入中断,请问有什么原因呢

 

点赞  2023-1-17 15:25
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复