SAMD21时钟配置

Posted

技术标签:

【中文标题】SAMD21时钟配置【英文标题】:SAMD21 clock configuration 【发布时间】:2017-02-03 13:41:14 【问题描述】:

我正在尝试将我的 SAMD21 时钟配置为尽可能快。所以我使用内部 8 MHz 振荡器为通用时钟发生器 1(预分频器为 8)提供通用时钟,以生成一个通用时钟来为数字锁相环提供信号,而数字锁相环又为通用时钟发生器 0(我的主时钟)提供信号应该给 CPU 计时,但是 micro 运行速度很慢,我哪里出错了? ​我遵循了这个指南http://borkedlabs.com/2014/08/21/asf-samd21-dpll-for-internal-clock-from-internal-8mhz/​,但它不起作用。这是我的代码:

void system_clock_init(void)

    SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;

    /* switch off all peripheral clocks to save power */
    //_switch_peripheral_gclk();

    /* configure and enable generic clock generator 1 (GENCTRL and GENDIV registers of GCLK module) */
    struct system_gclk_gen_config gclk_gen_config1;
    system_gclk_gen_get_config_defaults(&gclk_gen_config1);
    gclk_gen_config1.source_clock = SYSTEM_CLOCK_SOURCE_OSC8M;
    gclk_gen_config1.division_factor = 8;
    gclk_gen_config1.output_enable = false;
    system_gclk_gen_set_config(GCLK_GENERATOR_1,&gclk_gen_config1);
    system_gclk_gen_enable(GCLK_GENERATOR_1);

    /* configure and enable generic clock for DPLL (CLKCTRL of GCLK module) */
    struct system_gclk_chan_config gclk_chan_config;
    system_gclk_chan_get_config_defaults(&gclk_chan_config);
    gclk_chan_config.source_generator = GCLK_GENERATOR_1;
    system_gclk_chan_set_config(SYSCTRL_GCLK_ID_FDPLL,&gclk_chan_config);
    system_gclk_chan_enable(SYSCTRL_GCLK_ID_FDPLL);

    /* configure and enable clock source: DPLL (SYSCTRL registers) */
    struct system_clock_source_dpll_config dpll_config;
    system_clock_source_dpll_get_config_defaults(&dpll_config);
    dpll_config.reference_clock = SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_GCLK;
    dpll_config.reference_divider = 1;
    dpll_config.reference_frequency = 1000000;
    dpll_config.output_frequency = 30000000;
    system_clock_source_dpll_set_config(&dpll_config);
    system_clock_source_enable(SYSTEM_CLOCK_SOURCE_DPLL);

    /* set NVM wait states */
    system_flash_set_waitstates(2);

    /* configure and enable generic clock 0 (GCLK_MAIN) */
    struct system_gclk_gen_config gclk_gen_config0;
    system_gclk_gen_get_config_defaults(&gclk_gen_config0);
    gclk_gen_config0.source_clock = SYSTEM_CLOCK_SOURCE_DPLL;
    gclk_gen_config0.division_factor = 1;
    system_gclk_gen_set_config(GCLK_GENERATOR_0,&gclk_gen_config0);
    system_gclk_gen_enable(GCLK_GENERATOR_0);

我更新了 conf_clocks.h 标头以反映更改(我不知道这些宏是否在其他地方被引用,以防万一)并且我更改了从 system_init() 调用的 system_clock_init() 函数。

【问题讨论】:

【参考方案1】:

我从来都不喜欢使用 Atmel 的 ASF,因为您从来没有真正只做自己想做的事。 我建议您更多地查看数据表,因为与潜伏在 ASF 中相比,完成任务所需的时间更少。 Atmel 数据表甚至有一个“初始化”一章,它一步一步地解释了要做什么。

启用 OSC8 基本上是 5 行代码:

/* Various bits in the INTFLAG register can be set to one at startup.
This will ensure that these bits are cleared */
SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;

/* OSC8M Internal 8MHz Oscillator */
SYSCTRL->OSC8M.bit.PRESC = SYSTEM_OSC8M_DIV_1;
SYSCTRL->OSC8M.bit.ONDEMAND = CONF_CLOCK_OSC8M_ON_DEMAND;
SYSCTRL->OSC8M.bit.RUNSTDBY = CONF_CLOCK_OSC8M_RUN_IN_STANDBY;

/* Enable OSC8M */
SYSCTRL->OSC8M.reg |= SYSCTRL_OSC8M_ENABLE;

