PWM互补输出带死区时间和刹车控制代码讲解
时隔11天,我又回来了
之间写了STM32的三种定时器的一些基本概念,今天我来讲解一下相关的程序应用。
我们直接从野火的固件库例程中拷贝一个串口的例程:USART接发。
由于我们这个例程是关于互补输出,因此需要两个GPIO;死区时间用软件配置,而刹车控制也需要使用一个GPIO。所以我们在正通道中选择PA8,在互补通道中选择PB13,刹车控制选用PB12。
首先,在目标文件下的User文件夹下新建AdvanceTim文件夹,在其目录下创建bsp_AdvanceTim.c和bsp_AdvanceTim…h两个文件。
然后先在.h文件中添加条件编译语句:
#ifndef __BSP_ADVANCETIME_H
#define __BSP_ADVANCETIME_H
#include "stm32f10x.h"
#endif /* __BSP_ADVANCETIME_H */
在编写主体程序时,我们先来看看其原理:
我们先假设占空比为50%,需要配置CNT,CCR,ARR,当CNT从0向上计数时,初始设的有效电平为高电平,当CNT<CCR,则CNT一直为高电平,当CNT到达CCR时,电平就会发生跳变,从高变低,当到达ARR时,CNT清零,电平翻转,如此反复。
我们在刚刚创建的.c文件中配置GPIO为复用输出模式:
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*输出比较通道初始化*/
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
/*输出比较通道互补通道GPIO初始化*/
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1N_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);
/*输出比较刹车通道GPIO初始化*/
RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_BKIN_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN); //BKIN引脚默认先输出低电平
}
然后,由于我们使用的是TIM1的三个引脚,因此我们需要在一开始新建的.h文件中写宏定义:
//当使用不同的定时器的时候,对应的GPIO是不一样的,这里我们使用的是TIM1
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1
//PWM信号的频率 F=TIM_CLK/{(ARR+1)*(PSC+1)}
#define ADVANCE_TIM_PERIOD (8-1)
#define ADVANCE_TIM_PSC (9-1)
#define ADVANCE_TIM_PULSE 4
#define ADVANCE_TIM_IRQ TIM1_UP_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_UP_IRQHandler
//TIM1输出比较通道
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8
//TIM1输出比较通道的互补通道
#define ADVANCE_TIM_CH1N_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_CH1N_PORT GPIOB
#define ADVANCE_TIM_CH1N_PIN GPIO_Pin_13
//TIM1输出比较通道的刹车通道
#define ADVANCE_TIM_BKIN_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_BKIN_PORT GPIOB
#define ADVANCE_TIM_BKIN_PIN GPIO_Pin_12
紧接着,我们在.c函数中初始化我们需要用到的结构体:
static void ADVANCE_TIM_Mode_Config(void)
{
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
/*时基结构体初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;
//ARR的值,累计TIM_Period+1个频率后产生一个更新或中断
TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;
//驱动CNT时钟
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
//时钟分频因子,配置死区时间要用
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
//计数模式,向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
//重复CNT的值,没用到可以不管
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
//初始化定时器
/*输出比较结构体初始化*/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
//互补输出使能
TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_PULSE;
//设置占空比大小
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//输出通道电平极性配置
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
//互补输出通道电平极性配置
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
//输出通道空闲电平极性配置
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
//互补输出通道空闲电平极性配置
TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);
/*刹车和死区结构体初始化*/
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寄存器的描述
TIM_BDTRInitStructure.TIM_DeadTime = 11;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
//输出比较信号死区时间配置
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
//当BKIN引脚检测到高电平的时候,输出比较信号被禁止,就好像刹车一样
TIM_Cmd(ADVANCE_TIM, ENABLE);
//使能计数器
TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
//主输出使能,当我们使用的是通用定时器时,这句不需要
}
然后,在我们初始化完结构体之后,我们还需要对高级定时器写一个初始化函数:
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_GPIO_Config();
ADVANCE_TIM_Mode_Config();
}
最后,我们还需要一个主函数:
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_AdvanceTim.h"
int main(void)
{
USART_Config();
ADVANCE_TIM_Init();
while(1)
{
}
}
在开发板上测试程序的时候,使用示波器,一个连PA8,一个连PB12,一个连PB13,一个连GND。在观看波形是,PA8的波形从1到0,而PB13的波形应从0到1,但是没有立即变化,这是为了让MOS管完全导通,当互补通道从1变0时,PA8并没有立即开启,同样也是为了让PB13驱动的MOS管完全关闭。
在最后的最后,我留一个小问题:我们在大一大二学习C语言的时候,我们在定义任意结构体时,都是在第一个大括号之后全部定义,而我在刚刚初始化结构体的时候,则是在语句之后进行定义的,这是为什么呢?下期开始我会公布答案,有想法的童鞋们可以留言回复,在这里咱就提前祝大家国庆快乐。