小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

  本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

  通常输出PWM波形的时候是一直输出的。但是在电机控制中往往只需要输出一定个数的脉冲,不需要一直输出,那么这就需要每次输出PWM时,输出的脉冲个数可控。要实现这个功能,一般有三种方法。

  方法一:

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);	//主输出使能
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM1, ENABLE);				//使能计数器
    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)			//设置输出脉冲数
        {
            y = 0;
            TIM_Cmd(TIM1, DISABLE);
        }
    }
}

  这个方法实现的原理最简单,就是开启PWM输出的更新中断功能,当计时器值重新加载一次时,就会产生一个中断,就代表输出了一个脉冲。这样每进一次中断,就统计一次中断次数,当中断的次数和需要的脉冲数相等时就关闭PWM的输出。这样也就实现了指定脉冲输出的功能。

  方法二:

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;			//频率
    TIM_TimeBaseStructure.TIM_Prescaler = 71;			//分频值  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;	  //输出脉冲个数
    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;		// 低电平时间  占空比
    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);
}

  在方法一中是通过代码自己统计ARR的装载次数,而单片机内部也自带了这个功能,方法二中就直接使用这个系统自带的功能。这个功能就是重复计数器。

STM32F103单片机PWM单脉冲输出模式
STM32F103单片机PWM单脉冲输出模式
STM32F103单片机PWM单脉冲输出模式

  在初始化时将要输出的脉冲个数写入到重复计数器中,这样系统就会自动统计输出PWM的脉冲个数,当输出脉冲个数完成后就会自动停止PWM输出。

  方法三:

/***********************TIM1初始化函数*************************/
/****参数:****************************************************/
/******u32 Cycle用于设定计数频率(计算公式:Cycle=1Mhz/目标频率)*/
/****返回值:**************************************************/
/******无*****************************************************/
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); //时钟使能
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;                   //TIM1_CH1 PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;             //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    TIM_TimeBaseStructure.TIM_Period = Cycle-1;                 //使用Cycle来控制频率(f=72/(71+1)/Cycle)  当Cycle为100时脉冲频率为10KHZ                           
    TIM_TimeBaseStructure.TIM_Prescaler =71;         //设置用来作为TIMx时钟频率除数的预分频值                                                     
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //设置时钟分割:TDTS= Tck_tim            
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;            //重复计数,一定要=0!!!(高级定时器特有)
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);                                       
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;          		//选择定时器模式:TIM脉冲宽度调制模式2     
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//比较输出使能
    TIM_OCInitStructure.TIM_Pulse = Cycle/2-1;                    	//设置待装入捕获寄存器的脉冲值(占空比:默认50%,这可也可以调节如果需要的话将它作为一个参数传入即可)                                   
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;      	//输出极性       
    TIM_OC3Init(TIM1, &TIM_OCInitStructure);        				       //使能通道1                                              
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);	//设置为主从模式
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);			//选择定时器1的触发方式(使用更新事件作为触发输出)
    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);               //使能通道1预装载寄存器               
    TIM_ARRPreloadConfig(TIM1, ENABLE);                             //使能TIM1在ARR上的预装载寄存器       
}
/***********************TIM2初始化函数*************************/
/****参数:****************************************************/
/******u32 PulseNum用于设定脉冲数量****************************/
/****返回值:*************************************************/
/******无*****************************************************/
void TIM2_config(u32 PulseNum)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure; 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);		//使能定时器2的时钟
    TIM_TimeBaseStructure.TIM_Period = PulseNum;   			//脉冲数
    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);	//选择定时器2的输入触发源(内部触发(TIM1))
    TIM2->SMCR|=0x07;                    //设置从模式寄存器(SMS[2:0]:111 外部时钟模式1) 
    TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE);		//更新中断失能
    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);								//定时器2中断初始化
}
/************************脉冲输出函数**************************/
/****参数:****************************************************/
/******u32 Cycle用于设定计数频率(计算公式:Cycle=1Mhz/目标频率)*/
/******u32 PulseNum用于设定输出脉冲的数量(单位:个)************/
/****返回值:**************************************************/
/******无*****************************************************/
void Pulse_output(u32 Cycle,u32 PulseNum)
{
    TIM2_config(PulseNum);						//设置脉冲数量
    TIM_Cmd(TIM2, ENABLE);						//使能TIM2(从定时器)
    TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	//清除中断标志位
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);	//使能更新中断
    TIM1_config(Cycle);							//使能定时器1(主定时器)
    TIM_Cmd(TIM1, ENABLE);						//使能定时器1
    TIM_CtrlPWMOutputs(TIM1, ENABLE);   		//高级定时器一定要加上,主输出使能
}
/********************定时器2的中断服务函数**********************/
/****参数:****************************************************/
/******u32 PulseNum用于设定脉冲数量****************************/
/****返回值:*************************************************/
/******无*****************************************************/
/****函数说明:************************************************/
/*当TIM的CNT寄存器的值到达设定的Update值会触发更新中断,此时设定的脉冲数已输出完毕,关闭TIM1和TIM2*/
void TIM2_IRQHandler(void) 
{ 
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 	//TIM_IT_Update
    { 
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 	 // 清除中断标志位 
        TIM_CtrlPWMOutputs(TIM1, DISABLE);  				   //主输出失能
        TIM_Cmd(TIM1, DISABLE); 						           //关闭定时器 
        TIM_Cmd(TIM2, DISABLE); 						          //关闭定时器 
        TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); 	//关闭TIM2更新中断
    } 
} 

  方法三使用了系统自带的定时器同步功能,也就是定时器1输出PWM波,然后将定时器1的输出同步到定时器2上,然后通过定时器2的计数器给定时器1输出的脉冲计数,当计数值等于指定值时,关闭定时器1的输出。

STM32F103单片机PWM单脉冲输出模式
STM32F103单片机PWM单脉冲输出模式

  定时器1设置为主模式,定时器2设置为从模式。将定时器1的PWM输出作为定时器2的时钟。这样设置定时器2的频率值,也就相当于设置的是定时器1的脉冲输出个数。

  将这三种模式统一在主函数中进行测试


int main(void)
{    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    delay_init();	
    while(1)
    {			
      //调用一次 发一次脉冲
      //TIM1_PWM_Init(899,0);			//方法一
      Pulse_output(100,16);			//方法二		
      //PWMS8_Init(1000,6);                     //方法三	
      delay_ms(500);
    }
}

在主函数中分别调用这三种方法,然后通过示波器就可以看出,当PWM输出一定的脉冲数后就会停止输出。