IAR中cortex-m4启动流程分析

更新时间:2024-06-12 18:12:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

软件环境:windows7旗舰版,IAR V6105(EWARM-EV-WEB-6105)

ARM芯片:飞思卡尔K60N512VMD100 (cortex-m4核心) 示例程序:飞思卡尔官方的 KINETIS512_SC ======================

最近分析了一下飞思卡尔官方提供的k60系列demo程序在IAR上的启动流程,现写一下笔记,以备以后参考。先看一下K60N512VMD100内部存储器的分布情况,飞思卡尔K60N512VMD100有512K的flash和128k的SRAM.其中:

Flash地址空间: 0x00000000--0x00080000,共512k SRAM地址空间: SRAM1 0x1FFF0000--0x20000000 64k SRAM2 0x20000000--0x20010000 64k 总共的SRAM大小是128k

我要在RAM中调试代码,下面以代码的执行过程为顺序分析一下启动流程。

首先看一下源文件中提供的128KB_Ram.icf文件。*.icf文件是IAR中的分散描述文件,相当于ADS中的*.src文件或keil中的*.sct文件或GNU中的*.lds链接脚本文件。 这个文件中前面部分是各个变量的定义,关键看后面部分:

1. 2. 3.

};

/*128KB_Ram.icf后面部分*/ ***********

place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvecplace at address mem:__code_start__ { readonly section .noinit };

4. 5. 6. 7. 8. 9. 10.

place in RAM_region { readonly, block CodeRelocate };

place in RAM_region { readwrite, block CodeRelocateRam, block CSTACK, block HEAP }; ************

①place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }

这段代码表示要把.intvec代码段中的只读部分放在存储空间(mem,前面已定义的名称)中__ICFEDIT_intvec_start__ 地址上,前面部分已经定义__ICFEDIT_intvec_start__=0x1fff0000,是SRAM的起始地址。也就是先把向量表放到内存中的最前面。 .intvec 这个段是在vectors.c文件中出现的,

1. 2. 3. 4.

/*vectors.c片段*/

typedef void (*vector_entry)(void);

5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.

#pragma location = \

const vector_entry __vector_table[] = //@ \ =

{

VECTOR_000, /* Initial SP */ VECTOR_001, /* Initial PC */ VECTOR_002, VECTOR_003, ......(中间省略) VECTOR_254, VECTOR_255, CONFIG_1, CONFIG_2, CONFIG_3, CONFIG_4,

};

从源文件中可以看到这里定义了一个向量表__vector_table(前面的const 很重要不能省,这样才能保证向量表是只读的),向量表中的每一项都是一个指向函数的指针,这里总共有256+4=260个指针,所以占据空间为260*4=1040=0x410.

所以SRAM空间的前0x410的空间已经被向量表占据。即占据了0x1fff0000--0x1fff0410.

②place at address mem:__code_start__ { readonly section .noinit }

这段代码表示要把 .noinit段中的只读部分放到地址空间 __code_start__ 开始的地址上,前面有定义 __code_start__= 0x1fff0410 ,也就是把 .noinit段放到0x1fff0410开始的地址上。所以在内存中代码就连续了,先是向量表,接着的是.noinitd 段。 .noinit 段在crt0.s汇编文件中出现:

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.

SECTION .noinit : CODE EXPORT __startup __startup

MOV r0,#0 ; Initialize the GPRs MOV r1,#0 MOV r2,#0 MOV r3,#0 MOV r4,#0 MOV r5,#0 MOV r6,#0 MOV r7,#0 MOV r8,#0 MOV r9,#0 MOV r10,#0

16. 17. 18. 19. 20. 21. 22. 23. 24. 25.

MOV r11,#0 MOV r12,#0

CPSIE i ; Unmask interrupts import start

BL start ; call the C code __done B __done

END

这段代码算是芯片复位后执行的第一段代码(如果没有其他异常的话)。作为一个通常的规则,推荐先把通用寄存器(R0-R12)清零。然后是使能中断,跳转到start标号(或函数)处继续执行。

========================

在start.c文件中找到了start函数:

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.

