MSP430学习笔记(五)--定时器实战

第一个Demo来自官方例程中的 msp430g2xx3_ta_01 ,该例中Timer_A工作在最典型的定时状态,按照 50000 个SMCLK周期的时间间隔产生中断。代码中SMCLK的频率为DOC默认工作频率,约为1MHz,每次定时中断都会翻转P1.0管脚的输出电平,所以P1.0管脚会出现10Hz左右的方波信号。可接LED1的跳线帽直接观察现象。

#include <msp430.h>

int main(void)
{
  //关闭看门狗
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  //设置 P1.0 管脚为输出
  P1DIR |= 0x01;                            // P1.0 output
  //设置定时器 CCR0 中断使能
  CCTL0 = CCIE;                             // CCR0 interrupt enabled
  //设置中断间隔
  CCR0 = 50000;
  //设置 Timer_A 的时钟源为SMCLK,工作模式为 Continuous 模式
  TACTL = TASSEL_2 + MC_2;                  // SMCLK, contmode

  //MSP430 进入低功耗模式 LPM0 ,并使能全局中断
  _BIS_SR(LPM0_bits + GIE);                 // Enter LPM0 w/ interrupt
}

// Timer A0 interrupt service routine
// Timer_A0 中断服务函数
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
  //翻转 P1.0 管脚输出电平
  P1OUT ^= 0x01;                            // Toggle P1.0
  //由于工作在 Continuous 模式,比较值增加 50000
  CCR0 += 50000;                            // Add Offset to CCR0
}

示波器检测波形如图所示:
TA_01波形图msp430g2xx3_ta_01 到 msp430g2xx3_ta_14 均差不多,按上一小节内容解读就可。
msp430g2xx3_ta_16:定时器工作在比较模式,利用 Timer_A 从TA1(P1.2)输出占空比为75%的PWM信号。

#include <msp430.h>

int main(void)
{
  //关闭看门狗
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  //设置 P1.2 管脚为输出
  P1DIR |= 0x0C;                            // P1.2 and P1.3 output
  //设置 P1.2 管脚为 TA0.1 输出功能
  P1SEL |= 0x0C;                            // P1.2 and P1.3 TA1/2 options
  //设置 PWM 周期
  CCR0 = 512-1;                             // PWM Period
  // CCR1 工作在 reset/set 模式
  CCTL1 = OUTMOD_7;                         // CCR1 reset/set
  //设置 PWM 占空比 384/512 = 0.75
  CCR1 = 384;                               // CCR1 PWM duty cycle
  // Timer_A 的时钟源为 SMCLK ,工作模式为 Up 模式
  TACTL = TASSEL_2 + MC_1;                  //SMCLK, up mode
  //关闭未使用的 CPU
  _BIS_SR(CPUOFF);                          // Enter LPM0
}

很多人看完程序,会发现明明是P1.2 和 P1.3管脚为定时器输出,可是示波器并未检测到 P1.3 管脚的输出PWM,很好的,说明考虑了一些实际问题,问题答案也非常简单,建议翻一下芯片手册就知道了,这块设置本人认为是官方程序的一个Bug,理解了,也就会改了。
同样,该程序示波器检测波形如图所示:75.15%占空比会发现图一的占空比并非特别整齐,于是我修改了CCR0与CCR1的值,分别为2048-1与1536,得到如图的检测波形:
75%占空比大家现在可以观察到,占空比数值已变整齐,但是周期与频率也改变了,这是因为CCR0的值决定周期,CCRx决定占空比,周期=CCR0/CLK,设 ACLK = TACLK = LFXT1 = 32768Hz,MCLK = SMCLK = DCOCLK = 32 x ACLK = 1.048576 MHz,故周期为 512/1048576 = 488us,本次演示中的周期约为 CCR0/1MHz,可以发现理论值与实测值之间存在误差。
下面展示一个开源的 G2553 的 PWM库,其他系列子MCU也可仿照类似书写。
PWM.c

#include <msp430g2553.h>

#define DEADTIME 20							//预设死区时间,以TA的clk为单位

