Chinaunix首页 | 论坛 | 博客
  • 博客访问: 365911
  • 博文数量: 242
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1134
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-20 10:53
文章分类

全部博文(242)

文章存档

2015年(1)

2014年(10)

2013年(18)

2012年(213)

分类:

2012-11-09 17:24:30

原文地址:系统时钟和定时器 作者:txgc_wm

系统时钟和定时器

S3C2410/S3C2440的时钟控制逻辑既可以外接晶振,然后通过内部电路产生时钟源;也可以直接使用外部提供的时钟源,它们通过引脚的设置来选择。

     时钟控制逻辑给整个芯片提供3种时钟:FCLK用于CPU核;HCLK用于AHB总线上设备,主要用于高性能模块之间的连接,比如CPU核、寄存器控制器、中断控制器、LCD控制器、DMAUSB主机模块等;PCLK用于APB总线上的设备,主要用于低带宽的周边外设之间的连接,比如WATCHDOGIISI2CPWM定时器、MMC接口、ADCUARTGPIORTCSPI

     为了降低电磁干扰、降低板间布线的要求,S3C2410/S3C2440外接的晶振频率通常很低,开发板上一般为12MHz,需要通过时钟控制逻辑的PLL提高系统时钟。S3C2410/S3C2440有两个PLLMPLLUPLLUPLL专用于USB设备,MPLL用于设置FCLKHCLKPCLK

    上电时,PLL没被启动,FCLK即等于外部输入的时钟,称为Fin。若要提高系统时钟,需要软件来启动PLL,可跟随FCLK的图像了解启动过程。

1)上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),nRESET信号恢复高电平后,CPU开始执行指令。

2)可以在程序开头启动MPLL,设置MPLL的几个寄存器后,需要等待一段时间(Lock Time),MPLL输出才稳定。在这段时间内,FCLK停振,CPU停止工作。Lock Time的长短由寄存器LOCKTIME设定。

3Lock Time之后,MPLL输出正常,CPU工作在新的FCLK下。

 FCLKHCLKPCLK的比例是可以改变的,设置它们三者的比例,启动MPLL只需设置3个寄存器(对于S3C2440的一些时钟比例,还需要额外设置一个寄存器)。

1LOCKTIME寄存器:用于设置“Time Lock”的长度。

2MPLLCON寄存器:用于设置FCLKFin的倍数。

3CLKDIVN寄存器:用于设置FCLKHCLKPCLK三者的比例。对于S3C2440的一些时钟比例,还需要额外设置一个寄存器CAMDIVN

 

#define S3C2410_MPLL_200MHZ     ((0x5c<<12)|(0x04<<4)|(0x00))

#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))

/*

 * 对于MPLLCON寄存器,[19:12]MDIV[9:4]PDIV[1:0]SDIV

 * 有如下计算公式:

 *  S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)

 *  S3C2410: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)

 *  其中: m = MDIV + 8, p = PDIV + 2, s = SDIV

 * 对于本开发板,Fin = 12MHz

 * 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4

 * FCLK=200MHz,HCLK=100MHz,PCLK=50MHz

 */

void clock_init(void)

{

    // LOCKTIME = 0x00ffffff;   // 使用默认值即可

    CLKDIVN  = 0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

 

    /* 如果HDIVN0CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode */

    __asm__(

        "mrc    p15, 0, r1, c1, c0, 0\n"        /* 读出控制寄存器 */

        "orr    r1, r1, #0xc0000000\n"          /* 设置为“asynchronous bus mode */

        "mcr    p15, 0, r1, c1, c0, 0\n"        /* 写入控制寄存器 */

        );

 

    /* 判断是S3C2410还是S3C2440 */

    if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))

    {

        MPLLCON = S3C2410_MPLL_200MHZ;  /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */

    }

    else

    {

        MPLLCON = S3C2440_MPLL_200MHZ;    /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */

    }      

}

 

 

PWM定时器

S3C2410/S3C2440的定时器部件完全一样,共有516位的定时器。其中定时器012PWM功能,即它们都有一个输出引脚,可以通过定时器来控制引脚周期性的高、低电平变化;定时器4没有输出引脚。

定时器部件的时钟电源位PCLK,首先通过两个8位的预分频器降低频率:定时器01共用第一个预分频,定时器234共用第二个预分频器。预分频器的输出将进入第二级分频器,它们输出5种频率的时钟:2分频、4分频、8分频、16分频或者外部时钟TCLK0/TCLK1。每个定时器的工作时钟可以从这5种频率中选择。

