MSP430G2553单片机定时器A有3个捕获比较寄存器CCR0,CCR1,CCR2.。MSP430G2553捕获程序应用很广泛,电子工程师可以多加了解。
所谓捕获,就是我们来检测外围的信号跳变时刻(此时信号理解为数字信号,即脉冲),此信号乃为我们捕获的对象,可以测量信号的脉冲宽度,即频率等。
捕获首先需要考虑的初始化工作
1.设置BCS模块,确定系统时钟MCLK子系统时钟SMCLK
把MCLK设置为8MHZ,SMCLK设置为1MHZ。
2.捕获输入引脚的选择
选择IO引脚时应查阅器件的手册,能够快速的查阅PDF资料找到正确的答案是一个程序员的基本素质。
3.程序设计思路
根据测频的原理,需要2次捕获才能测量一次输入信号的频率。因此要定义2个变量保存2次捕获结果。变量是无符号的整数型变量(与捕获寄存器的字长匹配)。
输入信号与CPU的工作是异步的,所以设计程序的时候是不知道什么时候才有捕获输入。
程序处理何时发生了捕获的方法有2种
一是查询的方法,定时器硬件在发生捕获事件后会置捕获中断表示CCIF为1,程序在主循环里不断的查询这个标志即可判断是否有捕获事件发生。
二是定时器中断法,当发生捕获事件时必产生定时器中断,在中断中读取捕获寄存器即可。
查询的方法不是好的程序设计方法,因为查询时要占用CPU,使得CPU不能再做其他任务。中断的方法对初学者有一定的困难。即中断程序如何与主程序通信(交换信息)。理解中断及设计中断服务程序要困难一些。
捕获模式
捕获外部输入的信号的上升沿或下降沿或上升沿下降沿都捕捉,当捕捉发生时,把TAR的值装载到TACCRx中,同时也可以进入中断,执行相应的操作。这样利用捕捉上升沿或下降沿就可以计算外部输入信号的周期,得出频率。利用捕捉上升沿和下降沿可以得出输入信号的高电平或低电平的持续时间。也可以算出占空比。下面是一个例子,是TImer_A捕获初始化的程序:
void TImer_init() //使用TImer1_A时要特别注意各个寄存器的写法,因为TImer0_A的寄存器都简写了,所以在写
//Timer1_A的寄存器时,要特别注意与Timer0_A的不同
{
P1SEL |= BIT2; //选择P12作为捕捉的输入端子 Timer0_A
//TACCTL1 |=CM_3+SCS+CAP+CCIE; //上下沿都触发捕捉,用于测脉宽,同步模式、时能中断 CCI1A
TACCTL1 |=CM_1+SCS+CAP+CCIE; //上升沿触发捕捉,同步模式、时能中断 CCI1A
TACTL |= TASSEL1+MC_2; //选择SMCLK时钟作为计数时钟源,不分频 增计数模式不行,必须连续计数模式
P2SEL |= BIT1; //选择P21作为捕捉的输入端子 Timer1_A
//TA1CCTL1 |=CM_3+SCS+CAP+CCIE; //上下沿都触发捕捉,用于测脉宽,同步模式、时能中断 CCI1A
TA1CCTL1 |=CM_1+SCS+CAP+CCIE; //上升沿触发捕捉,同步模式、时能中断 CCI1A
TA1CTL |= TASSEL1+MC_2; //选择SMCLK时钟作为计数时钟源,不分频 增计数模式不行,必须连续计数模式
}
相对应的中断函数如下:
#pragma vector=TIMER0_A1_VECTOR //Timer0_A CC1 的中断向量
__interrupt void Timer_A(void)
{
// CCI0A 使用的捕捉比较寄存器是TA0CCR0,TA0CCR0单独分配给一个
//中断向量TIMER1_A0_VECTOR,所以进入中断后直接就是Timer0_A CC0产生的中断,不用经过类似
//下面的方法判断中断源了 。
//Timer0_A CC1-4, TA0公用一个中断向量 TIMER0_A1_VECTOR,所以进入了中断后还要用下面
//的方法进行判断是哪一个中断源产生的中断
switch(TAIV) //如果是Timer0_A CC1产生的中断
{
case 2:
{
flag=1;
LPM1_EXIT; //退出低功耗模式
// _BIC_SR_IRQ(LPM1_bits);
//_bic_SR_register_on_exit(LPM1_bits);
break;
}
case 4: break;
case 10:break;
}
}
#pragma vector=TIMER1_A1_VECTOR //Timer1_A CC1 的中断向量
__interrupt void Timer_A1(void)
{
// P1OUT|=BIT0; //led调试用的
// LPM1_EXIT; //退出低功耗模式 因为使用的是CCI0A 使用的捕捉比较寄存器是TA1CCR0,TA1CCR0单独分配给一个
//中断向量TIMER1_A0_VECTOR,所以进入中断后直接就是Timer1_A CC0产生的中断,不用经过类似
//下面注释掉的方法判断 。
//而Timer1_A CC1-4, TA1则公用一个中断向量 TIMER1_A1_VECTOR,所以进入了中断后还要用下面
//的方法进行判断是哪一个中断源产生的中断
switch(TA1IV) //如果是Timer1_A CC1产生的中断
{
case 2:
{
flag=2;
LPM1_EXIT; //退出低功耗模式
// _BIC_SR_IRQ(LPM1_bits);
//_bic_SR_register_on_exit(LPM1_bits);
break;
}
case 4:break;
case 10:break;
}
}
//如果要测量更低频率的信号的话,可以在中断中判断溢出中断发生的次数,这样就可以得到溢出的次数,从而可以测量更
//低频率的信号
程序例子---利用捕获功能测一个正弦波信号的频率
1.在进行测量之前,你需要对正弦波进行转换,把它变为方波。这个很简单的电路。
2.测频率,下面的代码是我自己写的,可以测到100K。精确度0.01HZ。总的来说,用TIEMEA产生一个2S的中断,2S后去去读计算频率。TIMERA0是对脉冲宽度的测量,TIMERA1是对定时器timerA中断的处理
void Init_Capture(void)
{
P1DIR&=~BIT1;
P1SEL|=BIT1;
BCSCTL2 |= SELS; // SMCLK=XT2=16M
BCSCTL2 |= DIVS_1; //SMCLK 2分频,即SMCL=8MHZ
TACTL |=TASSEL_1+TAIE+TACLR; //8分频,选择ACLK为timerA的时钟源(ACLK),开中断,增计数模式
TACCTL0 |=CM_1+SCS+CAP+CCIS_0+CCIE; //上升沿捕获+同步捕获+开捕获+timerA为捕获+打开捕获中断
TACTL |=MC_2;
}
int main()
{
Init_Capture();
while(1)
{
if(global_a.Conver==1)//捕获频率
{
_DINT();
global_a.Conver=0;
global_a.CapCount=(float)((32768.0*global_a.pulse)/global_a.time);//计算频率,注意理解!
Print_Fre();//显示频率
_EINT();
}
}
}
#pragma vector=TIMERA0_VECTOR
__interrupt void timer_A(void)
{
if(global_a.Cap_Tar==0)
{
global_a.Cap_First = TACCR0;
global_a.Cap_Tar++;
}
else
{
global_a.Cap_Last = TACCR0;
global_a.Cap_Tar++;
}
}
#pragma vector=TIMERA1_VECTOR
__interrupt void timeA1(void)
{
switch(TAIV)
{case 2:
break;
case 4:
break;
case 10: if(global_a.Cap_Tar==0)
global_a.pulse=0;
else
{
global_a.pulse=global_a.Cap_Tar-1;
global_a.time = global_a.Cap_Last-global_a.Cap_First;
global_a.Cap_Tar=0;
TACTL &=~BIT0;
// BIC_SR_IRQ(LPM3_bits);
global_a.Conver=1;
_DINT();
}
break;
}
}