LPC1114时钟配置
要对LPC1114的时钟进行配置,必须要了解它的时钟结构,先来看一下它的时钟结构图,如下图所示。
本文引用地址:https://www.eepw.com.cn/article/201611/316320.htm
首先来看主时钟,给主时钟(main clock)提供选择有4个源,分别是:内部RC振荡器、未倍频之前的PLL时钟、内部看门狗时钟、经过PLL倍频之后的时钟。由对主时钟源选择寄存器(MAINCLKSEL)的操作来进行选择,同一时刻只能选择一种时钟做为主时钟。下面就给出MAINCLKSEL寄存器的位结构(要注意,因为LPC1114是32位的,所以所有的寄存器都是32位的),如下表所示。
位 | 符号 | 值 | 描述 | 复位值 |
1∶0 | SEL | 00 | 内部RC振荡器 | 0x00 |
01 | PLL倍频之前的时钟 | |||
10 | 内部看门狗时钟 | |||
11 | PLL倍频之后的时钟 | |||
31∶2 | - | - | 保留 | 0x00 |
前面说过,一般情况下CPU内部都是要进行倍频的,因此大多数时候该寄存器的值都会选择11(0x03),执行语句“SYSCON->MAINCLKSEL=0x00000003”就可以了。
接下来看系统PLL时钟,PLL即是频率锁相环的意思,PLL的功能很多,有兴趣的可自行参考其它资料,这里主要利用它来实现倍频的功能。从上图中可以看出,供给PLL选择的有2个源,一个是片内RC振荡器,另一个是晶体振荡器。由对系统PLL时钟选择寄存器(SYSPLLCLKSEL)的操作来进行选择,同一时刻只能选择一种时钟做为输入时钟。下面给出SYSPLLCLKSEL寄存器的位结构,如下表所示。
位 | 符号 | 值 | 描述 | 复位值 |
1∶0 | SEL | 00 | 内部RC振荡器 | 0x00 |
01 | 晶体振荡器 | |||
10 | 保留 | |||
11 | 保留 | |||
31∶2 | - | - | 保留 | 0x00 |
一般情况下,为了提高时钟精度都会选择01(0x01),以选择外部晶体振荡器做为时钟输入。执行语句“SYSCON->SYSPLLCLKSEL=0x00000001”就可以了。
在时钟结构图中还可以看出,主时钟分成6路供给不同的模块。其中一路主时钟经过系统时钟分频器(SYSAHBCLKDIV)后做为系统时钟,提供给AHB。在LPC1114中,只有通用输入输出端口(GPIO)需要AHB。为了给不同速度的模块(如内核、存储器、APB等)提供时钟,需要对SYSAHBCLKDIV寄存器进行操作,以对主时钟进行分频。下面给出SYSAHBCLKDIV寄存器的位结构,如下表所示。
位 | 符号 | 值 | 描述 | 复位值 |
7∶0 | DIV | 00000000 | 关闭系统时钟 | 0x01 |
00000001 | 1 | |||
00000010 | 2 | |||
…… | …… | |||
11111111 | 255 | |||
31∶8 | - | - | 保留 | 0x00 |
由上述可见,居于Cotex-M0内核的CPU由于强化了时钟配置,所以在一般情况下要使用该类型的CPU,首要的任务就是为其配置正确的时钟。下面就来讨论一下如何通过程序来配置LPC1114的时钟。
先给出一个时钟初始化的函数,如下:
void SysCLK_config(void)
{
uint8_t i;
LPC_SYSCON->PDRUNCFG &= ~(1 << 5); //给系统振荡器上电
LPC_SYSCON->SYSOSCCTRL = 0x00000000; //系统振荡器未旁路,1~12MHz输入
for (i = 0; i < 200; i++) __nop(); //延时等待振荡器稳定
LPC_SYSCON->SYSPLLCLKSEL = 0x00000001; //PLL输入选择外部晶体振荡
LPC_SYSCON->SYSPLLCLKUEN = 0x00;
LPC_SYSCON->SYSPLLCLKUEN = 0x01; //先写0后写1更新时钟源
while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01)); //等待更新完成
LPC_SYSCON->SYSPLLCTRL = 0x00000013; //M=4、P=2,倍频后的时钟为48MHz
LPC_SYSCON->PDRUNCFG &= ~(1 << 7); //给PLL上电
while (!(LPC_SYSCON->SYSPLLSTAT & 0x01)); //等待PLL锁定
LPC_SYSCON->MAINCLKSEL = 0x00000003; //主时钟选择PLL倍频后的时钟
LPC_SYSCON->MAINCLKUEN = 0x00;
LPC_SYSCON->MAINCLKUEN = 0x01; //先写0后写1更新时钟源
while (!(LPC_SYSCON->MAINCLKUEN & 0x01)); //等待更新完成
LPC_SYSCON->SYSAHBCLKDIV = 0x00000001; //AHB为1分频,AHB时钟为48MHz
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); //使能GPIO时钟
}
如果使用的是12MHz的外部晶振,该函数执行完后,LPC1114的时钟被设置成为主时钟48MHz,AHB时钟48MHz,输入输出端口(GPIO)时钟使能的状态。
在上述程序中,PLL输入选择寄存器(SYSPLLCLKSEL),主时钟选择寄存器(MAINCLKSEL),AHB分频选择寄存器(SYSAHBCLKDIV)均在前面讨论过,下面来看一下没讨论过的寄存器。先看“掉电配置寄存器(PDRUNCFG)”,下表是它的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
0 | IRCOUT_PD (IRC振荡器输出掉电) | 1 | 掉电 | 0 |
0 | 上电 | |||
1 | IRC_PD (IRC振荡器掉电) | 1 | 掉电 | 0 |
0 | 上电 | |||
2 | FLASH_PD (Flash掉电) | 1 | 掉电 | 0 |
0 | 上电 | |||
3 | BOD_PD (BOD掉电) | 1 | 掉电 | 0 |
0 | 上电 | |||
4 | ADC_PD (ADC掉电) | 1 | 掉电 | 1 |
0 | 上电 | |||
5 | SYSOSC_PD (系统振荡器掉电) | 1 | 掉电 | 1 |
0 | 上电 | |||
6 | WDTOSC_PD (看门狗振荡器掉电) | 1 | 掉电 | 1 |
0 | 上电 | |||
7 | SYSPLL_PD (系统PLL掉电) | 1 | 掉电 | 1 |
0 | 上电 | |||
8 | - | - | 保留 | 1 |
9 | - | 0 | 保留,置0 | 0 |
10 | - | - | 保留 | 1 |
11 | - | 1 | 保留,置1 | 1 |
12 | - | 0 | 保留,置0 | 0 |
15∶13 | - | - | 保留 | 1 |
31∶16 | - | - | 保留 |
从表中可以看出,系统振荡器和系统PLL在默认情况下是掉电的,也就是说默认它们不处于工作状态,要让它们工作就必须给它们置0上电。所以要通过执行语句“SYSCON->PDRUNCFG &= ~(1 << 5)”和“SYSCON->PDRUNCFG &= ~(1 << 7)”来把SYSOSC和SYSPLL置0。
接下来看“系统振荡器控制寄存器(SYSOSCCTRL)”,下表是它的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
0 | BYPASS (旁路系统振荡器) | 0 | 未被旁路 | 0 |
1 | 被旁路 | |||
1 | FREQRANGE (低功耗振荡器确定频率范围) | 0 | 1~20MHz | 0 |
1 | 15~25MHz | |||
31∶2 | - | - | 保留 | 0 |
从表中可以看出,如果要让系统振荡器工作,则其第0位就应该选择0,即不被旁路,只有在从外部直接输入振荡信号的情况下才会选择旁路(比如使用有源晶振)。第1位用于选择外部晶振的频率范围,使用12MHz时该位选择0。执行语句“SYSCON->SYSOSCCTRL = 0x00000000”就可以实现上述配置,但实际上复位后的值就是该配置,也可不执行该语句。
从上述程序中还可以看出,在配置了时钟后(无论是PLL时钟还是主时钟),都需要更新一下才能正常工作。而更新的操作则根据管方手册,必须要给相应的允许寄存器“toggle”一下(即先向其写0再紧接着写1)。“系统PLL时钟源更新允许寄存器(SYSPLLCLKUEN)”和“主时钟源更新允许寄存器(MAINCLKUEN)”的位结构是一样的,下表是MAINCLKUEN寄存器的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
0 | ENA (允许主时钟源更新) | 0 | 无变化 | 0 |
1 | 更新时钟源 | |||
31∶1 | - | - | 保留 | 0 |
在主时钟源及PLL时钟源更改后,要紧接着及时更新相应的允许寄存器才能让其正常工作。此外还要注意一点,“toggle”后需要查询相应的允许寄存器是否已更新,若没有就需要等待直到其更新为止(例如在更新PLL时钟源更新允许寄存器SYSPLLCLKUEN后要执行语句“while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01));”,以等待其更新完成)。
接下来看一下PLL的配置,要让PLL对输入时钟进行倍频或分频,就要配置“系统倍频控制寄存器(SYSPLLCTRL)”,下表是该寄存器的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
4∶0 | MSEL (反馈分频器的值, 分频器的值M是MSEL+1) | 00000 | M=1 | 0x00 |
…… | …… | |||
11111 | M=32 | |||
6∶5 | PSEL (后分频器的值, 分频器的值为2×P) | 00 | P=1 | 0x00 |
01 | P=2 | |||
10 | P=4 | |||
11 | P=8 | |||
31∶7 | - | - | 保留,不能写1 | 0x00 |
PLL的输出频率要符合下面的公式。

