Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Usually output PWM waveform is always output. But in motor control often only need to output a certain number of pulses, do not need to output all the time, so this requires every time the output OF PWM, the number of output pulses controllable. There are generally three ways to do this.

Method one:

void TIM1_PWM_Init(u16 arr, u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitSturcture;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);

    TIM_OCStructInit(& TIM_OCInitSturcture);

    TIM_OCInitSturcture.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitSturcture.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitSturcture.TIM_Pulse = 0;
    TIM_OCInitSturcture.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM1, &TIM_OCInitSturcture);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);	// Enable the main output
    TIM_ARRPreloadConfig(TIM1, ENABLE);

    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM1, ENABLE);				// Enable the counter
    TIM_SetCompare1(TIM1, arr / 2);
}

void TIM1_UP_IRQHandler(void)
{
    static  u16 y;
    if(TIM_GetITStatus(TIM1, TIM_IT_Update) ! = RESET) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update ); y++;if(y > 60)			// Set the output pulse count
        {
            y = 0; TIM_Cmd(TIM1, DISABLE); }}}Copy the code

The principle of this method is the simplest, is to open the UPDATE interrupt function of THE PWM output, when the timer value is reloaded once, it will produce an interrupt, on behalf of the output of a pulse. In this way, every time into an interrupt, the statistics of an interrupt number, when the interrupt number and the number of required pulses is equal to turn off the PWM output. This also realizes the function of specifying the pulse output.

Method 2:

void PWMS8_Init(u32 Cycle, u32 PulseNum)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    TIM_TimeBaseStructure.TIM_Period = Cycle;			/ / frequency
    TIM_TimeBaseStructure.TIM_Prescaler = 71;			// The dividing frequency is 72M/(71+1) = 1M
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = PulseNum - 1;	  // The number of pulses
    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);

    TIM_GenerateEvent(TIM8, TIM_EventSource_Update);
    TIM_InternalClockConfig(TIM8);
    TIM_SelectOCxM(TIM8, TIM_Channel_2, TIM_OCMode_PWM2);

    /* PWM1 Mode configuration: Channel2 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = Cycle / 2;		// Low level duty cycle
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCNPolarity_Low;
    TIM_OC2Init(TIM8, &TIM_OCInitStructure);	
		
    TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM8, ENABLE);
		
    /* TIM8 enable counter */
    TIM_CtrlPWMOutputs(TIM8, ENABLE);

    TIM_SelectOnePulseMode(TIM8, TIM_OPMode_Single);
    TIM_Cmd(TIM8, ENABLE);
}

Copy the code

In the first method, the loading times of ARR are counted by the code, and the microcontroller has its own function. In the second method, the system’s own function is directly used. This feature is the repeat counter.

In the initialization will output the number of pulses written into the repeat counter, so that the system will automatically count the number of pulse output PWM, when the output pulse number is completed will automatically stop PWM output.

Method 3:

