基于STM32芯片三路互补PWM输出的设计实现

电子说

1.2w人已加入

描述

硬件:Stm32f103c8t6最小系统。

开发平台:MDK-Arm。

目的:使用Stm32高级定时器TIM1。配置中心对齐模式输出三路互补PWM。

(1)Stm32的高级定时器:

Stm32f103c8t6有一个高级定时器TIM1。STM32的高级定时器比通用定时器增加了可编程死区互补输出,重复计数器,带刹车(短路)功能。这些功能为电机控制提供了便利。其中重复计数器下篇文章单独讲。

TIM1的IO分配:

PWM波

2)高级定时器框图分析:

PWM波

图1

图1高级定时器框图,可分为6部分,①时钟源选择,②控制器,③时基单元,④输入捕获,⑤输出比较,⑥刹车断路。

①时钟选择:

时钟源有:内部时钟(CK_INT),外部时钟模式1,外部时钟模式2,内部触发输入(ITRx)。具体可以看TIMx_SMCR寄存器SMS位和ECE位这里我们使用内部时钟64MHz(没使用外部晶振,系统时钟是64MHz)。

②控制器

控制器部分包含触发控制器,从模式控制器,编码器接口。触发控制器可以为片内外设提供触发信号。

③⑤时基单元和输出比较

时基单元的计数模式选中心对齐模式和比较输出的比较模式选PWM模式来讲解。

首先时钟源通过预分频寄存器PSC分频得到CK_CNT,得到计数器CNT计数频率,计数器开始从0向上计数,每计一次,和CCR比较一次,当CRR>CNT, 输出高电平,反之输出低电平,计数器CNT继续计数,当计数器等于ARR值时,计数器向下计数,当计数器CNT>CCR时,输出低电平,反之输出高电平。计数器周而复始向上向下计数并不断的和CRR值比较,参考图2。

PWM波

图2

实际此过程我们可以看成一个比较器,CRR作为参考电压,接到比较器同相输入端,CNT作为信号电压,接到比较器反相输入端,这样根据比较输出PWM。反之把CNT和CRR调换位置,会输出相反极性的PWM,参考图2.

计数器计数模式有:向上计数,向下计数,中心对齐计数。

比较输出比较模式:冻结,匹配时设置通道x为有效电平,匹配时设置通道x为无效电平,翻转,强制为无效电平,强制为有效电平,PWM1,PWM2。

死区发生器:

通过寄存器BDTR的位UTG[7:0]来配置死区时间,关于死区时间设置可以这篇文章:

⑥短路功能

断路功能用于电控的刹车功能,可以通过寄存器BDTR的BKE位使能断路功能,BKP位设置断路输入引脚的有效电平

④输入捕获

输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输 入信号的脉宽和测量 PWM 输入信号的频率和占空比这两种。

输入捕获的大概的原理就是,当捕获到信号的跳变沿的时候,把计数器 CNT 的值锁存 到捕获寄存器 CCR 中,把前后两次捕获到的 CCR 寄存器中的值相减,就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出,这个我 们需要做额外的处理。

(3)程序设计

程序使用ST官方固件库,编程步骤:

①GPIO初始化;

②时基结构体TIM_TimeBaseInitTypeDef 初始化;

③比较结构体 TIM_OCInitTypeDef 初始化;

④刹车和死区结构体 TIM_BDTRInitTypeDef 初始化。

程序分析:

①宏定义:

#define   RCC_TIMGPIO_CLK      RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB
#define   TIM_CHxGPIO_Pinx     GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11
#define   TIM_CHxGPIOx         GPIOA
#define   TIM_CHxNGPIO_Pinx    GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15
#define   TIM_CHxNGPIOx        GPIOB
#define   TIM_BKINGPIO_Pinx    GPIO_Pin_2
#define   TIM_BKINGPIOx        GPIOB

//TIM1 macro definition

/ PWM频率和死区时间计算 */

//电机控制载波频率一般配置在15-20KHz,频率配置低了电机噪声大,配置高了,对MOS

//管开关频率要求高,且开关损耗大,这里载波配置为20KHz。

