单片机 > 单片机程序设计 > 详情

lpc1114 SystemInit函数解释

发布时间:2020-06-06 发布时间:
|
  1. void SystemInit (void) {

  2. volatile uint32_t i;

  3. #if (CLOCK_SETUP)                                     /* Clock Setup           */

  4. #if ((SYSPLLCLKSEL_Val & 0x03) == 1)

  5. LPC_SYSCON->PDRUNCFG  &= ~(1 << 5);            /* Power-up System Osc */

  6. LPC_SYSCON->SYSOSCCTRL = SYSOSCCTRL_Val;

  7. for (i = 0; i < 200; i++) __NOP();

  8. #endif

  9. LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_Val;     /*Select PLL Input            */

  10. LPC_SYSCON->SYSPLLCLKUEN  = 0x01;                /* Update Clock Source      */

  11. LPC_SYSCON->SYSPLLCLKUEN  = 0x00;                /* Toggle Update Register    */

  12. LPC_SYSCON->SYSPLLCLKUEN  = 0x01;

  13. while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01));        /* Wait Until Updated        */

  14. #if ((MAINCLKSEL_Val & 0x03) == 3)                      /* Main Clock is PLL Out    */

  15. LPC_SYSCON->SYSPLLCTRL    = SYSPLLCTRL_Val;

  16. LPC_SYSCON->PDRUNCFG     &= ~(1 << 7);          /* Power-up SYSPLL          */

  17. while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));                /* Wait Until PLL Locked    */

  18. #endif

  19. #if (((MAINCLKSEL_Val & 0x03) == 2) )

  20. LPC_SYSCON->WDTOSCCTRL    = WDTOSCCTRL_Val;

  21. LPC_SYSCON->PDRUNCFG     &= ~(1 << 6);           /* Power-up WDT Clock      */

  22. for (i = 0; i < 200; i++) __NOP();

  23. #endif

  24. LPC_SYSCON->MAINCLKSEL    = MAINCLKSEL_Val;     /* Select PLL Clock Output    */

  25. LPC_SYSCON->MAINCLKUEN    = 0x01;                /* Update MCLK Clock Source */

  26. LPC_SYSCON->MAINCLKUEN    = 0x00;                /* Toggle Update Register     */

  27. LPC_SYSCON->MAINCLKUEN    = 0x01;

  28. while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated     */

  29. LPC_SYSCON->SYSAHBCLKDIV  = SYSAHBCLKDIV_Val;

  30. #endif

  31. }

该函数位于KEIL自带的system_LPC11xx.c文件中,首先定义了一个变量,用来计数,然后一共使用了4个条件编译,其中1个总体的条件编译和3个嵌套条件编译。

  1. volatile uint32_t i;

定义了一个变量i

uint32_t 是指定义一个32位的无符号变量,把鼠标放到uint32_t的上面,单击鼠标右键,在弹出的菜单中选择“Go To Definition Of’uint32_t’”,如下图所示:

然后,stdint.h文件打开,在文件中,找到uint32_t的定义符,如下所示:

可以看到,uint32_t 实际上是unsigned int类型,用typedef重新起了个类型名字而已。

在SystemInit()函数中,i的变量定义前面还加上了volatile关键字,其作用是不让编译器优化此变量,以免造成程序的错误。

第3行的#if和第30行的#endif共同组成了一个条件编译。

如果CLOCK_SETUP为1,则执行里面的语句,如果不为1,则从endif后面执行,在此函数中,endif后面没有任何语句了,所以,如果CLOCK_SETUP为0的话,系统时钟配置函数实际上什么也没有执行,如果想让单片机运行在默认的IRC 12MHz主频下,把CLOCK_SETUP定义为0即可。

把鼠标放到CLOCK_SETUP上面,单击鼠标右键,在弹出的菜单中选择“Go To Definition Of‘CLOCK_SETUP’”。

上图中看到,CLOCK_SETUP在文件中定义为1,所以里面的语句被编译执行。我们可以在这里把CLOCK_SETUP设置为0来禁止执行系统初始化函数.

第4行到到第8行,亦是一个条件编译,如果if括号中的条件为真,则执行

包含在里面的语句。由SYSPLLCLKSEL_VAL的值分析得出,这里是判断,是否需要启动系统振荡器。

在这里,涉及到了两个寄存器:PDRUNCFG和SYSOSCCTRL。

PDRUNCFG:掉电配置寄存器

