NXP

NXP LPC17xx之时钟模块

2019-07-12 12:09发布


1 模块介绍

图1.系统框图
    时钟模块的晶振源一共有3个:主晶振、内部RC晶振、RTC晶振。    


1.1 主时钟

    一般为12M的晶振,外部输入,LPC1765的引脚名是XTAL1/XTAL2。它是CPU主频的主要时钟源,主晶振的频率范围为1M Hz ~  25MHz

1.2 内部RC振荡器

    顾名思义,内置的RC振荡器,当然不会很准了,所以该时钟源不能用于USB模块。内部RC振荡器是芯片复位后默认的系统时钟源,频率为:4M Hz

1.3 RTC时钟

    RTC晶振(1 Hz ~ 32768 Hz)主要用于RTC模块实现时钟功能,还有就是看门狗模块,当然,如果你发神经病的话,你可以拿它作为PLL0或CPU的时钟源。RTC晶振一般都是使用32768 Hz的频率,这个频率用来跑秒是最好不过的了。外部输入,引脚名为RTCX1/RTCX2。

1.4 Main PLL(PLL0)

    通过寄存器CLKSRCSEL[1:0]可以选择介入Main PLL的时钟源,PLL的功能就是实现稳定的高频率时钟输出,大概框图如下:

图2.PLL系统框图
    这里要注意,pllclkin的时钟范围是32768Hz~50MHz,。pllclkin会先被N分频(NSEL[7:0]),得到refclk,refclk和M-DIVIDER(MSEL[14:0])输出的频率差通过鉴频鉴相器、滤波器,最后通过流控晶体振荡器产生的频率Fcco,Fcco最后又通过2*M分频后与Frefclk进行比较,如此循环反馈和调节的过程,最终使得两个频率相同,该过程就是锁相。
    若pllclk为目标频率,则有:
(Fpllclk*M)/2 = Frefclk,
即:
Fpllclkin/N = Fpllclk/(2*M)
最后,结合图1,有
Fpllclk = (Fpllclkin*2*M)/N

1.5 usbclk、cclk、pclk

    pllclk是个很重要的时钟,它直接为usb模块、系统主频、外设提供时钟源,后端就很好理解了,结合图1很清楚能够明白,稍微配置下寄存器就OK了。其中,cclk就是CPU的主频了。

