飞思卡尔PIT定时中断程序和脉冲计数

刚开始接触这款单片机,由于看的书基本上都是以dg128为原型来讲解的,故很多东西都是按照dg128的情况来移植到xs128上的,导致出了很多错误。像定时器模块这部分,查了很多资料,最后发现xs128没有dg128所具有的MDC模数计数器模块,相对应的是定时模块PIT,然后在网上疯狂的找了很多资料,总结下来,自己花了一晚上弄了个最最简单的定时中断程序,实现1秒钟LED灯的闪烁。

PIT说明:
S12PIT24B4CV1是一个模数递减计数器。首先给计数寄存器设定一个初值,每经过一个总线周期,计数器进行一次减一操作,当计数器自减溢出时,触发中断。因为总线周期是已知的,即可以通过计数器自减实现定时。
在XS128PIT模块中,需要用到得是如下几个寄存器。
1)、PIT Contorl and force Lad Micro Timer Register(PITCFLMT)
该寄存器用于PIT模块的使能设置和工作方式设置。通常设置该寄存器中的PITE为即可,即PITCFLMT_PITE=1,使PIT使能。
2)、PIT Channel Enable Register(PITCE)
该寄存器用于对PIT模块中的4个通道使能进行设置。如果使用某个通道时,对对应位进行置一即可,即PITCE_PCEx=1,其中x代表通道序号,为0~3。
3)、PIT Micro Timer Load Register 0 to 1 (PITMTLD0-1)
该寄存器用于设置PIT模块中的8位计数器初值,以实现24位的计数。设定值为0到255范围。
4)、PIT Load Register 0 to 3(PITLD0-3)
该寄存器用于设置PIT模块中的16位计数器初值,和8位计数器配合而成24位计数器。设定值范围0-65535。
5)、PIT Multiplex Register(PITMUX)
该寄存器对定时器通道的8位时基进行选择。因为8位计数器只有两个,所以在将8位计数器和16位计数器连接时,可以选择不同的8位时基。
当设置为0时,对应通道选择时基0;置一时,对应通道选择时基1。
如PITMUX_PMUX0=1为通道0选择时基1。
6)、PIT Interrupt Enable Register(PITINTE)
该寄存器为中断使能寄存器,为不同的PIT通道中断使能。设定为0时,相应通道中断禁止。置一时,相应通道使能。
如PITINTE_PINTE0=1时,PIT通道0定时中断使能,当计数器递减溢出时,申请中断。
7)、PIT Time-Out Flag Register(PITTF)
该寄存器为溢出标志位,当某一通道的8位计数器和16位计数器递减到0时,该位置一。给改位写1则清除该标志位。
可以通过查询该位来判断定时是否完成。




程序中要注意的问题:

1、头文件derivative.h中包含为:

#include
#pragma LINK_INFO DERIVATIVE "MC9S12XS128"


这是Codewarrior5.0版本中的默认设置。

2、设置PLL时钟时SYNR和REFDV须按照给的程序当中来设置,若按照程序注释中来设置的话时钟将出现很大误差,原因我不知道,我是不断测试得到的。

3、具体的寄存器要根据给定

的对应的头文件,不同的IDE版本中xs128的头文件可能不同,应根据实际情况来写。




代码如下:
#include /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */

#define PITTIME 1000//设定为5ms定时
uchar count=0;

void setbusclock(void) //32MHz 外部时钟16MHz
{
CLKSEL=0X00; // disengage PLL to system
PLLCTL_PLLON=1; // turn on PLL


SYNR =0x40 | 0x03;
REFDV=0x80 | 0x01;
//SYNR =1; //PLLCLK=2*fOSC*(SYNR + 1)/(REFDV + 1)
//REFDV=1;


POSTDIV=0x00; // 4:0, fPLL= fVCO/(2xPOSTDIV)
// If POSTDIV = $00 then fPLL is identical to fVCO (divide by one).
_asm(nop); // BUS CLOCK=16M
_asm(nop);
while(!(CRGFLG_LOCK==1)); //when pll is steady ,then use it;
CLKSEL_PLLSEL =1; //engage PLL to system;
}

void PORTB_init(void)//IO口初始化,B口为输出
{
DDRB=0xFF;
PORTB=0x00;
}