/*******设定TA输出IO口,目前设定为MSP430G2553,20Pin封装无TA0.2********/
#define TA01_SET 	P1SEL |= BIT6; P1DIR |= BIT6		//P1.6
#define TA02_SET 	P3SEL |= BIT0; P3DIR |= BIT0		//P3.0
#define TA11_SET 	P2SEL |= BIT2; P2DIR |= BIT2		//P2.2
#define TA12_SET 	P2SEL |= BIT4; P2DIR |= BIT4		//P2.4
#define TA01_OFF 	P1SEL&= ~BIT6 					    //P1.6
#define TA02_OFF 	P3SEL &= ~BIT0 						//P3.0
#define TA11_OFF 	P2SEL &= ~BIT2 						//P2.2
#define TA12_OFF 	P2SEL &= ~BIT4						//P2.4
/******************************************************************************************************
* 名       称:TA0_PWM_Init()
* 功       能:TA0定时器作为PWM发生器的初始化设置函数
* 入口参数:Clk:时钟源 'S'=SMCLK;   'A'=ACLK ;   'E'=TACLK(外部输入); 'e'= TACLK(TACLK取反)
                    Div:时钟分频系数: 1/2/4/8
		    Mode1:通道1的输出模式 'F'设为超前PWM(模式7),'B'滞后PWM(模式3) ,'D'带死区增PWM(模式6),0=禁用
		    Mode2:通道2的输出模式 'F'设为超前PWM(模式7),'B'滞后PWM (模式3),'D'带死区减PWM(模式2),0=禁用
		    设置输出带死区控制的PWM时,两通道均需使用,且均为死区模式。
* 出口参数:1表示设置成功,0表示参数错误,设置失败。
* 说       明 : 在调用PWM相关函数之前,需要调用该函数设置TA的模式和时钟源。
* 范       例 : TA0_PWM_Init('A',1,'F','P')TA时钟设为ACLK,通道1和通道2均为超前PWM输出
               TA0_PWM_Init('S',4,'D','D')TA时钟设为SMCLK/4, 通道1为死区增PWM、通道2为死区减PWM
               TA0_PWM_Init('A',1,'F',0)TA时钟设为ACLK,通道1超前PWM输出,通道2不作TA用。
 ******************************************************************************************************/
char TA0_PWM_Init(char Clk,char Div,char Mode1,char Mode2)
{
  TA0CTL =0;																		// 清除以前设置

  switch(Mode1)																	//为定时器选择计数模式
  {
  case 'F': case 'f':																//普通PWM
		  TA0CTL |=MC_1; break;											//主定时器为增计数
  case 'B':case 'b':
	  	  TA0CTL |=MC_1; break;											//主定时器为增计数
  case 'D': case 'd':																//死区PWM
     	  TA0CTL |=MC_3; break;											//主定时器为增减计数
  default : return(0);															//其他情况都是设置参数有误,返回0
  }

  switch(Clk)  																		//为定时器TA选择时钟源
  {
    case 'A': case 'a':  	TA0CTL|=TASSEL_1; break;   			//ACLK
    case 'S': case 's': 	TA0CTL|=TASSEL_2; break;  			//SMCLK
    case 'E':            	TA0CTL|=TASSEL_0; break;  			//外部输入(TACLK)
    case 'e':          		TA0CTL|=TASSEL_3; break;   			//外部输入(TACLK取反)
    default :  return(0);  														//设置参数有误,返回0
  }
  switch(Div) 																		//为定时器TA选择分频系数
  {
    case 1:   TA0CTL|=ID_0; break;   //1
    case 2:   TA0CTL|=ID_1; break;   //2
    case 4:   TA0CTL|=ID_2; break;   //4
    case 8:   TA0CTL|=ID_3; break;   //8
    default :  return(0);  														//设置参数有误,返回0
  }
  switch(Mode1)										 							//设置PWM通道1的输出模式。
  {
     case 'F':	case 'f':
              TA0CCTL1 = OUTMOD_7;
              TA01_SET;
              break;
     case 'B':	case 'b':
              TA0CCTL1 = OUTMOD_3;
              TA01_SET;
              break;
     case 'D': case'd':
	     TA0CCTL1 = OUTMOD_6;
    	     TA01_SET;
    	     break;
      case '0':case 0:   															//如果设置为禁用
             TA01_OFF;   															//TA0.1恢复为普通IO口
              break;
     default :  return(0); 							  							//设置参数有误,返回0
  }
  switch(Mode2) 																//设置PWM通道2的输出模式。
  {
      case 'F':	 case 'f':
              TA0CCTL2 = OUTMOD_7;
              TA02_SET;  break;
       case 'B':	case 'b':
              TA0CCTL2 = OUTMOD_3;
              TA02_SET;
                break;
       case 'D': case 'd':
	       	   TA0CCTL2 = OUTMOD_2;
	       	   TA02_SET;
	       	   break;
       case '0':case 0:   														//如果设置为禁用
           	 	TA02_OFF;   							 						//TA0.1恢复为普通IO口
           	 	break;
       default :  return(0); 												//设置参数有误,返回0
    }
  return(1);
}
/******************************************************************************************************
* 名       称:TA0_PWM_SetPeriod()
* 功       能:设置PWM发生器的周期
* 入口参数:Channel: TA0=0, TA1=1
*                     Period:周期(0~65535) 时钟个数
* 出口参数:1:设置成功     0:设置失败
* 说       明 : 普通PWM与带死区PWM周期相差一倍
* 范       例 : TA0_PWM_SetPeriod(500)设置PWM方波周期为500或1000个时钟周期
 ******************************************************************************************************/
