Работа трех АЦП

7 Марта 2015 К комментариям

Столкнулся с интересной особенностью работы АЦП в микроконтроллерах STM32F40x.

Данные микроконтроллеры имеют три АЦП и для повышения частоты преобразований (дискретизации) можно организовать их совместную работу. Это позволит увеличить частоту в три раза соответственно.

В этом режиме работы АЦП целесообразно использовать контроллер прямого доступа к памяти (ПДП, DMA).

Для снижения количества транзакций ПДП за один запрос забирает сразу два преобразования АЦП, т.е. одна транзакция имеет размер четыре байта (слово), соответственно и запросы формируются через два преобразования.

Для лучшего понимания скриншот из документации:

image

Производитель предоставляет пример для данного режима на основе своих же библиотек. Ни чего особенного в них нет, последовательно настраиваются ПДП, АЦП и дается старт на начало преобразований:

static void ADC_Config(void)
{
  GPIO_InitTypeDef       GPIO_InitStructure;
  DMA_InitTypeDef        DMA_InitStructure;
  ADC_InitTypeDef        ADC_InitStructure;
  ADC_CommonInitTypeDef  ADC_CommonInitStructure;  
  
  /* Enable peripheral clocks *************************************************/
  RCC_AHB1PeriphClockCmd( ADC1_2_CHANNEL_GPIO_CLK , ENABLE);
  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2 , ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1 , ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC2 , ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC3 , ENABLE);  

  /* Configure ADC Channel 12 pin as analog input *****************************/ 
  GPIO_InitStructure.GPIO_Pin = GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init(GPIO_PORT, &GPIO_InitStructure);

  /* DMA2 Stream0 channel0 configuration **************************************/
  DMA_InitStructure.DMA_Channel = DMA_CHANNELx;  
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&aADCTripleConvertedValue;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 3;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  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;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA_STREAMx, &DMA_InitStructure);

  /* DMA2_Stream0 enable */
  DMA_Cmd(DMA_STREAMx, ENABLE);

  /* ADC Common configuration *************************************************/
  ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_Interl;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;  
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; 
  ADC_CommonInit(&ADC_CommonInitStructure);

  /* ADC1 regular channel 12 configuration ************************************/
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_3Cycles);
  /* Enable ADC1 DMA */
  ADC_DMACmd(ADC1, ENABLE);

  /* ADC2 regular channel 12 configuration ************************************/
  ADC_Init(ADC2, &ADC_InitStructure);
  /* ADC2 regular channel12 configuration */ 
  ADC_RegularChannelConfig(ADC2, ADC_CHANNEL, 1, ADC_SampleTime_3Cycles);

  /* ADC3 regular channel 12 configuration ************************************/
  ADC_Init(ADC3, &ADC_InitStructure); 
  /* ADC3 regular channel12 configuration */
  ADC_RegularChannelConfig(ADC3, ADC_CHANNEL, 1, ADC_SampleTime_3Cycles);

  /* Enable DMA request after last transfer (multi-ADC mode) ******************/
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

  /* Enable ADC1 **************************************************************/
  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC2 **************************************************************/
  ADC_Cmd(ADC2, ENABLE);

  /* Enable ADC3 **************************************************************/
  ADC_Cmd(ADC3, ENABLE);
} 

Основной цикл:

 
int main(void)
{
  /*!> At this stage the microcontroller clock setting is already configured, 
       this is done through SystemInit() function which is called from startup
       files (startup_stm32f40_41xxx.s/startup_stm32f427_437xx.s/startup_stm32f429_439xx.s)
       before to branch to application main. 
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32f4xx.c file
     */     

  /* ADC configuration */
  ADC_Config();
   
  /* Start ADC1 Software Conversion */ 
  ADC_SoftwareStartConv(ADC1);

  while (1)
  {
  }
}

При детальном анализе получаемых данных выявилась интересная особенность.

Если использовать для начала транзакций ПДП события от первого АЦП, то получаю “тройной” запрос на передачу данных для второго и третьего канала.

Как проверял.

Изменил режим работы ПДП с цикличного на нормальный, для оцифровки буфера один раз.

Разрешил прерывания по заполнению половины и полного буфера. Увеличил размер буфера.

В обработчике прерываний сразу после входа устанавливаю высокий уровень на тестовой линии, а при выходе низкий.

Сигнал с тестовой линии подал на вход АЦП и вход осциллографа.

Используя осциллограф определил длительность тестового импульса ~1,5 мкс.

Тактовая частота микроконтроллера 144 МГц, частота шины АЦП 72 МГц, делитель АЦП двойка – эти настройки позволяют получить максимальную частоту дискретизации 7,2 МГц при использовании трех АЦП.

Таким образом после оцифровки тестового сигнала должно получиться ~11 выборок АЦП.

Однако реально получил 23 выборки (скриншот STM Studio):

image

Для определения принадлежности данных к каналу АЦП решил изменить разрядность данных для каждого канала: АЦП1 – 12 бит, АЦП2 – 10 бит, АЦП3 – 8 бит.

image

Получилась вот такая странная картина (смещение влево и уменьшение отсчетов это результат изменения разрядности преобразований).

Ещё более интересный результат получается при использовании цикличного режима работы АЦП, там такую картину можно наблюдать только при размере буфера кратном трем, все остальные значения создают видимость нормальной работы.

В общем долго думая, смотря примеры и читая доку ни чего найти не смог, чтобы объяснило данное поведение АЦП.

В конечном итоге пришел к тому, что если привязывать транзакции ПДП к событиям второго АЦП, то во при любом делителе АЦП и размере буфера ПДП всегда получаю стабильные результаты.

Вот “правильное” преобразования при различной разрядности:

image

И для одинаковой разрядности 12 бит:

image

Имеем как и положено 11 выборок на тестовый импульс.



comments powered by Disqus