STM32高级定时器TIM1生成互补PWM
- 硬件: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引脚接入示波器,结果如下:
因为时钟设置的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;
可以看到,互补PWM波形不再试完全对称的,而是有约2us的低电平重合时间,这就是插入的0x8f死区时间。这个值是怎么算出来的呢?《STM32F10xxx参考手册》中有说明。在刹车和死区寄存器(TIMx_BDTR)表格中
比如本实验中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;
现在周期变成了1ms。什么!没有死区了?!放大看一下。
可以看到死区时间依然为2us多一点,其精确的值其实应该就是2194ns左右。相比1ms的pwm周期,在全周期显示下,确实很难看到了(能看到的还是人吗)。
上一篇: 一个广告轮播系统的例子(内含文件上传的方法)_PHP
下一篇: 详谈PHP文件目录基础操作