Skip to main content
n2wx
Senior
February 15, 2020
Solved

STM32H7 triggering ADC with TIM6 TRGO

  • February 15, 2020
  • 4 replies
  • 5843 views

The ADC (ADC3 if that's relevant) provides for triggering by way of the TIM6 TRGO event. I have the ADC configured to convert with "ExternalTrigConv" pointing to ADC_EXTERNAL_TRIG_T6_TRGO, and the update event's rising edge. In no case am I able to get the thing to trigger though, not off of TIM6, only by software conversion start. I'm sure TIM6 is firing because when I intercept its ISR it's running on time, it's just not triggering the ADC.

The errata for the 32F4xx suggests that one has to enable the DAC RCC clock domain for it to work. This doesn't help on the H7 (and it's not mentioned in the H7 errata).

Has anyone managed to use TIM6 to trigger conversions of the ADC? I can start the s/w conversion in a roundabout way (roundabout because of the HAL locks if I do stuff in ISRs) after TIM6 fires its ISR but I'd rather not couple things so closely.

TIM6 when it's mentioned in the docs is always in relation to the DAC and not the ADC so I wonder if the connection just doesn't exist on the H7 part. Anyone? TIA

This topic has been closed for replies.
Best answer by berendi

Here, a fully functional example, tested on a NUCLEO-H743ZI2

void ADC3_IRQHandler() {
	ADC3->ISR = ADC_ISR_EOC; // clear interrupt flag
	GPIOB->ODR ^= 1; // toggle green
}
 
int main() {
	RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_ADC3EN;
	RCC->APB1LENR |= RCC_APB1LENR_TIM6EN;
	GPIOB->MODER &= ~((2u << 28u) | (2u << 0u)); // red, greed led output
	GPIOE->MODER &= ~(2u << 2u); // yellow led output
	TIM6->PSC = 0x3FF; // set some prescaler
	TIM6->EGR = TIM_EGR_UG; // load prescaler
	TIM6->CR2 = TIM_CR2_MMS_1; // master mode 010: update
	TIM6->ARR = 0xFFFF; // set period
	TIM6->SR = 0; // clear update status caused by TIM_EGR_UG
	ADC3_COMMON->CCR = ADC_CCR_CKMODE; // set a valid clock, hclk / 4
	ADC3->CR = 0; // clear sleep mode
	ADC3->CR; // wait a bit
	ADC3->CR = ADC_CR_ADVREGEN; // enable ADC regulator
	some_delay();
	ADC3->CR |= ADC_CR_ADEN; // enable ADC
	while(!(ADC3->ISR & ADC_ISR_ADRDY)) // wait for ADC ready
		;
	ADC3->IER |= ADC_IER_EOCIE; // enable EOC interrupt, just for control
	ADC3->PCSEL = 1; // preselect channel 1
	ADC3->SQR1 = 0; // convert channel 1 only
	ADC3->CFGR = ADC_CFGR_EXTEN_0 | // enable trigger on rising edge
			(13 << ADC_CFGR_EXTSEL_Pos); // trigger #13 is TIM6_TRGO
	ADC3->CR |= ADC_CR_ADSTART; // start ADC (in triggered mode)
	TIM6->CR1 = TIM_CR1_CEN; // start timer
	NVIC_EnableIRQ(ADC3_IRQn);
	while(1) {
		some_delay();
		led_yellow(0); // just a heartbeat blink
		some_delay();
		led_yellow(1);
	}
}

The ADC interrupt handler is not actually needed, I just left it in as a visual indicator that the ADC is doing conversions roughly with the timer frequency.

4 replies

waclawek.jan
Super User
February 16, 2020

Read out and check the ADC and TIM6 registers' content. Have you set TRGO properly in TIM6_CR2?

JW

n2wx
n2wxAuthor
Senior
February 16, 2020

I have. I have the ADC trigger source set for ADC_EXTERNALTRIGCONV_T6_TRGO and the trigger edge set as ADC_EXTERNALTRIGCONVEDGE_RISING (I tried the other two settings too) . Maddeningly it doesn't work for TIM8 TRGO2 either, an ADC-specific trigger, so I must be doing something stupid. And I thought it was hard getting DMA to work, this one's totally difficult to troubleshoot. Obv I want the timer to trigger the DMA conversion so I don't have to spend any processor time on the silly ADC.