/*start.c片段*/ void start(void) {

/* Disable the watchdog timer */ wdog_disable();

/* Copy any vector or data sections that need to be in RAM */ common_startup();

/* Perform processor initialization */ sysinit();

printf(\);

/* Determine the last cause(s) of reset */ if (MC_SRSH & MC_SRSH_SW_MASK) printf(\); if (MC_SRSH & MC_SRSH_LOCKUP_MASK) printf(\); if (MC_SRSH & MC_SRSH_JTAG_MASK) printf(\);

if (MC_SRSL & MC_SRSL_POR_MASK) printf(\);

25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45.

if (MC_SRSL & MC_SRSL_PIN_MASK) printf(\); if (MC_SRSL & MC_SRSL_COP_MASK) printf(\); if (MC_SRSL & MC_SRSL_LOC_MASK) printf(\); if (MC_SRSL & MC_SRSL_LVD_MASK)

printf(\); if (MC_SRSL & MC_SRSL_WAKEUP_MASK) printf(\);

/* Determine specific Kinetis device and revision */ cpu_identify();

/* Jump to main process */ main();

/* No actions to perform after this so wait forever */ while(1); }

start函数中,首先执行 wdog_disable()函数来禁用看门狗,然后调用 common_startup()函数初始化RAM(复制向量表、清零.bss段等,为C语言运行环境做准备),接着执行sysinit()函数初始化芯片(时钟、用到的外设等)。下面依次分析这3个函数。 ①wdog_disable()

对系统的设定无非是对各个寄存器值的修改。wdog_disable()函数在

wdog.c文件中

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.

/*wdog.c片段*/

void wdog_disable(void) {

/* First unlock the watchdog so that we can write to registers */ wdog_unlock();

/* Clear the WDOGEN bit to disable the watchdog */ WDOG_STCTRLH &= ~WDOG_STCTRLH_WDOGEN_MASK; }

void wdog_unlock(void) {

/* NOTE: DO NOT SINGLE STEP THROUGH THIS */

/* There are timing requirements for the execution of the unlock. If * you single step through the code you will cause the CPU to reset. */

18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32.

/* This sequence must execute within 20 clock cycles, so disable * interrupts will keep the code atomic and ensure the timing. */

DisableInterrupts;

/* Write 0xC520 to the unlock register */ WDOG_UNLOCK = 0xC520;

/* Followed by 0xD928 to complete the unlock */ WDOG_UNLOCK = 0xD928;

/* Re-enable interrupts now that we are done */ EnableInterrupts; }

禁用看门狗流程很简单:先是解锁寄存器,然后更改看门狗寄存器里面的值来禁用看门狗。解锁看门狗寄存器:向解锁寄存器里连续写入0xC520和0xD928,两次写入的时间必须小于20个时钟周期。所以在解锁过程中不能单步运行,期间也不能被中断打断,解锁函数是

wdog_unlock()。上面DisableInterrupts和EnableInterrupts已经在arm_cm4.h中定义过: #define DisableInterrupts asm(\ #define EnableInterrupts asm(\

解锁看门狗寄存器后,向看门狗寄存器里写入适当的值就可以禁用看门狗了。 也就是把WDOG_STCTRLH 寄存器(地址是0x40052000)的第0位置0.

②common_startup

初始化RAM(复制向量表、清零.bss段等,为C语言运行环境做准备)。

1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

*/

1 /* File: startup.c */ 2 #include \

3 #pragma section = \ 4 #pragma section = \ 5 #pragma section = \ 6 #pragma section = \ 7 #pragma section = \

8 /******************************************************************* 9 void

10 common_startup(void) 11 {

11. 12. 13. 14. 15.

12 /* Declare a counter we'll use in all of the copy loops */

16. 17. 18. 19. 20. 21. 22. 23. 24.

le */

25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38.

n) */

39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56.

13 uint32 n;

14 /* Declare pointers for various data sections. These pointers 15 * are initialized using values pulled in from the linker file 16 */

17 uint8 * data_ram, * data_rom, * data_rom_end; 18 uint8 * bss_start, * bss_end;

19 /* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker fi 20 extern uint32 __VECTOR_TABLE[]; 21 extern uint32 __VECTOR_RAM[];

22 /* Copy the vector table to RAM */ 23 if (__VECTOR_RAM != __VECTOR_TABLE) 24 {

25 for (n = 0; n < 0x410; n++)

26 __VECTOR_RAM[n] = __VECTOR_TABLE[n]; 27 }

28 /* Point the VTOR to the new copy of the vector table */ 29 write_vtor((uint32)__VECTOR_RAM);