这两个预分频都可以通过TCFG0寄存器来设置,每个定时器工作在哪种频率下也可以通过TCFG1寄存器来选择。

     上面只是确定了定时器的工作频率,至于定时器如何工作还得了解其内部结构。

     定时器内部控制逻辑的工作流程如下。

     1)程序初始,设定TCMPBnTCNTBn这两个寄存器,它们表示定时器n的比较值、初始计数值。

     2)随之设置TCON寄存器启动定时器n,这时,TCMPBnTCNTBn的值将被装入其内部存储器TCMPnTCNTn中。在定时器n的工作频率下,TCNTn开始减1计数,其值可以通过读取TCNTOn寄存器得知。

3)当TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续减1计数。

4)当TCNTn的值到达0时,其输出管脚TOUTn再次反转,并触发定时器n的中断(如果中断使能的话)。

5)当TCNTn的值到达0时,如果在TCON寄存器中将定时器n设为“自动加载”,则TCMPBnTCNTBn寄存器的值被自动装入TCMPnTCNTn寄存器中,下一个计数流程开始。

定时器n的输出管脚TOUTn初始状态为高电平,以后在TCNTn的值等于TCMPn的值、TCNTn的值等于0时反转。也可以通过TCON寄存器设置其初始电平,这样TOUTn的输出就完全相反了。通过设置TCMPBnTCNTBn的值可以设置管脚TOUTn输出信号的占空比,这就是所谓的可调制脉冲,所以这些定时器又被称为PWM定时器。

下面讲解定时器的寄存器使用方法。

1TCFG0寄存器:位[70]、位[15:8]分别被用于控制预分频器01,它们的值为0~255.经过预分频器出来的时钟频率为:PCLK/{prescaler value+1}

2TCFG1寄存器:经过预分频器得到的时钟将被2分频、4分频、8分频和16分频,除这4种频率外,额外地,定时器01还可以工作在外接的TCLK0时钟下,定时器234还可以工作在外接的TCLK1时钟下。通过TCFG1寄存器来设置这5个定时器,分别工作于这5个频率中哪一个之下。

3TCNTBn/TCMPBn寄存器:n0~4,这两个寄存器都只用到位[15:0]TCNTBn中保存定时器的初始计数值,TCMPBn中保存比较值。它们的值在启动定时器时,被传到定时器内部寄存器TCNTnTCMPn中。没有TCMPB4,因为定时器4没有输出引脚。

4TCNTOn寄存器:n0~4,定时器n被启动后,内部寄存器TCNTn在其工作时钟下不断减1计数,可以通过读取TCNTOn寄存器得知其值。

5TCON寄存器:有四个作用,a、第一次启动定时器时“手动”将TCNTBn/TCMPBn寄存器的值装入内部寄存器TCNTnTCMPn中;b、启动、停止定时器;3、决定在定时器计数达到0时是否自动将TCNTBn/TCMPBn寄存器的值装入内部寄存器TCNTnTCMPn中;4、决定定时器的管脚TOUTn的输出电平是否反转。

在第一次使用定时器时,需要设置“手动更新”位为1以使TCNTBn/TCMPBn寄存器的值装入内部寄存器TCNTnTCMPn中。下一次如果还要设置这一位,需要先将它清0

#define PCLK            50000000

void delay_ms(int time)

{

    int val = (PCLK>>3)/1000-1;

   

    TCFG0 &= ~(0xff<<8);     //TCFG0设置预分频器01TCFG1用于设置二次分频

    TCFG0 |= 3<<8;           //prescaler = 3+1

    TCFG1 &= ~(0xf<<12);

    TCFG1 |= 0<<12;      //mux = 1/2

 

    TCNTB3 = val;

    TCMPB3 = val>>1;     // 50%

    TCON &= ~(0xf<<16);

    TCON |= 0xb<<16;     //interval, inv-off, update TCNTB3&TCMPB3, start timer 3

    TCON &= ~(2<<16);    //clear manual update bit

    while(time--)

    {

       while(TCNTO3>=val>>1);

       while(TCNTO3>1);

    };

}

 

#define GPB5_out        (1<<(5*2))      // LED1

#define GPB6_out        (1<<(6*2))      // LED2

#define GPB7_out        (1<<(7*2))      // LED3

#define GPB8_out        (1<<(8*2))      // LED4

 

/*

 * Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}

 * {prescaler value} = 0~255

 * {divider value} = 2, 4, 8, 16

 * 本实验的Timer0的时钟频率=100MHz/(99+1)/(16)=62500Hz

 * 设置Timer0 0.5秒钟触发一次中断:

 */

void timer0_init(void)

{

    TCFG0  = 99;        // 预分频器0 = 99       

    TCFG1  = 0x03;      // 选择16分频

    TCNTB0 = 31250;     // 0.5秒钟触发一次中断

    TCON  |= (1<<1);   // 手动更新

    TCON   = 0x09;      // 自动加载,清“手动更新”位,启动定时器0

}

 

 

 // 定时器0中断使能

 

