STM32 - general timer - input capture

Keywords: stm32

one   General timer input capture overview

one point one   How input capture works

         In the general timer block diagram, input capture mainly involves the top part (selection of counting clock), the middle part (time base unit) and the bottom left part (input capture). Here we mainly explain the lower left part (input capture), and the other two parts can refer to the article: STM32 - general timer - timer interrupt.

         The input capture mode can be used to measure pulse width or frequency. Except TIM6 and TIM7, other timers of STM32 have the function of input capture. Taking a simple pulse input as an example, the following briefly describes the working principle of input capture for measuring pulse width:

         First set the input capture as rising edge detection, and record the timx when the rising edge occurs_ Value of CNT. Then configure the acquisition signal as falling edge acquisition, capture occurs when the falling edge arrives, and record the timx at this time_ Value of CNT. In this way, timx twice before and after_ The difference between the CNT values is the pulse width of the high level. At the same time, according to the counting frequency of TIM, we can know the accurate time of high-level pulse width.

1.2 channel overview of input capture

         Each acquisition / comparison channel is surrounded by an acquisition / comparison register (including shadow register), including the acquisition input part (digital filter, multiplexer and prescaler), and the output part (comparator and output control).
         The capture / compare module consists of a preloaded register and a shadow register. The read-write process only operates on preloaded registers.
                1) In capture mode, the capture occurs on the shadow register and then copied to the preload register.  
                2) In comparison mode, the contents of the preloaded register are copied into the shadow register, and then the contents of the shadow register are compared with the counter.

         The input part samples the corresponding TIx input signal and generates a filtered signal TIxF. Then, an edge detector with polarity selection generates a signal (TIxFPx), which can be triggered as an input from the mode controller or as a capture control. The signal enters the capture register (ICxPS) through pre frequency division.
         Summarize the work process in one sentence: by detecting timx_ For the edge signal on CHX channel, when the edge signal jumps (such as rising edge / falling edge), store the value of the current timer (TIMx_CNT) in the corresponding capture / comparison register (TIMx_CCRx) to complete a capture. At the same time, you can also configure whether to trigger interrupt / DMA during capture.

two   Enter the captured workflow

         The input captured channel diagram is decomposed into four parts. The following four parts are analyzed to understand the working process of input capture:

2.1 setting the input capture filter

         Input capture filter IC1F[3:0], which is used to set the sampling frequency and digital filter length. Including: fCK_INT is the input frequency of the timer, and fDTS is based on timx_ The CKD[1:0] setting of CR1 is determined.
         What does the function of the filter mean here? The digital filter consists of an event counter, which will produce an output jump after recording N events. In other words, if N consecutive samples are at high level, it indicates that this is an effective trigger and will enter the input capture interrupt (if set). In this way, the pulse signals with high-level pulse width less than 8 sampling periods can be filtered, so as to achieve the function of filtering.

two point two   Set input snap polarity

         Here is to set whether the capture event occurs on the rising edge or the falling edge.

two point three   Set input capture mapping relationship

         Since we only show the channel diagram of one channel, if there are several channels:

         In TIMx_CH1 and timx_ In the case of CH2 two channels, we can see that in addition to timx_ The signal captured by ch1 can be connected to IC1, timx_ The signal captured by CH2 can be connected outside IC2, timx_ The signal captured by ch1 can also be connected to IC2 and timx_ The signal captured by CH2 can also be connected to IC1.
         In general, we set it to timx_ The signal captured by ch1 can be connected to IC1, timx_ The signal captured by CH2 can be connected to IC2.

two point four   Set input capture divider

         The setting here is to trigger a capture every N events. That is, we can set it to trigger a capture every 2 rising edge events.

3 input capture related configuration register

three point one   Capture / compare mode register 1 (TIMx_CCMR1)

         Function: in the input acquisition mode, determine the digital filter, channel mapping and pre frequency division coefficient.

three point two   Capture / compare enable register (TIMx_CCER)

         Function: in the input capture mode, determine the capture polarity and capture enable.

