jicheng0622

【技术分享】【原创】从零入手Kinetis系统开发(十三)之PWM模块

0
阅读(7625)

    时光荏苒,岁月如梭(咳咳,回家一趟,文艺了许多来,哈哈),入驻AET已经一年多的时间了,自己不知不觉中也成了老人儿了,我骄傲啊有木有,呵呵。当然这一年多自己受益良多,虽然不能娓娓道来,但俺是懂的感恩的人,受益于斯必然回报于斯,在此感谢AET及广大博友的支持,多谢~

    一年的时间,人家国产航母都下水了,舰载机都“走你”了,十八大都召开了,我这从零入手系列却才写到第十三篇,真是难产至极啊,估摸着时间来算的话,不说是“十年磨一剑”,还真差不多是一个月磨一剑啊,自惭形秽了,咳咳。不过说真的,真是每次写从零入手系列都头大的慌,一方面每篇模块例程都需要测试通过,另一方面还要保证文章的质量,字字斟酌,真心虚的慌,哈哈,所以希望能尊重俺的劳动,转载请注明文章出处及jicheng0622原作者信息,要求不高,仅此而已,呵呵。好了,又扯多了,下面进入正题吧(觉着可以的希望能投个票支持一下,呵呵):

    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的输出,即屏蔽或者开启单独某个通道。

image

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 */

5、将上述代码添加到工程中,运行之后,可以从示波器上看到PWM的波形图(图中示波器只显示了其中一部分),如下所示,是不是很帅,哈哈:

image

呼。。。一个晚上过去了,终于写完了这一篇了,真的有种如释重负的感觉啊,不多说了,赶紧休息会,烧壶水喝去,呵呵,未完待续~

附件为PWM例程源代码PWM模块.rar