时钟配置

要让LPC824正常工作,首先要对它的时钟源进行配置。LPC824的最高工作频率为30MHz,因此给它的主时钟频率最大不能超过30MHz。实际上,通常都是使用频率较低的晶振,以降低外部电磁干扰,然后再通过内部倍频的方式把主时钟频率提高。根据管方手册给出的数据,外部晶振的频率范围是1MHz~25MHz,一般情况下使用12MHz晶振,然后内部进行2倍频,主时钟工作频率为24MHz。
要对LPC824的时钟进行配置,必须要了解它的时钟结构,先来看一下它的时钟结构图,如下图所示。

首先来看主时钟,给主时钟(main clock)提供选择有4个源,分别是:内部RC振荡器、未倍频之前的PLL时钟、内部看门狗时钟、经过PLL倍频之后的时钟。由对主时钟源选择寄存器(MAINCLKSEL)的操作来进行选择,同一时刻只能选择一种时钟做为主时钟。

下表给出了主时钟源选择寄存器MAINCLKSEL的全部位结构,其字节地址为0x40048070。注意,由于LPC824是32位微控制器,所以它内部的寄存器全都是32位的。

(1)第0、1两位(SEL)为主时钟源选择位,这两位的值从0x0到0x3,分别对应选择内部RC时钟、未倍频之前的PLL时钟、内部看门狗时钟、经过PLL倍频之后的时钟。
(2)第2到31位为保留位。
一般情况下都会选择系统PLL倍频后的时钟作为主时钟,因此大多数时候MAINCLKSEL寄存器的值都会选择0x3(执行语句“LPC_SYSCON->MAINCLKSEL=0x00000003”)。
接下来看系统PLL时钟,PLL(Phase Locked Loop)即锁相环,它的功能很多,这里主要利用它来实现倍频的功能。从时钟结构图中可以看出,供给PLL选择的有3个源,一个是片内RC振荡器,另一个是系统振荡器,再一个是外部输入。通过对系统PLL时钟选择寄存器的操作可进行选择,但同一时刻只能选择一种时钟作为输入时钟。
下表给出了系统PLL时钟选择寄存器SYSPLLCLKSEL的全部位结构,其字节地址为0x40048040。

(1)第0、1两位(SEL)为系统PLL时钟源选择位,值为0x0时选择内部RC时钟,值为0x1时选择系统振荡时钟(即外部晶振),值为0x3时选择外部时钟输入,其他值为保留。
(2)第2到31位为保留位。
为了提高LPC824的时钟精度,一般情况下SYSPLLCLKSEL寄存器的值会选择0x1(执行语句“LCP_SYSCON->SYSPLLCLKSEL=0x00000001”),以选择外部晶体振荡器作为时钟输入。
从时钟结构图中可以看到,主时钟分成4路供给不同的模块。其中一路主时钟经过系统AHB时钟分频选择(SYSAHBCLKDIV)后作为系统时钟提供给AHB。在LPC824中,只有高速设备(如GPIO、SRAM等)才需要AHB。为了给不同速度的模块(如内核、存储、APB等)提供时钟,需要对系统AHB时钟分频选择寄存器进行操作,以对主时钟进行分频。
下表给出了系统AHB时钟分频选择寄存器SYSAHBCLKDIV的全部位结构,其字节地址为0x40048078。

(1)第0到7位(DIV)为系统AHB时钟选择位,值为0时禁止系统时钟,其他值为AHB时钟的分频值。
(2)第8到31位为保留位。
从表中可以看到,AHB时钟分频的最大值为255。系统的默认值为1(也可执行语句“LPC_SYSCON->SYSAHBCLKDIV=0x00000001”来实现),即为AHB提供不分频的主时钟。剩余3个模块(USART、IOCON、CLKOUT)的时钟也一样由主时钟来分频,只不过它们默认的分频值为0,即默认不提供时钟,也就是说剩余的3个模块在默认状态下不工作,需要的时候再通过程序打开,以降低功耗。

下表给出的是掉电配置寄存器PDRUNCFG的全部位结构,其字节地址为0x40048238。

 

(1)第0位(IRCOUT_PD)为内部RC振荡器输出掉电控制位,置0时上电,置1时掉电,默认为上电。
(2)第1位(IRC_PD)为内部RC振荡器掉电控制位,置0时上电,置1时掉电,默认为上电。
(3)第2位(FLASH_PD)为FLASH掉电控制位,置0时上电,置1时掉电,默认为上电。
(4)第3位(BOD_PD)为BOD掉电控制位,置0时上电,置1时掉电,默认为上电。
(5)第4位(ADC_PD)为ADC掉电控制位,置0时上电,置1时掉电,默认为掉电。
(6)第5位(SYSOSC_PD)为系统振荡器掉电控制位,置0时上电,置1时掉电,默认为掉电。
(7)第6位(WDTOSC_PD)为看门狗振荡器掉电控制位,置0时上电,置1时掉电,默认为掉电。
(8)第7位(SYSPLL_PD)为系统PLL掉电控制位,置0时上电,置1时掉电,默认为掉电。
(9)第8到14位为保留位,但它们有默认值,在对这些位进行写入操作时,必须按照默认值来写。
(10)第15位(ACMP)为模拟比较器掉电控制位,置0时上电,置1时掉电,默认为掉电。
(11)第16到31位为保留位。
从上表中可以看出,系统振荡器和系统PLL在默认情况下是掉电的,也就是说默认状态下它们处于不工作状态,要让它们工作就必须给它们的控制位置0(执行语句“LPC_SYSCON->PDRUNCFG &= ~(1 << 5)”和“LCP_SYSCON->PDRUNCFG &= ~(1 << 7)”),以更改为上电状态。