three point three   Capture / compare register 1 (timx#u CCR1)

         Function: in input capture mode, determine the count value of the last input capture event transmission.

4 input capture related configuration library functions

four point one   1 input initialization function

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
typedef struct
{
  uint16_t TIM_Channel; //Capture channels 1-4   
  uint16_t TIM_ICPolarity; //Capture polarity
  uint16_t TIM_ICSelection; //Mapping relationship
  uint16_t TIM_ICPrescaler; //division factor 
  uint16_t TIM_ICFilter;  //wave filter
} TIM_ICInitTypeDef;

         Function: initialize acquisition channel, filter, acquisition polarity, mapping relationship, frequency division coefficient and other parameters.
         Note: since the output initialization function separates the functions of all four channels, each defines a function, while the input initialization function does not. Therefore, in the input initialization function, you need to specify the capture channel.

four point two   1 parameter acquisition function

uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

         Function: select one of the four channels to determine the count value of the last input capture event transmission.

four point three   1 parameter setting function

void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

         Function: select one of the four channels and set the channel polarity. Generally, the channel polarity has been set in the initialization function, which is used for modifications other than initialization.

five   General steps for input capture

         Example requirements: use channel 1 (PA0) of TIM5 as input capture, capture the pulse width of high level on PA0 (input high level with WK_UP key), and print the high level pulse time through serial port.

                 1) Initialization timer and clock of IO corresponding to channel: RCC_APB1PeriphClockCmd();
                 2) Initialize the IO port, and the mode is input. Calling function: GPIO_Init();
                 3) Initialize timer ARR, PSC. Calling function: TIM_TimeBaseInit();
                 4) Initialize the input capture channel. Calling function: TIM_ICInit();
                 5) If you want to turn on capture interrupt. Calling function: TIM_ITConfig(); NVIC_Init();
                 6) Enable timer. Calling function: TIM_Cmd();
                 7) Write an interrupt service function. Calling function: TIMx_IRQHandler().

         Follow this general procedure to perform a simple input capture procedure:

//Timer 5 channel 1 input capture configuration
 
TIM_ICInitTypeDef  TIM5_ICInitStructure;
 
void TIM5_Cap_Init(u16 arr,u16 psc)
{	 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
   	NVIC_InitTypeDef NVIC_InitStructure;
 
   //Enable timer and associated IO clock
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//Enable TIM5 clock
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //Enable GPIOA clock

   //Set remapping as required. It is not required here, so it is omitted


   //Initialize IO port as function 	 Set this pin as input 	 GPIOA.0
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 clear previous settings  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 input  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 drop down
	
	//Initialize timer 5 TIM5	 
	TIM_TimeBaseStructure.TIM_Period = arr; //Set the counter auto reload value 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//Prescaler   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //Set clock division: TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM up count mode
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //According to Tim_ The parameter specified in timebaseinitstruct initializes the time base unit of TIMx
  
	//Initialize TIM5 input capture parameters
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01  	 Select input IC1 to map to TI1
  	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//Rising edge capture
  	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //Map to TI1
  	TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //Configure input frequency division without frequency division 
  	TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 configure input filter without filtering
  	TIM_ICInit(TIM5, &TIM5_ICInitStructure);
	
	//Interrupt packet initialization
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3 interrupt
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //Preemptive priority level 2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //From priority level 0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enabled
	NVIC_Init(&NVIC_InitStructure);  //According to NVIC_ Initializes the peripheral NVIC register with the parameters specified in initstruct 

   //Allow update interrupt
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//Allow update interrupt and CC1IE capture interrupt	

   //Enable timer
   	TIM_Cmd(TIM5,ENABLE ); 	//Enable timer 5
 
}
 
u8  TIM5CH1_CAPTURE_STA=0;	//Input capture status		    				
u16	TIM5CH1_CAPTURE_VAL;	//Enter capture value
 