char TA0_PWM_SetPeriod(unsigned int Period)
{
	if (Period>65535)	return(0);
	 TA0CCR0 = Period;
	return(1);
}

/******************************************************************************************************
* 名    称:TA0_PWM_SetPermill()
* 功    能:设置PWM输出的占空比(千分比)
* 入口参数:Channel: 当前设置的通道号  1/2
            Duty: PWM高电平有效时间的千分比 (0~1000),
* 出口参数:1设置成功,0设置失败
* 说    明: 1000=100.0%  500=50.0% ,依次类推。死区模式时,两channel同时设定。
* 范    例: TA0_PWM_SetPermill(1,300)设置PWM通道1方波的占空比为30.0%
               TA0_PWM_SetPermill(2,,825)设置PWM通道2方波的占空比为82.5%
 ******************************************************************************************************/
char TA0_PWM_SetPermill(char Channel,unsigned int Duty)
{
	unsigned char Mod = 0;
	unsigned int DeadPermill=0;
	unsigned long int Percent=0;							//防止乘法运算时溢出
	Percent=Duty;
	DeadPermill=((DEADTIME*1000)/TACCR0);		//将绝对死区时间换算成千分比死区时间
	switch (Channel)												//先判断出通道的工作模式
		{
	case 1:
		Mod = (TA0CCTL1& 0x00e0)>>5;		break;	//读取输出模式,OUTMOD0位于5-7位
	case 2:
		Mod = (TA0CCTL2 & 0x00e0)>>5;	break;	//读取输出模式,OUTMOD1位于5-7位
	default:	return(0);
		}

	switch(Mod)														//根据模式设定TACCRx
		{
	case 2: case 6:			/**死区模式2,6时,需要判断修正死区时间,且同时设定TA0CCR1/2 的值*/
		{
			if((1000-2*Percent)<=DeadPermill)			//预留死区时间
				Percent=(1000-DeadPermill)/2;
			TA0CCR1=Percent*TA0CCR0/1000;
			TA0CCR2= TA0CCR0-TA0CCR1;
			break;
		}
		case 7:
		{
			if(Percent>1000)	Percent=1000;
			if(Channel==1) TA0CCR1=Percent* TA0CCR0/1000;
			if(Channel==2) TA0CCR2=Percent* TA0CCR0/1000;
			break;
		}
		case 3:		//占空比一律为正脉宽,所以需要 TA0CCR0减去占空比
		{
			if(Percent>1000)	Percent=1000;
			if(Channel==1) TA0CCR1= TA0CCR0-Percent*TA0CCR0/1000;
			if(Channel==2) TA0CCR2= TA0CCR0-Percent*TA0CCR0/1000;
			break;
		}
		default: return(0);
		}
		return (1);
	}

