这一篇拖了近一个月,中间去烟台调小卫星程序折腾大概半个月,现在终于有时间写这一篇了。MSP430系列不管哪个型号一定是有内部定时器的,区别就是定时器数目和精度不同。顾名思义,定时器可以精确产生一定时间,其原理是数脉冲个数,一般是每个定时器时钟周期计一个脉冲,将脉冲计数和设定值进行比较,如果等于设定值则产生中断,或产生脉宽调制输出等。通常和定时器相关的功能有定时器/脉冲捕获/脉宽输出等功能,可以用来产生精确时间或产生脉宽调制输出信号控制电机转速等。MSP430的电容式触摸按键也是定时器的应用之一。
除了定时器,MSP430还有一种编译器指令可以较精确的延时,代码如下:
#define CPU_F ((double)8000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
这种方法和编译器、芯片型号密切相关,8位单片机目前我还不知道有哪个型号的编译器支持这个指令。首先在第一行double后面填上系统时钟频率,对于有内部锁相环/锁频环的芯片,这里要填上CPU实际运行频率,而不是晶振频率,然后程序中就可以调用delay_ms()和delay_us()使用延时功能了,前一个函数是ms级延时,精度1ms,后一个是us级延时,精度1us。这种方法精度可以到1个CPU时钟周期,不足时这种方法实际上和for循环延时是一样的,都是让CPU不停数数,在延时过程中CPU不能执行别的任务,而定时器延时是用一个外设模块计时,延时过程中CPU可以运行别的任务。
下面仍然结合代码介绍MSP430定时器基本使用。芯片以MSP430G2553为例,高级使用请自行参考手册。
首先放一张定时器模块框图
右边CCR0,1,2表示一个定时器模块有三个模块,每一个模块可以单独控制,上方表示三个模块共用部分,下面表示出其中一路模块的结构。三个模块使用同一时钟输入,输入时钟经过分频器到达定时器计数模块,计数值传递到下方TACCRx模块,与设定值进行比较,如果相同,则按这个模块设定功能执行。下方TACCRx设定每个模块的计数设定值,计数设定值和定时器计数值在ComparatorX中进行比较,然后执行设定的功能。CAP选择该路模块功能,可在脉冲捕获或定时器输出中选择,在输入脉冲捕获模式下,CCISx选择输入脉冲捕获的输入引脚,在定时器输出模式下OUTMODx选择输出模式。
定时器有几种计数模式,正向计数,连续计数或正/反计数,即计数模块计数方式。在正向计数模式,计数模块从0每次加1计数到TACCR0设定值,然后跳到0并重新开始计数;在连续计数模式,计数模块从0每次加1计数到FFFF,然后跳到0并重新开始计数,在正/反计数模式,计数模块从0每次加1计数到TACCR0设定值,然后再每次减1计数到0,然后重复。在这三种模式下,当计数值等于TACCRx设定值时会执行设定动作。几个模块可以联合使用以完成较复杂的计数操作。
下面放两个代码,一个是作为定时器功能,周期性控制引脚输出高/低电平,另一个是作为脉宽输出功能,产生占空比为75%的PWM信号。代码来源于MSPWare,有修改。
第一个代码:
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR |= 0x01; // P1.0 output
CCTL0 = CCIE; // CCR0 interrupt enabled
CCR0 = 50000;
TACTL = TASSEL_2 + MC_1; // SMCLK, upmode
while(1);
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
P1OUT ^= 0x01; // Toggle P1.0
}
这个代码中,首先在程序开始时关闭看门狗,然后将P1.0设为输出引脚,然后打开定时器中断,将CCR0值设为50000,即计数到50000后产生中断,并执行中断函数中的操作。然后TACTL设定定时器时钟源为SMCLK,模式为正向计数模式,然后等待定时器中断。在中断函数中改变P1.0输出电平。根据MSP430手册,由于定时器中断进入后中断标志位可以自动清零,这里可以不进行手动清零中断标志位操作。
第二个代码:使用定时器输出占空比为75%的PWM信号。
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR |= 0x0C; // P1.2 and P1.3 output
P1SEL |= 0x0C; // P1.2 and P1.3 TA1/2 options
CCR0 = 128; // PWM Period/2
CCTL1 = OUTMOD_6; // CCR1 toggle/set
CCR1 = 32; // CCR1 PWM duty cycle
TACTL = TASSEL_2 + MC_3; // SMCLK, up-down mode
while(1);
}
首先同样是关闭看门狗,然后将P1.2和P1.3设为外设功能输出,然后将CCR0值设为128,将CCR1值设为32,将定时器设为正/反计数模式,即从0计数到CCR0值,然后反向计数到0,并在计数值等于CCR1值时改变引脚输出状态,从而得到脉宽信号输出。因为脉宽信号由定时器模块直接控制输出,因此不需要占用CPU资源,也不需要在中断中进行处理。
更多定时器的其他用法可以参考手册中定时器部分。