硬件: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互补输出通道,对应的引脚如下:
另外还有刹车输入引脚PB12。
下面开始实操
时钟设置
static void RCC_Configuration(void){RCC_DeInit();RCC_HSEConfig(RCC_HSE_ON); //开启HSEwhile(RCC_WaitForHSEStartUp() == ERROR); //等待HSE开启成功RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK=72MHzRCC_PLLCmd(ENABLE); //开启PLL(必须先配置PLL后才能开启PLL)RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //SYSCLK=PLLCLKRCC_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的输入,72MHzSysTick_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_BDTRInitTypeDefTIM_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周期,在全周期显示下,确实很难看到了(能看到的还是人吗)。