PWM模块,对我们搞过电机控制的算是再熟悉不过了,也算是MCU中最常用的模块之一了,本来PWM模块该算是属于定时器模块范围内的,不过这里由于它应用的太广泛了,索性就单拿出来开一篇,源代码可以从本篇博客下面的附件中下载,下面按照老套路来分步介绍一下Kinetis的PWM模块: 1、Kinetis的PWM模块,是在飞思卡尔已经成熟应用多年的8位单片机上的PWM模块上扩展而来的(即早期的HCS08系列的Timer PWM Module),而以飞思卡尔在汽车电子的老大地位来说,自然其PWM模块的性能是没得说的,毕竟是经过工业现场多年的成熟应用考验过了; 2、Kinetis内部PWM模块的具体特性如下,仍旧是挑了几个重点特色简单介绍一下,当然由于PWM模块属于FTM模块的范围内,所以少不了牵涉到一些定时器的一些特性: (1)时钟源可选择,FTM的时钟源可以来自系统时钟或外部时钟。可对时钟分频,分频比为1,2,4,8,16,32,64,128,这属于定时器的特性; (2)最多3个FTM定时器FTM0、FTM1、FTM2,即可以有3个自由计数器,也即可以存在独立的3路PWM模块; (3)所有的通道都可以设置为中心对齐的PWM输出模式,且某个FTM定时器中的每对通道都可以级联以产生PWM信号; (4) PWM通道可成对工作在相同输出或互补输出,也可各通道独立输出,当通道成对互补输出时可使用死区插入功能,这对一些全桥控制电路有很大意义,省去了外围死区电路; (5)可软件控制PWM的输出,即屏蔽或者开启单独某个通道。
3、基本特性介绍完毕,下面就直入主题,开始着手PWM软件驱动的编写,这里就挨个寄存器的介绍了,太麻烦了,这次就直接列出代码,并附带详细的注释,代码风格仍旧采用模块化的概念,参数可选择,大家可以对照着Kinetis的数据手册了解其含义,个人觉着这种方式反而效果更好,呵呵: (1)PWM初始化函数 /**********************************************************************************
@ Routine:FTM_initPWM
@ Parameter: ftm FTM模块
FTM0
FTM1
FTM2
channel PWM通道号
FTM0--0~7,FTM1--0~1, FTM2--0~1
div 时钟分频数
000 Divide by 1
001 Divide by 2
010 Divide by 4
011 Divide by 8
100 Divide by 16
101 Divide by 32
110 Divide by 64
111 Divide by 128
polarity 左对齐方式下,
0--先低后高
1--先高后低
period PWM周期
duty PWM占空比
0 =< duty <= period
@ Description:PWM初始化
**********************************************************************************/
static void FTM_initPWM(FTM_MemMapPtr ftm, uint8 channel, uint8 div, uint8 polarity, uint16 period, uint16 duty)
{
if(FTM0==ftm)
{
SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK; /* 使能FTM0时钟 */
}
else if(FTM1==ftm)
{
SIM_SCGC6 |= SIM_SCGC6_FTM1_MASK; /* 使能FTM1时钟 */
}
else if(FTM2==ftm)
{
SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK; /* 使能FTM2时钟 */
}
FTM_MODE_REG(ftm) |= FTM_MODE_WPDIS_MASK; /* 禁用写保护 */
if(polarity)
FTM_CnSC_REG(ftm,channel) = (FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK)&(~FTM_CnSC_ELSA_MASK);
/* (MSB = 1, ELSB:ELSA = 10)左对齐,先高后低 */
else
FTM_CnSC_REG(ftm,channel) = (FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK);
/* (MSB = 1, ELSB:ELSA = 01)右对齐,先低后高 */
FTM_COMBINE_REG(ftm) = 0; /* DECAPEN=0,双边沿捕捉禁止,COMBINE=0,不级联 */
FTM_QDCTRL_REG(ftm) &= ~FTM_QDCTRL_QUADEN_MASK; /* 设置QUADEN = 0,不使用正交解码 */
FTM_MODE_REG(ftm) &= ~FTM_MODE_FTMEN_MASK; /* 使能兼容型TPM,不使用FTM特定的寄存器 */
FTM_MODE_REG(ftm) |= FTM_MODE_INIT_MASK; /* 使能初始化通道输出状态 */
FTM_OUTINIT_REG(ftm) |= polarity << channel; /* 根据极性设置初始通道状态 */
FTM_OUTMASK_REG(ftm) |= (1 << channel); /* 禁止channel通道PWM输出 */
FTM_MOD_REG(ftm) = period - 1; /* 计数终值,周期为(MOD-CNTIN+1)*时钟周期 */
FTM_CNTIN_REG(ftm) = 0; /* 计数初值 */
FTM_CNT_REG(ftm) = 0; /* 复位FTM计数器为CNTIN */
FTM_CnV_REG(ftm,channel) = duty; /* 设置占空比 */
FTM_SC_REG(ftm) = (FTM_SC_CLKS(1) | FTM_SC_PS(div)) & (~FTM_SC_CPWMS_MASK);
/* 选择BusClock时钟分频,CPWMS = 0,即左对齐 */
} (2)PWM启动函数,注意上面的PWM初始化之后PWM没有输出,需要此部分代码启动已设置好的PWM /**********************************************************************************
@ Routine:Start_PWM
@ Parameter: ftm FTM模块
FTM0
FTM1
FTM2
channel PWM通道号
FTM0--0~7,FTM1--0~1, FTM2--0~1
@ Description:启动PWM
**********************************************************************************/
void Start_PWM(FTM_MemMapPtr ftm, uint8 channel)
{
FTM_CNT_REG(ftm) = 0;
FTM_OUTMASK_REG(ftm) &= ~(1 << channel); /* 使能channel通道PWM输出 */
} (3)PWM关闭函数,可以动态关闭PWM通道,关闭之后可以用PWM启动函数重新开启PWM /**********************************************************************************
@ Routine:Stop_PWM
@ Parameter: ftm FTM模块
FTM0
FTM1
FTM2
channel PWM通道号
FTM0--0~7,FTM1--0~1, FTM2--0~1
@ Description:停止PWM
**********************************************************************************/
void Stop_PWM(FTM_MemMapPtr ftm, uint8 channel)
{
FTM_OUTMASK_REG(ftm) |= (1 << channel); /* 禁止channel通道PWM输出 */
} (4)PWM参数设置函数,PWM初始化之后,也可以通过该函数动态修改PWM参数 /**********************************************************************************
@ Routine:Set_PWM_Parameter
@ Parameter: ftm FTM模块
FTM0
FTM1
FTM2
channel PWM通道号
FTM0--0~7,FTM1--0~1, FTM2--0~1
period PWM周期
duty PWM占空比
@ Description:停止PWM
**********************************************************************************/
void Set_PWM_Parameter(FTM_MemMapPtr ftm, uint8 channel,uint16 period, uint16 duty)
{
FTM_CNT_REG(ftm) = 0; /* 复位FTM计数器为CNTIN */
FTM_MOD_REG(ftm) = period - 1; /* 计数终值,周期为(MOD-CNTIN+1)*时钟周期 */
FTM_CnV_REG(ftm,channel) = duty;
} 4、驱动编写完毕之后,就可以在主函数内调用各个函数应用PWM功能了,这里给出一个简单的应用,直接放在main函数下即可,具体实现功能为使能FTM0的CH4通道(PWM周期为48KHz、占空比为50%),使能FTM2的CH0通道(PWM周期为40Hz),注意同一个FTM定时器下的多个通道其PWM周期是一样的,所以如果想要得到两种周期的PWM的话就需要使用两个FTM定时器,这点有点不方便。 //insert the port router code
PORTD_PCR4 = PORT_PCR_MUX(4); /* 设置引脚PTD4引脚为FTM0_CH4功能 */
PORTB_PCR18 = PORT_PCR_MUX(3); /* 设置引脚PTB18引脚为FTM2_CH0功能 */
//insert your PWM initialization function
FTM_initPWM(FTM0,4,1,1,500,250); /* 使能FTM0的CH4通道,2分频Busclk48MHz/2=24MHz,
左对齐,周期为48kHz=24MHz/500,占空比50% */
FTM_initPWM(FTM2,0,5,0,37500,468); /* 使能FTM2的CH0通道,32分频Busclk48MHz/32=1.5MHz,
右对齐,周期为40Hz=1.5MHz/37500=1200*48kHz,低电平时间为15/48kHz */
|