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 8:42 AM
Read out and check/post content of TIM, ADC, DMAMUX and DMA registers.
JW
2025-08-27 8:45 AM
I didn't get you. I have already shared Initialization code in chat.
2025-08-27 9:12 AM
The DMAMUX needs to be configured to tie the ADC to that DMA channel.
2025-08-27 9:14 AM
share the snippet if possible.
2025-08-27 9:15 AM - edited 2025-08-27 9:22 AM
Can you share the example?already doing this part. n STM32G070 (G0x0) there is no DMAMUX, and there’s nothing to tie.
The ADC to DMA mapping is fixed in hardware: ADC1 → DMA1 Channel 1.
* --- 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);
Already
2025-08-27 9:25 AM - edited 2025-08-27 9:27 AM
> I have already shared Initialization code in chat.
The mcu works out of the registers content rather than source code. There may be e.g. errors in the library you are using.
> STM32G070 (G0x0) there is no DMAMUX, and there’s nothing to tie.
> The ADC to DMA mapping is fixed in hardware: ADC1 → DMA1 Channel 1.
Where do you have this information from, AI?
Have you had a look at the Reference Manual?
As TDK said, you need to set DMAMUX channel related to the DMA channel you are using, the numbering does not match so it's DMAMUX channel 0. At the beginning of DMAMUX chapter you'll see the table of triggers, so enter the number related to given ADC to DMAMUX_CxCR.DMAREQ_ID, that's all. There certainly are LL functions and defines for these things, you can find them in Cube, which is open source.
Or maybe TDK will provide you with ready made solution, but do you want to rely on that?
JW
2025-08-27 9:32 AM
I am sorry but it didn't intend to hurt anyone. As a reference I prefer to use it. By the way I can't see any code share by you.
2025-08-27 9:35 AM
> n STM32G070 (G0x0) there is no DMAMUX, and there’s nothing to tie.
Did AI tell you that? The Reference Manual says it's there. See the "Request Generator" part.
Here's a working example:
2025-08-27 9:40 AM - edited 2025-08-27 9:41 AM
Thank you for giving this solution but it didn't help me. I need a selective channels for adc conversion and they are 11 in number. The solution you gave goes upto 8 channels only. I am trying to use configurable sequence and not fixed sequence.