cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G070: 11 ADC channels + DMA with TIM15 TRGO trigger — DMA IRQ not firing

pradeepwagre
Associate III

Setup Description

  • MCU: STM32G070RE

  • Peripherals:

    • ADC1 sampling 11 channels (IN0–IN8, IN17, IN18).

    • DMA1 Channel1 configured circular, PERIPH→MEMORY, increment on memory.

    • Timer 15 used to generate a 3 ms integration window (for TPIC8101 knock sensor).

      • CC1 = start of window (INT pin LOW).

      • CC2 = end of window (INT pin HIGH) and also used as TRGO (OC2REF rising edge) to trigger ADC conversions.(ADC should only come into picture when INT pin goes high)

  • DMA buffer: uint16_t adc_raw_buffer[NUM_CHANNELS] where NUM_CHANNELS = 11.


What Works

  • TIM15 interrupts (CC1 and CC2) are firing as expected.

  • GPIO pin toggles correctly at CC1 and CC2.

  • TRGO is configured as LL_TIM_TRGO_OC2REF.


What Does Not Work

  • DMA1_Channel1_IRQHandler never fires.

  • The DMA buffer (adc_raw_buffer[]) remains unchanged.

  • It looks like the ADC never actually starts conversions on TRGO.                                                                             

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{
    /* Clock first */
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

    /* NVIC for DMA1 Channel 1 */
    NVIC_SetPriority(DMA1_Channel1_IRQn, 1);
    NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

static void MX_ADC1_Init(void)
{
    /* --- Clocks --- */
	LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
    /**ADC1 GPIO Configuration
    PA0   ------> ADC1_IN0
    PA1   ------> ADC1_IN1
    PA2   ------> ADC1_IN2
    PA3   ------> ADC1_IN3
    PA4   ------> ADC1_IN4
    PA5   ------> ADC1_IN5
    PA6   ------> ADC1_IN6
    PA7   ------> ADC1_IN7
    PC4   ------> ADC1_IN17
    PC5   ------> ADC1_IN18
    PB0   ------> ADC1_IN8
    */
    GPIO_InitStruct.Pin = PS1_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(PS1_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = PS2_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(PS2_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Knock_Sense_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Knock_Sense_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Temp_Sense1_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Temp_Sense1_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Temp_Sense2_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Temp_Sense2_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = CSense1_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(CSense1_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = CSense2_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(CSense2_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = CSense3_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(CSense3_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Tht_Psn_Ana_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Tht_Psn_Ana_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Gas_Leakage_Ana_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Gas_Leakage_Ana_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = Batt_Fail_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(Batt_Fail_GPIO_Port, &GPIO_InitStruct);
    /* --- ADC core --- */
    LL_ADC_InitTypeDef adc = {0};
    adc.Clock         = LL_ADC_CLOCK_SYNC_PCLK_DIV2;      // 64/2 = 16 MHz
    adc.Resolution    = LL_ADC_RESOLUTION_12B;
    adc.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
    adc.LowPowerMode  = LL_ADC_LP_MODE_NONE;
    LL_ADC_Init(ADC1, &adc);

    /* --- Regular group (TRGO from TIM15 CC2) --- */
    LL_ADC_REG_InitTypeDef reg = {0};
    reg.TriggerSource    = LL_ADC_REG_TRIG_EXT_TIM15_TRGO;
    reg.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
    reg.ContinuousMode   = LL_ADC_REG_CONV_SINGLE;
    reg.DMATransfer      = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
    reg.Overrun          = LL_ADC_REG_OVR_DATA_PRESERVED;
    LL_ADC_REG_Init(ADC1, &reg);
    LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING);
    /* --- Sequencer: 11 channels --- */
    LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_FIXED);
    LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
    LL_ADC_REG_SetSequencerChannels(ADC1,
          LL_ADC_CHANNEL_0  | LL_ADC_CHANNEL_1  | LL_ADC_CHANNEL_2  |
          LL_ADC_CHANNEL_3  | LL_ADC_CHANNEL_4  | LL_ADC_CHANNEL_5  |
          LL_ADC_CHANNEL_6  | LL_ADC_CHANNEL_7  | LL_ADC_CHANNEL_8  |
          LL_ADC_CHANNEL_17 | LL_ADC_CHANNEL_18);

    LL_ADC_SetSamplingTimeCommonChannels(ADC1,
        LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5);

    /* --- DMA1 Channel 1 for ADC --- */
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
    /* Direction PERIPH -> MEMORY (you missed this earlier) */
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1,     LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetPeriphAddress (DMA1, LL_DMA_CHANNEL_1, (uint32_t)&ADC1->DR);
    LL_DMA_SetMemoryAddress (DMA1, LL_DMA_CHANNEL_1, (uint32_t)adc_raw_buffer);
    LL_DMA_SetDataLength    (DMA1, LL_DMA_CHANNEL_1, NUM_CHANNELS);   // 11
    LL_DMA_SetPeriphSize    (DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
    LL_DMA_SetMemorySize    (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
    LL_DMA_SetPeriphIncMode (DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetMode          (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
    LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_VERYHIGH);
    /* Enable TC (and TE if you want) interrupts at channel level */
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
    LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
    /* Clear any stale flags before enabling */
    LL_DMA_ClearFlag_GI1(DMA1); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TE1(DMA1);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);

    /* --- ADC power: regulator + calibration + enable --- */
    LL_ADC_EnableInternalRegulator(ADC1);
    for (volatile uint32_t d=0; d<(SystemCoreClock/100000U); ++d) { __NOP(); } // ~1ms

    /* IMPORTANT: ADC must be disabled here (it is) when starting calibration */
    LL_ADC_StartCalibration(ADC1);
    while (LL_ADC_IsCalibrationOnGoing(ADC1)) { /* small wait */ }

    LL_ADC_Enable(ADC1);
    while (!LL_ADC_IsActiveFlag_ADRDY(ADC1)) { /* small wait */ }
    /* Do NOT call StartConversion when using external trigger; TIM15 CC2 will fire it */
}

void TIM15_Init_3ms_Window(void)
{
  /* INT/HOLD pin (example PB1) */
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  LL_GPIO_SetPinMode (GPIOB, LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT);
  LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_1, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetPinPull (GPIOB, LL_GPIO_PIN_1, LL_GPIO_PULL_NO);
  LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_1);  /* idle HIGH */

  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);

  /* 1 us tick */
  uint32_t psc = (SystemCoreClock / 1000000UL);
  LL_TIM_SetPrescaler   (TIM15, psc - 1);
  LL_TIM_SetAutoReload  (TIM15, 4999);                      /* 9 ms period */
  LL_TIM_SetCounterMode (TIM15, LL_TIM_COUNTERMODE_UP);
  LL_TIM_EnableARRPreload(TIM15);

  /* CC1 at 0 us -> pull LOW (ISR) */
  LL_TIM_OC_SetMode(TIM15, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_TOGGLE);
  LL_TIM_OC_SetCompareCH1(TIM15, 0);
  LL_TIM_OC_EnablePreload(TIM15, LL_TIM_CHANNEL_CH1);
  LL_TIM_CC_EnableChannel(TIM15, LL_TIM_CHANNEL_CH1);
  LL_TIM_EnableIT_CC1(TIM15);

  /* CC2 at 3000 us -> set HIGH (ISR) & TRGO edge for ADC */
  /* Use PWM2 so OC2REF has a RISING edge at compare */
  LL_TIM_OC_SetMode(TIM15, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_PWM2);
  LL_TIM_OC_SetCompareCH2(TIM15, 3000);
  LL_TIM_OC_EnablePreload(TIM15, LL_TIM_CHANNEL_CH2);
  LL_TIM_CC_EnableChannel(TIM15, LL_TIM_CHANNEL_CH2);
  LL_TIM_EnableIT_CC2(TIM15);

  /* TRGO on OC2REF (ADC listens for rising edge) */
  LL_TIM_SetTriggerOutput(TIM15, LL_TIM_TRGO_OC2REF);

  /* NVIC + start */
  NVIC_SetPriority(TIM15_IRQn, 2);
  NVIC_EnableIRQ(TIM15_IRQn);

  LL_TIM_GenerateEvent_UPDATE(TIM15);    /* load PSC/ARR/CCR */
  LL_TIM_SetCounter(TIM15, 0);
  LL_TIM_EnableCounter(TIM15);
}

 

12 REPLIES 12
waclawek.jan
Super User

Maybe you misunderstood the purpose of this forum.

We don't provide ready-made code, we provide explanation.

JW

The channel configuration is independent from the DMA configuration. You could take the working aspects of the example and adapt them.

If you feel a post has answered your question, please click "Accept as Solution".

I am also looking for the same. At least the solution given should be nearer for what I am looking.