judoka_acl judoka_acl - 7 days ago 4
C Question

Execution never jumps to DMA IRQHandler in stm32f4105x

I'm trying to get a sequence of 1000 samples from ADC1 of STM32f4105VC of STM32f401c-disco, using the DMA transactions. I want the DMA to generate an interrupt when the transfer is complete and to stop there so that no data is overwritten. Here is the code:

/* Private macro -------------------------------------------------------------*/
#define M 5
#define F_S 42000000
#define LOG_SIZE 1000
#define MEAS_PERIODS 1
#define DMA_BUFFER_SIZE 1000
/* Private variables ---------------------------------------------------------*/
__IO uint16_t uhADCxConvertedValue[DMA_BUFFER_SIZE] = {0};
__IO uint32_t uwADCxConvertedVoltage = 0;
double voltage=0;
double S=0, rms=0, F=0; // S quadratic sums, rms - the rms value, F the frequency
uint32_t N; //number of samplings
double unscaled_voltage=0;
uint32_t max_N=0, steady_N = 0;

typedef char MyString[50];
MyString Log[LOG_SIZE];



/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
static void ADC_Config(void);


void DMA2_Stream0_IRQHandler(void) {
if(DMA_GetITStatus(DMA_STREAMx,DMA_IT_TCIF0)!=RESET) {
DMA_ClearITPendingBit(DMA_STREAMx,DMA_STREAMx,DMA_IT_TCIF0|DMA_IT_HTIF0);
}
}


/**
* @brief Main program
* @param None
* @retval None
*/
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
*/



double R = 3.0/0xfff; //conversion ratio
double dc_offset = 1.5;
double ac_scaling = 0.0022;
double mv_filter[M]={0};//moving average filter
uint8_t wave_pos=0; //0 upper half wave, 1 lower half wave
uint32_t i=0;
uint8_t i_period = 1;
/* ADC configuration */
ADC_Config();

/* Start ADC Software Conversion */
// ADC_SoftwareStartConv(ADCx);
ADC_SoftwareStartConv(ADCx);
while (1) {}
}

static void ADC_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

/* Enable ADCx, DMA and GPIO clocks ****************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_AHB1PeriphClockCmd(ADCx_CHANNEL_GPIO_CLK, ENABLE);
RCC_APB2PeriphClockCmd(ADCx_CLK, ENABLE);

/* Enable the DMA2 Stream0 Global Interrupt (to handle the Transfer Complete Interrupt TCIF) */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

/* DMA2 Stream0 channel15 configuration **************************************/
DMA_InitStructure.DMA_Channel = DMA_CHANNELx;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADCx_DR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&uhADCxConvertedValue[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = DMA_BUFFER_SIZE;
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_Normal;
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);
DMA_Cmd(DMA_STREAMx, ENABLE);

/* Configure ADC1 Channel15 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);

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

/* ADC1 Init ****************************************************************/
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(ADCx, &ADC_InitStructure);

/* ADC1 regular channel15 configuration **************************************/
ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_3Cycles);

// /* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADCx, ENABLE);

// /* Enable ADC1 DMA */
ADC_DMACmd(ADCx, ENABLE);

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

DMA_ITConfig(DMA_STREAMx,DMA_IT_TC,ENABLE);
}


When I debug the program after some time the DMA stops and the flags
TCIF0
and
HTIF0
are set in the the register
DMA_LISR
, though a breakpoint in the
DMA2_Stream0_IRQHandler
function is only once activated. Why?

Answer

Just for those facing similar problem. When the DMA_IT_TCIF0 was triggered simultaneously there was an overrun interrupt in the ADC. Therefore the program needs to recover from that according to the sequence described in the RM0090 page 403, 13.8.1 Using the DMA. Here is the updated IRQHandler:

void DMA2_Stream0_IRQHandler(void) {
    if(DMA_GetITStatus(DMA_STREAMx,DMA_IT_TCIF0)!=RESET) {
        //clear the ADC->CR2 DDS and DMA flag to disable further DMA requests
        ADCx->CR2 &= ~(ADC_CR2_DDS|ADC_CR2_DMA);
        //do processing
        DMA_ClearITPendingBit(DMA_STREAMx,DMA_IT_TCIF0|DMA_IT_HTIF0);
        //abjust the DMA NDTR counter
        DMA_STREAMx->NDTR = (uint32_t)DMA_BUFFER_SIZE;
        DMA_STREAMx->CR |= (uint32_t)DMA_SxCR_EN;
        //set the DMA bit
        ADCx->CR2 |= (ADC_CR2_DMA|ADC_CR2_DDS);
        //clear the overload
        ADCx->SR &= ~(ADC_SR_OVR|ADC_SR_STRT);
        //start the ADC
        ADCx->CR2 |= (uint32_t)ADC_CR2_SWSTART;
    }
    ;
}

Apart from that everything else is the same.