欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32利用定时器主从和RCR方式输出可控数量的PWM波

程序员文章站 2022-03-14 08:05:35
...

近期在学习和培训时重新熟悉了一下这方面的内容,一方面自己也是整理了一下,一方面也和新人小白们分享一下经验,大神轻喷。

话不多说,直接进入正题。首先知道正常的PWM波是用一个定时器输出的,配置好以后就开始源源不断地输出波形。那么如何控制波形脉冲的个数呢?这里我提出三种设计思路:

  1. 一个定时器输出PWM波,另一个定时器也通过对时间的计数功能来判断波形个数。比如要输出的波形周期是12.5μs\mu s ,输出100个,那么把第二个定时器的溢出周期定为1.25msms就好了,然后去做中断处理。
  2. 利用定时器的主从模式,即一个是主定时器,一个是从定时器,主定时器产生的更新触发传递给从定时器进行计数,这样保证计数更加精准,CPU计算也会相比1简单一些。
  3. 利用高级定时器的重复计数功能去做。利用到了高级定时器的RCR寄存器,该寄存器在库函数配置的时候对应的是TIM_TimeBaseInitStruct->TIM_RepetitionCounter,但是缺点在于该寄存器只有8位,最大只能输出256个脉冲。当然,要输出更多也不是不可以,那就是在中断里再做累加和判断。

第一种方法非常普通,本文中我介绍一下后面两种方法的实现。

用定时器的主从模式来做

这里先上代码,再做解释

pwm.c文件:

#include "pwm.h"
void TIM1_PWM_Init(u16 arr,u16 psc)//主定时器
{  
  GPIO_InitTypeDef GPIO_InitStructure;
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 TIM_OCInitTypeDef  TIM_OCInitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//TIM1时钟使能
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //GPIO外设时钟使能
 
 //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);

 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  80K
 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
 TIM_OCInitStructure.TIM_Pulse = arr/2-1; //设置待装入捕获比较寄存器的脉冲值,即占空比50%
 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
 TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx

 TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//主从模式
 TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);//TIM1更新产生触发,把TIM1的CR2的MMS位置为010

 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能  
 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
 }

void TIM2_PWM_Init(u16 PulseNum)//从定时器
{
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 NVIC_InitTypeDef NVIC_InitStructure; 
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

 TIM_TimeBaseStructure.TIM_Period = PulseNum-1;//设置输出的波形数量
 TIM_TimeBaseStructure.TIM_Prescaler =0;  TIM_TimeBaseStructure.TIM_ClockDivision=0;
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  

 TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);//设置SMCR寄存器的TS位,进行触发选择
 TIM2->SMCR|=0x07; //设置SMCR寄存器的SMS位,选择外部时钟源模式1
 
 TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE); //定时器2更新先失能
 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);
 }
//进入TIM2中断意味着脉冲数量够了,之后停止输出
void TIM2_IRQHandler(void) 
{
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
 { 
  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); 
 }
}

void Pulse_output(u16 arr,u16 psc,u16 PulseNum)
{
  TIM2_PWM_Init(PulseNum);
   TIM_Cmd(TIM2, ENABLE);
  TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
   TIM1_PWM_Init(arr,psc);
  TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能 
  TIM_Cmd(TIM1, ENABLE);//先使能TIM2再使能TIM1,保证每个脉冲都被检测到。
}

文件里面包括了4个函数,TIM1_PWM_Init(u16 arr,u16 psc)和 TIM2_PWM_Init(u16 PulseNum)都是配置的函数,Pulse_output(u16 arr,u16 psc,u16 PulseNum)是入口函数,它调用了两个配置函数并先后使能,它也在主函数里被调用。
要注意的一点是,由于TIM1是高级定时器,所以一定需要一个主输出使能以后才会真正有输出。pwm.h文件是在声明这几个函数,就没什么好说的了。

主函数main.c

#include "delay.h"
#include "sys.h"
#include "pwm.h"
int main(void)
 { delay_init();       //延时函数初始化     
   while(1)
 {Pulse_output(899,0,100);
   delay_ms(10);  }
 }

主函数就十分简单了,我这里设置波形的周期是12.5μs\mu s ,每次输出100个(用了1.25msms),然后每10msms重复一次。
通过波形仿真可以看到效果
STM32利用定时器主从和RCR方式输出可控数量的PWM波再放大一点看,一次是100个脉冲
STM32利用定时器主从和RCR方式输出可控数量的PWM波

用高级定时器的重复计数功能来做

首先要注意的是对于STM32F103系列的来说只有TIM1和TIM8有重复计数功能。
利用这个功能来做的时候就只用一个定时器就好了。重复计数的本质就是设置定时器溢出多少次再产生一个更新中断。RCR的基本知识参考手册里也有

STM32利用定时器主从和RCR方式输出可控数量的PWM波
这里也直接贴上代码
pwm.c:

void TIM1_PWM_Init(u16 arr,u16 psc)
{  
 GPIO_InitTypeDef GPIO_InitStructure;
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 TIM_OCInitTypeDef  TIM_OCInitStructure;
 NVIC_InitTypeDef NVIC_InitStructure;
   
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIO外设时钟使能       
                                                              
 //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  80K
 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
 TIM_TimeBaseStructure.TIM_RepetitionCounter = 10;
 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
 TIM_OCInitStructure.TIM_Pulse = arr/2-1; //设置待装入捕获比较寄存器的脉冲值
 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 
 TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
 
 TIM_ClearITPendingBit(TIM1, TIM_IT_Update); 
 TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
 
 NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;  
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//先占优先级0级
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;      //从优先级0级
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure); 
 TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能 
 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能  
 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
 TIM_Cmd(TIM1, ENABLE);  //使能TIM1
 }

主函数直接调用这个函数就OK了。改变数量就是改变TIM_TimeBaseStructure.TIM_RepetitionCounter的值,但由于RCR只有8位,所以它最大255