查看: 5392|回复: 1

时钟和定时器

[复制链接]
  • TA的每日心情
    郁闷
    2013-1-24 12:15
  • 签到天数: 9 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2013-1-19 16:02:41 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 妈妈跟我说名字 于 2013-1-19 16:04 编辑

    Clock Generator 时钟发生器
    时钟发生器
    PTW7X$J}PTIB[651YC1}Q9T.jpg
    由图可以看出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位。
    通过得到的时钟可以为外设以及各种模块提供时钟源。如下图
    6X%IRJA`NK(3KV_QWM@LD.jpg
    MCK给所有外围设备提供时钟。MCK的时钟源可以通过配置PMC_MCKR寄存器的CSS区域获得。
    [4}22@SC_SRCDB3UODOLNGV.jpg
    SysTick Clock定时器时钟;

    以上是看着官方datasheet摘录下来的一些比较重要的内容。下面来分析下例子。
    如下图建立一样的例子。
    FWZJ9HEK`7VA9IN@Y(CUBMH.jpg
    阅读下文件的说明可以得到一些信息。
    ~ZN%J2UH2RJOI7FXHZN9H]D.jpg
    这个例子是展示怎样初始化时钟以及让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结构括号里面的参数。
    如图找到定义处
    VJM%L]IYXRS6Q(@K9MC29]R.jpg
    在时钟配置文件里面可以看到下面预定义
    #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结构你面完成的是这段代码。
    可以知道本例子系统使用的时钟源是外部时钟源,
    [JI2$DBC~05C))W43L1UUDS.jpg
    图中可以看出,晶振的频率为12Mhz。
    由代码
    #define SYSCLK_SRC_PLLACK                       8       //!< Use PLLACK as master source clock
    可以知道,MCK的时钟源为PLLACK。
    然后看到pll_config_defaults(&pllcfg, 0);通过分析可以知道CKGR_PLLAR的DIVA区域被设置为0;
    我在datasheet找到下面的描述
    2MDVXNDSJ_H%7Y`0)V0IE.jpg
    这句话不知道大家怎么理解,我的理解为就是分频器没有被激活,以原频率输出,也就是说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 */
    }
    这个函数是完成定时器配置的,参数是用来设置定时器的值,函数完成了定时器中断的配置,还有定时器时钟的选择,选择的时钟为系统时钟。
    T8C27XGJ{0WT[5MU`E(%T9R.jpg
    所以,完成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

    回复

    使用道具 举报

  • TA的每日心情
    郁闷
    2013-1-24 12:15
  • 签到天数: 9 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2013-1-19 16:11:16 | 显示全部楼层
    以前很讨厌看外文文献,现在越来越喜欢了,大家有没有这种感觉???
    今天把电脑搬到窗台,由于我的无线网是限时的,所以很省着用,不需要查资料或者发帖的时候就断掉,下午研究程序的时候突然自动连上了一个不认识的无线网,很开心很激动啊有木有,虽然有点慢,但是上qq,看下网页还是可以的啊,哈哈,感谢那位网不设密码的好人,我也会替你节约点用的,哈哈哈。
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

    手机版|小黑屋|与非网

    GMT+8, 2024-11-19 12:32 , Processed in 0.125420 second(s), 18 queries , MemCache On.

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

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.