cancel
Showing results for 
Search instead for 
Did you mean: 

Input capture without HAL with nucleo STM32g474RE

Tommino
Senior

Hello,

I would like to measure the period of a square wave (around 1 kHz) with a timer in input capture configuration. I am using TIM5_CH3 on pin PA2 (nucleo G474RE).

I would like to receive a bit of help in debugging my code since i am not able to measure the period of an external waveform (also tracked on my oscilloscopes).

void TIM5_Init_ToMeasurePeriod (void){
	 
	 // Enable the GPIOA Clock
	 RCC-> AHB2ENR |= (1<<0); // Enable the GPIOA clock writing 1 in bit 0
	 // Configure the GPIO
	 GPIOA -> MODER |= (1<<5); // Port PA2 configured as alternate function
	 GPIOA -> AFR[0] |= (1<<9); // Set alternate Function 2 i.e. TIM5_CH3 for PA2 
	 // Enable the timer clock
	 RCC->APB1ENR1 |= (1<<3); // Enable the tim5 clock writing 1 in bit 3
	 // Set the timer timebase
	 TIM5 -> PSC = 1; // 80 MHz/2= 40MHz clock --> 0.025 us count set the resolution (1ms=40000 counts)
	 TIM5-> ARR = 0xFFFF; 
	 TIM5 -> CCMR2 |= (1<<0); // CC3 channel configured as input and tim_ic3 is mapped on tim_ti3
	 TIM5 -> CCMR2 |= (1<<5); // Input capture filter set to N=4. 4 consecutive samples with the new level are required (to avoid noise during the toggling)
	 TIM5-> TISEL |= (0<<16); // TIM5_ch3 is the input of the timer
	  TIM5->CCER |= (1<<8)|(0<<0)|(0<<0); // Enable capture from the counter into the capture register
	 //TIM5->DIER |= (1<<3); // interupt enable for channel 3 not used now
	 // start the timer
	 TIM5 -> CR1|= TIM_CR1_CEN;
	 /* Clear the Capture event flag for channel 1 */
	 //TIM5->SR = ~TIM_SR_CC3IF;
 
 
}
uint32_t Period;
int main(void)
{
 
TIM5_Init_ToMeasurePeriod();
 
while (!(TIM5->SR & TIM_SR_CC3IF)){}
    	 /* An active edge was detected, so store the timestamp */
    	 period = TIM5 -> CCR3; // store the CCR register in 0.025 us counts
 
 

Thanks a lot

0693W00000Nq2E1QAJ.png0693W00000Nq2EGQAZ.png

1 ACCEPTED SOLUTION

Accepted Solutions

GPIOA->MODER6 = 0xabfff7bf => for PA6 the MODER field is 0b11 - note that GPIOx_MODER in 'G4 is not zero after reset.

JW

View solution in original post

11 REPLIES 11
KnarfB
Principal III

On that Nucleo board PA2 is also connected to VCP_RX, may that interferes. If you don't want HAL, you may generate low level (LL) code in STM32CubeIDE and compare to yours. LL is close to register level.

hth

KnarfB

Tommino
Senior

HI @KnarfB​ 

My goal is to use the register based approach to mesure the period of an incoming square wave.

and basically I followed this paragraph in AN4776

0693W00000Nq7FnQAJ.pngAs you pointed out the PA2 might interfere with STlink in nucleo g474re so I moved to PA6 (TIM3_ch1) but at the end things are not working because CCR1 keeps staying at 0. I have plugged to PA6 a squarewave with suitable voltage levels (0-3v3 at 1 kHZ).

this is my code now

void TIM3_Init_ToMeasurePeriod (void){
	 
	 // configure PA6 pin to TIM3_CH1 with alternate function AF to measureperiod
	 // Enable the GPIOA Clock
	 RCC-> AHB2ENR |= (1<<0); // Enable the GPIOA clock writing 1 in bit 0
	 // Configure the GPIO
	 GPIOA -> MODER |= (1<<13); // Port PA6 configured as alternate function
	 GPIOA -> AFR[0] |= (1<<25); // Set alternate Function 2 i.e. TIM3_CH1 for PA6 
	 // Enable the timer clock
	 RCC->APB1ENR1 |= (1<<1); // Enable the tim3 clock writing 1 in bit 1
	 // Set the timer timebase
	 TIM3 -> PSC = 1; 
	 TIM3-> ARR = 0xFFFF; // it should set the lowest measurable framerate
	 TIM3 -> CCMR1 |= (1<<0); // CC1 channel configured as input 
	 //TIM3 -> CCMR1 |= (1<<5); // Input capture filter set to N=4. 4 consecutive samples with the new level are required (to avoid noise during the toggling)
	 TIM3-> TISEL |= (0<<16); 	 
	 // enable the capture compare
	 TIM3->CCER |= (1<<0); // Enable capture from the counter into the capture register
	 //TIM3->DIER |= (1<<3); // interrupt enable for channel 3
	 // start the timer
	 TIM3 -> CR1|= TIM_CR1_CEN;
	
}
 
uint32_t Timestamp;
 
int main(void)
{
TIM3_Init_ToMeasurePeriod();
 /* Clear the Capture event flag for channel 3 */
	 //TIM3->SR = ~TIM_SR_CC1IF;
  while (1)
  {
	  TIM3->SR = ~TIM_SR_CC1IF;
	    while (!(TIM3->SR & TIM_SR_CC1IF));
	    /* An active edge was detected, so store the timestamp */
	   Timestamp = TIM3 -> CCR1; // store the CCR register in 0.025 us counts
  
 
  }
 
}

Can you help me in solving this?

Thanks

> I moved to PA6 (TIM3_ch1)

Check/post registers' content.

Also try this the other way round: generate PWM onto PA6 from TIM3_CH1, and measure it.

JW

Tommino
Senior

HI @Community member​ 

These are the registers settings while I am doing input capture on PA6/Tim3_Ch1 with the code I shared in my last post

0693W00000NqAgSQAV.png0693W00000NqAhfQAF.png0693W00000NqAgcQAF.png 

Ok I will try also to generate a pwm with this timer. I will let you know as soon as I do it

Thanks a lot

GPIOA->MODER6 = 0xabfff7bf => for PA6 the MODER field is 0b11 - note that GPIOx_MODER in 'G4 is not zero after reset.

JW

Tommino
Senior

HI @Community member​ 

Thanks!!!

Indeed the point is that the reset value is : 0xABFF FFFF (for port A)

However, there is a weird issue coming out when writing the MODER register...

If I write

GPIOA -> MODER |= (1<<13)|(0<<12);

It does not change the register content of mode6

On the contrary if I write

GPIOA -> MODER = 0xabffefff;

It changes the register content of mode6

Have you ever experienced something like this?

The "or" operator can only make a 0 to a 1. You need to mask out bits and set the new value like

GPIOA -> MODER = (GPIOA -> MODER & ~(3<<12)) | (2<<12);

hth

KnarfB

Tommino
Senior

Ok thank @KnarfB​ 

I got it.

With the previous mentioned TIM3_Init_ToMeasurePeriod function I can see the CCR1 register updating.

Now, how can I properly compute the period of the external PWM?

I check the CC1F flag with a bitwise & operator and then tried to compute the difference between the last two captured values.

This is the code that I put inside the main loop and that was supposed to measure the period. However the variables keeps at zero.

Is this "TIM3->SR & 0x00000002" correct to check the flag? Since I am using CC1 I checked the first bit but maybe I am doing some mess with the operator

while (1)
  {
	  	  
	   if ( TIM3->SR & 0x00000002 )
	   {
 
		   ICValue = TIM3 -> CCR1;
		   Period= ICValue-ICValue_old; // us count
		   ICValue_old=ICValue;
		   TIM3->SR = ~TIM_SR_CC1IF;
	   }
   }

> Is this "TIM3->SR & 0x00000002" correct to check the flag?

Yes, although I'd write

if (TIM3->SR & TIM_SR_CC1IF) {}

you use the TIM_SR_CC1IF symbol anyway.

If you don't "consume" the Period variable's value, compiler/optimizer may keep it in register, or optimize it out entirely. You prevent it by qualifying the Period variable as volatile.

JW