符号描述复位值
0IRCOUT_PD
IRC 振荡器输出掉电0
0上电
1掉电
1IRC_PD
IRC 振荡器掉电0
0上电
1掉电
2FLASH_PD
Flash掉电0
0上电
1掉电
3BOD_PD
BOD掉电0
0上电
1掉电
4ADC_PD
ADC掉电1
0上电
1掉电
5SYSOSC_PD
系统振荡器掉电1
0上电
1掉电
6WDTOSC_PD
看门狗振荡器掉电1
0上电
1掉电
7SYSPLL_PD
系统PLL掉电1
0上电
1掉电
8
保留位. 只能给此位写1.1
9
保留位. 只能给此位写0.0
10
保留位. 只能给此位写1.1
11
保留位. 只能给此位写1.1
12
保留位. 只能给此位写0.0
15:13
保留位. 只能给此位写111.111
31:16
保留位

掉电控制寄存器中的bit0~bit7控制着7个模拟模块的掉电状态,对对应为写0上电,写1掉电。复位状态下,IRC、FLASH、BOD是上电状态,ADC模块、系统振荡器模块、看门狗振荡器模块、系统PLL是掉电状态。由此处就可以看到,为什么复位后是IRC在担当着单片机的工作时钟。

SYSOSCCTRL:系统振荡器控制寄存器

符号描述复位值
0BYPASS
系统振荡器控制位0x0
0系统振荡器有效
1系统振荡器失效。直接从XTALIN引脚输入外部时钟信号
1FREQRANGE
低功耗振荡器频率范围0x0
01 – 20 MHz 频率范围
115 – 25 MHz 频率范围
31:2保留0x00

 

系统振荡器控制寄存器只用到了bit0和bit1。

bit0 决定了是否启用系统振荡器。

bit0 = 0 代表启用,bit = 1代表不启用。

由复位值可以看到,默认是启用系统振荡器的。

在不启用系统振荡器的时候,可以用外部时钟信号直接输入到XTALIN引脚,就可以不使用外部晶振了。(所以,当你看到一个电路板上,某个单片机没有接晶振的时候,不要妄言断定它使用的是内部时钟,因为还有可能是使用外部时钟发生器来工作,或者是别的单片机产生的时钟输出引脚连接到了XTALIN引脚上。)

介绍完了这两个寄存器,再看程序第567行。

第5行,给寄存器PDRUNCFG寄存器的bit5写0,即给系统振荡器上电。(一眼看不出是给bit5写0的童鞋,请看第一章相关介绍)

第6行,把SYSOSCCTRL_VAL的值写到寄存器SYSOSCCTRL。

第7行,一个简单的延时函数,目的是等待系统振荡器上电完成。

第9行到第13行,设置PLL,涉及到两个寄存器:SYSPLLCLKSEL和SYSPLLCLKUEN。

SYSPLLCLKSEL:系统PLL时钟源选择寄存器

符号描述复位值
1:0SEL
系统PLL时钟源
0x0IRC振荡器
0x1系统振荡器
0x2保留
0x3保留
31:2
保留

该寄存器只用到了前两位,可以选择IRC振荡器或者系统振荡器作为PLL的时钟源。注意:切换时钟的时候,两个振荡器必须同时上电工作,等待所选择的振荡器稳定工作以后,才可以关系另外一个振荡器,当然,不关也可以,对于对功耗有要求的,最好还是关闭用不着的时钟。当使用CAN模块,且CAN的通信速率大于100kbit/s时,必须选择系统振荡器工作。

SYSPLLCLKUEN:系统PLL时钟更新寄存器

符号描述复位值
0ENA
允许系统PLL时钟更新0x0
0没有改变
1更新时钟源
31:1保留0x00

当SYSPLLCLKSEL中的值改变以后,需要对此更新寄存器先写0再写1达到时钟更新的目的。

第9行,当SYSPLLCLKSEL_Val = 0x1,选择IRC时钟,当SYSPLLCLKSEL_Val = 0x2,选择系统振荡器时钟。

第10~12行,更新系统PLL时钟。

第13行,在时钟更新未成功时,一直在while循环;当时钟更新成功后,跳出while循环向下执行。

第14~18行,条件编译,如果主时钟选择PLL输出,则执行里面的语句。涉及到3个寄存器:SYSPLLCTRL、PDRUNCFG、SYSPLLSTAT

其中,PDRUNCFG寄存器在前面已经讲过。

SYSPLLCTRL:系统PLL控制寄存器

