查看: 2858|回复: 0

【赚周年币】技术帖Week1-Day6——LPC824 Breakout之六、30M时钟

[复制链接]
  • TA的每日心情
    开心
    3 小时前
  • 签到天数: 3752 天

    连续签到: 78 天

    [LV.Master]伴坛终老

    发表于 2017-1-6 22:46:33 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 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”。添加部分文件到工程,添加完效果如下图。
             傲游截图20170106224613.jpg

            为了方便程序跟踪调试,我们今天把去掉之前peripherals_lib和utilities_lib两个工程生成的“.lib文件”,而添加工程的各自的源文件,共三个。“utilities.c”,“lpc8xx_i2c.c”,“lpc8xx_gpio.c”。

            主文件“Example_hello_LPC842.c”,把多余的代码删除,最后编辑效果如下:
    1. /*
    2. ===============================================================================
    3. Name        : Example_hello_LPC842.c
    4. Author      : $(author)
    5. Version     :
    6. Copyright   : $(copyright)
    7. Description : main definition
    8. ===============================================================================
    9. */

    10. #include "LPC8xx.h"

    11. //#include <cr_section_macros.h>

    12. #include <stdio.h>

    13. #include "lpc8xx_gpio.h"
    14. #include "lpc8xx_syscon.h"
    15. #include "utilities.h"


    16. <font color="#ff0000">int main(void) {</font>

    17.   // Local variables
    18.   uint32_t number, temp;

    19.   // Reset the GPIO module and enable its clock. See peripherals_lib
    20.   //GPIOInit();

    21.   // Config. ports (red LED), (blue LED), (green LED) as outputs,
    22.   // with LEDs off ('1' = off). See utilities_lib
    23.   //Config_LEDs(RED | BLUE | GREEN);

    24. <font color="#ff0000">        printf("Hello world !\r\n");</font>
    25.         
    26.         while(1)
    27.         {
    28.                 ;
    29.         }
    30.         
    31.         return 0;

    32. } // end of main
    复制代码
    只有一句,printf("Hello world !\r\n");

            没有IO资源,没有硬件外设需要执行的代码。我们今天以软仿真方式来探讨30M系统时钟的由来。
            目标板设置选项中设置如下图:
             傲游截图20170106225654.jpg

            点仿真按钮,或快捷方式“CTRL+F5”,开始软件仿真。
             傲游截图20170106225942.jpg

            可以看到程序停在了“Keil_startup_LPC8xx.s”中,这个文件是本工程的启动代码。
             傲游截图20170106230456.jpg
           硬件复位后,第一步是执行复位处理程序,这个程序的入口在启动代码里(默认)
    1. Reset_Handler   PROC                        ; PROC等同于FUNCTION,表示一个函数的开始,与ENDP相对?
    2.                 EXPORT  Reset_Handler             [WEAK]
    3.                 IMPORT  SystemInit                          ;IMPORT 是告诉函数在其它程序中有定义,类似函数申明吧,可能不太准确
    4.                 IMPORT  IRC_Only_SystemInit         ;IRC_Only_SystemInit函数先不管,本程序中没用到
    5.                 IMPORT  __main                               ;主函数链接点
    6.                 LDR     R0, =SystemInit
    7.                 BLX     R0
    8.                 LDR     R0, =__main
    9.                 BX      R0
    10.                 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”中。
             傲游截图20170106232127.jpg

            我们把编译忽略掉的代码行,合起来,这样更直观些,就这么一点点。
             傲游截图20170106232331.jpg
          
            实质上就是把4个宏定义参数赋给特定的寄存器。4个参数是“SYSPLLCLKSEL_Val”、“SYSPLLCTRL_Val”、“MAINCLKSEL_Val”、“SYSAHBCLKDIV_Val”。
    1. //   <h> System Oscillator Control (SYSOSCCTRL)
    2. //     <o.0>      BYPASS: System Oscillator Bypass Enable
    3. //                     <i> If enabled then PLL input (sys_osc_clk) is fed
    4. //                     <i> directly from XTALIN and XTALOUT pins.
    5. //     <o.1>      FREQRANGE: System Oscillator Frequency Range
    6. //                     <i> Determines frequency range for Low-power oscillator.
    7. //                   <0=> 1 - 20 MHz</font>
    8. //                   <1=> 15 - 25 MHz
    9. //   </h>
    10. #define SYSOSCCTRL_Val        0x00000000              // Reset value: 0x000
    复制代码
    1. //
    2. //   <o.0..1> System PLL Clock Source Select (SYSPLLCLKSEL)
    3. //        <0=> IRC Oscillator
    4. //        <1=> Crystal Oscillator (SYSOSC)
    5. //        <3=> CLKIN pin
    6. #define SYSPLLCLKSEL_Val      0x00000000              // Reset value: 0x000
    复制代码
    1. //     <h> System PLL Setting (SYSPLLCTRL)
    2. //              <i> F_clkout = M * F_clkin = F_CCO / (2 * P)
    3. //              <i> F_clkin must be in the range of  10 MHz to  25 MHz
    4. //              <i> F_CCO   must be in the range of 156 MHz to 320 MHz
    5. <font color="#ff0000">//       <o.0..4> MSEL: Feedback Divider Selection
    6. //              <i> M = MSEL + 1</font>
    7. //            <0-31>
    8. /<font color="#ff0000">/       <o.5..6> PSEL: Post Divider Selection
    9. //              <i> Post divider ratio P. Division ratio is 2 * P</font>
    10. //            <0=> P = 1
    11. //            <1=> P = 2
    12. //            <2=> P = 4
    13. //            <3=> P = 8
    14. //     </h>
    15. //#define SYSPLLCTRL_Val      0x00000041 // For 24 MHz           // Reset value: 0x000
    16. #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
    1. //     <o.0..1> Main Clock Source Select (MAINCLKSEL)
    2. //        <0=> IRC Oscillator
    3. //        <1=> PLL Input
    4. //        <2=> WD Oscillator
    5. //        <3=> PLL Output
    6. #define MAINCLKSEL_Val        0x00000003              // Reset value: 0x000
    复制代码
    1. //     <o.0..7>   System AHB Clock Divider (SYSAHBCLKDIV.DIV)
    2. //            <i> Divides main clock to provide system clock to core, memories, and peripherals.
    3. //            <i> 0 = is disabled
    4. //          <0-255>
    5. //#define SYSAHBCLKDIV_Val    0x00000001 // For 24 MHz           // Reset value: 0x001
    6. #define SYSAHBCLKDIV_Val      0x00000002 // For 30 MHz           // Reset value: 0x001
    复制代码
    上面得到的F_clkout是 60M,被“SYSAHBCLKDIV_Val”二分频,得到内核、系统时钟为30MHz

            我们不妨看看整个系统的的路径走向。
             傲游截图20170107000250.jpg

            LPC824数据手册和用户手册,以及测试程序包一并附近上
           LPC82X.pdf (1.89 MB, 下载次数: 16)

    评分

    参与人数 2 +51 收起 理由
    EEboard爱板网 + 21 3周发帖养成记 奖励
    loveeeboard + 30

    查看全部评分

    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-27 03:20 , Processed in 0.128204 second(s), 19 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.