本帖最后由 妈妈跟我说名字 于 2013-1-19 16:04 编辑
Clock Generator 时钟发生器 时钟发生器
由图可以看出SAM4S的几种时钟源。内部有一个嵌入的32khz的时钟源,有一个32768hz的晶振。还有一个可以选择的4/8/12Mhz的内部时钟源,以及一个外接3~20Mhz的时钟源。 通过时钟发生器可以产生4个时钟,分别为低速时钟,主时钟,PLLA和PLLB。 低速时钟:是唯一不变的系统内部时钟。,低速时钟可以由两种方式产生,由图中可以看出,通过对XTALSE位的配置可以选择需要的时钟源。32768HZ的时钟必须外接32768hz的晶振以及外部匹配电容。 主时钟:主时钟有两个时钟源,可以自己配置。 当复位的时候4/8/12MHZ会选择4mhz作为主时钟的时钟源。主时钟是唤醒系统的默认时钟。4m,8m,12m,可以由用户自己选择,通过设置CKGR_MOR寄存器的MOSCRCF位,当改变时钟源频率的时候(PMC_SR寄存器的MOSCRCS位会被清零),MAINCK会停止,直到时钟源稳定(MOSCRCS置1)。 当复位的时候3 to 20 MHz是被禁止的 用户可以选择主时钟的时钟源通过设置MOSCSEL 寄存器的MOSCSEL位。 通过得到的时钟可以为外设以及各种模块提供时钟源。如下图 MCK:给所有外围设备提供时钟。MCK的时钟源可以通过配置PMC_MCKR寄存器的CSS区域获得。
SysTick Clock:定时器时钟;
以上是看着官方datasheet摘录下来的一些比较重要的内容。下面来分析下例子。 如下图建立一样的例子。 阅读下文件的说明可以得到一些信息。 这个例子是展示怎样初始化时钟以及让LED以1hz的频率闪烁。 有三个重要的文件,一个是例子的主程序,一个是板子的配置,一个是时钟的配置。
clock_example1_sam.c文件里面有三个函数 static void mdelay(uint32_t ul_dly_ticks),ms延时函数。 void SysTick_Handler(void) 定时器中断函数(这个函数很重要!!!) int main(void) 主函数
延时函数和定时器中断函数都不叫简单,这里就不分析了,分析下main函数里面的一些东西。 int main(void) { sysclk_init(); board_init();
/* Setup SysTick Timer for 1 msec interrupts */ if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) { while (1) { /* Capture error */ } }
while (1) { #if (SAM4L) ioport_toggle_pin_level(LED0_GPIO); #else gpio_toggle_pin(LED0_GPIO); #endif mdelay(500); } } 首先可以看到有两个初始化函数,sysclk_init();board_init();可以找到这两个函数的原型。 首先看board_init();函数,这个主要是完成一些引脚的配置,以及对看门狗定时器的关闭,这个大家有兴趣可以自己看,本文主要讲时钟,这个函数就不深入分析了。 然后看sysclk_init();函数。 void sysclk_init(void) { struct pll_config pllcfg;
/* Set a flash wait state depending on the new cpu frequency */ system_init_flash(sysclk_get_cpu_hz());
/* Config system clock setting */ switch (CONFIG_SYSCLK_SOURCE) { case SYSCLK_SRC_SLCK_RC: osc_enable(OSC_SLCK_32K_RC); osc_wait_ready(OSC_SLCK_32K_RC); pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_SLCK_XTAL: osc_enable(OSC_SLCK_32K_XTAL); osc_wait_ready(OSC_SLCK_32K_XTAL); pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_SLCK_BYPASS: osc_enable(OSC_SLCK_32K_BYPASS); osc_wait_ready(OSC_SLCK_32K_BYPASS); pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_MAINCK_4M_RC: /* Already running from SYSCLK_SRC_MAINCK_4M_RC */ break;
case SYSCLK_SRC_MAINCK_8M_RC: osc_enable(OSC_MAINCK_8M_RC); osc_wait_ready(OSC_MAINCK_8M_RC); pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_MAINCK_12M_RC: osc_enable(OSC_MAINCK_12M_RC); osc_wait_ready(OSC_MAINCK_12M_RC); pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_MAINCK_XTAL: osc_enable(OSC_MAINCK_XTAL); osc_wait_ready(OSC_MAINCK_XTAL); pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES); break;
case SYSCLK_SRC_MAINCK_BYPASS: osc_enable(OSC_MAINCK_BYPASS); osc_wait_ready(OSC_MAINCK_BYPASS); pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES); break;
#ifdef CONFIG_PLL0_SOURCE case SYSCLK_SRC_PLLACK: pll_enable_source(CONFIG_PLL0_SOURCE); pll_config_defaults(&pllcfg, 0); pll_enable(&pllcfg, 0); pll_wait_for_lock(0); pmc_switch_mck_to_pllack(CONFIG_SYSCLK_PRES); break; #endif
#ifdef CONFIG_PLL1_SOURCE case SYSCLK_SRC_PLLBCK: pll_enable_source(CONFIG_PLL1_SOURCE); pll_config_defaults(&pllcfg, 1); pll_enable(&pllcfg, 1); pll_wait_for_lock(1); pmc_switch_mck_to_pllbck(CONFIG_SYSCLK_PRES); break; #endif }
/* Update the SystemFrequency variable */ SystemCoreClockUpdate();
#if (defined CONFIG_SYSCLK_DEFAULT_RETURNS_SLOW_OSC) /* Signal that the internal frequencies are setup */ sysclk_initialized = 1; #endif } 可以看到这个函数是对时钟的一些配置,我们看到switch结构括号里面的参数。 如图找到定义处
在时钟配置文件里面可以看到下面预定义 #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLLACK
以及下面的 #define CONFIG_PLL0_SOURCE PLL_SRC_MAINCK_XTAL
我们可以看到switch结构里面的这段代码 #ifdef CONFIG_PLL0_SOURCE case SYSCLK_SRC_PLLACK: pll_enable_source(CONFIG_PLL0_SOURCE); pll_config_defaults(&pllcfg, 0); pll_enable(&pllcfg, 0); pll_wait_for_lock(0); pmc_switch_mck_to_pllack(CONFIG_SYSCLK_PRES); break; #endif 可以知道在switch结构你面完成的是这段代码。 可以知道本例子系统使用的时钟源是外部时钟源, 图中可以看出,晶振的频率为12Mhz。 由代码 #define SYSCLK_SRC_PLLACK 8 //!< Use PLLACK as master source clock 可以知道,MCK的时钟源为PLLACK。 然后看到pll_config_defaults(&pllcfg, 0);通过分析可以知道CKGR_PLLAR的DIVA区域被设置为0; 我在datasheet找到下面的描述 这句话不知道大家怎么理解,我的理解为就是分频器没有被激活,以原频率输出,也就是说PLLA LCK的频率等于外部晶振的频率(12m);
然后是下面这句 pmc_switch_mck_to_pllack(CONFIG_SYSCLK_PRES); 以及 #define CONFIG_SYSCLK_PRES SYSCLK_PRES_2 可知道预分频器设置为2分频。
好了 现在可以总结下时钟的初始化了,MCK的时钟源为PLLACK,预分频为2,PLLACK使用的是外部时钟源,也就是12Mhz,没有预分频。所以MCK = 12M/2=6Mhz
接着可以看到 __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ SysTick->VAL = 0; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */ } 这个函数是完成定时器配置的,参数是用来设置定时器的值,函数完成了定时器中断的配置,还有定时器时钟的选择,选择的时钟为系统时钟。
所以,完成1ms定时定时器里面装的值应该为系统时钟 /1000 很明显main函数里面sysclk_get_cpu_hz()函数是用来得到系统时钟的。这样就完成了1ms的定时。 然后调用gpio_toggle_pin(LED0_GPIO);函数,这个函数是完成电平翻转的函数,大家应该很熟悉了。 接着延时500ms 这样就完成了LED1hz闪烁的功能。
我在这里有个疑问了,根据前面算出来的系统时钟的频率为6mhz,那么6m/1000=6000。但是把6000直接作为参数带入进去的话得到的明显不是1hz的闪烁。 程序如下: int main(void) { sysclk_init(); board_init();
/* Setup SysTick Timer for 1 msec interrupts */ if (SysTick_Config(6000)) { while (1) { /* Capture error */ } }
while (1) { #if (SAM4L) ioport_toggle_pin_level(LED0_GPIO); #else gpio_toggle_pin(LED0_GPIO); #endif mdelay(500); } }
根据实验,娶96000的时候正好又是1hz的频率,难道我前面的分析错了??
检查了好久,分析了好久,觉得没有错啊,希望高手看到了能指点一二。。。
|