符号描述复位值
4:0MSEL
反馈分频值, M=MSEL+1


00000: M = 1

to

11111: M = 32.

0x000
6:5PSEL
后置分频比率0x00
0x0P = 1
0x1P = 2
0x2P = 4
0x3P = 8
31:7保留位,不准给这些位写10x0

该寄存器决定了PLL的乘数M和除数P,bit0~4决定了乘数M = MSEL +1,bit5和bit6决定了除数P。

 

SYSPLLSTAT:系统PLL状态寄存器

符号描述复位值
0LOCK
PLL 锁状态0x0
0PLL没有锁
1PLL 已锁
31:1
保留0x00

这是一个只读存储器,只用到了bit0,用来观察PLL是否锁定。只有当PLL锁定的时候,PLL才可以正常工作。

第15行,写入MSEL和PSEL值。

第16行,给PDRUNCFG的bit7写0,即PLL上电。

第17行,循环读取SYSPLLSTAT的值,直到PLL锁定后先下执行。

第19~23行,条件编译,如果主时钟源选择看门狗振荡器时钟,就执行里面的的语句。涉及到2个寄存器:WDTOSCCTRL和PDRUNCFG。

其中,PDRUNCFG寄存器前面以已经讲过。

WDTOSCCTRL:看门狗振荡器控制寄存器

符号描述复位值
4:0DIVSEL
Fclkana分频值


wdt_osc_clk = Fclkana/ (2 (1 + DIVSEL))

00000: 2 (1 + DIVSEL) = 2

00001: 2 (1 + DIVSEL) = 4

to

11111: 2 (1 + DIVSEL) = 64

0
8:5FREQSEL
看门狗振荡器模拟输出频率选择位(Fclkana).0x0
0x10.6 MHz
0x21.05 MHz
0x31.4 MHz
0x41.75 MHz


0x52.1 MHz
0x62.4 MHz
0x72.7 MHz
0x83.0 MHz
0x93.25 MHz
0xA3.5 MHz
0xB3.75 MHz
0xC4.0 MHz
0xD4.2 MHz
0xE4.4 MHz
0xF4.6 MHz
31:9保留

看门狗振荡器控制寄存器用来控制看门狗时钟的数值。一共有两个值,一个是DIVSEL,另一个是FREQSEL。

bit0~bit4决定DIVSEL值,bit5~8决定FREQSEL值。

最后输出的看门狗时钟值由下式决定:

wdt_osc_clk = Fclkana/ (2 (1 + DIVSEL))

wdt_osc_clk:配置好的看门狗振荡器时钟输出

Fclkana:由bit5~8 FREQSEL值决定。

第20行,写入DIVSEL和FREQSEL值;

第21行,给PDRUNCFG寄存器的bit6写0,给看门狗振荡器上电。

第22行,简单的延时函数,等待看门狗振荡器稳定。

第24~28行,用来设置主时钟源。涉及到了两个寄存器:MAINCLKSEL和MAINCLKUEN。

MAINCLKSEL:主时钟源选择寄存器

符号描述复位值
1:0SEL
主时钟时钟源选择0x00
0x1IRC 振荡器
0x2系统PLL输入时钟
0x3看门狗振荡器
0x4系统PLL输出时钟
31:2保留0x00

主时钟源选择寄存器当中,只有bit1和bit0位有效,此两位的值用来决定主时钟源,有4个时钟源可供选择。分别是IRC振荡器、看门狗振荡器、输入到PLL的时钟和PLL输出的时钟。

MAINCLKUEN:主时钟源更新寄存器

符号描述复位值
0ENA
允许主时钟源更新0x0
0没有改变
1更新时钟源
31:1保留0x00

当MAINCLKSEL中的值改变以后,需要对此更新寄存器先写0再写1达到时钟更新的目的。

第24行,选择主时钟源;

第25~27行,更新主时钟源;

第28行,等待主时钟源更新成功。

第29行,涉及到一个寄存器:SYSAHBCLKDIV

SYSAHBCLKDIV:系统AHB时钟分频寄存器

符号描述复位值
7:0DIV系统AHB时钟分频值


0: 禁止系统时钟.

1: 分频值为1.

to

255: 分频值为255.

0x01
31:8Reserved0x00

该寄存器可以给主时钟分频,分频后的时钟,提供给内核,存储器和单片机内部所有的外设模块。当DIV = 0X0时,关闭主时钟。

到这里,SystemInit()函数就讲完了!



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM基础知识八