30 /* Get the addresses for the .data section (initialized data sectio 31 data_ram = __section_begin(\); 32 data_rom = __section_begin(\); 33 data_rom_end = __section_end(\); 34 n = data_rom_end - data_rom;

35 /* Copy initialized data from ROM to RAM */ 36 while (n--)

37 *data_ram++ = *data_rom++;

38 /* Get the addresses for the .bss section (zero-initialized data) */ 39 bss_start = __section_begin(\); 40 bss_end = __section_end(\);

41 /* Clear the zero-initialized data section */ 42 n = bss_end - bss_start; 43 while(n--) 44 *bss_start++ = 0;

57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71.

45 /* Get addresses for any code sections that need to be copied from 46 * The IAR tools have a predefined keyword that can be used to mark 47 * functions for execution from RAM. Add \ before the retur 48 * the function prototype for any routines you need to execute from R 49 * of ROM. ex: __ramfunc void foo(void); 50 */

51 uint8* code_relocate_ram = __section_begin(\); 52 uint8* code_relocate = __section_begin(\); 53 uint8* code_relocate_end = __section_end(\);

ROM to RAM. individual n type in AM instead

54 /* Copy functions from ROM to RAM */ 55 n = code_relocate_end - code_relocate; 56 while (n--)

57 *code_relocate_ram++ = *code_relocate++; 58 }

在IAR中, #pragma section=\ 用来在C语言中指定一个名称是

NAME的段,align指定对齐方式。指定的段可以被段操作符来引用,段操作符包括 __section_begin, __section_end, 和 __section_size. 个人理

解.date、.date_init和.bss应该是IAR中保留的段名称,.date代表数据段中的常量,.date_init代表数据段中已初始化的变量,.bss代表未初始化的变量(zero)。

上面代码中,先是指定了5个不同名称的段(前3个是保留段名称,代表这些段是从这里开始的),CodeRelocate和CodeRelocateRam是在*.icf文件中定义的块(block): define block CodeRelocate { section .textrw_init }; define block CodeRelocateRam { section .textrw }; quote:

The __ramfunc keyword makes a function execute in RAM. Two code

sections will be created: one for the RAM execution (.textrw), and one for the ROM initialization (.textrw_init).

外部变量引用

extern uint32 __VECTOR_TABLE[]; extern uint32 __VECTOR_RAM[]; 来自IAR的链接文件(.icf),在.icf文件中已经定义了变量

__VECTOR_TABLE 和 __VECTOR_RAM 其值都是0x1fff0000.\RAM\这段代码进行判断,如果向量表不在RAM中,则把向量表拷贝到RAM开始的地址上,这里由于在RAM中调试,代码是直接下载到RAM中的,所以不用拷贝。

向量表已经在RAM中了,接下来要重定向向量表,以便在发生异常时到RAM中取得异常入口地址(默认情况下是在0x0取)。 write_vtor((uint32)__VECTOR_RAM) 这个函数用来写向量表偏移寄存器(VTOR,地址0xE000_ED08),这里写入的是RAM起始地址0x1FFF0000。注意这个地址是有要求的,并不是所有地址都能作为向量表起始地址,0x1FFF0000满足要求(这个要求就是:必须先求出系统中共有多少个向量,再把这个数字向上增大到是 2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有 32 个中断,则共有 32+16(系统异常)=48个向量,向上增大到 2的整次幂后值为 64,因此地址地址必须能被 64*4=256整除,从而合法的起始地址可以是:0x0,0x100,0x200 等----参见ARM Contex-M3权威指南)。另外,如果向量表在RAM区(相对于code区),需把bit[29]置位,这里0x1FFF0000也满足要求。

后面的代码是拷贝数据到RAM中,搭建好C语言运行环境。

③sysinit()函数

上次写了飞思卡尔官方给出的demo程序的启动流程的前面部分:

在存储器最前面放置好向量表-->把通用寄存器清零-->开中断-->跳转到start函数继续执行初始化。在start函数中,顺次执行三个函数:禁用看门狗-->初始化C语言环境(向量表重定向、拷贝数据段到RAM、清零bss段等)-->系统外设初始化。

③系统外设初始化函数 sysinit() 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.

#include \ #include \ #include \

/********************************************************************/

/* Actual system clock frequency */ int core_clk_khz; int core_clk_mhz; int periph_clk_khz;

/********************************************************************/

13. 14. 15. 16.

igure

