2015-01-20 04:40 AM
Using the STM32F207, I am trying to get the DMA2 Stream 4 interrupt to fire. Can someone take a look at the setup below, and tell me if I have a problem with the setup? (I'm sure Clive will only have to take a few seconds!)
I want TIM5, CC1 to be the trigger source. I purposely slowed down the timer to do debugging. I'm not sure if the plumbing is correct to get this timer expiry to be the trigger source to the ADC. I added a TIM5 interrupt just to make sure the CC1 is happening, and it is. This code simply does not vector to DMA2_Stream4_IRQHandler(). I do want to see xfer complete and half-transfer interrupts. Notes: This is from a C++ file; I hope I didn't miss anything trying to ''C''-ify it. Thanks in advance.{
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
}
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Stream4);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(&(dbg_buffer[0]));
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 8;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
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(DMA2_Stream4, &DMA_InitStructure);
DMA_ITConfig(DMA2_Stream4, DMA_IT_TC | DMA_IT_HT, ENABLE);
DMA_Cmd(DMA2_Stream4, ENABLE);
}
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef oc_config;
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 2000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0x752f; // pclk is 60MHz; timer clk ~= 2000Hz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM5, ENABLE);
TIM_ITConfig(TIM5, TIM_IT_CC1, ENABLE);
TIM_SelectOutputTrigger(TIM5, TIM_TRGOSource_OC1Ref);
TIM_OCStructInit(&oc_config);
oc_config.TIM_Pulse = 50000; // doesn't matter
oc_config.TIM_OCMode = TIM_OCMode_PWM1;
oc_config.TIM_OutputState = TIM_OutputState_Enable;
oc_config.TIM_OCPolarity = TIM_OCPolarity_High;
::TIM_OC1Init(TIM5, &oc_config);
::TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
TIM_Cmd(TIM5, ENABLE);
}
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T5_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_15Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConv(ADC1);
}
}
#adc-dma-trigger-timer
2015-01-20 05:09 AM
Not sure you need the TRGO on the timer, and the Pulse setting for CC1 needs to fall within the scope of the Period. ie 500 not 50000
2015-01-20 12:42 PM
Good suggestions, Clive. I've lowered the CC1 pulse to 20, and removed the TIM_SelectOutputTrigger() call. When I leave the DMA_BufferSize = 8, I still get no TIM5_Stream4 int.
But, when I play with that DMA size, I do get new behavior: when it is set to 2, I get a TC interrupt, but no half-transfer int. When I set it to 4, I get the half-transfer int. But--I only get one interrupt--I do not continue getting them. Which means that after the ADC transfers once to the DMA, it ceases to trigger the DMA transfer (that, or the ADC is not continually creating conversions!) Here is a summary of what I wanted to do: set the ADC to continuously convert on TIM5-CC1 trigger. I wanted the DMA to capture several samples (Samp1-ch1, Samp1-ch2, Samp2-ch1, Samp2-ch2, etc). I wanted the DMA half -transfer interrupt to happen when N/2 samples were transferred into the DMA buffer, and the transfer complete to happen when N samples were transferred in. In other words, I wanted the the ADC to convert in a constant, timer-triggered way, and use the DMA buffer size to determine when I need to process the buffer. I have the feeling that I am not setting the DMA and/or ADC correctly so that the DMA is the flow-controller. If what I'm describing doesn't make sense, please let me know and I'll try again. But if it does make sense, perhaps you could clarify the settings of the DMA and ADC. For now, it just does not look like the ADC is being triggered by the TIM5-CC1 event. Thank you.2015-01-20 12:50 PM
For what it's worth, here are the interrupts I'm currently working with. I am not restarting or controlling the ADC or DMA in the ISRs.
Should I be restarting conversions or restarting the DMA in the ISR?uint32_t g_xfer_cmplt_cnt = 0;
uint32_t g_half_xfer_cnt = 0;
//************************************************************************************************************
void DMA2_Stream4_IRQHandler()
{
bool xfer_cmplt = (::DMA_GetITStatus(DMA2_Stream4, DMA_IT_TCIF4) == SET);
bool half_xfer = (::DMA_GetITStatus(DMA2_Stream4, DMA_IT_HTIF4) == SET);
if (xfer_cmplt)
{
::DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TCIF4);
++g_xfer_cmplt_cnt;
}
if (half_xfer)
{
::DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_HTIF4);
++g_half_xfer_cnt;
}
}
//************************************************************************************************************
void TIM5_IRQHandler()
{
if (TIM_GetITStatus(TIM5, TIM_IT_CC1))
{
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
}
}
2015-01-21 01:34 AM
I've had success.
The following code triggers at about 1Hz, converting 2 channels per sample, and dumping 32 lovely bytes into dbg_buffer in a circular manner, as I intended. Put a breakpoint at the transfer complete block of the ISR to test this out. A couple of observations:uint16_t dbg_buffer[16] = { 0 } ;
void setup_adc1()
{
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
}
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Stream4);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(&(dbg_buffer[0]));
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 16;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
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(DMA2_Stream4, &DMA_InitStructure);
DMA_ITConfig(DMA2_Stream4, DMA_IT_TC | DMA_IT_HT, ENABLE);
DMA_Cmd(DMA2_Stream4, ENABLE);
}
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef oc_config;
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x752f; // pclk is 60MHz, so timer clk ~= 2000Hz
TIM_TimeBaseStructure.TIM_Period = 2000 - 1; // update freq ~= 1Hz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM5, ENABLE);
TIM_OCStructInit(&oc_config);
oc_config.TIM_Pulse = 20; // The PWM duty cycle; must be <TIM_Period.
oc_config.TIM_OCMode = TIM_OCMode_PWM1; // OCx active until duty cycle is done
oc_config.TIM_OutputState = TIM_OutputState_Enable;
oc_config.TIM_OCPolarity = TIM_OCPolarity_High; // OCx high during active duty cycle
::TIM_OC1Init(TIM5, &oc_config);
::TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
}
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
// PWM OCx rises at the start of the PWM period; choose that as the trigger signal
// See TIM_OCInitTypeDef::TIM_OCPolarity
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T5_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_15Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
TIM_Cmd(TIM5, ENABLE); // start the triggering source
}
}