void init_irq_timer0(void)

{         

    INTMSK   &= (~(1<<10));     // 定时器0中断使能

}

 

void Timer0_Handle(void)

{

    if(INTOFFSET == 10)

    {

        GPBDAT = ~(GPBDAT & (0xf << 5));  // 每次中断令4LED改变状态

    }

   

    SRCPND = 1 << INTOFFSET;    //清中断

    INTPND = INTPND;    

}

 

void Timer0_test(void)

{

    char c;

   

    GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;

    GPBDAT = 0xffff;

    timer0_init();

   

    printf("\nKey Scan Test, press ESC key to exit !\n");  

   

    ClearPending(BIT_TIMER0);

    pISR_TIMER0 = (int)Timer0_Handle;

    EnableIrq(BIT_TIMER0);  

    while( (c=get_c())!=0x1b);

    DisableIrq(BIT_TIMER0); 

}

 

//***************************[ BOARD BEEP ]*******************************

void Buzzer_Freq_Set( int freq )

{

    GPBCON &= ~3;        //set GPB0 as tout0, pwm output

    GPBCON |= 2;

      

    TCFG0 &= ~0xff;

    TCFG0 |= 15;         //prescaler = 15+1

    TCFG1 &= ~0xf;

    TCFG1 |= 2;          //mux = 1/8

    TCNTB0 = (PCLK>>7)/freq;

    TCMPB0 = TCNTB0>>1;  // 50%

    TCON &= ~0x1f;

    TCON |= 0xb;         //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0

    TCON &= ~2;          //clear manual update bit

}

 

void Buzzer_Stop( void )

{

    GPBCON &= ~3;        //set GPB0 as output

    GPBCON |= 1;

    GPBDAT &= ~1;

}

 

//***************************[ BOARD BEEP ]*******************************

void Beep(int freq, int ms)

{

    Buzzer_Freq_Set( freq ) ;

    delay_ms( ms ) ;

    Buzzer_Stop() ;

}

 

/****************************************************************************

【功能说明】蜂鸣器PWM测试

****************************************************************************/

void Buzzer_Pwm_Test( void )

{

    int freq =8000;// lci  1000 ;

      

    Buzzer_Freq_Set( freq ) ;

 

    while( 1 )

    {

       char key = get_c();

 

       if( key == '+' )

       {

           if( freq < 2000 )  //lci  20000

              freq += 10 ;            

           Buzzer_Freq_Set( freq ) ;

       }

 

       if( key == '-' )

       {

           if( freq > 11 )

              freq -= 10 ;            

           Buzzer_Freq_Set( freq ) ;

       }

      

       if( key == 0x1b )

       {

           Buzzer_Stop() ;

           return ;

       }

    }

}

 

WATCHDOG定时器

WATCHDOG定时器可以像一般16位定时器一样用于产生周期性的中断,也可以用于发出复位信号以重启失常的系统。

WATCHDOG定时器的8位预分频器将PCLK分频后,被再次分频得到4种频率:16分频、32分频、64分频、128分频,WATCHDOG定时器可以选择工作于哪种频率之下。WTCNT寄存器按照其工作频率减1计数,当达到0时,可以产生中断信号,可以输出复位信号。在第一次使用WATCHDOG定时器时,需要往WTCNT寄存器中写入初始计数值,以后在计数值到达0时自动从WATDAT寄存器中装入,重新开始下一个计数周期。

 

使用WATCHDOG定时器的“WATCHDOG功能”时,在正常的程序中,必须不断重新设置WTCNT寄存器使得它不为0,这样可以保证系统不被重启,这称为“喂狗”;当程序崩溃时不能正常“喂狗”,计数值达到0后系统将被重启,这样程序将重新运行。为了克服各种干扰、避免各类系统错误时系统彻底死机,经常使用WATCHDOG功能。

WATCHDOG定时器所涉及的寄存器如下。

1WTCON寄存器:用于设置预分频系数,选择工作频率,决定是否使能中断、是否启动WATCHDOG功能(即是否输出复位信号)。

2WTDAT寄存器:被用来决定WATCHDOG定时器的超时周期,在定时器启动后,当计数达到0时,WTDAT寄存器的值会自动传入WTCNT寄存器。不过,第一次启动WATCHDOG定时器时,WTDAT寄存器的值不会自动传入WTCNT寄存器。

3WTCNT寄存器:在启动WATCHDOG定时器前,必须往这个寄存器写入初始计数值。启动定时器后,它减1计数,当计数值达到0时:如果中断被使能的话发出中断,如果WATCHDOG功能被使能的话发出复位信号,装载WTDAT寄存器的值并重新计数。

 

 

 

 系统时钟和定时器.doc   

阅读(534) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~