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

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

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

  1. 一个定时器输出PWM波,另一个定时器也通过对时间的计数功能来判断波形个数。比如要输出的波形周期是12.5 μ s \mu s ,输出100个,那么把第二个定时器的溢出周期定为1.25 m s ms 就好了,然后去做中断处理。
  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.25 m s ms ),然后每10 m s ms 重复一次。
通过波形仿真可以看到效果
在这里插入图片描述再放大一点看,一次是100个脉冲
在这里插入图片描述

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

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

扫描二维码关注公众号,回复: 11557143 查看本文章

在这里插入图片描述
这里也直接贴上代码
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

猜你喜欢

转载自blog.csdn.net/m0_46659414/article/details/107838120