下表给出了系统振荡器控制寄存器SYSOSCCTRL的全部位结构,其字节地址为0x40048020。


(1)第0位(BYPASS)为系统振荡器旁路控制位,置0时未旁路,置1时被旁路,默认为未旁路。
(2)第1位(FREQRANGE)为低功耗振荡器确定频率范围选择位,置0时外部晶振的频率范围为1~20MHz,置1时为15~20MHz,默认为1~20MHz。
(3)第2到31位为保留位。
从上表中可以看出,如果要让系统振荡器工作,则SYSOSCCTRL寄存器的第0位就应该选择0,即不被旁路,只有从外部直接输入振荡信号的情况下才会选择旁路(比如使用有源晶振)。若系统使用12MHz外部晶振,则第1位应该选择0。实际上LPC824复位后的值就是该配置值,当然也可执行语句“LCP_SYSCON->SYSOSCCTRL = 0x00000000”来实现。
在配置了LPC824的时钟之后(无论是PLL时钟还是主时钟),都需要“更新”一下才能正常工作。根据官方数据手册,更新操作要给相应的更新允许寄存器进行“toggle”操作(即先向其写0再紧接着写1),这样之后时钟才能正常运行。
以下两表分别给出了系统PLL时钟源更新允许寄存器SYSPLLCLKUEN和主时钟源更新允许寄存器MAINCLKUEN的全部位结构,它们的字节地址分别为0x40048044、0x40048074。

(1)第0位(ENA)为允许系统PLL时钟源更新控制位,置0时无变化,置1时更新,默认为无变化。
(2)第1到31位为保留位。

(1)第0位(ENA)为允许主时钟源更新控制位,置0时无变化,置1时更新,默认为无变化。
(2)第1到31位为保留位。

在PLL时钟源和主时钟源更改后,紧接着要及时更新相应的允许寄存器才能让它们正常工作。此外还要注意,“toggle”后需要再次查询相应的允许寄存器是否已更新,若没有就需要等待直到其更新为止(例如在更新PLL时钟源更新允许寄存器SYSPLLCLKUEN后要执行语句“while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01));”,以等待其更新完成)。
接下看PLL的配置,要让PLL对输入时钟进行倍频或分频,就得配置系统倍频控制寄存器,下表给出了系统倍频控制寄存器SYSPLLCTRL的全部位结构,其字节地址为0x40048008。

 

(1)第0到4位(MSEL)为反馈分频器的值,分频器的值M为MSEL+1。
(2)第5、6两位(PSEL)为后分频器的值,分频器的值为2×P。
(3)第7到31位为保留位,不能对它们写1。
PLL的输出频率要符合下面的公式:

上式中,Fclkout为PLL的输出频率,Fclkin为外部晶振的频率,FCCO的值必须在156MHz ~320MHz之间,M为倍频的倍数,P值要符合要求。若以12MHz的晶振做为输入,系统主时钟要为24MHz,则M=2(MSEL=00001),P的值只能取4(PSEL=10)才能满足公式要求。因此寄存器SYSPLLCTRL的值应该为1000001(0x41),所以要配置此项可执行语句“LPC_SYSCON->SYSPLLCTRL = 0x00000041”。
下表给出了倍频状态寄存器SYSPLLSTAT的全部位结构,其字节地址为0x4004800C。

 

(1)第0位(LOCK)为PLL锁定状态标志位,为0时未锁定,为1时锁定。
(2)第1到31位为保留位。
在改变了PLL的倍频之后,要查询SYSPLLSTAT寄存器来确定PLL锁定了没有,若没有就进入等待直到其锁定为止(执行语句“while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));”)。
CPU要对GPIO进行操作,就必须给GPIO时钟信号,即需要使能GPIO的时钟。在默认情况下GPIO时钟是允许的,也可以对系统AHB时钟控制寄存器中相应的位进行操作来选择允许时钟。
下表给出了系统AHB时钟控制寄存器SYSAHBCLKCTRL的全部位结构,其字节地址为0x40048080。

  

