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

STM32高级定时器TIM1生成互补PWM

程序员文章站 2022-06-08 19:42:07
...
  • 硬件:stm32f103zet6
  • 开发工具:Keil uVision V5.26.2.0
  • 下载调试工具:J-Link

最近在研究三相无刷电机FOC控制,肯定要对互补PWM了解透彻。记录一下学习过程。

准备工作

从《STM32F10xxx参考手册》中可知,F1系列的定时器分为高级定时器(TIM1和TIM8)、通用定时器(TIMx)、基本定时器(TIM6和TIM7)。

stm32标准库V3.5.0版本对定时器外设建立了4个初始化结构体,针对不同的定时器需求,要使用不同的初始化结构体。下面是4个初始化结构体的适用分类:

TIM_TimeBaseInitTypeDef  // 高级定时器、通用定时器、基本定时器
TIM_OCInitTypeDef        // 高级定时器、通用定时器
TIM_ICInitTypeDef        // 高级定时器、通用定时器
TIM_BDTRInitTypeDef      // 高级定时器

要使用好一个外设,肯定要了解每一项配置的具体作用。

/* 基本初始化 */
typedef struct
{
	uint16_t TIM_Prescaler;        /* 定时器预分频值。value: 0x0000 ~ 0xFFFF */
	
	uint16_t TIM_CounterMode;      /* 计数器模式。value:
	#define TIM_CounterMode_Up                 ((uint16_t)0x0000)
	#define TIM_CounterMode_Down               ((uint16_t)0x0010)
	#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
	#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
	#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060) */
    
    uint16_t TIM_Period;           /* 自动重装载周期的值。value: 0x0000 ~ 0xFFFF */
    
    uint16_t TIM_ClockDivision;    /* 时钟分频因子,与互补PWM的死区时间有关。value:
    #define TIM_CKD_DIV1                       ((uint16_t)0x0000)
	#define TIM_CKD_DIV2                       ((uint16_t)0x0100)
	#define TIM_CKD_DIV4                       ((uint16_t)0x0200) */
    
    uint8_t TIM_RepetitionCounter; /* 重复计数器的值,仅TIM1和TIM8会用到。 */
} TIM_TimeBaseInitTypeDef;

/* 比较输出初始化 */
typedef struct
{
    uint16_t TIM_OCMode;       /* 比较输出模式选择。value:
    #define TIM_OCMode_Timing                  ((uint16_t)0x0000)
	#define TIM_OCMode_Active                  ((uint16_t)0x0010)
	#define TIM_OCMode_Inactive                ((uint16_t)0x0020)
	#define TIM_OCMode_Toggle                  ((uint16_t)0x0030)
	#define TIM_OCMode_PWM1                    ((uint16_t)0x0060)
	#define TIM_OCMode_PWM2                    ((uint16_t)0x0070) */
    
    uint16_t TIM_OutputState;  /* 比较输出使能。value:
    #define TIM_OutputState_Disable            ((uint16_t)0x0000)
	#define TIM_OutputState_Enable             ((uint16_t)0x0001) */
    
    uint16_t TIM_OutputNState; /* 比较互补输出使能。value:
    #define TIM_OutputNState_Disable           ((uint16_t)0x0000)
	#define TIM_OutputNState_Enable            ((uint16_t)0x0004) */
    
    uint16_t TIM_Pulse;        /* 比较输出的脉冲宽度。value: 0x0000 ~ 0xFFFF */
    
    uint16_t TIM_OCPolarity;   /* 比较输出极性。value:
    #define TIM_OCPolarity_High                ((uint16_t)0x0000)
	#define TIM_OCPolarity_Low                 ((uint16_t)0x0002) */
    
    uint16_t TIM_OCNPolarity;  /* 比较互补输出极性。value:
    #define TIM_OCNPolarity_High               ((uint16_t)0x0000)
	#define TIM_OCNPolarity_Low                ((uint16_t)0x0008) */
    
    uint16_t TIM_OCIdleState;  /* 空闲状态时通道输出电平设置。value:
    #define TIM_OCIdleState_Set                ((uint16_t)0x0100)
	#define TIM_OCIdleState_Reset              ((uint16_t)0x0000) */
    
    uint16_t TIM_OCNIdleState; /* 空闲状态时互补通道输出电平设置。value:
    #define TIM_OCNIdleState_Set               ((uint16_t)0x0200)
	#define TIM_OCNIdleState_Reset             ((uint16_t)0x0000) */
} TIM_OCInitTypeDef;