剩下的步骤只是使用已经启用的 OSC8M 来配置 dpll 寄存器,也只有几行代码。 (写入 GCLK.GENDIV/GENCTRL 和 CLKCTRL 寄存器以及写入 SYSCTRL.DPLL 寄存器。

【讨论】:

【参考方案2】:

为了将 SAMD21 CPU 配置为其支持的最大频率 (48 MHz),我不使用 ASF,而是使用从 Arduino SAMD 内核获取的代码;您可以在https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/startup.c 的 SystemInit() 函数中找到它的作用。引用该文件中的 cmets,在 SystemInit() 中完成的相关步骤是:

启用 XOSC32K 时钟(外部板载 32.768Hz 振荡器),将用作 DFLL48M 参考 将 XOSC32K 作为通用时钟发生器 1 的源 将通用时钟发生器 1 作为通用时钟多路复用器 0 的源(DFLL48M 参考) 启用 DFLL48M 时钟 将通用时钟发生器 0 切换到 DFLL48M。 CPU 将以 48MHz 运行

相关代码行(假设您的电路板安装了一个外部 32.768 kHz 晶振)是:

/* Set 1 Flash Wait State for 48MHz, cf tables 20.9 and 35.27 in SAMD21 Datasheet */
NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val ;

/* Turn on the digital interface clock */
PM->APBAMASK.reg |= PM_APBAMASK_GCLK ;

/* Enable XOSC32K clock (External on-board 32.768Hz oscillator) */
SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP( 0x6u ) | /* cf table 15.10 of product datasheet in chapter 15.8.6 */
                       SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K ;
SYSCTRL->XOSC32K.bit.ENABLE = 1 ; /* separate call, as described in chapter 15.6.3 */
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY) == 0 )

  /* Wait for oscillator stabilization */


/* Software reset the module to ensure it is re-initialized correctly */
GCLK->CTRL.reg = GCLK_CTRL_SWRST ;
while ( (GCLK->CTRL.reg & GCLK_CTRL_SWRST) && (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) )

  /* Wait for reset to complete */


/* Put XOSC32K as source of Generic Clock Generator 1 */
GCLK->GENDIV.reg = GCLK_GENDIV_ID( GENERIC_CLOCK_GENERATOR_XOSC32K ) ; // Generic Clock Generator 1
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )

  /* Wait for synchronization */


/* Write Generic Clock Generator 1 configuration */
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID( GENERIC_CLOCK_GENERATOR_OSC32K ) | // Generic Clock Generator 1
                    GCLK_GENCTRL_SRC_XOSC32K | // Selected source is External 32KHz Oscillator
//                  GCLK_GENCTRL_OE | // Output clock to a pin for tests
                    GCLK_GENCTRL_GENEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )

  /* Wait for synchronization */


/* Put Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference) */
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( GENERIC_CLOCK_MULTIPLEXER_DFLL48M ) | // Generic Clock Multiplexer 0
                  GCLK_CLKCTRL_GEN_GCLK1 | // Generic Clock Generator 1 is source
                  GCLK_CLKCTRL_CLKEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )

  /* Wait for synchronization */


/* Enable DFLL48M clock */  
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )

  /* Wait for synchronization */

SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP( 31 ) | // Coarse step is 31, half of the max value
                       SYSCTRL_DFLLMUL_FSTEP( 511 ) | // Fine step is 511, half of the max value
                       SYSCTRL_DFLLMUL_MUL( (VARIANT_MCK + VARIANT_MAINOSC/2) / VARIANT_MAINOSC ) ; // External 32KHz is the reference
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )

  /* Wait for synchronization */


/* Write full configuration to DFLL control register */
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_MODE | /* Enable the closed loop mode */
                         SYSCTRL_DFLLCTRL_WAITLOCK |
                         SYSCTRL_DFLLCTRL_QLDIS ; /* Disable Quick lock */
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )

  /* Wait for synchronization */


/* Enable the DFLL */
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_ENABLE ;
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) == 0 ||
        (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF) == 0 )

  /* Wait for locks flags */

while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )

  /* Wait for synchronization */


/* Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz. */
GCLK->GENDIV.reg = GCLK_GENDIV_ID( GENERIC_CLOCK_GENERATOR_MAIN ) ; // Generic Clock Generator 0
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )

  /* Wait for synchronization */


/* Write Generic Clock Generator 0 configuration */
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID( GENERIC_CLOCK_GENERATOR_MAIN ) | // Generic Clock Generator 0
                    GCLK_GENCTRL_SRC_DFLL48M | // Selected source is DFLL 48MHz
//                  GCLK_GENCTRL_OE | // Output clock to a pin for tests
                    GCLK_GENCTRL_IDC | // Set 50/50 duty cycle
                    GCLK_GENCTRL_GENEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )

  /* Wait for synchronization */

【讨论】:

请注意,XOSC32K 在通电后可能需要很长时间才能稳定 - 最多一秒钟。 DFLL48M 也需要时间来锁定,但远不及 32K。 (请参阅器件数据表末尾附近的振荡器时序特性部分。)因此,在检查 SYSCTRL_PCLKSR_XOSC32KRDY 之前启动 XOSC32K,然后关闭并执行其他初始化任务(例如 C 运行时内存初始化)可能是有意义的。 CPU 在复位后从 OSC8M 运行,因此您可以在其他(可能)死区时间内完成很多工作。

以上是关于SAMD21时钟配置的主要内容,如果未能解决你的问题,请参考以下文章

STM32学习——RCC时钟配置

STM32学习——RCC时钟配置

STM32的系统默认时钟是多少

Zynq PL端调用PS端的时钟

STM32时钟配置问题

STM32--RCC时钟配置寄存器