/ * * * * * * * * * * * * * * * * * * * * * * * TIM1 initialization function * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * *, * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/******u32 Cycle Set counting frequency (calculation formula: Cycle=1Mhz/ target frequency) */
/ return value: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * no * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void TIM1_config(u32 Cycle)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_TIM1 , ENABLE); // The clock is enabled
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;                   //TIM1_CH1 PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;             // reuse push-pull output
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    TIM_TimeBaseStructure.TIM_Period = Cycle- 1;                 // Use Cycle to control the frequency (f=72/(71+1)/Cycle)
    TIM_TimeBaseStructure.TIM_Prescaler =71;         // Sets the predivision value used as the divisor of the TIMx clock frequency
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     // Set clock split: TDTS= Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM up count mode
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;            // Repeat count, must =0!! (Exclusive to advanced timers)
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);                                       
 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;          		// Select timer mode: TIM pulse width modulation mode 2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	// Enable the comparison output
    TIM_OCInitStructure.TIM_Pulse = Cycle/2- 1;                    	// Set the pulse value to be loaded into the capture register (duty cycle: 50% by default, this can also be adjusted as a parameter if necessary)
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;      	// Output polarity
 
    TIM_OC3Init(TIM1, &TIM_OCInitStructure);        				       // Enable channel 1
 
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);	// Set it to master/slave mode
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);			// Select the trigger mode for timer 1 (use update event as trigger output)
    
 
    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);               // Enable channel 1 preloaded register
    TIM_ARRPreloadConfig(TIM1, ENABLE);                             // Enable TIM1 preloaded register on ARR
}
/ * * * * * * * * * * * * * * * * * * * * * * * TIM2 initialization function * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * *, * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * u32 PulseNum is used to set the pulse number * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ return value: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * no * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void TIM2_config(u32 PulseNum)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure; 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);		// Enable the clock of timer 2
 
    TIM_TimeBaseStructure.TIM_Period = PulseNum;   			/ / pulse number
    TIM_TimeBaseStructure.TIM_Prescaler =0;    
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  
 
    TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);	// Select the input trigger source for timer 2 (internal trigger (TIM1))
 
    TIM2->SMCR|=0x07;                    // Set slave mode register (SMS[2:0]:111 external clock mode 1)
 
    TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE);		// Update interrupt failure
 
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;        
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;     
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);								// Timer 2 interrupts initialization
}
/ * * * * * * * * * * * * * * * * * * * * * * * * pulse output function * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * *, * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/******u32 Cycle Set counting frequency (calculation formula: Cycle=1Mhz/ target frequency) */
/******u32 PulseNum Used to set the number of output pulses (unit: unit) ************/
/ return value: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * no * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void Pulse_output(u32 Cycle,u32 PulseNum)
{
    TIM2_config(PulseNum);						// Set the number of pulses
    TIM_Cmd(TIM2, ENABLE);						// Enable TIM2 (slave timer)
    TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	// Clear the interrupt flag bit
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);	// Enable update interrupts
    TIM1_config(Cycle);							// Enable timer 1 (primary timer)
    
    TIM_Cmd(TIM1, ENABLE);						// Enable timer 1
    TIM_CtrlPWMOutputs(TIM1, ENABLE);   		// Advanced timer must be added, main output enable
}
  
/ * * * * * * * * * * * * * * * * * * * * timer interrupt service function of 2 * * * * * * * * * * * * * * * * * * * * * * /
/ * * * *, * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * u32 PulseNum is used to set the pulse number * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ return value: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * no * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * function description: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* TIM1 and TIM2*/ are closed when TIM's CNT register reaches its Update value
void TIM2_IRQHandler(void) 
{ 
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) ! = RESET)//TIM_IT_Update
    { 
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 	 // Clear the interrupt flag bit
        TIM_CtrlPWMOutputs(TIM1, DISABLE);  				   // Main output is disabled
        TIM_Cmd(TIM1, DISABLE); 						           // Turn off the timer
        TIM_Cmd(TIM2, DISABLE); 						          // Turn off the timer
        TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); 	// Turn off TIM2 update interrupts}}Copy the code

Method three uses the timer synchronization function of the system, that is, timer 1 outputs PWM wave, and then synchronizes the output of timer 1 to timer 2, and then counts the output pulse of timer 1 through the counter of timer 2. When the count value is equal to the specified value, the output of timer 1 is closed.

Timer 1 is set to master mode and timer 2 to slave mode. The PWM output of timer 1 is used as the clock of timer 2. Thus setting the frequency value of timer 2 is equivalent to setting the pulse output number of timer 1.

Test all three patterns together in the main function.


int main(void)
{    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    delay_init();	
    while(1)
    {			
      // Each call emits a pulse
      / / TIM1_PWM_Init (899, 0); / / method
      Pulse_output(100.16);			/ / method 2
      / / PWMS8_Init (1000, 6); / / method 3
      delay_ms(500); }}Copy the code

In the main function respectively call these three methods, and then through the oscilloscope can be seen, when the PWM output a certain number of pulses will stop output.