/*************TA1*******************/

/******************************************************************************************************
* 名       称:TA1_PWM_Init()
* 功       能:TA1定时器作为PWM发生器的初始化设置函数
* 入口参数:Clk:时钟源 'S'=SMCLK;   'A'=ACLK ;   'E'=TACLK(外部输入); 'e'= TACLK(TACLK取反)
                    Div:时钟分频系数: 1/2/4/8
		    Mode1:通道1的输出模式 'F'设为超前PWM(模式7),'B'滞后PWM(模式3) ,'D'带死区增PWM(模式6),0=禁用
		    Mode2:通道2的输出模式 'P'设为超前PWM(模式7),'B'滞后PWM (模式3),'D'带死区减PWM(模式2),0=禁用
		    设置输出带死区控制的PWM时,两通道均需使用,且均为死区模式。
* 出口参数:1表示设置成功,0表示参数错误,设置失败。
* 说       明 : 在调用PWM相关函数之前,需要调用该函数设置TA的模式和时钟源。
* 范       例 :    TA1_PWM_Init('A',1,'P','P')TA时钟设为ACLK,通道1和通道2均为超前PWM输出
                   TA1_PWM_Init('S',4,'D','D')TA时钟设为SMCLK/4, 通道1为死区增PWM、通道2为死区减PWM
                   TA1_PWM_Init('A',1,'P',0)TA时钟设为ACLK,通道1超前PWM输出,通道2不作TA用。
 ******************************************************************************************************/
char TA1_PWM_Init(char Clk,char Div,char Mode1,char Mode2)
{
  TA1CTL =0;																// 清除以前设置

  switch(Mode1)															//为定时器选择计数模式
  {
  case 'F': case 'f':														//普通PWM
	  TA1CTL |=MC_1; break;										//主定时器为增计数
  case 'B':case 'b':
	  TA1CTL |=MC_1; break;										//主定时器为增计数
  case 'D': case 'd':														//死区PWM
      TA1CTL |=MC_3; break;										//主定时器为增减计数
  default : return(0);													//其他情况都是设置参数有误,返回0
  }

  switch(Clk)  																//为定时器TA选择时钟源
  {
    case 'A': case 'a':  	TA1CTL|=TASSEL_1; break;   			//ACLK
    case 'S': case 's': 	TA1CTL|=TASSEL_2; break;  			//SMCLK
    case 'E':            		TA1CTL|=TASSEL_0; break;  			//外部输入(TACLK)
    case 'e':          			TA1CTL|=TASSEL_3; break;   			//外部输入(TACLK取反)
    default :  return(0);  														//设置参数有误,返回0
  }
  switch(Div) 																		//为定时器TA选择分频系数
  {
    case 1:   TA1CTL|=ID_0; break;   //1
    case 2:   TA1CTL|=ID_1; break;   //2
    case 4:   TA1CTL|=ID_2; break;   //4
    case 8:   TA1CTL|=ID_3; break;   //8
    default :  return(0);  														//设置参数有误,返回0
  }
  switch(Mode1)										 							//设置PWM通道1的输出模式。
  {
     case 'F':	case 'f':
              TA1CCTL1 =OUTMOD_7;
              TA11_SET;
              break;
     case 'B':	case 'b':
              TA1CCTL1 =OUTMOD_3;
              TA11_SET;
              break;
     case 'D': case'd':
	     TA1CCTL1 =OUTMOD_6;
    	     TA11_SET;
    	     break;
      case '0':case 0:   									//如果设置为禁用
             TA11_OFF;   									//TA0.1恢复为普通IO口
              break;
     default :  return(0); 							    //设置参数有误,返回0
  }
  switch(Mode2) 										//设置PWM通道2的输出模式。
  {
      case 'F':	 case 'f':
              TA1CCTL2 =OUTMOD_7;
              TA12_SET;
       case 'B':	case 'b':
              TA1CCTL2 =OUTMOD_3;
              TA12_SET;
                break;
       case 'D': case 'd':
	       	   TA1CCTL2 =OUTMOD_2;
	       	   TA12_SET;
	       	   break;
       case '0':case 0:   									//如果设置为禁用
           	 	TA12_OFF;   								//TA0.1恢复为普通IO口
           	 	break;
       default :  return(0); 							//设置参数有误,返回0
    }
  return(1);
}
/******************************************************************************************************
* 名       称:TA1_PWM_SetPeriod()
* 功       能:设置PWM发生器的周期
* 入口参数:Channel: TA0=0, TA1=1
*                     Period:周期(0~65535) 时钟个数
* 出口参数:1:设置成功     0:设置失败
* 说       明 : 普通PWM与带死区PWM周期相差一倍
* 范       例 : TA1_PWM_SetPeriod(500)设置PWM方波周期为500或1000个时钟周期
 ******************************************************************************************************/