(1)第0位(SYS)为允许AHB时钟标志位,该位为只读,值为0时保留,为1时允许,默认为允许。
(2)第1位(ROM)为允许ROM时钟控制位,置0时禁止,置1时允许,默认为允许。
(3)第2位(RAM0_1)为允许SRAM0和SRAM1时钟控制位,置0时禁止,置1时允许,默认为允许。
(4)第3位(FLASHREG)为允许flash寄存器接口时钟控制位,置0时禁止,置1时允许,默认为允许。
(5)第4位(FLASH)为允许flash时钟控制位,置0时上电禁止,置1时允许,默认为允许。
(6)第5位(I2C0)为允许I2C0时钟控制位,置0时禁止,置1时允许,默认为禁止。
(7)第6位(GPIO)为允许GPIO时钟控制位,置0时禁止,置1时允许,默认为允许。
(8)第7位(SWM)为允许开关矩阵时钟控制位,置0时禁止,置1时允许,默认为允许。
(9)第8位(SCT)为允许配置SCTimer/PWM时钟控制位,置0时禁止,置1时允许,默认为禁止。
(10)第9位(WKT)为允许自唤醒定时器时钟控制位,置0时禁止,置1时允许,默认为禁止。
(11)第10位(MRT)为允许多速率定时器时钟控制位,置0时禁止,置1时允许,默认为禁止。
(12)第11位(SPI0)为允许SPI0时钟控制位,置0时禁止,置1时允许,默认为禁止。
(13)第12位(SPI1)为允许SPI1时钟控制位,置0时禁止,置1时允许,默认为禁止。
(14)第13位(CRC)为允许CRC时钟控制位,置0时禁止,置1时允许,默认为禁止。
(15)第14位(UART0)为允许USART0时钟控制位,置0时禁止,置1时允许,默认为禁止。
(16)第15位(UART1)为允许USART1时钟控制位,置0时禁止,置1时允许,默认为禁止。
(17)第16位(UART2)为允许USART2时钟控制位,置0时禁止,置1时允许,默认为禁止。
(18)第17位(WWDT)为允许WWDT时钟控制位,置0时禁止,置1时允许,默认为禁止。
(19)第18位(IOCON)为允许IOCON时钟控制位,置0时禁止,置1时允许,默认为禁止。
(20)第19位(ACMP)为允许模拟比较器时钟控制位,置0时禁止,置1时允许,默认为禁止。
(21)第20位为保留位。
(22)第21位(I2C1)为允许I2C1时钟控制位,置0时禁止,置1时允许,默认为禁止。
(23)第22位(I2C2)为允许I2C2时钟控制位,置0时禁止,置1时允许,默认为禁止。
(24)第23位(I2C3)为允许I2C3时钟控制位,置0时禁止,置1时允许,默认为禁止。
(25)第24位(ADC)为允许ADC时钟控制位,置0时禁止,置1时允许,默认为禁止。
(26)第25位为保留位。
(27)第26位(MTB)为允许微跟踪缓冲区控制寄存器时钟控制位,置0时禁止,置1时允许,默认为禁止。
(28)第27、28两位为保留位。
(29)第29位(DMA)为允许DMA时钟控制位,置0时禁止,置1时允许,默认为禁止。
(30)第30、31两位为保留位。

从上表中可以看出,第6位即为“通用输入输出端口GPIO”的时钟配置项,执行语句“LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6)”就可以开启GPIO的时钟。在打开了GPIO的时钟后,PIO0_0~PIO0_28端口才能正常使用,不过默认状态就是开启的。

由上述可见,基于Cortex-M0+内核的微控制器由于强化了时钟配置,所以在一般情况下使用该类型的芯片,首要的任务就是配置正确的时钟。
下面给出一个时钟初始化的配置函数,可通过它来了解LPC824时钟配置的整个过程。
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->SYSAHBCLKCTRL |= (1 << 18);  //使能IOCON时钟
 LPC_IOCON->PIO0_8 &= ~(3 << 3);          //把P0_8引脚配置为无上下拉电阻方式
  LPC_IOCON->PIO0_9 &= ~(3 << 3);          //把P0_9引脚配置为无上下拉电阻方式
  LPC_SWM->PINENABLE0 &= ~(3 << 6);     //把P0_8、P_9引脚配置为XTALIN、XTALOUT引脚
 LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 18);  //禁止IOCON时钟
 LPC_SYSCON->SYSPLLCLKSEL = 0x00000001;  //PLL输入选择外部晶体振荡
 LPC_SYSCON->SYSPLLCLKUEN = 0x00;
 LPC_SYSCON->SYSPLLCLKUEN = 0x01;  //先写0后写1更新时钟源
 while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01)); //等待更新完成
 LPC_SYSCON->SYSPLLCTRL = 0x00000041;  //M=2、P=4,倍频后的时钟为24MHz
 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时钟为24MHz

在上述配置中,系统外部时钟晶振为12MHz,则在该函数执行完后,LPC824时钟就会被设置为主时钟24MHz,AHB时钟24MHz。另外,由于在默认状态下,LPC824的PIO0_8和PIO0_9两个引脚是GPIO功能,所以要连接外部晶振先要把它们配置为XTALIN、XTALOUT引脚,否则芯片不能正常工作。

上述时钟配置程序是最基本的,也是必须的,因此在任何程序开始前,都应该先调用该时钟配置函数,以对LPC824进行基本的时钟配置,为后续程序提供保障。另外,系统的主时钟也可以由内部RC时钟或看门狗时钟来充当,这将会在后面再讨论。

posted @ 2020-04-24 10:14  fxzq  阅读(1245)  评论(0编辑  收藏  举报