I could use an example somewhere. We seem to have few examples laying around for this speedy little big part. The F4 errata says to enable the clock for the DAC when using TIM6 as a trigger and that was promising but didn't help, perhaps I messed it up and need to try again now that I have an entire day in front of me

berendi
berendiBest answer
Principal
February 16, 2020

Here, a fully functional example, tested on a NUCLEO-H743ZI2

void ADC3_IRQHandler() {
	ADC3->ISR = ADC_ISR_EOC; // clear interrupt flag
	GPIOB->ODR ^= 1; // toggle green
}
 
int main() {
	RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_ADC3EN;
	RCC->APB1LENR |= RCC_APB1LENR_TIM6EN;
	GPIOB->MODER &= ~((2u << 28u) | (2u << 0u)); // red, greed led output
	GPIOE->MODER &= ~(2u << 2u); // yellow led output
	TIM6->PSC = 0x3FF; // set some prescaler
	TIM6->EGR = TIM_EGR_UG; // load prescaler
	TIM6->CR2 = TIM_CR2_MMS_1; // master mode 010: update
	TIM6->ARR = 0xFFFF; // set period
	TIM6->SR = 0; // clear update status caused by TIM_EGR_UG
	ADC3_COMMON->CCR = ADC_CCR_CKMODE; // set a valid clock, hclk / 4
	ADC3->CR = 0; // clear sleep mode
	ADC3->CR; // wait a bit
	ADC3->CR = ADC_CR_ADVREGEN; // enable ADC regulator
	some_delay();
	ADC3->CR |= ADC_CR_ADEN; // enable ADC
	while(!(ADC3->ISR & ADC_ISR_ADRDY)) // wait for ADC ready
		;
	ADC3->IER |= ADC_IER_EOCIE; // enable EOC interrupt, just for control
	ADC3->PCSEL = 1; // preselect channel 1
	ADC3->SQR1 = 0; // convert channel 1 only
	ADC3->CFGR = ADC_CFGR_EXTEN_0 | // enable trigger on rising edge
			(13 << ADC_CFGR_EXTSEL_Pos); // trigger #13 is TIM6_TRGO
	ADC3->CR |= ADC_CR_ADSTART; // start ADC (in triggered mode)
	TIM6->CR1 = TIM_CR1_CEN; // start timer
	NVIC_EnableIRQ(ADC3_IRQn);
	while(1) {
		some_delay();
		led_yellow(0); // just a heartbeat blink
		some_delay();
		led_yellow(1);
	}
}

The ADC interrupt handler is not actually needed, I just left it in as a visual indicator that the ADC is doing conversions roughly with the timer frequency.

waclawek.jan
Super User
February 16, 2020

Okay so what's the content of the TIM6 and ADC registers?

JW

n2wx
n2wxAuthor
Senior
February 16, 2020

I don't mean to sound unappreciative but the contents are too voluminous to copy and unless you like interpreting bitmapped fields I don't think you'd enjoy viewing them. Likely Yet Another HAL bug. I need to rid myself of all HAL elements. I stuck to direct access and a little LL thrown in for my uart receive stuff and everything worked the first time through, I should know better

waclawek.jan
Super User
February 16, 2020

This is where I wanted to get you anyway... ;)

JW

ADunc.1
Senior
March 11, 2020

Did you ever get this working? I am having the same problem on an STM32H753, using TIM1 and ADC1. The ADC works correctly using software triggering, the PWM output is working on the timer, but no combination of trigger options will begin a conversion under hardware trigger! I used this same feature on an L4 to synchronize ADC measurements with PWM, but I cant for the life of me get it to work on the H7!

berendi
Principal
March 11, 2020

Yes, this is working, tested.

ADunc.1
Senior
March 11, 2020

OK, thanks for confirming that. I will make a minimal example and send to ST support for help. This application does require microsecond accuracy to sample from the edges of a PWM signal, so I need it to work. Frustrating as it is probably some simple setting...

ADunc.1
Senior
March 11, 2020

Actually, I did manage to resolve it. It works perfectly. TBH, I am not completely sure why it wasn't working yesterday, but it is today. I suspect I did not have master mode enabled...

So for future reference, STM32H753, TIM1 OC2REF or UpdateEvent can be used on TRGO2 to drive ADC1 to start conversions. USe update event for when PWM is off, then switch to OCxREF when PWM is on to trigger on each PWM edge.