/* 输入捕获初始化(此处互补PWM功能用不到) */
typedef struct
{
    uint16_t TIM_Channel;     /* 输入通道选择。value:
    #define TIM_Channel_1                      ((uint16_t)0x0000)
	#define TIM_Channel_2                      ((uint16_t)0x0004)
	#define TIM_Channel_3                      ((uint16_t)0x0008)
	#define TIM_Channel_4                      ((uint16_t)0x000C) */
    
    uint16_t TIM_ICPolarity;  /* 输入捕获边沿触发选择。value:
    #define  TIM_ICPolarity_Rising             ((uint16_t)0x0000)
	#define  TIM_ICPolarity_Falling            ((uint16_t)0x0002)
	#define  TIM_ICPolarity_BothEdge           ((uint16_t)0x000A)
	需要说明的是:TIM1到TIM5,还有TIM8没有双边沿触发模式 */
    
    uint16_t TIM_ICSelection; /* 输入通道选择。value:
    #define TIM_ICSelection_DirectTI           ((uint16_t)0x0001)
	#define TIM_ICSelection_IndirectTI         ((uint16_t)0x0002)
	#define TIM_ICSelection_TRC                ((uint16_t)0x0003) */
    
    uint16_t TIM_ICPrescaler; /* 输入捕获通道预分频。value:
    #define TIM_ICPSC_DIV1                     ((uint16_t)0x0000)
	#define TIM_ICPSC_DIV2                     ((uint16_t)0x0004)
	#define TIM_ICPSC_DIV4                     ((uint16_t)0x0008)
	#define TIM_ICPSC_DIV8                     ((uint16_t)0x000C) */
    
    uint16_t TIM_ICFilter;    /* 输入捕获滤波器设置。value: value: 0x0 ~ 0xF */
} TIM_ICInitTypeDef;

/* 刹车和死区初始化(仅TIM1和TIM8有此设置) */
typedef struct
{
    uint16_t TIM_OSSRState;       /* 运行模式下关闭状态选择。Value:
    #define TIM_OSSRState_Enable               ((uint16_t)0x0800)
	#define TIM_OSSRState_Disable              ((uint16_t)0x0000) */
    
    uint16_t TIM_OSSIState;       /* 空闲模式下关闭状态选择。Value:
    #define TIM_OSSIState_Enable               ((uint16_t)0x0400)
	#define TIM_OSSIState_Disable              ((uint16_t)0x0000) */
    
    uint16_t TIM_LOCKLevel;       /* 锁定设置。value:
    #define TIM_LOCKLevel_OFF                  ((uint16_t)0x0000)
	#define TIM_LOCKLevel_1                    ((uint16_t)0x0100)
	#define TIM_LOCKLevel_2                    ((uint16_t)0x0200)
	#define TIM_LOCKLevel_3                    ((uint16_t)0x0300) */
    
    uint16_t TIM_DeadTime;        /* 死区时间。value: 0x00 ~ 0xFF */
    
    uint16_t TIM_Break;           /* 刹车输入使能。value:
    #define TIM_Break_Enable                   ((uint16_t)0x1000)
	#define TIM_Break_Disable                  ((uint16_t)0x0000) */
    
    uint16_t TIM_BreakPolarity;   /* 刹车输入极性。value:
    #define TIM_BreakPolarity_Low              ((uint16_t)0x0000)
	#define TIM_BreakPolarity_High             ((uint16_t)0x2000) */
    
    uint16_t TIM_AutomaticOutput; /* 自动输出使能。Value:
    #define TIM_AutomaticOutput_Enable         ((uint16_t)0x4000)
	#define TIM_AutomaticOutput_Disable        ((uint16_t)0x0000) */
} TIM_BDTRInitTypeDef;

本次实验选用高级定时器TIM1,开3路PWM互补输出通道,对应的引脚如下:

CH CHN
1 PA8 PB13
2 PA9 PB14
3 PA10 PB15

另外还有刹车输入引脚PB12。

下面开始实操

时钟设置

static void RCC_Configuration(void)
{
	RCC_DeInit();
	RCC_HSEConfig(RCC_HSE_ON);                           //开启HSE
	while(RCC_WaitForHSEStartUp() == ERROR);             //等待HSE开启成功
	
	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK=72MHz
	RCC_PLLCmd(ENABLE);                                  //开启PLL(必须先配置PLL后才能开启PLL)
	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);           //SYSCLK=PLLCLK
	RCC_HCLKConfig(RCC_SYSCLK_Div1);                     //SYSCLK的1分频作为HCLK的输入
	RCC_PCLK1Config(RCC_HCLK_Div2);                      //HLK的2分频作为PCLK1的输入,36MHz(注意TIM定时器是2倍频的,72MHz)
	RCC_PCLK2Config(RCC_HCLK_Div1);                      //HLK的1分频作为PCLK2的输入,72MHz
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);     //SysTick滴答定时器
	
	RCC_ClearFlag();
}

