cancel
Showing results for 
Search instead for 
Did you mean: 

Simple 32F417 TIM1 usage to measure TIM1_CH1 pulse duration

PHolt.1
Senior III

I have PE9 as an input, and using AF1, PE9 should be connecting to TIM1_CH1 input.

Input is 400Hz square wave, 0 to +3.3V.

This is the code

// Set up TIM1 for ~400Hz waveform period measurement, via PE9
static void KDE_config_TIM1_period_measure(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
 
	__GPIOE_CLK_ENABLE();				// already enabled in switch init but never mind
	GPIO_InitStruct.Pin  = GPIO_PIN_9;
	GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;	// PE9 = TIM1_CH1
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;		// PE9 = TIM1_CH1 input, not the output!
	GPIO_InitStruct.Pull = GPIO_PULLUP;	// PE9 = pullup because comparator driving it is open drain o/p
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
	// Set up TIM1 to count up and on PE9 +ve edge transfer the count to CCR and reload with 0
	__HAL_RCC_TIM1_CLK_ENABLE();		// TIM1EN=1 in RCC2ENR and does a short delay
 
	TIM1->CR1 = 0;						// TIM1 upcounter, counter disabled for now
 
	TIM1->ARR = 0xffff;					// auto reload value
 
	TIM1->PSC = 3;						// prescaler=4, for 10.5MHz counter speed
 
	TIM1->CCMR1 = (1 << 4)				// IC1F: input filter = 2
				 |(0 << 2)				// IC1PSC: input prescaler off
				 |(1 << 0);				// CC1S: 01 = channel is input, from TI1
 
	TIM1->CCER = (0 << 3)				// CC1NP: CC1 rising edge capture
			    |(0 << 2)				// CC1NE: oc1n not active
				|(0 << 1)				// CC1P: rising edge capture
				|(1 << 0);				// CC1E: enable CC1 capture
 
	TIM1->EGR = 0;						// CC1G=0
 
	TIM1->CNT = 0;
	TIM1->CR2 = 0;						// TI1S=0 - TIM1_CH1 pin is connected to TI1 input
	TIM1->RCR = 0;
	TIM1->SMCR = 0;						// slave mode disabled, internal clock source
	TIM1->DIER = 0;						// interrupts disabled
 
	TIM1->CR1 = (1 << 0);				// CEN=1 - enable TIM1
 
}
 

and this is the RTOS tack which is reading CCR1

	while (1)
	{
 
		uint16_t cnt = TIM1->CNT;
//		TIM1->EGR = (1<<1);	// force capture CNT to CCR1
		uint16_t ccr1 = TIM1->CCR1;
		uint16_t sr = TIM1->SR;
		debug_thread_printf("===== TIM1 = %5u %5u %05x", cnt, ccr1, sr);
 
		osDelay(1000);
 
	}

The output I am getting is

==== TIM1 = 63286   0 0001d

with various values for the CNT, so the counter is running ok. If I uncomment the above force capture line, I get the software-triggered capture taking place (3rd value not shown here)

===== TIM1 = 16491 16493

===== TIM1 = 58404 58406

===== TIM1 = 34750 34753

===== TIM1 = 11067 11069

===== TIM1 = 52958 52961

===== TIM1 = 29275 29277

so it just looks like the PE9 signal is not getting through to IC1PS. I have checked it with a scope on the pin. I al also suspecting that my PE9 config is not right; I cannot find any reference to actual init code for PE9 for the TIM1_CH1 input; all examples I found show PE9 to be the TIM1_CH1 pwm output. Is it correct that selecting AF1 and selecting PE9 as an input is sufficient to select TIM1_CH1 input?

Many thanks for any tips. I've spent 2 days on this so far, and reading the RM, appnots, etc. Page 662/1751 of the RM tells you how to do exactly this, so something must be missing.

9 REPLIES 9
TDK
Guru

> GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

This sets the pin in input mode, not AF mode.

To use a pin with a timer, the pin needs to be in AF mode. whether it's being used as an input or an output. You can step through the logic in HAL_GPIO_Init to see how this is done.

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

What mode should I use?

The available modes are:

#define GPIO_MODE_INPUT            0x00000000U  /*!< Input Floating Mode          */

#define GPIO_MODE_OUTPUT_PP          0x00000001U  /*!< Output Push Pull Mode         */

