Инициализация АЦП проходит так (под копирку из примеров библиотеки стандартной периферии):
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);
}
{
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);
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);
}
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мкс...