//计数器频率计算CK_CNT = 64MHz/(PSC+1) = 64MHz/1=64MHz

//中心对齐PWM频率=CK_INT/2(ARR+1)=64M/2(1599+1)=20KHz。

//死区时间配置为2us

//64M/(CKD+1)/ UTG = 500KHz,换算成时间位2us

// TIMx_CR1寄存器CKD 位,TIMx_BDTR寄存器UTG位。

#define   TIMx                 TIM1
#define   RCC_TIM_CLK          RCC_APB2Periph_TIM1
#define   TIM_ARR              1599
#define   TIM_CK_PSC           0
#define   TIM_RCR              0
#define   TIM_CCRx             799
#define   PWM_DeadTime         128

②GPIO初始化:

static void GPIO_TIM_Config(void){
   GPIO_InitTypeDef GPIO_InitStructure;
   RCC_APB2PeriphClockCmd(RCC_TIMGPIO_CLK, ENABLE);
   //TIM1_PA8CH1 PA9CH2 PA10CH3 PA11CH4
   GPIO_InitStructure.GPIO_Pin =  TIM_CHxGPIO_Pinx;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   //PB13CH1N PB14CH2N PB15CH3N PB12BKIN      
   GPIO_InitStructure.GPIO_Pin =  TIM_CHxNGPIO_Pinx; 
   GPIO_Init(TIM_CHxNGPIOx, &GPIO_InitStructure);      
   //PB12BKIN
   GPIO_InitStructure.GPIO_Pin =  TIM_BKINGPIO_Pinx; 
   GPIO_Init(TIM_BKINGPIOx, &GPIO_InitStructure);          
   GPIO_ResetBits(TIM_BKINGPIOx,TIM_BKINGPIO_Pinx);//刹车
}

③定时器时基单元,输出比较,刹车死区初始化:

static void AdvanceTIM1_Config(void){
  //开启定时器时钟,即内部时钟CK_INT=64MHz
  RCC_APB2PeriphClockCmd(RCC_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;     
  TIM_TimeBaseStructure.TIM_Period = TIM_ARR;   //自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断      
  TIM_TimeBaseStructure.TIM_Prescaler = TIM_CK_PSC;   //驱动CNT计数器的时钟 = Fck_int/(psc+1)
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //时钟分频因子 ,配置死区时间时需要用到             
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; //中心对齐1             
  TIM_TimeBaseStructure.TIM_RepetitionCounter = TIM_RCR;  //重复计数器的值,没用到不用管      
  TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);  //初始化定时器
/*--------------------输出比较结构体初始化-------------------*/             
  TIM_OCInitTypeDef  TIM_OCInitStructure;     
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                //PWM1模式
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;    //输出使能
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;  //互补输出使能       
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;        //输出通道电平极性配置
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;      //互补输出通道电平极性配置
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;       //输出通道空闲电平极性配置
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;   //互补输出通道空闲电平极性配置                  
  TIM_OCInitStructure.TIM_Pulse = TIM_CCRx;                        //设置占空比大小    
  TIM_OC1Init(TIMx, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIMx, TIM_OCPreload_Enable);
  TIM_OC2Init(TIMx, &TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIMx, TIM_OCPreload_Enable); 
  TIM_OC3Init(TIMx, &TIM_OCInitStructure);
  TIM_OC3PreloadConfig(TIMx, TIM_OCPreload_Enable);               
/*-------------------刹车和死区结构体初始化-------------------*/
  //有关刹车和死区结构体的成员具体可参考BDTR寄存器的描述
  TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
  //输出比较信号死区时间配置,具体如何计算可参考 BDTR:UTG[7:0]的描述
  //这里配置的死区时间为2uS
  TIM_BDTRInitStructure.TIM_DeadTime = 128;
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
  //当BKIN引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
  TIM_BDTRConfig(TIMx, &TIM_BDTRInitStructure);
  TIM_Cmd(TIMx, ENABLE);      
  //主输出使能,当使用的是通用定时器时,这句不需要
  TIM_CtrlPWMOutputs(TIMx, ENABLE);                  
}

4)程序烧录验证:图3是一对PWM互补输出波形。

PWM波

图3

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分