基于CKS32F4xx系列的MCU互补PWM方案

控制/MCU

1876人已加入

描述

MCU微课堂

CKS32F4xx系列

MCU互补PWM

互补输出简介

互补输出只能在高级控制定时器(TIM1和TIM8)上使用,可以输出两路互补信号,包括主输出OCx和互补输出OCxN。基于比较输出一节的内容,OCx和OCxN都可以输出一定频率和占空比的PWM波形,且他们的极性是相反的,如图1所示,OCxREF是参考信号,OCx输出信号与参考信号相同,只是它的上升沿相对于参考信号的上升沿有一个延迟,OCxN输出信号与参考信号相反,只是它的上升沿相对于参考信号的下降沿有一个延迟,这个延迟时间,我们是可以通过死区寄存器配置的,本节中我们可以设置为0。

控制电机

图1 带死区插入的互补输出

控制电机

图2捕获比较通道的输出阶段

如图3所示,因为互补输出多用于PWM控制电机的项目中,所以紧急情况下的刹车控制是必不可少的,OSSR的含义是运行模式下的关闭状态选择,OSSI的含义是空闲模式下的关闭状态选择,MOE的含义是主输出使能,一般的应用模式为,当短路输入为有效状态,MOE又硬件异步清零,OSSI设置为0,禁止OCOCN输出,OSI1OSI1N设置为0,OC1OC1N输出为0,达到紧急刹车的目的。

控制电机

图3 控制位和输出状态

配置步骤

互补输出实际跟比较输出章节一样使用的是定时器的功能,所以相关的函数设置同样在库函数文件CKS32f4xx_tim.h和CKS32f4xx_tim.c文件中。 1)开启TIM1和GPIO时钟,配置PA7、PA8选择复用功能GPIO_AF_TIM1输出。 要使用TIM1,我们必须先开启TIM1的时钟,这点相信大家看了这么多代码,应该明白了。这里我们还要配置PA7、PA8为复用(GPIO_AF_TIM1)输出,才可以实现TIM1_CH1的互补PWM经过PA7、PA8输出。库函数使能TIM1时钟的方法是:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //>>TIM1 时钟使能
这在前面章节已经提到过。当然,这里我们还要使能GPIOA的时钟。然后我们要配置PA7引脚映射至GPIO_AF_TIM1,复用为定时器1,调用的函数为:
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1); //>>GPIOA7 复用为定时器1
配置PA8引脚映射至GPIO_AF_TIM1,复用为定时器1,调用的函数为:
GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //>>GPIOA8 复用为定时器1
这个方法跟我们串口实验讲解一样,调用的同一个函数,最后设置PA7为复用功能输出这里我们只列出GPIO初始化为复用功能的一行代码:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //>>复用功能
这里还需要说明一下,对于定时器通道的引脚关系,大家可以查看CKS32F4对应的数据手册,比如我们PWM实验,我们使用的是定时器1的通道1,对应的引脚PA7可以从数据手册表中查看:

 

 

控制电机

2)初始化TIM1,设置TIM1的ARR和PSC等参数。 在开启了TIM1的时钟之后,我们要设置ARR和PSC两个寄存器的值来控制输出PWM的周期。这在库函数是通过TIM_TimeBaseInit函数实现的,在上一节定时器中断章节我们已经有讲解,这里就不详细讲解,调用的格式为:

 

TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值


TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx
3)设置TIM1_CH1的PWM模式,使能TIM1的CH1输出。 设置TIM1_CH1为PWM模式(默认是冻结的)通过配置TIM1_CCMR1的相关位来控制TIM1_CH1的模式。在库函数中,PWM通道设置是通过函数TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里我们使用的是通道1,所以使用的函数是TIM_OC1Init()。
Void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
这种初始化格式大家学到这里应该也熟悉了,所以我们直接来看看结构体TIM_OCInitTypeDef的定义:
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState; */
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;
这里我们讲解一下与我们要求相关的几个成员变量: 参数TIM_OCMode设置模式是PWM还是输出比较,这里我们是PWM模式。 参数TIM_OutputStateOutputNState用来设置比较输出使能,也就是使能PWM输出到端口。参数TIM_OCPolarityOCNPolarity用来设置极性是高还是低。参数TIM_OCIdleState和TIM_OCNIdleState用来设置空闲时的输出状态。 要实现我们上面提到的场景,方法是:
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //>>选择模式 PWM
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High; //>>输出极性高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //>>输出极性低
TIM_OCInitStructure. TIM_OCIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OCInitStruct.TIM_Pulse=100;//待装入捕获比较寄存器的脉冲值
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //>>根据指定的参数初始化外设TIM1 OC1
 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器
