回答

收藏

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

#线上活动 #线上活动 3569 人阅读 | 0 人回复 | 2017-01-06

本帖最后由 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”,把多余的代码删除,最后编辑效果如下:
  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系统时钟的由来。
        目标板设置选项中设置如下图:
        


        点仿真按钮,或快捷方式“CTRL+F5”,开始软件仿真。
        


        可以看到程序停在了“Keil_startup_LPC8xx.s”中,这个文件是本工程的启动代码。
        

       硬件复位后,第一步是执行复位处理程序,这个程序的入口在启动代码里(默认)
  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”中。
        


        我们把编译忽略掉的代码行,合起来,这样更直观些,就这么一点点。
        

      
        实质上就是把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

        我们不妨看看整个系统的的路径走向。
        


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

      
UM10800.pdf (3.2 MB, 下载次数: 15)

      
LPC824_Example_Code_Bundle_Keil_r1.0(20170107).rar (2.28 MB, 下载次数: 18)
关注下面的标签,发现更多相似文章

评分

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

查看全部评分

分享到:
回复

使用道具 举报

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

本版积分规则

关闭

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