Конфигурация часов SAMD21

Я пытаюсь настроить часы SAMD21 так, чтобы они шли как можно быстрее. Таким образом, я использую внутренний 8-мегагерцовый генератор для питания общего тактового генератора 1 (с предварительным делителем 8), чтобы сгенерировать общий тактовый сигнал для питания цифрового контура фазовой автоподстройки частоты, который, в свою очередь, питает общий тактовый генератор 0 (мой основной тактовый генератор), который должен тактировать процессор, но микро работает очень медленно, где я ошибся? Я следовал этому руководству 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_clock_init(), которая вызывается из system_init().


person Luca    schedule 03.02.2017    source источник


Ответы (2)


Я никогда не был поклонником использования ASF Atmel, поскольку вы никогда не делаете только то, что хотите. Я предлагаю вам подробнее изучить спецификацию, так как это требует гораздо меньше времени для завершения вашей задачи, чем рыскание в 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;

Оставшиеся шаги заключаются только в настройке регистра dpll с использованием уже включенного OSC8M, который также состоит всего из нескольких строк кода. (Запись в регистры GCLK.GENDIV/GENCTRL и CLKCTRL, а также запись в регистр SYSCTRL.DPLL.

person Patrick Schmelzer    schedule 17.02.2017

Чтобы настроить ЦП SAMD21 для работы на максимальной поддерживаемой частоте (48 МГц), я не использую ASF, а вместо этого использую код, взятый из ядра Arduino SAMD; вы можете узнать, что он делает в функции SystemInit() по адресу https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/startup.c. Цитируя комментарии в этом файле, соответствующие шаги, выполняемые в SystemInit(), следующие:

  • Включить часы XOSC32K (внешний встроенный генератор 32,768 Гц), будет использоваться в качестве эталона DFLL48M
  • Поместите XOSC32K в качестве источника Generic Clock Generator 1
  • Поместите универсальный тактовый генератор 1 в качестве источника для универсального тактового мультиплексора 0 (ссылка на DFLL48M)
  • Включить часы DFLL48M
  • Переключите универсальный тактовый генератор 0 на DFLL48M. ЦП будет работать на частоте 48 МГц

Соответствующие строки кода (при условии, что на вашей плате установлен внешний резонатор 32,768 кГц):

/* 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 */
}
person Francesco Lavra    schedule 24.10.2018
comment
Обратите внимание, что XOSC32K может долго стабилизироваться после включения питания — до одной секунды. DFLL48M также требует времени для блокировки, но далеко не так долго, как 32K. (См. раздел временных характеристик генератора в конце описания устройства.) Таким образом, может иметь смысл запустить XOSC32K, затем уйти и выполнить другие задачи инициализации (например, инициализацию памяти во время выполнения C) перед проверкой SYSCTRL_PCLKSR_XOSC32KRDY. ЦП работает с OSC8M после сброса, поэтому вы можете многое сделать в это иначе (потенциально) мертвое время. - person Jeremy; 14.01.2019