« 上一篇下一篇 »

LPC1768系统初始化(时钟配置)

LPC1768的系统初始化函数在官方的例程中已经给出了,用户可以直接使用。然而在使用定时器的时候,发现自己对时钟的配置情况很模糊,不清楚目前CPU的时钟频率是多少,所以也就无法准确的计算定时器的定时时间,在网上搜了下,有说到这个问题的,然而大多数都是一笔带过,没有分析的过程,只给出最后的配置结果,于是干脆自己来分析下,在花了一定的时间的详细的分析了一下初始化的源代码后,终于搞明白了,下面是我的分析过程。

#define CLOCK_SETUP           1
#define SCS_Val               0x00000020
#define CLKSRCSEL_Val         0x00000001
#define PLL0_SETUP            1
#define PLL0CFG_Val           0x0000000B
#define PLL1_SETUP            1
#define PLL1CFG_Val           0x00000023
#define CCLKCFG_Val           0x00000003
#define USBCLKCFG_Val         0x00000000
#define PCLKSEL0_Val          0x00000080
#define PCLKSEL1_Val          0x00000000
#define PCONP_Val             0x042887DE
#define CLKOUTCFG_Val         0x00000000

#define FLASH_SETUP           1
#define FLASHCFG_Val          0x0000303A



/*----------------------------------------------------------------------------
  Define clocks
 *----------------------------------------------------------------------------*/
#define XTAL        (12000000UL)        /* Oscillator frequency               */
#define OSC_CLK     (      XTAL)        /* Main oscillator frequency          */
#define RTC_CLK     (   32000UL)        /* RTC oscillator frequency           */
#define IRC_OSC     ( 4000000UL)        /* Internal RC oscillator frequency   */

/*----------------------------------------------------------------------------
  Clock Variable definitions
 *----------------------------------------------------------------------------*/
uint32_t SystemFrequency = IRC_OSC; /*!< System Clock Frequency (Core Clock)  */

/**
 * Initialize the system
 *
 * @param  none
 * @return none
 *
 * @brief  Setup the microcontroller system.
 *         Initialize the System and update the SystemFrequency variable.
 */
