硬件:Stm32f103c8t6最小系统。
开发平台:MDK-Arm。
用途:使用Stm32高级定时器TIM1。配置中心对齐模式以输出三个互补的PWM。
(1)Stm32的高级定时器:
stm32f103c8t6有一个高级定时器TIM1。 STM32的高级定时器相比通用定时器增加了可编程死区互补输出、重复计数器、制动(短路)功能。这些特性为电机控制提供了便利。关于重复计数器的下一篇文章将单独讨论。
TIM1的IO分配:
(2)高级定时器框图分析:
图1
图1 高级定时器框图,可分为6部分,时钟源选择,控制器,时基单元,输入捕获,输出比较,制动断路。
时钟选择:
时钟源包括:内部时钟(CK_INT)、外部时钟模式1、外部时钟模式2、内部触发输入(ITRx)。具体可以查看TIMx_SMCR寄存器的SMS位和ECE位。这里我们使用内部时钟64MHz(没有使用外部晶振,系统时钟为64MHz)。
控制器
控制器部分包括触发控制器、从模式控制器和编码器接口。触发控制器可以为片内和片上外设提供触发信号。
时基单位及输出比较
时基单元的计数模式为中心对齐模式,比较输出的比较模式为PWM模式。
首先,时钟源经过预分频寄存器PSC分频得到CK_CNT,得到计数器CNT的计数频率。计数器从0开始向上计数,每次计数都会与CCR进行比较。当CRRCNT时,输出高电平,否则输出低电平。计数器CNT继续计数。当计数器等于ARR值时,计数器递减计数。当计数器CNTCCR时,输出低电平,否则输出高电平。计数器不断地向上和向下计数,并不断与CRR值进行比较,参见图2。
图2
事实上,我们可以把这个过程看作一个比较器。 CRR用作参考电压,连接到比较器的同相输入端。 CNT作为信号电压,连接到比较器的反相输入端。这样根据比较结果输出PWM。反之,如果CNT 和CRR 的位置互换,则会输出相反极性的PWM,参见图2。
计数器计数模式包括:加计数、减计数、居中计数。
比较输出比较方式:冻结,匹配时设置通道x为有效电平,设置通道
死区发生器:
死区时间通过寄存器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_1 4|GPIO_Pin_15#define TIM_CHxNGPIOx GPIOB#define TIM_BKINGPIO_Pinx GPIO_Pin_2#define TIM_BKINGPIOx GPIOB//TIM1宏定义
/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 初始化:
静态无效GPIO_TIM_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_TIMGPIO_CLK,启用); //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_BKINGGPIO_Pinx; GPIO_Init( TIM_BKINGGPIOx, GPIO_InitStructure); GPIO_ResetBits(TIM_BKINGGPIOx,TIM_BKINGGPIO_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_OCNP olarity=TIM_OCNPolity_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_OCInit) 结构); TIM_OC3PreloadConfig(TIMx, TIM_OCPreload_Enable); /*- ------------------制动和死区结构初始化-----------------*///关于Braks 死区结构体成员详细信息请参考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, 启用); //主输出已启用。当使用通用定时器时,这句不需要TIM_CtrlPWMOutputs (TIMx, ENABLE); } (4) 程序烧写验证:图3是一对PWM互补输出波形。
图3