2 LPC1700CMSIS SystemInit()函数分析


    LPC1700CMSIS标准库是专门为LPC17xx系列芯片所设计的底层驱动标准代码,它提供所有外设的控制接口,能大量缩短项目的开发周期。其实之前我对使用库文件这种行为是很不齿的,呵呵,其实也没啥,就是希望当时能够多做一些多学一些,因为写底层驱动还是一个很重要的工作。随着后面慢慢工作重心转向产品功能实现以后,我也慢慢理解了库文件所带来的便利了,呵呵!不过也是建立在已经有了较多底层驱动设计经验的基础上的情况下,言归正传,贴代码:
    以下是一些宏定义: /** @addtogroup LPC17xx_System_Defines LPC17xx System Defines @{ */ #define CLOCK_SETUP 1 #define SCS_Val 0x00000020 #define CLKSRCSEL_Val 0x00000001 #define PLL0_SETUP 1 #define PLL0CFG_Val 0x00050063 #define PLL1_SETUP 1 #define PLL1CFG_Val 0x00000023 #define CCLKCFG_Val 0x00000003 #define USBCLKCFG_Val 0x00000000 #define PCLKSEL0_Val 0x00000000 #define PCLKSEL1_Val 0x00000000 #define PCONP_Val 0x042887DE #define CLKOUTCFG_Val 0x00000000
    SystemInit()函数源码: 00499 void SystemInit (void) 00500 { 00501 #if (CLOCK_SETUP) /* Clock Setup */ 00502 LPC_SC->SCS = SCS_Val; 00503 if (LPC_SC->SCS & (1 << 5)) { /* If Main Oscillator is enabled */ 00504 while ((LPC_SC->SCS & (1<<6)) == 0);/* Wait for Oscillator to be ready */ 00505 } 00506 00507 LPC_SC->CCLKCFG = CCLKCFG_Val; /* Setup Clock Divider */ 00508 /* Periphral clock must be selected before PLL0 enabling and connecting 00509 * - according errata.lpc1768-16.March.2010 - 00510 */ 00511 LPC_SC->PCLKSEL0 = PCLKSEL0_Val; /* Peripheral Clock Selection */ 00512 LPC_SC->PCLKSEL1 = PCLKSEL1_Val; 00513 00514 #if (PLL0_SETUP) 00515 LPC_SC->CLKSRCSEL = CLKSRCSEL_Val; /* Select Clock Source for PLL0 */ 00516 00517 LPC_SC->PLL0CFG = PLL0CFG_Val; /* configure PLL0 */ 00518 LPC_SC->PLL0FEED = 0xAA; 00519 LPC_SC->PLL0FEED = 0x55; 00520 00521 LPC_SC->PLL0CON = 0x01; /* PLL0 Enable */ 00522 LPC_SC->PLL0FEED = 0xAA; 00523 LPC_SC->PLL0FEED = 0x55; 00524 while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0 */ 00525 00526 LPC_SC->PLL0CON = 0x03; /* PLL0 Enable & Connect */ 00527 LPC_SC->PLL0FEED = 0xAA; 00528 LPC_SC->PLL0FEED = 0x55; 00529 while (!(LPC_SC->PLL0STAT & ((1<<25) | (1<<24))));/* Wait for PLLC0_STAT & PLLE0_STAT */ 00530 #endif 00531 00532 #if (PLL1_SETUP) 00533 LPC_SC->PLL1CFG = PLL1CFG_Val; 00534 LPC_SC->PLL1FEED = 0xAA; 00535 LPC_SC->PLL1FEED = 0x55; 00536 00537 LPC_SC->PLL1CON = 0x01; /* PLL1 Enable */ 00538 LPC_SC->PLL1FEED = 0xAA; 00539 LPC_SC->PLL1FEED = 0x55; 00540 while (!(LPC_SC->PLL1STAT & (1<<10)));/* Wait for PLOCK1 */ 00541 00542 LPC_SC->PLL1CON = 0x03; /* PLL1 Enable & Connect */ 00543 LPC_SC->PLL1FEED = 0xAA; 00544 LPC_SC->PLL1FEED = 0x55; 00545 while (!(LPC_SC->PLL1STAT & ((1<< 9) | (1<< 8))));/* Wait for PLLC1_STAT & PLLE1_STAT */ 00546 #else 00547 LPC_SC->USBCLKCFG = USBCLKCFG_Val; /* Setup USB Clock Divider */ 00548 #endif 00549 LPC_SC->PCONP = PCONP_Val; /* Power Control for Peripherals */ 00550 00551 LPC_SC->CLKOUTCFG = CLKOUTCFG_Val; /* Clock Output Configuration */ 00552 #endif 00553 00554 #if (FLASH_SETUP == 1) /* Flash Accelerator Setup */ 00555 LPC_SC->FLASHCFG = FLASHCFG_Val; 00556 #endif 00557 00558 // Set Vector table offset value 00559 #if (__RAM_MODE__==1) 00560 SCB->VTOR = 0x10000000 & 0x3FFFFF80; 00561 #else 00562 SCB->VTOR = 0x00000000 & 0x3FFFFF80; 00563 #endif 00564 }

2.1 阶段1-时钟源和分频选择

LPC_SC->SCS = SCS_Val; (1) if (LPC_SC->SCS & (1 << 5)) { /* If Main Oscillator is enabled */ (2) while ((LPC_SC->SCS & (1<<6)) == 0);/* Wait for Oscillator to be ready */ } LPC_SC->CCLKCFG = CCLKCFG_Val; /* Setup Clock Divider */ (3) /* Periphral clock must be selected before PLL0 enabling and connecting * - according errata.lpc1768-16.March.2010 - */ LPC_SC->PCLKSEL0 = PCLKSEL0_Val; /* Peripheral Clock Selection */ (4) LPC_SC->PCLKSEL1 = PCLKSEL1_Val; (5)
    (1)中SCS_Val = 0x00000020,主要是配置Main OSC作为PLL0的时钟源,之后(2)则不停轮询,直到Main OSC准备好为止,这里我觉得是等待晶振稳定。(3)中CCLKCFG_Val =0x00000003,实现对PLL0输出时钟的4分频——Fpll0/4。(5)中PCLKSEL0_Val 和PCLKSEL1_Val都是0,表示所有外设的时钟源都为CCLK/4,即CPU主频的1/4。

2.2 阶段2-PLL0初始化

LPC_SC->CLKSRCSEL = CLKSRCSEL_Val; /* Select Clock Source for PLL0 */ (1) LPC_SC->PLL0CFG = PLL0CFG_Val; /* configure PLL0 */ (2) LPC_SC->PLL0FEED = 0xAA; LPC_SC->PLL0FEED = 0x55; LPC_SC->PLL0CON = 0x01; /* PLL0 Enable */ (3) LPC_SC->PLL0FEED = 0xAA; LPC_SC->PLL0FEED = 0x55; while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0 */ (4) LPC_SC->PLL0CON = 0x03; /* PLL0 Enable & Connect */ (5) LPC_SC->PLL0FEED = 0xAA; LPC_SC->PLL0FEED = 0x55; while (!(LPC_SC->PLL0STAT & ((1<<25) | (1<<24)))); /* Wait for PLLC0_STAT & PLLE0_STAT */
    这里主要就是实现对锁相环0的配置了,就是前面提到的N值、M值等的配置。(1)中CLKSRCSEL_Val = 0x00000001,选择PLL0时钟源,这里选择Main OSC,(3)就是对M值和N值进行设定了,PLL0CFG_Val = 0x00050063,即M=100,注意,M的实际值为寄存器的值+1,即99+1,同理N=6,(5)就是使能PLL0了。每次对PLL0的寄存器进行配置时需要依次向PLL0FEED写入0xAA、0x55以使配置生效。OK,最后我们来计算验证下PLL0的输出频率是多少?根据公式:Fpllclk = (Fpllclkin*2*M)/N,若Main OSC为12M,则Fpllclk = 12*2*100/6=400M。最后Fcclk = Fpllclk / 4 = 100M,就是系统时钟频率,那么外设时钟源频率为25M。

2.3 阶段3-初始化PLL1

    这里略过了,很简单,暂时还没用到USB模块,先不管了,其实理解了PLL0,这个也基本就理解了,无外乎它专用于USB时钟生成,肯定会有一些区别而已。

2.4 阶段4-初始化FLASH加速模块

#if (FLASH_SETUP == 1) /* Flash Accelerator Setup */ LPC_SC->FLASHCFG = FLASHCFG_Val; #endif    FLASHCFG_VAL = 0x0000303A,为默认值。FLASH加速模块在后面再介绍了。

2.5 阶段5-设置VTOR

#if (__RAM_MODE__==1) SCB->VTOR = 0x10000000 & 0x3FFFFF80; #else SCB->VTOR = 0x00000000 & 0x3FFFFF80; #endif