void PIT_init(void)//定时中断初始化函数 5MS定时中断设置
{
PITCFLMT_PITE=0; //定时中断通道0关
PITCE_PCE0=1;//定时器通道0使能
PITMTLD0=160-1;//8位定时器初值设定,160分频,在32MHzBusClock下,为0.2MHz。即5us
PITLD0=PITTIME-1;//16位定时器初值设定。PITTIME*0.005MS
PITINTE_PINTE0=1;//定时器中断通道0中断使能
PITCFLMT_PITE=1;//定时器通道0使能
}

void main(void) {
/* put your own code here */

setbusclock();
PORTB_init();
PIT_init();
EnableInterrupts;
for(;;) {
_FEED_COP(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}


#pragma CODE_SEG __NEAR_SEG NON_BANKED //指示该程序在不分页区
void interrupt 66 PIT0(void)
{
count++;
if(count==200)
{
PORTB=~PORTB;//输出取反
count=0;
}
PITTF_PTF0=1;//清中断标志位
}



对PWM脉冲计数可能困难一些。因为计数器对脉冲宽度有一定要求,而PWM的脉冲占空比一般在0-100%之间可调,在占空比很低或者很高的时候计数都不准确。如果能够在PWM的脉宽中断服务程序里加上计数程序就准确了。但是对PWM脉冲计数并不能够告诉我们马达走了多远。因为同样一个PWM脉冲,脉宽比较宽的,马达会走得比较远,脉宽窄的,马达就走不了那么远。就算同样的脉宽,负载不同的时候马达转过的距离也不一样。所以用PWM脉宽的计数来计算马达走了多远是不可能的。
三轴移动,不可能没有编码器,编码器的脉冲是把一个圆周分成若干等分,一般是几百到几十万。每转过一个角度,就输出一个脉冲。把编码器输出接到计数器上,输入160个脉冲就停止,这个脉冲才是计量运动位置的脉冲。不过注意,现在编码器一般都是双线四分精度输出,用两根信号线输出达到四倍精度。TI的DSP有一个四倍精度编码器输入接口,Freescale 的应该也有,你查查手册看看有没有相应的功能



飞思卡尔单片机定时器应用

程序示例?
// 16位累加器测速程序(PACB累加器),PT0口输入脉冲
//每次记录脉冲时间可修改宏:TIME_INTER
//
///////////////////////////////////////////////////////////////////
#include /* common defines and macros */
#include /* derivative information */
#pragma LINK_INFO DERIVATIVE "mc9s12dg128b"

#define TIME_INTER 1000 //1000个ms=1s
unsigned int Get_pulse;


void Start_PLL(void) //busCLK=32M
{
REFDV=0x01; // PLLCLK =2*OSCCLK*(SYNR + 1)/(REFDV + 1)
SYNR=0x03; // =2*16M*3/2=64M
asm{
BRCLR CRGFLG,#$08,*
BSET CLKSEL,#$80
}
}


void PACBInit()
{
TCTL4 = 0X02; //下降沿捕捉脉冲
PBCTL = 0x40; //级联两个8位累加器(PAC0和PAC1)
ICPAR = 0X03; //使能累加器
PACN10 = 0X0000;
}


void MDCInit(void)
{
MCCTL = MCCTL&0Xfb; //模数计数器禁止运行
MCCTL = 0Xe3; //允许中断,模数计数方式
//返回时重新加载所用的常数,分频常数为16
MCCTL = MCCTL|0X04; //模数计数器使能
MCCNT = 2000; //(1/32M)*16*2000= 1ms
MCCTL = MCCTL|0X08 ; //把模数常数寄存器的值加载到模数计数器FLMC;
}



void main(void)
{
DisableInterrupts;
Start_PLL();
PACBInit();
MDCInit();
EnableInterrupts;

for(;;)
{

}
}


#pragma CODE_SEG __NEAR_SEG NON_BANKED
void interrupt 26 MDC_ISR(void)
{
static unsigned int number_count;
MCFLG = 0x80;
number_count ++;

if( number_count==TIME_INTER) //1秒取一次
{
number_count=0;
Get_pulse = PACN10;
PACN10 = 0X0000;

}
}



相关文档
最新文档