void sysinit (void) {

/*

* Enable all of the port clocks. These have to be enabled to conf * pin muxing options, so most code will need all of these on anyw */

SIM_SCGC5 |= (SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK );

17.

ay.

18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29.

iables

/* Ramp up the system clock */

core_clk_mhz = pll_init(CORE_CLK_MHZ, REF_CLK);

/*

* Use the value obtained from the pll_init function to define var * for the core clock in kHz and also the peripheral clock. These * variables can be used by other functions that need awareness of the * system frequency. */

core_clk_khz = core_clk_mhz * 1000;

periph_clk_khz = core_clk_khz / (((SIM_CLKDIV1 & SIM_CLKDIV1_OUTDIV2_

30. 31. 32. 33. 34. 35. 36. 37.

at

MASK) >> 24)+ 1);

/* For debugging purposes, enable the trace clock and/or FB_CLK so th * we'll be able to monitor clocks and know the PLL is at the freque * that we expect. */

trace_clk_init(); fb_clk_init();

38.

ncy

39. 40. 41. 42. 43. 44. 45. 46. 47. 48.

is pin

/* Enable the pins for the selected UART */ if (TERM_PORT == UART0_BASE_PTR) {

/* Enable the UART0_TXD function on PTD6 */

PORTD_PCR6 = PORT_PCR_MUX(0x3); // UART is alt3 function for th

49.

50. 51.

is pin

/* Enable the UART0_RXD function on PTD7 */

PORTD_PCR7 = PORT_PCR_MUX(0x3); // UART is alt3 function for th }

52. 53. 54. 55. 56. 57.

if (TERM_PORT == UART1_BASE_PTR) {

/* Enable the UART1_TXD function on PTC4 */

PORTC_PCR4 = PORT_PCR_MUX(0x3); // UART is alt3 function for this

pin

58.

59. 60.

PORTC_PCR3 pin

61. 62.

63. 64. 65. 66.

PORTD_PCR3 pin

67.

68. 69.

PORTD_PCR2 pin

70. 71.

72. 73. 74. 75.

PORTC_PCR17 s pin

76.

77. 78.

PORTC_PCR16 s pin

79. 80. 81. 82. 83.

PORTE_PCR24 s pin

84.

85.

/* Enable the UART1_RXD function on PTC3 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for this}

if (TERM_PORT == UART2_BASE_PTR) {

/* Enable the UART2_TXD function on PTD3 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for this

/* Enable the UART2_RXD function on PTD2 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for this}

if (TERM_PORT == UART3_BASE_PTR) {

/* Enable the UART3_TXD function on PTC17 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for thi

/* Enable the UART3_RXD function on PTC16 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for thi}

if (TERM_PORT == UART4_BASE_PTR) {

/* Enable the UART3_TXD function on PTC17 */

= PORT_PCR_MUX(0x3); // UART is alt3 function for thi

/* Enable the UART3_RXD function on PTC16 */

86.

s pin

PORTE_PCR25 = PORT_PCR_MUX(0x3); // UART is alt3 function for thi }

if (TERM_PORT == UART5_BASE_PTR) {

/* Enable the UART3_TXD function on PTC17 */

PORTE_PCR8 = PORT_PCR_MUX(0x3); // UART is alt3 function for this

87. 88. 89. 90. 91.

pin

92. 93. 94.

pin

/* Enable the UART3_RXD function on PTC16 */

PORTE_PCR9 = PORT_PCR_MUX(0x3); // UART is alt3 function for this }

/* UART0 and UART1 are clocked from the core clock, but all other UA * clocked from the peripheral clock. So we have to determine whic * to send to the uart_init function. */

if ((TERM_PORT == UART0_BASE_PTR) | (TERM_PORT == UART1_BASE_PTR)) uart_init (TERM_PORT, core_clk_khz, TERMINAL_BAUD); else

uart_init (TERM_PORT, periph_clk_khz, TERMINAL_BAUD); }

/********************************************************************/ void trace_clk_init(void) {

/* Set the trace clock to the core clock frequency */ SIM_SOPT2 |= SIM_SOPT2_TRACECLKSEL_MASK;

95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123.

RTs are h clock

/* Enable the TRACE_CLKOUT pin function on PTA6 (alt7 function) */ PORTA_PCR6 = ( PORT_PCR_MUX(0x7)); }

/********************************************************************/ void fb_clk_init(void) {

/* Enable the clock to the FlexBus module */ SIM_SCGC7 |= SIM_SCGC7_FLEXBUS_MASK;

/* Enable the FB_CLKOUT function on PTC3 (alt5 function) */ PORTC_PCR3 = ( PORT_PCR_MUX(0x5)); }

/********************************************************************/

首先写SIM_SCGC5寄存器。SCGC5表示的是System Clock Gating Control Register 5(系统时钟门控制寄存器5).系统集成模块 --System integration module (SIM)--控制寄存器组中共有8个SCGC寄存器,它们分别控制不同外设所需要的时钟的开关,SCGC5寄存器控制的是PORTA~PORTE、TSI、REGFILE和LPTIMER时钟的开关,向对应的位写入1表示使能时钟。第一段代码分别打开了PORTA~PORTE的时钟开关。

上面pll_init(unsigned char,unsigned char)函数用来增加系统时钟。 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27.

ialize

unsigned char pll_init(unsigned char clk_option, unsigned char crystal_val) {

unsigned char pll_freq;

if (clk_option > 3) {return 0;} //return 0 if one of the available option

s is not selected

if (crystal_val > 15) {return 1;} // return 1 if one of the available cry

stal options is not available

//This assumes that the MCG is in default FEI mode out of reset.

// First move to FBE mode

#if (defined(K60_CLK) || defined(ASB817)) MCG_C2 = 0; #else

// Enable external oscillator, RANGE=2, HGO=1, EREFS=1, LP=0, IRCS=0 MCG_C2 = MCG_C2_RANGE(2) | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK; #endif

// after initialization of oscillator release latched state of oscillato SIM_SCGC4 |= SIM_SCGC4_LLWU_MASK; LLWU_CS |= LLWU_CS_ACKISO_MASK;

// Select external oscilator and Reference Divider and clear IREFS to start// CLKS=2, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0 MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);

r and GPIO

ext osc

/* if we aren't using an osc input we don't need to wait for the osc t#if (!defined(K60_CLK) && !defined(ASB817))

while (!(MCG_S & MCG_S_OSCINIT_MASK)){}; // wait for oscillator to init#endif

o init */

28. 29.

30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59.

/4

while (MCG_S & MCG_S_IREFST_MASK){}; // wait for Reference clock Status b

it to clear

while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}; // Wa

it for clock status bits to show clock source is ext ref clk

// Now in FBE

#if (defined(K60_CLK))

MCG_C5 = MCG_C5_PRDIV(0x18); #else

// Configure PLL Ref Divider, PLLCLKEN=0, PLLSTEN=0, PRDIV=5

// The crystal frequency is used to select the PRDIV value. Only even frequ// that will produce a 2MHz reference clock to the PLL.

MCG_C5 = MCG_C5_PRDIV(crystal_val); // Set PLL ref divider to match the c#endif

ency crystals are supported

rystal used

// Ensure MCG_C6 is at the reset default of 0. LOLIE disabled, PLL disab MCG_C6 = 0x0;

// Select the PLL VCO divider and system clock dividers depending on clocki switch (clk_option) { case 0:

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG, FlexBus = MCG, Flash clock= MCG/2 set_sys_dividers(0,0,0,1);

// Set the VCO divider and enable the PLL for 50MHz, LOLIE=0, PLLS= MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(1); //VDIV = 1 (x25) pll_freq = 50; break; case 1:

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG set_sys_dividers(0,1,1,3);

// Set the VCO divider and enable the PLL for 100MHz, LOLIE=0, PLLS= MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(26); //VDIV = 26 (x50) pll_freq = 100; break;

led, clk monitor disabled, PLL VCO divider is clear

ng option

1, CME=0, VDIV=1

60. 61. 62. 63. 64.

1, CME=0, VDIV=26

65. 66. 67.

/4

case 2:

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG set_sys_dividers(0,1,1,3);

// Set the VCO divider and enable the PLL for 96MHz, LOLIE=0, PLLS= MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(24); //VDIV = 24 (x48) pll_freq = 96; break; case 3:

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG, FlexBus = MCG, Flash clock= MCG/2 set_sys_dividers(0,0,0,1);

// Set the VCO divider and enable the PLL for 48MHz, LOLIE=0, PLLS= MCG_C6 = MCG_C6_PLLS_MASK; //VDIV = 0 (x24) pll_freq = 48; break; }

while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98.

1, CME=0, VDIV=24

1, CME=0, VDIV=0

while (!(MCG_S & MCG_S_LOCK_MASK)){}; // Wait for LOCK bit to set

// Now running PBE Mode

// Transition into PEE by setting CLKS to 0 // CLKS=0, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0 MCG_C1 &= ~MCG_C1_CLKS_MASK;

// Wait for clock status bits to update

while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){};

// Now running PEE Mode

return pll_freq; } //pll_init

这个函数中两个参数clk_option和crystal_val是两个枚举变量(enum),从它们的定义来看它们的取值范围是0~3和0~15.所以函数开始先进行有效性判断。

MCG表示的是 Multipurpose Clock Generator(多用途时钟发生器).MCG模块向MCU提供几个时钟源选择。这个模块包含了一

个 frequency-locked loop (FLL)和一个 phase-locked loop (PLL).FLL可以通过

内部的或外部的参考时钟源来控制,PLL可以通过外部参考时钟来控制。模块可以选择FLL或PLL的输出时钟或者内部或外部时钟源作为MCU的系统时钟。上面MCG_2指的是 MCG Control 2 Register. MCG一共有9种操作模式:FEI, FEE, FBI, FBE, PBE, PEE, BLPI,BLPE,and Stop. 不同运行模式的功耗互不相同,要进入各个模式需要写MCG_1~MCG_6寄存器组中某几个寄存器中的某几个位。 我们可以这样说,采用内部组件的模式所消耗的功率少于采用外部组件的模式。而MCG包括两种专门为低功率应用设计的模式——旁通低功耗内部(BLPI)和旁通低功耗外部(BLPE)。驱动BLPI模式的总线频率要比BLPE 模式低,因此BLPI 模式消耗的功率最小。下图2总结了每一种运行模式的功耗。

在sysinit()函数中对pll_init(unsigned char,unsigned char)函数的引用包含两个参数:CORE_CLK_MHZ和REF_CLK,从其定义可以看出其值分别为PLL96和XTAL8,分别代表2和3.K60_CLK 被定义为1.因此,在引用pll_init(unsigned char,unsigned char)函数时根据条件编译,执行的语句依次是: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.

// First move to FBE mode MCG_C2 = 0;

// after initialization of oscillator release latched state of oscillato

r and GPIO

SIM_SCGC4 |= SIM_SCGC4_LLWU_MASK; LLWU_CS |= LLWU_CS_ACKISO_MASK;

// Select external oscilator and Reference Divider and clear IREFS to start

ext osc

// CLKS=2, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0 MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);

// wait for Reference clock Status bit to clear while (MCG_S & MCG_S_IREFST_MASK){};

// Wait for clock status bits to show clock source is ext ref clk while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){};

MCG_C5 = MCG_C5_PRDIV(0x18);

/* Ensure MCG_C6 is at the reset default of 0. LOLIE disabled, PLL disable MCG_C6 = 0x0;

d, clk monitor disabled, PLL VCO divider is clear*/

23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45.

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG/4 set_sys_dividers(0,1,1,3);

// Set the VCO divider and enable the PLL for 96MHz, LOLIE=0, PLLS=1, CME= MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(24); //VDIV = 24 (x48) pll_freq = 96;

0, VDIV=24

while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

while (!(MCG_S & MCG_S_LOCK_MASK)){}; // Wait for LOCK bit to set

// Now running PBE Mode

// Transition into PEE by setting CLKS to 0 // CLKS=0, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0 MCG_C1 &= ~MCG_C1_CLKS_MASK;

// Wait for clock status bits to update

while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){};

// Now running PEE Mode

return pll_freq;

最后,系统运行在PEE模式下。

23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45.

// Set system options dividers

//MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG/4 set_sys_dividers(0,1,1,3);

// Set the VCO divider and enable the PLL for 96MHz, LOLIE=0, PLLS=1, CME= MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(24); //VDIV = 24 (x48) pll_freq = 96;

0, VDIV=24

while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

while (!(MCG_S & MCG_S_LOCK_MASK)){}; // Wait for LOCK bit to set

// Now running PBE Mode

// Transition into PEE by setting CLKS to 0 // CLKS=0, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0 MCG_C1 &= ~MCG_C1_CLKS_MASK;

// Wait for clock status bits to update

while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){};

// Now running PEE Mode

return pll_freq;

最后,系统运行在PEE模式下。

本文来源:https://www.bwwdw.com/article/brg6.html

Top