char TA1_PWM_SetPeriod(unsigned int Period)
{
	if (Period>65535)	return(0);
	 TA1CCR0 = Period;
	return(1);
}

/******************************************************************************************************
* 名    称:TA0_PWM_SetPermill()
* 功    能:设置PWM输出的占空比(千分比)
* 入口参数:Channel: 当前设置的通道号  1/2
            Duty: PWM高电平有效时间的千分比 (0~1000),
* 出口参数:1设置成功,0设置失败
* 说    明: 1000=100.0%  500=50.0% ,依次类推。死区模式时,两channel同时设定。
* 范    例: TA_PWM_SetPermill(1,300)设置PWM通道1方波的占空比为30.0%
            TA_PWM_SetPermill(2,825)设置PWM通道2方波的占空比为82.5%
 ******************************************************************************************************/
char TA1_PWM_SetPermill(char Channel,unsigned int Duty)
{
	unsigned char Mod;
	unsigned long int Percent=0;								//防止乘法运算时溢出
	Percent=Duty;
	switch (Channel)													//先判断出通道的工作模式
		{
	case 1:
		Mod = (TA1CCTL1 & 0x00e0)>>5;		break;	//读取输出模式,OUTMOD0位于5-7位
	case 2:
		Mod = (TA1CCTL2 & 0x00e0)>>5;		break;	//读取输出模式,OUTMOD1位于5-7位
	default:	return(0);
		}

	switch(Mod)			//根据模式设定TACCRx
		{
	case 2: case 6:			/**死区模式2,6时,需要判断修正死区时间,且同时设定TA1CCR1/2 的值*/
		{
			if((1000-2*Percent)<=DEADTIME)	//预留死区时间
				Percent=(1000-DEADTIME)/2;
			TA1CCR1=Percent* TA1CCR0/1000;
			TA1CCR2= TA1CCR0-TA1CCR1;
			break;
		}
		case 7:
		{
			if(Percent>1000)	Percent=1000;
			if(Channel==1) TA1CCR1=Percent* TA1CCR0/1000;
			if(Channel==2) TA1CCR2=Percent* TA1CCR0/1000;
			break;
		}
		case 3:		//占空比一律为正脉宽,所以需要 TA1CCR0减去占空比
		{
			if(Percent>1000)	Percent=1000;
			if(Channel==1) TA1CCR1= TA1CCR0-Percent* TA1CCR0/1000;
			if(Channel==2) TA1CCR2= TA1CCR0-Percent* TA1CCR0/1000;
			break;
		}
		default: return(0);
	}
		return (1);
}

PWM.h

#ifndef TA_PWM_H_
#define TA_PWM_H_

extern char TA0_PWM_Init();
extern char TA0_PWM_SetPeriod();
extern char TA0_PWM_SetPermill();

extern char TA1_PWM_Init();
extern char TA1_PWM_SetPeriod();
extern char TA1_PWM_SetPermill();

#endif /* TA_PWM_H_ */
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值