1赞

1回答

0收藏

时钟和定时器

其他 其他 5818 人阅读 | 1 人回复 | 2013-01-19

本帖最后由 妈妈跟我说名字 于 2013-1-19 16:04 编辑

Clock Generator 时钟发生器
时钟发生器

由图可以看出SAM4S的几种时钟源。内部有一个嵌入的32khz的时钟源,有一个32768hz的晶振。还有一个可以选择的4/8/12Mhz的内部时钟源,以及一个外接3~20Mhz的时钟源。
通过时钟发生器可以产生4个时钟,分别为低速时钟,主时钟,PLLAPLLB
低速时钟:是唯一不变的系统内部时钟。,低速时钟可以由两种方式产生,由图中可以看出,通过对XTALSE位的配置可以选择需要的时钟源。32768HZ的时钟必须外接32768hz的晶振以及外部匹配电容。
主时钟:主时钟有两个时钟源,可以自己配置。
              当复位的时候4/8/12MHZ会选择4mhz作为主时钟的时钟源。主时钟是唤醒系统的默认时钟。4m8m12m,可以由用户自己选择,通过设置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的频率,难道我前面的分析错了??

检查了好久,分析了好久,觉得没有错啊,希望高手看到了能指点一二。。。


时钟和定时器.doc

316 KB, 下载次数: 25

分享到:
回复

使用道具 举报

回答|共 1 个

倒序浏览

沙发

w453114735

发表于 2013-1-19 16:11:16 | 只看该作者

以前很讨厌看外文文献,现在越来越喜欢了,大家有没有这种感觉???
今天把电脑搬到窗台,由于我的无线网是限时的,所以很省着用,不需要查资料或者发帖的时候就断掉,下午研究程序的时候突然自动连上了一个不认识的无线网,很开心很激动啊有木有,虽然有点慢,但是上qq,看下网页还是可以的啊,哈哈,感谢那位网不设密码的好人,我也会替你节约点用的,哈哈哈。
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

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