void SystemInit (void)
{
#if (CLOCK_SETUP)                       /* Clock Setup                        */
  LPC_SC->SCS       = SCS_Val;
  if (SCS_Val & (1 << 5)) 
  {             /* If Main Oscillator is enabled      */
    while ((LPC_SC->SCS & (1<<6)) == 0);/* Wait for Oscillator to be ready    */
  }
  LPC_SC->CCLKCFG   = CCLKCFG_Val;      /* Setup Clock Divider                */
#if (PLL0_SETUP)
  LPC_SC->CLKSRCSEL = CLKSRCSEL_Val;    /* Select Clock Source for PLL0       */
  LPC_SC->PLL0CFG   = PLL0CFG_Val;
  LPC_SC->PLL0CON   = 0x01;             /* PLL0 Enable                        */
  LPC_SC->PLL0FEED  = 0xAA;
  LPC_SC->PLL0FEED  = 0x55;
  while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0                    */
  LPC_SC->PLL0CON   = 0x03;             /* PLL0 Enable & Connect              */
  LPC_SC->PLL0FEED  = 0xAA;
  LPC_SC->PLL0FEED  = 0x55;
#endif
#if (PLL1_SETUP)
  LPC_SC->PLL1CFG   = PLL1CFG_Val;
  LPC_SC->PLL1CON   = 0x01;             /* PLL1 Enable                        */
  LPC_SC->PLL1FEED  = 0xAA;
  LPC_SC->PLL1FEED  = 0x55;
  while (!(LPC_SC->PLL1STAT & (1<<10)));/* Wait for PLOCK1                    */
  LPC_SC->PLL1CON   = 0x03;             /* PLL1 Enable & Connect              */
  LPC_SC->PLL1FEED  = 0xAA;
  LPC_SC->PLL1FEED  = 0x55;
#else
  LPC_SC->USBCLKCFG = USBCLKCFG_Val;    /* Setup USB Clock Divider            */
#endif
// stop
  // 除了UART0的时钟 = CCLK/2 ,PCKLSEL0负责的其他外设时钟频率均 = CCLK/4 . 即TIMER0/1 频率为72M/4 = 18MHZ
  LPC_SC->PCLKSEL0  = PCLKSEL0_Val;     /* Peripheral Clock Selection         */
//LPC_SC->PCLKSEL0 |= 0x04;// 设置TIMER0 为CCLK/1
  LPC_SC->PCLKSEL1  = PCLKSEL1_Val;
  LPC_SC->PCONP     = PCONP_Val;        /* Power Control for Peripherals      */
  LPC_SC->CLKOUTCFG = CLKOUTCFG_Val;    /* Clock Output Configuration         */
#endif
  /* Determine clock frequency according to clock register values             */
  if (((LPC_SC->PLL0STAT >> 24)&3)==3) 
  {/* If PLL0 enabled and connected      */
    switch (LPC_SC->CLKSRCSEL & 0x03) 
{
      case 0:                           /* Internal RC oscillator => PLL0     */
      case 3:                           /* Reserved, default to Internal RC   */
        SystemFrequency = (IRC_OSC * 
                          (((2 * ((LPC_SC->PLL0STAT & 0x7FFF) + 1))) /
                          (((LPC_SC->PLL0STAT >> 16) & 0xFF) + 1))   /
                          ((LPC_SC->CCLKCFG & 0xFF)+ 1));
        break;
      case 1:                           /* Main oscillator => PLL0            */ // 12M晶振,最终CPU时钟频率配置为72MHz
        SystemFrequency = (OSC_CLK * 
                          (((2 * ((LPC_SC->PLL0STAT & 0x7FFF) + 1))) /
                          (((LPC_SC->PLL0STAT >> 16) & 0xFF) + 1))   /
                          ((LPC_SC->CCLKCFG & 0xFF)+ 1));
        break;
      case 2:                           /* RTC oscillator => PLL0             */
        SystemFrequency = (RTC_CLK * 
                          (((2 * ((LPC_SC->PLL0STAT & 0x7FFF) + 1))) /
                          (((LPC_SC->PLL0STAT >> 16) & 0xFF) + 1))   /
                          ((LPC_SC->CCLKCFG & 0xFF)+ 1));
        break;
    }
  } 
  else 
  {
    switch (LPC_SC->CLKSRCSEL & 0x03) 
{
      case 0:                           /* Internal RC oscillator => PLL0     */
      case 3:                           /* Reserved, default to Internal RC   */
        SystemFrequency = IRC_OSC / ((LPC_SC->CCLKCFG & 0xFF)+ 1);
        break;
      case 1:                           /* Main oscillator => PLL0            */
        SystemFrequency = OSC_CLK / ((LPC_SC->CCLKCFG & 0xFF)+ 1);
        break;
      case 2:                           /* RTC oscillator => PLL0             */
        SystemFrequency = RTC_CLK / ((LPC_SC->CCLKCFG & 0xFF)+ 1);
        break;
    }
  }
#if (FLASH_SETUP == 1)                  /* Flash Accelerator Setup            */
  LPC_SC->FLASHCFG  = FLASHCFG_Val;
#endif
}

因为要对官方的系统初始化函数进行一个分析,所以先把相关的代码贴在上面,有点长,我们一条一条来分析。

从SystemInit()函数开始,

#if (CLOCK_SETUP)                       /* Clock Setup                        */
  LPC_SC->SCS       = SCS_Val;
  if (SCS_Val & (1 << 5)) 
  {             /* If Main Oscillator is enabled      */
    while ((LPC_SC->SCS & (1<<6)) == 0);/* Wait for Oscillator to be ready    */
  }
  LPC_SC->CCLKCFG   = CCLKCFG_Val;      /* Setup Clock Divider                */

根据之前的宏定义

#define CLOCK_SETUP           1
#define SCS_Val               0x00000020
#define CCLKCFG_Val           0x00000003

我们可以知道,CLOCK_SETUP的值为1,所以#if (CLOCK_SETUP)满足条件,往下执行,

LPC_SC->SCS = 0X0000 0020,即SCS寄存的值为0x0000 0020,下面来看SCS寄存器的定义:

由SCS寄存器描述表可知,LPC1768被配置或说明为“主晶振频率范围为1MHz~20MHz、使能主晶振、主晶振暂未准备好用做时钟源“;

接下来是一条判断语句

