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,结束条件编译段,我们明天再继续。