#define GPIO_MODE_OUTPUT_OD          0x00000011U  /*!< Output Open Drain Mode        */

#define GPIO_MODE_AF_PP            0x00000002U  /*!< Alternate Function Push Pull Mode   */

#define GPIO_MODE_AF_OD            0x00000012U  /*!< Alternate Function Open Drain Mode  */

#define GPIO_MODE_ANALOG            0x00000003U  /*!< Analog Mode */

   

#define GPIO_MODE_IT_RISING          0x10110000U  /*!< External Interrupt Mode with Rising edge trigger detection     */

#define GPIO_MODE_IT_FALLING          0x10210000U  /*!< External Interrupt Mode with Falling edge trigger detection     */

#define GPIO_MODE_IT_RISING_FALLING      0x10310000U  /*!< External Interrupt Mode with Rising/Falling edge trigger detection */

#define GPIO_MODE_EVT_RISING          0x10120000U  /*!< External Event Mode with Rising edge trigger detection        */

#define GPIO_MODE_EVT_FALLING         0x10220000U  /*!< External Event Mode with Falling edge trigger detection       */

#define GPIO_MODE_EVT_RISING_FALLING      0x10320000U  /*!< External Event Mode with Rising/Falling edge trigger detection    */

I can see AF_PP or AF_OD but both of those are output structures, not an input pin.

TDK
Guru

> I can see AF_PP or AF_OD but both of those are output structures, not an input pin.

There are neither explicitly input nor output. Rather, the direction is controlled by the peripheral selected.

Use either of GPIO_MODE_AF_PP or GPIO_MODE_AF_OD as the output drive type doesn't matter here.

CubeMX can generate this code for you.

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

The problem is that TIM1_CH1 is the same name for two signals. Page 517/1751 of the RM. One is an input and one is an output. I think PE9 will connect to one or the other, according to whether it is configured as input or output... how else can this work? The problem is that all examples I can find online use PE9 as a PWM output.

You would save me another day of googling around forums and wading through countless posts, if you could tell me what I should use.

I have selected this

GPIO_InitStruct.Pin = GPIO_PIN_9;

GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; // PE9 = TIM1_CH1

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // PE9 = TIM1_CH1 input, not the output!

GPIO_InitStruct.Pull = GPIO_PULLUP; // PE9 = pullup because comparator driving it is open drain o/p

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

but I am worried that if PE9 is being driven by the chip, something will be getting hot.

But now I am seeing values in CCR1, plus CC1OF getting set which is as expected. The values are garbage though. Not quite garbage; CCR1 is a smaller value than CNT, with the difference being smaller as the frequency on PE9 increases. It looks like CNT is not getting reset upon the +ve edge arriving on TIM1_CH1.

TDK
Guru

> One is an input and one is an output.

They're the same signal. If it's being used as an input, the internal connections will route it appropriately. If it's an output, the internal connection will drive it.

> You would save me another day of googling around forums and wading through countless posts, if you could tell me what I should use.

I already did.

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

In case it helps, the direction of TIM1_CH1 is chosen when you configure the channel, not the pin. Specifically, the CCxS field in the TIMx->CCMRx register.

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

Great; thank you. I can see what they have done.

Could you help me with the CNT auto-reset setup? Upon +ve edge on PE9, I want CNT to reset to 0 but before it does so, transfer itself to CCR1. I have tried SMS=100 in SMCR but it doesn't do anything

	GPIO_InitTypeDef  GPIO_InitStruct;
 
	__GPIOE_CLK_ENABLE();				// already enabled in switch init but never mind
	GPIO_InitStruct.Pin  = GPIO_PIN_9;
	GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;	// PE9 = TIM1_CH1
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;		// PE9 = AF, and is an input because CH1=input in TIM1 cfg
	GPIO_InitStruct.Pull = GPIO_PULLUP;	// PE9 = pullup because comparator driving it is open drain o/p
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
	// Set up TIM1 to count up and on PE9 +ve edge transfer the count to CCR and reload with 0
	__HAL_RCC_TIM1_CLK_ENABLE();		// TIM1EN=1 in RCC2ENR and does a short delay
 
	TIM1->CR1 = 0;						// TIM1 upcounter, counter disabled for now with CEN=0
 
	TIM1->ARR = 0xffff;					// auto reload value
 
	TIM1->PSC = 3;						// prescaler=4, for 10.5MHz counter speed
 
	TIM1->CCMR1 = (1 << 4)				// IC1F: input filter = 2
				 |(0 << 2)				// IC1PSC: input prescaler off
				 |(1 << 0);				// CC1S: 01 = channel is input, from TI1
 
	TIM1->CCER = (0 << 3)				// CC1NP: CC1 rising edge capture
			    |(0 << 2)				// CC1NE: oc1n not active
				|(0 << 1)				// CC1P: rising edge capture
				|(1 << 0);				// CC1E: enable CC1 capture
 
	TIM1->EGR = 0;						// CC1G=0
 
	TIM1->CNT = 0;
	TIM1->CR2 = 0;						// TI1S=0 - TIM1_CH1 pin is connected to TI1 input
	TIM1->RCR = 0;
	TIM1->SMCR = (1 << 2);				// sms=100 (auto reset), internal clock source
	TIM1->DIER = 0;						// interrupts disabled
 
	TIM1->CR1 = (1 << 0);				// CEN=1 - enable TIM1