if (SCS_Val & (1 << 5)),由前面所述可知,SCS_Val = 0x0000 0020,所以满足条件,往下执行,

然后是一条等待语句

while ((LPC_SC->SCS & (1<<6)) == 0); , 根据SCS寄存器描述表可知,当主晶振已经准备好用做时钟源后,bit6(OSCSTAT)为1,即当主晶振准备就绪后,退出等待;

往下执行,

LPC_SC->CCLKCFG = 0x0000 0003,即CCLKCFG寄存器的值为0x0000 0003,下面来看CCLKCFG寄存器的定义:

由CCLKCFG寄存器描述表可知,LPC1768被配置为”PLL0输出被4分频以减小CPU时钟“;

再往下看代码,

#if (PLL0_SETUP)
  LPC_SC->CLKSRCSEL = CLKSRCSEL_Val;    /* Select Clock Source for PLL0       */
  LPC_SC->PLL0CFG   = PLL0CFG_Val;
  LPC_SC->PLL0CON   = 0x01;             /* PLL0 Enable                        */
  LPC_SC->PLL0FEED  = 0xAA;
  LPC_SC->PLL0FEED  = 0x55;
  while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0                    */
  LPC_SC->PLL0CON   = 0x03;             /* PLL0 Enable & Connect              */
  LPC_SC->PLL0FEED  = 0xAA;
  LPC_SC->PLL0FEED  = 0x55;
#endif

根据之前的宏定义

#define CLKSRCSEL_Val         0x00000001
#define PLL0_SETUP            1
#define PLL0CFG_Val           0x0000000B

我们可以知道,#if (PLL0_SETUP) 满足条件,往下执行,

LPC_SC->CLKSRCSEL = 0x0000 0001,即CLKSRCSEL寄存器的值为0x0000 0001,下面来看CLKSRCSEL寄存器的定义:

由CLKSRCSEL寄存器的描述表可知,LPC1768被配置为”选择主晶振做为PLL0时钟源“,这里的主晶振在我们的板子上频率为12MHz;

接着往下,

LPC_SC->PLL0CFG = 0x0000 000B,即PLL0CFG寄存器的值为0x0000 000B,下面来看PLL0CFG寄存器的定义:

由PLL0CFG寄存器的描述表可知,LPC1768被配置为”MSEL0 = 0x0B = 11、NSEL0 = 0x00 = 0“;

接着往下,

LPC_SC->PLL0CON = 0x01,即PLL0CON的值为0x01,下面来看PLL0CON寄存器的定义:

由PLL0CON寄存器的描述表可知,LPC1768被配置为”使能PLL0“;

继续往下,

LPC_SC->PLL0FEED = 0xAA;

LPC_SC->PLL0FEED = 0x55;

这两条指令是一个反馈序列,用于生效之前的寄存器的配置,来看一下PLL0FEED寄存器的定义:

由PLL0FEED寄存器的描述表可知,LPC1768被配置为”写入PLL0反馈序列以使PLL0配置和控制寄存器的更改生效“;

(注:由于这两条指令是使相关寄存器的更改生效,所以后面还会用到,对于下文中的使用作者不再进行解释。)

接着往下,

while (!(LPC_SC->PLL0STAT & (1<<26))); ,这又是一条等待语句,先来看一下PLL0STAT寄存器的定义:

由PLL0STAT寄存器描述表可知,当PLL0被锁存为之前请求请求的频率后,bit26(PLOCK0)为1,即当PLL0的时钟频率锁存完毕后,退出等待;

好累啊,这是一项工作量不小的工作,不过咱们还是继续,

LPC_SC->PLL0CON = 0x03,根据前面的PLL0寄存器的描述表可知,LPC1768被配置为”PLL0使能、PLL0连接“;

继续往下,

LPC_SC->PLL0FEED = 0xAA;

LPC_SC->PLL0FEED = 0x55;


又是同样的反馈序列,根据PLL0寄存器中的描述,如下:

意思是当PLL0被使能且被锁存后,连接PLL0,然后通过一个有效的PLL0反馈序列促使PLL0变为CPU,AHB外设及一般的APB外设的时钟源,所以当下的这个反馈序列就使得PLL0时钟用做CPU的时钟源。

#endif,结束条件编译段,我们明天再继续。