MM32 MCU互补PWM输出使能带死区、刹车功能

judy的头像
judy 发布于:周五, 11/03/2017 - 10:13 ,关键词:

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM最基本的调节就是频率和占空比,通过调节频率和占空比对外部元件进行控制或者捕捉外部信号源。

在大功率电机、变频器、开关电源等方案中,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。

MM32L0系列MCU有多达9个定时器,高级定时器TIM1可以产生互补的PWM,高级定时器TIM1是1 个 16 位 4 通道高级控制定时器,有 4 通道 PWM 输出,以及死区生成和紧急停止功能,可以通过相关寄存器的设置使能或关闭PWM的输出。

高级控制定时器(TIM1)能够输出两路互补信号,并且能够管理输出的瞬时关断和接通。这段时间通常被称为死区,用户应该根据连接的输出器件和它们的特性(电平转换的延时、电源开关的延时等)来调整死区时间。

使用紧急停止功能,使能输出信号和无效电平都会被改变,关闭PWM输出,能够有效的预防突发事件造成外部电机等不可控的状态,刹车源既可以是刹车输入管脚又可以是一个时钟失败事件。时钟失败事件由复位时钟控制器中的时钟安全系统产生。在安全方面,你可以把刹车输入连到电源驱动的报警输出、热敏传感器或者其他安全器件上。

在编写电机的驱动程序时,需要利用TIM1的Channel1,2,3三个通道生成三路互补的PWM波形,TIM1需要配置的寄存器有:捕捉/比较模式寄存器 1(TIMx_CCMR1)、捕捉/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~3)和刹车和死区寄存器(TIMx_BDTR).
程序配置流程如下:

void TIM1_PWM_Init(u16 arr,u16 psc)
{ 
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef  TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStruct;
      
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB , ENABLE);
 
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; //TIM3_CH1  TIM3_CH1N
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
      
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; //TIM3_CH1  TIM3_CH1N
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
      
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //TIM3_CH1  TIM3_CH1N
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
      
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8,GPIO_AF_2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,GPIO_AF_2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10,GPIO_AF_2);
             
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13,GPIO_AF_2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14,GPIO_AF_2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15,GPIO_AF_2);
             
GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_2);
      
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter =0;
TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
 
TIM_BDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
TIM_BDTRInitStruct.TIM_DeadTime = 0x99;
//TDTS = 125nS(8MHz)
//DTG[7: 5] = 0xx => DT = DTG[7: 0] * Tdtg, Tdtg = TDTS;
//DTG[7: 5] = 10x => DT =(64+DTG[5: 0]) * Tdtg, Tdtg = 2 * TDTS;
//DTG[7: 5] = 110 => DT =(32+DTG[4: 0]) * Tdtg, Tdtg = 8 * TDTS;
//DTG[7: 5] = 111=> DT =(32 + DTG[4: 0]) *  Tdtg, Tdtg = 16 * TDTS;
 
TIM_BDTRInitStruct.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig( TIM1, &TIM_BDTRInitStruct);     
      
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Reset;
             
TIM_OC1Init(TIM1, &TIM_OCInitStructure); 
TIM_OC2Init(TIM1, &TIM_OCInitStructure); 
TIM_OC3Init(TIM1, &TIM_OCInitStructure); 
 
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);  
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);  
 
TIM_ARRPreloadConfig(TIM1, ENABLE);
 
TIM_Cmd(TIM1, ENABLE);
      
}

第一步:开定时器TIM1的时钟和GPIOA\GPIOB的时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB , ENABLE);

第二步:配置GPIO的模式,根据UM_MM32L0系列产品手册的8.1.11外设的 GPIO 配置查表可以知配置CHx的输出比较通道x和互补输出通道x功能时,需将GPIO配置成推挽复用输出模式,配置定时器的刹车输入功能时GPIO需配置成浮空输入功能。

MM32 MCU互补PWM输出使能带死区、刹车功能

根据DS_MM32L0系列产品手册的表4和表5的端口功能复用列表可知,PA8\PA9\PA10的AF2是TIM1的CH1\CH2\CH3通道,PB13\PB14\PB15的AF2是TIM1的CH1N\CH2N\CH3N通道,所以在配置GPIO的复用功能选择时通过软件写入配置IO的复用功能为AF2。

