TA的每日心情 | 开心 昨天 00:02 |
---|
签到天数: 3942 天 连续签到: 13 天 [LV.Master]伴坛终老
|
本帖最后由 toofree 于 2017-1-17 23:39 编辑
【赚周年币】技术帖Week1-Day5——LPC824 Breakout之六、30M时钟
之前在写贴二,软件延时时,说是系统时钟是30MHz。本贴简单从程序中跟踪一下,一步一步探其究竟。
【赚周年币】技术帖Week1-Day2——LPC824 Breakout之二、软件延时https://www.cirmall.com/bbs/forum ... 59439&fromuid=17147
复制修改,新建一下工程,取名为“Example_hello_LPC842”。添加部分文件到工程,添加完效果如下图。
为了方便程序跟踪调试,我们今天把去掉之前peripherals_lib和utilities_lib两个工程生成的“.lib文件”,而添加工程的各自的源文件,共三个。“utilities.c”,“lpc8xx_i2c.c”,“lpc8xx_gpio.c”。
主文件“Example_hello_LPC842.c”,把多余的代码删除,最后编辑效果如下:- /*
- ===============================================================================
- Name : Example_hello_LPC842.c
- Author : $(author)
- Version :
- Copyright : $(copyright)
- Description : main definition
- ===============================================================================
- */
- #include "LPC8xx.h"
- //#include <cr_section_macros.h>
- #include <stdio.h>
- #include "lpc8xx_gpio.h"
- #include "lpc8xx_syscon.h"
- #include "utilities.h"
- <font color="#ff0000">int main(void) {</font>
- // Local variables
- uint32_t number, temp;
- // Reset the GPIO module and enable its clock. See peripherals_lib
- //GPIOInit();
- // Config. ports (red LED), (blue LED), (green LED) as outputs,
- // with LEDs off ('1' = off). See utilities_lib
- //Config_LEDs(RED | BLUE | GREEN);
- <font color="#ff0000"> printf("Hello world !\r\n");</font>
-
- while(1)
- {
- ;
- }
-
- return 0;
- } // end of main
复制代码 只有一句,printf("Hello world !\r\n");
没有IO资源,没有硬件外设需要执行的代码。我们今天以软仿真方式来探讨30M系统时钟的由来。
目标板设置选项中设置如下图:
点仿真按钮,或快捷方式“CTRL+F5”,开始软件仿真。
可以看到程序停在了“Keil_startup_LPC8xx.s”中,这个文件是本工程的启动代码。
硬件复位后,第一步是执行复位处理程序,这个程序的入口在启动代码里(默认)- Reset_Handler PROC ; PROC等同于FUNCTION,表示一个函数的开始,与ENDP相对?
- EXPORT Reset_Handler [WEAK]
- IMPORT SystemInit ;IMPORT 是告诉函数在其它程序中有定义,类似函数申明吧,可能不太准确
- IMPORT IRC_Only_SystemInit ;IRC_Only_SystemInit函数先不管,本程序中没用到
- IMPORT __main ;主函数链接点
- LDR R0, =SystemInit
- BLX R0
- LDR R0, =__main
- BX R0
- ENDP
复制代码 关于这段代码,网上其它资料是这么说的(如下引用)。这里SystemInit函数是我自己用C代码写的硬件底层时钟初始化代码,这个可不算是keil mdk给代劳的.初始化堆栈指针、执行完用户定义的底层初始化代码后,发现接下来的代码是调用了__main函数,这里之所以有__main函数,是因为在C代码中定义了main函数,函数标签 main() 具有特殊含义。main() 函数的存在强制链接器链接到 __main 和 __rt_entry 中的初始化代码。
其中,__main函数执行代码和数据复制、解压缩以及 ZI 数据的零初始化。解释一下,C代码中,已经赋值的全局变量被放在RW属性的输入节中,这些变量的初值被keil mdk压缩后放到ROM或Flash中(RO属性输入节)。什么是赋值的全局变量呢?如果你在代码中这样定义一个全局变量:int nTimerCount=20;变量nTimerCount就是已经赋值的变量,如果是这样定义:int nTimerCount;变量nTimerCount就是一个非赋值的变量,keil默认将它放到属性为ZI的输入节。为什么要压缩呢?这是因为如果赋值变量较多,会占用较多的Flash存储空间,keil 默认用自己的压缩算法。这个“解压缩”就是将存放在RO输入区(一般为ROM或Flash)的变量初值,按照一定算法解压缩后,拷贝到相应RAM区。ZI数据清零是指将ZI区的变量所在的RAM区清零。使用 UNINIT 属性对执行区进行标记可避免 __main 对该区域中的 ZI 数据进行零初始化。这句话很重要,比如我有一些变量,保存一些重要信息,不希望复位后就被清零,这时就可以用分散加载文件定义一块UNINIT属性的区,将不希望零初始化的变量定义到这个区即可。
这个我们了解一下就好了,不必深究。接下来,我们看一下“SystemInit”函数。查看函数方法,同C文件中,右键-->Go To Definition,或者直接按F11,让程序执行进入“SystemInit”函数。在程序“system_LPC8xx.c”中。
我们把编译忽略掉的代码行,合起来,这样更直观些,就这么一点点。
实质上就是把4个宏定义参数赋给特定的寄存器。4个参数是“SYSPLLCLKSEL_Val”、“SYSPLLCTRL_Val”、“MAINCLKSEL_Val”、“SYSAHBCLKDIV_Val”。- // <h> System Oscillator Control (SYSOSCCTRL)
- // <o.0> BYPASS: System Oscillator Bypass Enable
- // <i> If enabled then PLL input (sys_osc_clk) is fed
- // <i> directly from XTALIN and XTALOUT pins.
- // <o.1> FREQRANGE: System Oscillator Frequency Range
- // <i> Determines frequency range for Low-power oscillator.
- // <0=> 1 - 20 MHz</font>
- // <1=> 15 - 25 MHz
- // </h>
- #define SYSOSCCTRL_Val 0x00000000 // Reset value: 0x000
复制代码- //
- // <o.0..1> System PLL Clock Source Select (SYSPLLCLKSEL)
- // <0=> IRC Oscillator
- // <1=> Crystal Oscillator (SYSOSC)
- // <3=> CLKIN pin
- #define SYSPLLCLKSEL_Val 0x00000000 // Reset value: 0x000
复制代码- // <h> System PLL Setting (SYSPLLCTRL)
- // <i> F_clkout = M * F_clkin = F_CCO / (2 * P)
- // <i> F_clkin must be in the range of 10 MHz to 25 MHz
- // <i> F_CCO must be in the range of 156 MHz to 320 MHz
- <font color="#ff0000">// <o.0..4> MSEL: Feedback Divider Selection
- // <i> M = MSEL + 1</font>
- // <0-31>
- /<font color="#ff0000">/ <o.5..6> PSEL: Post Divider Selection
- // <i> Post divider ratio P. Division ratio is 2 * P</font>
- // <0=> P = 1
- // <1=> P = 2
- // <2=> P = 4
- // <3=> P = 8
- // </h>
- //#define SYSPLLCTRL_Val 0x00000041 // For 24 MHz // Reset value: 0x000
- #define SYSPLLCTRL_Val 0x00000024 // For 30 Mhz // Reset value: 0x000
复制代码 [0..4]位的值是0x04,[5..6]位的值是0x01。
根据公式F_clkout = M * F_clkin ,我们可以知道,PLL输出时钟为(4+1)*12M=60M;
再有 F_clkout= F_CCO / (2 * P) , F_CCO = 60 * (2*2) = 240M- // <o.0..1> Main Clock Source Select (MAINCLKSEL)
- // <0=> IRC Oscillator
- // <1=> PLL Input
- // <2=> WD Oscillator
- // <3=> PLL Output
- #define MAINCLKSEL_Val 0x00000003 // Reset value: 0x000
复制代码- // <o.0..7> System AHB Clock Divider (SYSAHBCLKDIV.DIV)
- // <i> Divides main clock to provide system clock to core, memories, and peripherals.
- // <i> 0 = is disabled
- // <0-255>
- //#define SYSAHBCLKDIV_Val 0x00000001 // For 24 MHz // Reset value: 0x001
- #define SYSAHBCLKDIV_Val 0x00000002 // For 30 MHz // Reset value: 0x001
复制代码 上面得到的F_clkout是 60M,被“SYSAHBCLKDIV_Val”二分频,得到内核、系统时钟为30MHz。
我们不妨看看整个系统的的路径走向。
LPC824数据手册和用户手册,以及测试程序包一并附近上
LPC82X.pdf
(1.89 MB, 下载次数: 16)
|
评分
-
查看全部评分
|