4)使能 TIM1。 在完成以上设置了之后,我们不开启刹车功能,并使能TIM1,使能TIM1的方法前面已经讲解过:
TIM_BDTRStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable;//自动输出功能使能
TIM_BDTRStructure.TIM_Break=TIM_Break_Disable;//失能刹车输入
TIM_BDTRStructure.TIM_BreakPolarity=TIM_BreakPolarity_High; //刹车输入管脚极性高
TIM_BDTRStructure.TIM_DeadTime=0; //输出打开和关闭状态之间的延时
TIM_BDTRStructure.TIM_LOCKLevel=TIM_LOCKLevel_OFF;// 锁电平参数: 不锁任何位
TIM_BDTRStructure.TIM_OSSIState=TIM_OSSIState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRStructure.TIM_OSSRState=TIM_OSSRState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRConfig(TIM1,&TIM_BDTRStructure);
TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIM1在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM1,ENABLE);   //PWM使能主输出MOE=1
TIM_Cmd(TIM1,ENABLE);   //打开TIM1
5)修改TIM1_CCR1来控制占空比。 最后,在经过以上设置之后,PWM其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改TIM1_CCR1则可以控制CH1的输出占空比。在库函数中,修改TIM1_CCR1占空比的函数是:
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare2);
理所当然,对于其他通道,分别有一个函数名字,函数格式为:

TIM_SetComparex(x=1,2,3,4)

通过以上5个步骤,我们就可以控制TIM1的CH1输出互补PWM波了。 3代码示例 添加PWM配置文件pwm.c和pwm.h。 pwm.c源文件代码如下:

//>>TIM1 PWM 部分初始化
//>>PWM 输出初始化
//>>arr:自动重装值 psc:时钟预分频数
void TIM1_PWM_Init(u32 arr,u32 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//TIM1 时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能 PORTA 时钟
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1); //PA7 复用为 TIM1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //PA8 复用为 TIM1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度 100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA7PA8
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);//初始化定时器 1
//初始化 TIM1 Channel1 互补PWM 模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //>>选择模式 PWM
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High; //>>输出极性低
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //>>输出极性低
TIM_OCInitStructure. TIM_OCIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //>>根据指定的参数初始化外设TIM1 OC1
 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器
TIM_BDTRStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable;//自动输出功能使能  
TIM_BDTRStructure.TIM_Break=TIM_Break_Disable;//失能刹车输入
TIM_BDTRStructure.TIM_BreakPolarity=TIM_BreakPolarity_High; //刹车输入管脚极性高
TIM_BDTRStructure.TIM_DeadTime=0; //输出打开和关闭状态之间的延时
TIM_BDTRStructure.TIM_LOCKLevel=TIM_LOCKLevel_OFF;// 锁电平参数: 不锁任何位
TIM_BDTRStructure.TIM_OSSIState=TIM_OSSIState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRStructure.TIM_OSSRState=TIM_OSSRState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRConfig(TIM1,&TIM_BDTRStructure);
TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIM1在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM1,ENABLE);   //PWM使能主输出MOE=1
TIM_Cmd(TIM1,ENABLE);              //打开TIM1
TIM_SetCompare1(TIM1,300); //>>设置pwm的占空比为300/500 = 60%
}
此部分代码包含了上面介绍的PWM输出设置的前5个步骤。这里我们关于TIM1的设置就不再说了。接下来,我们看看主程序里面的main函数如下:
int main(void)
{ 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//>>设置系统中断优先级分组 2
delay_init(168); //>>初始化延时函数
TIM1_PWM_Init(500-1,84-1); //>>定时器时钟为 84M,分频系数为 84,所以计数频率
//>> 84M/84=1Mhz,重装载值500,所以PWM频率为1M/500=2Khz.
while(1) 
{
}
}
这里,我们先设置好了NVIC终端优先级,然后初始化延时函数和timer,在timer的初始化参数中我们把PWM的频率设置成2K,将占空比设置成60%,完成PWM输出设置。


审核编辑:黄飞

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分