Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32f407 + interleaved mode ADC + DMA
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Sonya
Нужно сделать вот что: запускать АЦП по внешнему сигналу (External trigger = EXTI line11). Режим работы АЦП при этом - interleaved mode с DMA (три АЦП подряд ацепируют один и тот же сигнал на входе).
Инициализация АЦП проходит так (под копирку из примеров библиотеки стандартной периферии):
CODE
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 0 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 channel 0 configuration **************************************/
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&aADCTripleConvertedValue[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = num; //сколько точек взять для преобразования
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(DMA2_Stream0, ENABLE);

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

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

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);

ADC_DMACmd(ADC1, ENABLE);

/* ADC2 regular channel 0 configuration ************************************/
ADC_Init(ADC2, &ADC_InitStructure);
/* ADC2 regular channel 0 configuration */
ADC_RegularChannelConfig(ADC2,ADC_Channel_0, 1, ADC_SampleTime_3Cycles);

/* ADC3 regular channel 0 configuration ************************************/
ADC_Init(ADC3, &ADC_InitStructure);
/* ADC3 regular channel 0 configuration */
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);
}

Внешней командой (по Modbus на UARTе) инициализируется вход, на который приходит синхроимпульс, разрешаются прерывания и включается АЦП:
Код
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    GPIOC->MODER &=~GPIO_MODER_MODER11;      
    GPIOC->PUPDR &=~GPIO_PUPDR_PUPDR11;      
    SYSCFG->EXTICR[2] |= SYSCFG_EXTICR3_EXTI11_PC;
    EXTI->IMR    |=(EXTI_IMR_MR11);          
    EXTI->FTSR   |=(EXTI_FTSR_TR11);                  
    DMA2_Stream0 -> CR |= DMA_SxCR_TCIE;
    NVIC_EnableIRQ(DMA2_Stream0_IRQn);
      
    ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
    ADC_Cmd(ADC1, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
    ADC_Cmd(ADC3, ENABLE);


И что происходит в обработчике DMA:

Код
void DMA2_Stream0_IRQHandler(void){  
  DMA2 -> LIFCR |= DMA_LIFCR_CTCIF0; //обнуляем флаг Transfer Complete
  ADC_MultiModeDMARequestAfterLastTransferCmd(DISABLE);
  ADC_Cmd(ADC1, DISABLE);
  ADC_Cmd(ADC2, DISABLE);
  ADC_Cmd(ADC3, DISABLE);  
}



Синхронизация не работает при таком раскладе. Прерывание DMA срабатывает не связано с внешним сигналом: то до его спадающего фронта, то после.
Если эту ножку оторвать, то АЦП вообще не запускается (по крайней мере в обработчик DMA не входит). То есть как-то этот сигнал всё-таки действует.
Подскажите, пожалуйста, что я делаю не так? Я так понимаю, что секрет в этих двух строчках:
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Falling;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_Ext_IT11;

Выставление разных ADC_ExternalTrigConvEdge дает одинаковую картину (только если не выставить его в None).

Внешний сигнал с синхроимпульсом проверен - перепроверен: четко идет раз в 100мкс длительностью 50 мкс. Фронты адекватные.

Если в обработчике DMA не запрещать ADC, то это прерывание срабатывает каждые ~20мкс. Где-то в мануале было написано, что надо бы отрубать периферию от DMA каждый раз при срабатывания прерывания.

Если честно, не очень понимаю, как правильно инициализировать EXTI line11.
EXTI->IMR |=(EXTI_IMR_MR11); <- это пережитки прошлой программы, но вроде как и здесь как-то также должно быть или нет (внешний сигнал приходит на gpioc.11)?
EXTI->FTSR |=(EXTI_FTSR_TR11);

В программе никаких прерываний больше нет, то есть DMA ничего не должно мешать (команда по modbus приходит только один раз в начале).

Раньше версия программы была немного иной: АЦП запускалось в обработчике прерывания по внешнему сигналу. Всё работало. Но потребовалось изменить код, чтобы АЦП запускалось триггером по этому внешнему сигналу. У меня возникло подозрение, что возможно эта опция запуска АЦП по триггеру внешним сигналом в режиме interleaved + DMA вообще не работает в stm...
Может ли такое быть? )



Внешняя команда, приходящая по Modbus, обрабатывалась прямо в обработчике по таймеру 5. Видимо, из-за этого неправильно синхронизировалось АЦП с первым приходом фронта внешнего импульса (т.к. прерывание tim5 приоритетней, чем dma2_stream0). Поэтому сейчас этот код добавлен после ADC_config(). А в обработчик по таймеру, где раньше выполнялись эти строчки, добавлено выставление флага flag = 1; , проверка которого идет в основном цикле программы.
CODE

ADC_Config();
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
GPIOC->MODER &=~GPIO_MODER_MODER11;
GPIOC->PUPDR &=~GPIO_PUPDR_PUPDR11;
SYSCFG->EXTICR[2] |= SYSCFG_EXTICR3_EXTI11_PC;
EXTI->IMR |=(EXTI_IMR_MR11);
EXTI->FTSR |=(EXTI_FTSR_TR11);
DMA2_Stream0 -> CR |= DMA_SxCR_TCIE;
NVIC_EnableIRQ(DMA2_Stream0_IRQn);

while(1){
if (flag ==1) {
flag =0;
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_Cmd(ADC3, ENABLE);
}
}


И всё же непонятно, почему по второй, третьей .... и т.д. команде DMA срабатывает в какие-то произвольные моменты времени, несинхронизированно с внешним сигналом.
И почему , если ADC не выключать или выключать и включать прямо в обработчике DMA, то DMA срабатывает каждые ~20мкс, а не раз в 100мкс...
Sonya
Частично решила проблему:
1) Внешний триггер нужно задавать только первому АЦП, а остальным запрещать. И это написано в мануале:
Note: In multi ADC mode, when configuring conversion trigger by an external event, the
application must set trigger by the master only and disable trigger by slaves to prevent
spurious triggers that would start unwanted slave conversions.
2) Нужно после срабатывания прерывания по DMA АЦП переключать в режим ADC_Mode_Independent, перед следующим преобразованием. И это тоже написано в мануале:
Note: If the conversion sequence is interrupted (for instance when DMA end of transfer occurs),
the multi-ADC sequencer must be reset by configuring it in independent mode first (bits
DUAL[4:0] = 00000) before reprogramming the interleaved mode.

Правда DUAL[4:0] - это на самом деле MULTI[4:0]. Как всегда, мануал stm тренирует у разработчиков бдительность и смекалку.

Но проблема до конца не решена...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.