2025-08-27 8:14 AM - edited 2025-08-27 8:18 AM
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.
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.
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, ®);
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);
}
2025-08-27 11:14 AM
Maybe you misunderstood the purpose of this forum.
We don't provide ready-made code, we provide explanation.
JW
2025-08-27 11:59 AM
The channel configuration is independent from the DMA configuration. You could take the working aspects of the example and adapt them.
2025-08-27 5:23 PM - edited 2025-08-28 2:37 AM
I am also looking for the same. At least the solution given should be nearer for what I am looking.