Fclkout为PLL的输出频率,Fclkin为外部晶振的频率,FCCO的值必须在156MHz ~320MHz之间,M为倍频的倍数,P值要符合要求。若以12MHz的晶振做为输入,系统主时钟要为48MHz,则M=4(MSEL=0011),P的值只能取2(PSEL=01)才能满足公式要求。因此寄存器SYSPLLCTRL的值应该为10011(0x13),所以要配置此项只需要执行语句“SYSCON->SYSPLLCTRL = 0x00000013”就可以了。
在改变了PLL的倍频之后,需要查询“倍频状态寄存器(SYSPLLSTAT)”,看PLL锁定了没有,若没有就需要等待直到其锁定为止(执行语句“while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));”)。下面是“倍频状态寄存器(SYSPLLSTAT)”的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
0 | LOCK (PLL锁定状态) | 0 | 未锁定 | 0 |
1 | 已锁定 | |||
31∶1 | - | - | 保留 | 0 |
CPU要对GPIO进行操作,就必须给GPIO时钟信号,即需要使能GPIO的时钟。在默认情况下GPIO时钟是允许的,也可以对“系统AHB时钟控制寄存器(SYSAHBCLKCTRL)”中相应的位进行操作来选择允许时钟,下面是该寄存器的位结构。
位 | 符号 | 值 | 描述 | 复位值 |
0 | SYS (允许AHB时钟,只读) | 0 | 保留 | 1 |
1 | 允许 | |||
1 | ROM (允许ROM时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
2 | RAM (允许RAM时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
3 | FLASHREG (允许flash寄存器接口时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
4 | FLASHARRAY (允许flash阵列存取时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
5 | I2C (允许I2C时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
6 | GPIO (允许GPIO时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
7 | CT16B0 (允许16位计数/定时器0时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
8 | CT16B1 (允许16位计数/定时器1时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
9 | CT32B0 (允许32位计数/定时器0时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
10 | CT32B1 (允许32位计数/定时器1时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
11 | SSP0 (允许SPI0时钟) | 0 | 禁止 | 1 |
1 | 允许 | |||
12 | UART (允许UART时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
13 | ADC (允许ADC时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
14 | - | - | 保留 | 0 |
15 | WDT (允许WDT时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
16 | IOCON (允许IO配置模块时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
17 | - | - | 保留 | 0 |
18 | SSP1 (允许SPI1时钟) | 0 | 禁止 | 0 |
1 | 允许 | |||
31∶19 | - | - | 保留 | 0x00 |
上述的时钟配置程序是最基本的,也是必须的,因此在任何程序开始前,都应该先调用该时钟配置函数,以对LPC1114进行基本的时钟配置,为后续程序提供保障。
评论