As I've told you, this is described in the PWM input mode subchapter in TIM chapter of RM.

You need to set not only TIMx_SMCR.SMS to Reset mode, but also the source of trigger into the slave-mode controller to TI1FP1, i.e. set TIMx_SMCR.TS to 0b101.

JW

PHolt.1
Senior III

Excellent - thank you!

With a 400Hz input I am seeing CCR1=26242 which is right with PCLKx=42MHz and with the TIM1 counter running from 2x that, that works out exactly right. The code, which produces a continuously updated value in CCR, is below in case somebody finds this one day

 
/*
 *
 * Set up TIM1 for ~400Hz waveform period measurement, via PE9
 *  This produces a continuously updated value in TIM1->CCR1. This example code displays it:
 *	while (1)
 *	{
 *		uint16_t ccr1 = TIM1->CCR1;
 *		debug_thread_printf("CCR1 = %5u", ccr1);
 *		osDelay(1000);
 *	}
 *	TIM1 is clocked at 2xPCLK2 i.e. 84MHz here. The /8 prescaler drops this to 10.5MHz
 *	which is good for 400Hz and delivers a value around 26240.
 *
 */
 
static void KDE_config_TIM1_period_measure(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
 
	__GPIOE_CLK_ENABLE();				// already enabled in switch init but never mind
	GPIO_InitStruct.Pin  = GPIO_PIN_9;
	GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;	// PE9 = TIM1_CH1
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;		// PE9 = AF, and is an input because CH1=input in TIM1 cfg
	GPIO_InitStruct.Pull = GPIO_PULLUP;	// PE9 = pullup because comparator driving it is open drain o/p
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
	// Set up TIM1 to count up and on PE9 +ve edge transfer the count to CCR and reload with 0
	__HAL_RCC_TIM1_CLK_ENABLE();		// TIM1EN=1 in RCC2ENR and does a short delay
 
	TIM1->CR1 = 0;						// TIM1 upcounter, counter disabled for now with CEN=0
 
	TIM1->ARR = 0xffff;					// auto reload value
 
	TIM1->PSC = 7;						// prescaler=8, for 10.5MHz counter speed
 
	TIM1->CCMR1 = (1 << 4)				// IC1F: input filter = 2
				 |(0 << 2)				// IC1PSC: input prescaler off
				 |(1 << 0);				// CC1S: 01 = channel is input, from TI1
 
	TIM1->CCER = (0 << 3)				// CC1NP: CC1 rising edge capture
			    |(0 << 2)				// CC1NE: oc1n not active
				|(0 << 1)				// CC1P: rising edge capture
				|(1 << 0);				// CC1E: enable CC1 capture
 
	TIM1->EGR = 0;						// CC1G=0
 
	TIM1->CNT = 0;
	TIM1->CR2 = 0;						// TI1S=0 - TIM1_CH1 pin is connected to TI1 input
	TIM1->RCR = 0;
	TIM1->SMCR = (1 << 2)				// internal clock source, SMS=100 (reset mode)
				|(1 << 6)				// TS=101 (TI1FP1)
				|(1 << 4);
	TIM1->DIER = 0;						// interrupts disabled
 
	TIM1->CR1 = (1 << 0);				// CEN=1 - enable TIM1
 
}