定时器初始化

void bsp_pwm_init(void)
{
	GPIO_InitTypeDef        GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef       TIM_OCInitStructure;
	TIM_BDTRInitTypeDef     TIM_BDTRInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	
	/* TIM1 PWM CH1 CH2 CH3 --> PA8 PA9 PA10 */
	GPIO_StructInit(&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* TIM1 PWM CHN1 CHN2 CHN3 --> PB13 PB14 PB15 */
	GPIO_StructInit(&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/* TIM1 PWM Break --> PB12 */
	GPIO_StructInit(&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/* 基本初始化 */
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_TimeBaseStructure.TIM_Period        = 1000 - 1;
	TIM_TimeBaseStructure.TIM_Prescaler     = 0;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	
	/* 比较输出初始化 */
	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_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_OCInitStructure.TIM_Pulse = 500 - 1; /* 占空比50%(与基本初始化中的TIM_Period共同决定) */
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	TIM_OCInitStructure.TIM_Pulse = 500 - 1;
	TIM_OC2Init(TIM1, &TIM_OCInitStructure);
	TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	TIM_OCInitStructure.TIM_Pulse = 500 - 1;
	TIM_OC3Init(TIM1, &TIM_OCInitStructure);
	TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	/* 刹车和死区初始化 */
	TIM_BDTRStructInit(&TIM_BDTRInitStructure);
	TIM_BDTRInitStructure.TIM_OSSRState       = TIM_OSSRState_Enable;
	TIM_BDTRInitStructure.TIM_OSSIState       = TIM_OSSIState_Enable;
	TIM_BDTRInitStructure.TIM_LOCKLevel       = TIM_LOCKLevel_1;
	TIM_BDTRInitStructure.TIM_DeadTime        = 0;
	TIM_BDTRInitStructure.TIM_Break           = TIM_Break_Enable;
	TIM_BDTRInitStructure.TIM_BreakPolarity   = TIM_BreakPolarity_Low;
	TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
	TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
	
	TIM_Cmd(TIM1, ENABLE);
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

将代码编译并烧录到开发板上运行,并将PA8和PB13引脚接入示波器,结果如下:

STM32高级定时器TIM1生成互补PWM
因为时钟设置的72MHz,定时器基本初始化的TIM_Prescaler设置值为0,TIM_Period为999,所以PWM周期为(1/72MHz)*1000=13.89us。

需要注意的是,因为开启了刹车(TIM_Break)功能,且刹车输入极性设置为低,所以PB12引脚必须接入高电平,才有PWM波形输出。

因为死区时间TIM_DeadTime的值为0,所以互补PWM波形是完全对称的,下面给TIM_DeadTime设置一个值,比如0x8f,再来看看结果。

TIM_BDTRInitStructure.TIM_DeadTime = 0x8f;

STM32高级定时器TIM1生成互补PWM
可以看到,互补PWM波形不再试完全对称的,而是有约2us的低电平重合时间,这就是插入的0x8f死区时间。这个值是怎么算出来的呢?《STM32F10xxx参考手册》中有说明。在刹车和死区寄存器(TIMx_BDTR)表格中

STM32高级定时器TIM1生成互补PWM
比如本实验中TDTS = 1/72MHz = 13.89ns。0x8f属于第二个公式,所以DT = (64 + 0xf) * (2 * 13.89ns) = 2194.62ns,也就是约等于示波器中看到的2us。

需要注意的是TDTS由定时器时钟和基本初始化TIM_ClockDivision共同决定的,与基本初始化的TIM_Prescaler的值并没有关系。

把TIM_Prescaler改为71看看死区时间有什么区别。

TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
TIM_BDTRInitStructure.TIM_DeadTime = 0x8f;

STM32高级定时器TIM1生成互补PWM
现在周期变成了1ms。什么!没有死区了?!放大看一下。

STM32高级定时器TIM1生成互补PWM
可以看到死区时间依然为2us多一点,其精确的值其实应该就是2194ns左右。相比1ms的pwm周期,在全周期显示下,确实很难看到了(能看到的还是人吗)。