在本试验中,通过配置PB12作为刹车信号源的输入脚。根据端口功能复用列表可知,PB12的AF2是TIM1的刹车输入功能。

MM32 MCU互补PWM输出使能带死区、刹车功能

MM32 MCU互补PWM输出使能带死区、刹车功能

第三步:配置定时器的基本功能,首先来了解定时器初始化参数结构体指针,结构体类型是TIM_TimeBaseInitTypeDef,该结构体主要包含5个成员变量:

typedef struct
{
        uint32_t TIM_Prescaler;
        uint32_t TIM_CounterMode;
uint32_t TIM_Period;
        uint32_t TIM_ClockDivision;
        uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;

第一个参数TIM_Prescaler是用来设置分频系数的,MM32L0系列产品的高级定时器TIM1是挂在APB2上。在MM32所有例程中,在每次一启动时,在程序中就已经帮助客户配置好了APB的时钟,在system_MM32L0系列.c中如果您配置相对应的系统时钟,在时钟配置函数中,您可以看到已经对APB1\APB2\AHB的时钟做了配置,默认APB2的时钟是48MHz,所以定时器的时钟需用户根据自己的实际需求进行分频或者不分频操作。

MM32 MCU互补PWM输出使能带死区、刹车功能

第二个参数是TIM_CounterMode用来配置PWM模式,MM32L0系列产品的PWM模式有三种:向上计数模式、向下计数模式和中央对齐模式。
1、向上计数模式:例:当 TIMx_CNT < TIMx_CCRx 时,PWM 参考信号 OCxREF 为高,
否则低。如果 TIMx_CCRx 中的比较值大于自动重装载值(TIMx_ARR),则 OCxREF 保持为‘1’。
2、向下计数模式:当 TIMx_CNT > TIMx_CCRx 时参考信号 OCxREF 为低,否则为高。如果 TIMx_CCRx中的比较值大于 TIMx_ARR 中的自动重装载值,则 OCxREF 保持为‘1’。
3、中央对齐模式:中央对齐模式又分为三种模式,根据不同的 CMS 位的设置,比较标志可以在计数器向上计数时被置‘1’、在计数器向下计数时被置‘1’、或在计数器向上和向下计数时被置‘1’。

中央对齐模式选择
MM32 MCU互补PWM输出使能带死区、刹车功能

第三个参数是TIM_Period用来设置自动重载计数周期值,自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在 TIMx_CR1 寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件 UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMx_CR1 寄存器中的 UDIS位等于 0 时,产生更新事件。

第四个参数是TIM_ClockDivision用来设置时钟分频因子,该变量与后面死区持续时间配置有关,该变量是设置控制寄存器1(TIMx_CR1)的时钟分频因子CKD[1:0],这 2 位定义在定时器时钟(CK_INT) 频率、死区时间和由死区发生器与数字滤波器(ETR,TIx) 所用的采样时钟之间的分频比例。

第五个参数是TIM_RepetitionCounter用来设置重复计数值,该功能在上一篇文章有详细讲解。

第四步:配置定时器的刹车和死区,首先来了解定时器刹车和死区参数结构体指针,结构体类型是TIM_BDTRInitTypeDef,共7个成员变量:

    typedef struct
    {
        uint16_t TIM_OSSRState;
        uint16_t TIM_OSSIState;
        uint16_t TIM_LOCKLevel;
        uint16_t TIM_DeadTime;
        uint16_t TIM_Break;
        uint16_t TIM_BreakPolarity;
        uint16_t TIM_AutomaticOutput;
} TIM_BDTRInitTypeDef;

第一个参数是TIM_OSSRState用来配置运行模式下“关闭状态”选择。
第二个参数是TIM_OSSIState用来配置空闲模式下“关闭状态”选择。
第三个参数是TIM_LOCKLevel用来定时器的寄存器锁定保护,锁定级别分为3个级别,只能写入一次LOCK位,一旦写入TIMx_BDTR寄存器,则内容冻结至复位。
第四个参数是TIM_DeadTime设置死区时间,用户可根据UM_MM32L0xx手册的刹车和死区寄存器(TIM1x_BDTR)寄存器计算死区时间。
第五个参数是TIM_Break设置是否使能刹车功能。
第六个参数是TIM_BreakPolarity用来设置刹车输入电平。
第七个参数是TIM_AutomaticOutput设置BDTR寄存器的主输出使能(MOE)位。

第五步:配置定时器的PWM输出功能,

 typedef struct
    {
        uint16_t TIM_OCMode;
        uint16_t TIM_OutputState; 
        uint16_t TIM_OutputNState;          
        uint16_t TIM_Pulse;
        uint16_t TIM_OCPolarity;
        uint16_t TIM_OCNPolarity;
        uint16_t TIM_OCIdleState;
        uint16_t TIM_OCNIdleState;
    } TIM_OCInitTypeDef;

第一个参数是TIM_OCMode用来设置模式是输入捕获功能或者输出比较功能。
PWM 模式 1 - 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为有效电平,否则为 无效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为无效电平(OC1REF= 0) ,否 则为有效电平(OC1REF = 1)
PWM 模式 2 - 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为无效电平,否则为有效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为有效电平,否则为无效电平。
第二个和第三个参数TIM_OutputState和TIM_OutputNState是配置互补通道输出使能。
第四个参数是TIM_Pulse用来设置待装入捕获比较寄存器的脉冲值,它的取值必须在 0x0000 和 0xFFFF之间。
第五和第六个参数TIM_OCPolarity和TIM_OCNPolarity是设置互补通道的极性是高还是低。
第七和第八个参数是TIM_OCIdleState和TIM_OCNIdleState用来设置刹车后的互补通道的电平状态。

第六步:初始化TIM1的OC1\OC2\OC3通道,使能定时TIM1。

逻辑分析仪抓取PWM波形
MM32 MCU互补PWM输出使能带死区、刹车功能
(逻辑分析仪精度不高,建议使用示波器抓波性)

小结:

1、PWM的频率和占空比计算:
//Fpwm = 48M / ((arr+1)*(psc+1))(单位:Hz)
//duty circle = TIM1->CCR1 / arr(单位:%)
本次配置参数:arr=999,psc=47,Pulse=500
Fpwm = 48M / ((arr+1)*(psc+1))=48M/ ((999+1)*(47+1))=1KHz
duty circle = (TIM1->CCR1 / arr )*100= (500/999)*100 = 50%

2、PWM互补通道电平设置:
通过上面截图可以看到互补通道的输出电平极性是相反的,如果用户需要配置互补通道输出相同的电平极性,需要配置的TIMx_CCER寄存器,通过库函数设置TIM_OCPolarity和TIM_OCNPolarity即可改变互补通道的电平输出极性。

MM32 MCU互补PWM输出使能带死区、刹车功能

3、刹车电平配置:
在配置刹车输入有效电平时,需先配置刹车和死区寄存器(TIMx_BDTR)的BKP位,配置为0时表示刹车输入低电平有效,配置为1时表示刹车输入高电平有效,在库函数中通过设置TIM_BreakPolarity变量即可实现有效电平设置,用户需根据外面信号来源配置对应的极性。

MM32 MCU互补PWM输出使能带死区、刹车功能

如上图所示,中间表示有刹车信号输入,关断PWM输出。在刹车信号关闭时,PWM又恢复输出,用户可以根据实际需求在刹车信号后是否恢复PWM输出,需要配置寄存器刹车和死区寄存器(TIMx_BDTR)的AOE位,在设置AOE位为0的情况下,触发刹车时,MOE关断,不再开启。

4、死区时间设置:
用户如果需要配置插入互补输出之间的死区持续时,需要配置刹车和死区寄存器(TIMx_BDTR)的死区发生器设置DTG[7:0],如下图所示,DT表示死区持续时间,Tdts为系统时钟周期,Tdtg表示乘以倍数后死区设置时间步进值。

MM32 MCU互补PWM输出使能带死区、刹车功能

Tdts = 1/48M = 20.83ns,TIM1_BDTR = 0xFF,高三位是0,所以选择第一个公式:
DT=(32+0x1F)*16* Tdts=(32+32)*16*20.83 = 21.3us。

MM32 MCU互补PWM输出使能带死区、刹车功能
(逻辑分析仪精度不高,建议使用示波器抓波性)

转自: 灵动微电子

围观 1729