//Timer 5 interrupt service routine	 
void TIM5_IRQHandler(void)
{ 
 
 	if((TIM5CH1_CAPTURE_STA&0X80)==0)//Not captured successfully	
	{	  
		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
		 
		{	    
			if(TIM5CH1_CAPTURE_STA&0X40)//High level has been captured
			{
				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//The high level is too long
				{
					TIM5CH1_CAPTURE_STA|=0X80;//Tag successfully captured once
					TIM5CH1_CAPTURE_VAL=0XFFFF;
				}else TIM5CH1_CAPTURE_STA++;
			}	 
		}
	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//Capture event occurred in capture 1
		{	
			if(TIM5CH1_CAPTURE_STA&0X40)		//A falling edge was captured 		
			{	  			
				TIM5CH1_CAPTURE_STA|=0X80;		//The flag successfully captures a high-level pulse width
				TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 set as rising edge capture
			}else  								//Not yet started, first capture rising edge
			{
				TIM5CH1_CAPTURE_STA=0;			//empty
				TIM5CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM5,0);
				TIM5CH1_CAPTURE_STA|=0X40;		//The tag captures the rising edge
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 set as falling edge capture
			}		    
		}			     	    					   
 	}
 
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //Clear interrupt flag bit
 
}
extern u8  TIM5CH1_CAPTURE_STA;		//Input capture status		    				
extern u16	TIM5CH1_CAPTURE_VAL;	//Enter capture value	
 int main(void)
 {		
 	u32 temp=0; 
	delay_init();	    	 //Delay function initialization	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //Set NVIC interrupt packet 2: 2-bit preemption priority and 2-bit response priority
	uart_init(115200);	 //The serial port is initialized to 115200
 
 	TIM5_Cap_Init(0XFFFF,72-1);	//Count at 1Mhz 
   	while(1)
	{
 		delay_ms(10);
		 		 
 		if(TIM5CH1_CAPTURE_STA&0X80)//Successfully captured a rising edge
		{
			temp=TIM5CH1_CAPTURE_STA&0X3F;
			temp*=65536;//Total overflow time
			temp+=TIM5CH1_CAPTURE_VAL;//Get the total high level time
			printf("HIGH:%d us\r\n",temp);//Print total leveling time
			TIM5CH1_CAPTURE_STA=0;//Turn on next capture
		}
	}
 }

five point one   Code logic

         The initialization part of input capture here is relatively simple, just refer to the general steps. But in the interrupt handler TIM5_IRQHandler() is difficult. Why is it complicated?
         As we capture the input, once the rising edge is captured, set the current value of the counter to 0 and let it start counting again from 0:

TIM_SetCounter(TIM5,0);

         However, if the pulse length is too wide, that is, a cycle from counting from 0 to automatic reload value ends, and the pulse still does not end. In this case, it is obvious that you can't just record the current value of the last counter.
         The solution to this problem:

         Set a variable TIM5CH1_CAPTURE_STA, bit5-0 is the number of times the timer overflows after capturing the high level, bit6 is the capture high level flag, and bit7 is the capture completion flag.
         Set two interrupts at the same time (update interrupt and capture interrupt):

TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//Allow update interrupt and CC1IE capture interrupt	

         In the interrupt processing function, first judge whether the capture is successful. If the capture is successful, it means that nothing needs to be done in the pulse low-level stage; If the capture is not successful, it indicates that it is in the high-level phase of the pulse, you need to continue to judge the interrupt type, and then process it separately. In the update interrupt, it indicates that the pulse length is too long at this time, TIM5CH1_CAPTURE_STA plus 1. In the capture interrupt, judge whether the captured edge is a rising edge. If so, the current value of the counter is cleared and TIM5CH1_CAPTURE_STA is cleared, the flag is marked at the same time, and the falling edge capture of polarity is set; If not, mark that the capture is complete, save the value of the current counter, and set the polarity rising edge capture.

five point two   extern keyword

         In C language, extern can be placed in front of variables or functions to represent the definition of variables or functions. In other files, it prompts the compiler to find the definition of such variables and functions in other modules.
         Note: extern al variables can be declared multiple times, but defined only once.

Posted by thecookie on Tue, 05 Oct 2021 15:19:59 -0700