Перейти к основному содержимому

Настройка тактовых сигналов

примечание

АО «БАЙКАЛ ЭЛЕКТРОНИКС», ИНН: 7707767484
АО «БАЙКАЛ ЭЛЕКТРОНИКС» оставляет за собой право вносить любые изменения в настоящий документ без дополнительного уведомления.

Скачать PDF ⬇️ AppNote_clock_v6.pdf

Введение

В данном документе представлена подробная информация, примеры и разъяснения по настройке системы тактирования микроконтроллера BE-U1000 (далее – МК).

Документ снабжен примерами в виде кода на языке Си.

Для ознакомления с общей информацией о МК см. документы «BE-U1000. Краткое описание» и «BE-U1000. Руководство пользователя».

Для ознакомления с характеристиками МК см. документы «BE-U1000. Техническая спецификация» и «BE-U1000. Reference Manual».

Условные обозначения, термины и сокращения

В документе используются условные обозначения, сокращения и термины, указанные в таблицах ниже.

Таблица 2-1 – Сокращения и термины

ОбозначениеРасшифровка
МКМикроконтроллер BE-U1000.
ADCАЦП (Аналого-цифровой преобразователь) – электронное устройство, преобразующее непрерывный аналоговый сигнал в дискретный цифровой код.
Core 0/1/2Ядро 0/1/2 микроконтроллера BE-U1000.
CLICАппаратный блок в архитектуре RISC-V, предназначенный для обработки прерываний на уровне отдельного процессорного ядра.
CRUControl Registers Unit – блок МК, отвечающий за общие настройки: тактирование, сброс, выбор режимов работы ядер Core 1 и Core 2, GPIO и т.д.
eFlashВстроенная флэш-память МК. Содержит две области – eFlash Main и NVR.
GF MUXGlitch-free Multiplexer – устройство, предназначенное для переключения между несколькими входными тактовыми сигналами без возникновения паразитных импульсов на выходе.
PLLPhase-Locked Loop (блок фазовой автоподстройки частоты, блок ФАПЧ, блок PLL) – устройство, используемое для формирования тактовых сигналов с заданной частотой.
PWMGPulse Width Modulation Generator – генератор ШИМ
QSPIQuad SPI – расширение классического SPI, позволяющее передавать данные по четырём линиям данных параллельно вместо одной, что значительно увеличивает пропускную способность последовательного интерфейса.
SPISerial Peripheral Interface (последовательный периферийный интерфейс) – интерфейс, используемый для подключения внешней SPI-памяти к МК.
VCOVoltalge Controlled Oscilator (генератор, управляемый напряжением) – электронное устройство, генерирующее периодический сигнал, частота которого изменяется в зависимости от величины управляющего напряжения.

Общая информация

Схема тактирования микроконтроллера представлена на рисунке 3-1.

image

Рисунок 3-1 – Схема взаимодействия и доступа

Микроконтроллер BE-U1000 (далее – МК) для работы может использовать следующие источники тактирования:

  • Внешний тактовый сигнал, поступающий на вывод CLKI
  • Внутренний генератор IGEN

При помощи системы мультиплексоров можно произвольным образом коммутировать промежуточные сигналы

Сигналы SYS CLK и ALT CLK могут непосредственно использоваться в качестве источника синхросигнала для различных доменов тактирования микроконтроллера или сигнала для тактирования PLL. Также сигнал SYS CLK участвует в процессе снятия сигнала NRESET с микроконтроллера.

Выходной тактовый сигнал PLL (PLL CLK) может использоваться в качестве синхросигнала для различных доменов тактирования микроконтроллера наравне с сигналами SYS CLK и ALT CLK.

Схема тактирования включает в себя CLK DET – блок отслеживания внешнего тактового сигнала CLKI. В случае пропадания сигнала CLKI осуществляется переключение на сигнал внутреннего генератора IGEN (IGEN CLK).

Микроконтроллер имеет 8 настраиваемых доменов тактирования для различных потребителей тактовых сигналов. Их перечень приведен в таблице ниже.

Домены тактирования

ДоменМаксимальная частотаПотребители
CCLK200 МГцОсновная коммутационная матрица, Ядра Core 0/1, eFlash, ROM MicroPython, блок трассировки
PCLK0100 МГцGPIO0, CANFD0, PWMG0, UART0/1/2, SPI0/1, QSPI0, I2C0/1, TIM0, I2S0, WDT0
PCLK1100 МГцGPIO1, CANFD1, PWMG1, I2S1, UART3/4/5, SPI2/3, QSPI1, I2C2/3, TIM1, WDT1
PCLK2100 МГцGPIO2, UART6/7, ADC0/1/2, PWMA0/1/2/3
HCLK100 МГцUSB 2.0, DMA0/1, Блок Mailbox, Ядро Core 2, Блок трассировки
TCLK100 МГцБлок трассировки
CANx2CLK200 МГцCANFD0/1 CANx2
CANCLK100 МГцCANFD0/1 CAN

Источники тактового сигнала

Внешний источник тактового сигнала

Внешним тактовым сигналом должен быть периодический сигнал, подаваемый извне на вывод CLKI. Частота внешнего тактового сигнала должна быть не более 50 МГц. Для использования CLKI в качестве опорного сигнала PLL частота должна быть 10,2 – 50 МГц.

Внутренний генератор

Использование внутреннего генератора предполагается в качестве:

  • Источника тактового сигнала низкой частоты в приложениях, не предъявляющих требований к точности тактовой частоты.
  • Источника тактового сигнала при старте МК в случае отсутствия внешнего тактового сигнала на выводе CLKI.
  • Аварийного источника тактирования микросхемы в случае пропадания внешнего тактового сигнала на выводе CLKI.

Частота внутреннего генератора зависит от образца МК и условий работы - напряжения питания в домене V1P2 и температуры окружающей среды. Как правило, при нормальных условиях частота внутреннего генератора находится в пределах 15 - 25 МГц. Типовая зависимость частоты генератора от напряжения в домене питания V1P2 и температуры приведена на рисунке 4-1.

Частота внутреннего генератора имеет большую девиацию, поэтому использование его в качестве опорного сигнала для PLL не рекомендуется.

clock

Рисунок 4-1 – Зависимость частоты внутреннего генератора от температуры и напряжения

Значение частоты внутреннего генератора в кГц можно прочитать из памяти NVR:

# define RC_FREQ_PTR ((uint16_t )(EFLASH_BASE + 0x28)) // Адрес ячейки памяти NVR, в которой хранится значение частоты сигнала внутреннего генератора при нормальных условиях.
uint32_t RC_FrequencyFromNVRGet(void){
uint32_t sysctrl0 = CRU->SYSCR0;
CRU->SYSCR0 |= CRU_SYSCR0_NVR_Msk; // Включение перенаправления прямого доступа в память из eFlash в NVR.
uint16_t freq = (RC_FREQ_PTR); // Считывание значения номинальной частоты внутреннего генератора в кГц.
CRU->SYSCR0 = sysctrl0;
return (uint32_t)freq * 1000; // Перевод частоты внутреннего генератора из кГц в Гц.
}

По умолчанию внутренний генератор включен.

Включение / выключение внутреннего генератора производится при помощи битового поля OSCGENEN регистра CLKCR0:

  • Выключение: 0
  • Включение: 1
void RC_TurnOff(){
CRU->CLKCR0 &= ~CRU_CLKCR0_RCGENEN_Msk;
}

void RC_TurnOn(){
CRU->CLKCR0 |= CRU_CLKCR0_RCGENEN_Msk;
}

Выбор основного источника сигнала SYS CLK

Какой сигнал (CLKI или IGEN CLK) будет использоваться в качестве основного тактового сигнала SYS CLK зависит от:

  1. Состояния страпового сигнала CLK STRAP (вывод PC[6])

  2. Битового поля BOOTCLKSEL регистра CLKCR0

В случае если, если BOOTCLKSEL = 1, то в качестве источника основного синхросигнала будет выбран CLKI.

Если BOOTCLKSEL = 0, то выбор источника основного синхросигнала будет зависеть от состояния страпового вывода PC[6] на момент снятия сигнала NRESET:

0 – внутренний генератор IGEN

1 – вывод CLKI

Таблица 4-1 – Таблица истинности выбора источника основного сигнала SYS CLK

CLK_STRAPCLKCR0.BOOTCLKSELSYS CLK
00CLKI
10IGEN CLK
01CLKI
11CLKI

При начальной загрузке МК битовое поле BOOTCLKSEL устанавливается в 0.

Состояние страповых выводов, считанное при снятии сигнала NRESET, можно прочитать в битовом поле STRAP регистра SYSSR0.

uint32_t CLK_Strap_Get(){
return (CRU->SYSSR0 & CRU_SYSSR0_STRAP_CLK_Msk) >> CRU_SYSSR0_STRAP_CLK_Pos;
}

Таким образом, если при старте МК вывод PC[6] был установлен в значение 1, то в процессе работы возможно изменить источник основного синхросигнала SYS CLK.

Отслеживание наличия внешнего тактового сигнала и переход на внутренний генератор

В МК реализовано устройство отслеживания наличия внешнего тактового сигнала. При его отсутствии осуществляется переход на сигнал IGEN CLK.

Если в течение установленного (поле ALARMLVL регистра SYSCR0) количества тактов внутреннего генератора устройство отслеживания не фиксирует наличие сигнала CLKI, оно отправляет сигнал, который переводит SYS CLK и ALT CLK на тактирование от внутреннего генератора, а PLL переводит в режим вывода опорного сигнала (BYPASS). Также по данному сигналу будет сформировано прерывание на ядрах Core 0 и Core 1.

Таким образом, если включен CLKI ALARM и в течение установленного количества тактов внутреннего генератора не было зафиксировано ни одного такта от внешнего генератора, то микроконтроллер будет принудительно переведен на тактирование от внутреннего генератора.

Включить устройство отслеживания можно записью 1 в битовое поле ALARM_EN регистра SYSCR0 (по умолчанию блок выключен):

uint8_t CLKI_Mon_TurnOn(uint8_t alarm_level){
alarm_level = alarm_level > 15 ? 15 : alarm_level; // Проверка переданного параметра на превышения возможного значения. В случае превышения устанавливается максимально возможное
CRU->SYSCR0 &= CRU_SYSCR0_CLKI_ALARM_EN_Msk; // Отключение блока монитора частоты CLKI
CRU->SYSCR0 &= CRU_SYSCR0_CLKI_ALARM_LVL_Msk; // Сброс ранее установленного порога срабатывания монитора частоты
CRU->SYSCR0 |= alarm_level << CRU_SYSCR0_CLKI_ALARM_LVL_Pos; //Установка нового порога срабатывания монитора частоты
CRU->SYSCR0 |= CRU_SYSCR0_CLKI_ALARM_EN_Msk; // Включение блока монитора частоты CLKI
return alarm_level; // Возврат реально установленного порогового значения
}
void CLKI_Mon_TurnOff(){
CRU->SYSCR0 &= ~CRU_SYSCR0_CLKI_ALARM_EN_Msk;

По данному событию может быть настроено прерывание. Сигнал от устройства отслеживания заведен на 74-ю линию маскируемых прерываний Core 0 и Core 1. Для сброса прерывания необходимо записать 0 в битовое поле CLKI_ALARM_EN регистра SYSCR0.

void CLKI_Mon_IrqTurnOn(){
RC_TurnOn(); // Включение внутреннего генератора
CLKI_Mon_TurnOn(15); // Включение монитора частоты CLKI
CLIC_SetCfgNlbits(0); // Настройка и включение прерывания от монитора частоты CLKI.
CLIC_SetLevelThreshold(0);
CLIC_SetLevel(CLIC_CLKI_Mon_IRQn, 1);
CLIC_SetVectorMode(CLIC_CLKI_Mon_IRQn, CLIC_INTATTR_SHV_VECTORED);
CLIC_SetTrigType(CLIC_CLKI_Mon_IRQn, CLIC_INTATTR_TRIG_TYPE_LEVEL);
CLIC_SetTrigPolarity(CLIC_CLKI_Mon_IRQn, CLIC_INTATTR_TRIG_POL_P);
CLIC_SetPrivMode(CLIC_CLKI_Mon_IRQn, CLIC_INTATTR_MODE_MACHINE);
CLIC_EnableIRQ(CLIC_CLKI_Mon_IRQn);
__enable_irq(); // Глобальное разрешение прерываний.
}
__attribute__((interrupt))
void CLKI_Mon_IRQHandler(){
CRU->SYSCR0 &= ~CRU_SYSCR0_CLKI_ALARM_EN; // Сброс флага прерывания и выключение монитора частоты CLKI
CLIC_DisableIRQ(CLIC_CLKI_Mon_IRQn); // Выключение обработки прерывания от монитора частоты CLKI
uint32_t rc_freq = RC_FrequencyFromNVRGet(); // Чтение частоты внутреннего генератора из NVR
con_ctx.freq = rc_freq; // Используется ROM XCP Protocol. Установка частоты внутреннего генератора как частоты тактирования UART-консоли.
XCP_PROTO_GET(console)->init(&con_ctx); // Используется ROM XCP Protocol. Инициализация UART-консоли с учетом новой частоты тактирования.
XCP_PROTO_GET(stdio)->printf("CLKI Monitor. Alarm!\n"); // Используется ROM XCP Protocol. Вывод в консоль сообщения о прерывании.
}

PLL

Модуль PLL предназначен для обеспечения тактирования в широком диапазоне частот.

PLL имеет конфигурируемые делители:

NR – делитель частоты опорного сигнала;

NF – делитель частоты сигнала в петле обратной связи

OD – делитель выходной частоты.

PLL имеет следующие сигналы управления:

  • POWER DOWN – отключает питание аналоговой схемы PLL, работоспособной остается только цифровая часть модуля (делители). В результате снижается энергопотребление модуля.
  • RESET – управляет перезапуском PLL. Отключает опорный сигнал VCO от его входа, устанавливает для всех делителей коэффициент деления 1.
  • BYPASS – управляет выходным мультиплексором. Если BYPASS включен, опорный сигнал PLL поступает напрямую на выход, если отключен – опорный сигнал поступает на делитель выходной частоты.

В зависимости от значения делителей частоты и комбинации сигналов управления определяется наличие выходного сигнала PLL и его частота.

После снятия сигнала NRESET с МК сигналы PLL RESET и BYPASS в активном состоянии. Исключением является случай старта микроконтроллера с использованием USB.

Расчет частоты PLL

Режим, в котором все сигналы управления находятся в неактивном состоянии, является основным рабочим режимом PLL. Выходная частота PLL в этом режиме определяется по формуле:

Fout=Fref×(NFNR×OD)F_{out} = F_{ref} \times \left(\frac{NF}{NR \times OD}\right)

, где:

Fref– опорная частота PLL;

NR – коэффициент деления частоты опорного сигнала;

NF – коэффициент деления частоты обратной связи,

OD – коэффициент деления частоты VCO.

Опорная частота PLL (Fref) и частота фазового детектора (Fref/ NR) должны находиться в диапазоне 10,2 – 50 МГц. Частота VCO PLL (FVCO) должна находиться в диапазоне 260 – 1300 МГц:

FVCO=FrefNR×NFF_{VCO} = \frac{F_{ref}}{NR \times NF}

Коэффициенты деления модуля PLL могут находится в пределах:

  • NR: 1 – 16
  • NF: 1 – 64
  • OD: 1 – 16

После снятия сигнала NRESET значение всех делителей равно 1 (кроме варианта загрузки в режиме «USB»).

В режиме передачи опорного сигнала (сигнал BYPASS активен) опорный сигнал проходит в обход схемы PLL и подается непосредственно на выход:

Fout=FrefF_{out}= F_{ref}

В случае активного BYPASS аналоговая схема PLL остается включенной, но не используется. Ввиду этого использование этого сигнала можно комбинировать с сигналом POWER DOWN для уменьшения потребления.

внимание

При активном сигнале RESET VCO PLL продолжает работать и на выходе PLL может формироваться сигнал произвольной частоты.

Во избежание этого рекомендуется переводить PLL в режим POWERDOWN при длительном включении сигнала RESET.

Настройка PLL

С помощью мультиплексора в качестве источника опорного сигнала может быть выбран сигнал SYS CLK или ALT CLK. Управление мультиплексором осуществляется с помощью битового поля I_PLL_CLK_SEL регистра CLKSEL.

typedef enum {
SYS_CLK=0,
ALT_CLK=1
} REF_CLK_e;

void PLL_RefSourceSet(REF_CLK_eref_clk){ // Функция принимает параметр: SYS_CLK или ALT_CLK, в результате чего в битовое поле регистра управления мультиплексором записывается 0 или 1.
if (ref_clk == SYS_CLK){
CRU->CLKSEL &=~CRU_CLKSEL_REFCLKSEL_Msk;
} else if (ref_clk == ALT_CLK){
CRU->CLKSEL |= CRU_CLKSEL_REFCLKSEL_C1;
}
}

Настройка коэффициентов NR, NF и OD осуществляется с помощью битовых полей CLKR, CLKF и CLKOD регистра PLLSET. Значения рассчитываются по формулам:

CLKR=NR1,CLKF=NF1,CLOD=OD1.\begin{aligned} CLKR &= NR - 1, \\ CLKF &= NF - 1, \\ CLOD &= OD - 1. \end{aligned}

Битовое поле BWADJ регистра PLLSET конфигурирует ширину полосы пропускания петли PLL. Как правило, значение BWADJ совпадает со значением CLKF.

void PLL_DividersSet(uint8_t nr, uint8_t nf, uint8_t od){
uint32_t clkr = (nr - 1) << CRU_PLLSET_CLKR_Pos; // Сдвиг значений делителей на позиции, занимаемые в регистре
uint32_t clkf = (nf - 1) << CRU_PLLSET_CLKF_Pos;
uint32_t clkod = (od - 1) << CRU_PLLSET_CLKOD_Pos;
uint32_t clkbw = (nf - 1) << CRU_PLLSET_BWADJ_Pos;
clkr &= CRU_PLLSET_CLKR_Msk; // Наложение маски на значения делителей для отсекания лишних бит
clkf &= CRU_PLLSET_CLKF_Msk;
clkod &= CRU_PLLSET_CLKOD_Msk;
clkbw &= CRU_PLLSET_BWADJ_Msk;
uint32_t pllset = CRU->PLLSET; // Запись значения регистра `PLLSET` в переменную
pllset &= ~(CRU_PLLSET_CLKR_Msk | CRU_PLLSET_CLKF_Msk | CRU_PLLSET_CLKOD_Msk| CRU_PLLSET_BWADJ_Msk); // Сброс старых значений делителей в переменной
pllset |= clkr | clkf | clkod | clkbw; // Установка новых значений делителей в переменной
CRU->PLLSET = pllset; // Сохранение нового значения регистра `PLLSET` из переменной
}

Битовое поле INTFB регистра PLLSET необходимо установить в 1:

void PLL_FeedbackSet(){
CRU->PLLSET |= CRU_PLLSET_INTFB_Msk;
}
void PLL_FeedbackRelease(){
CRU->PLLSET &= ~CRU_PLLSET_INTFB_Msk;
}

Включить сигнал RESET можно с помощью установки в 1 битового поля RESET регистра PLLSET:

void PLL_ResetSet(){
CRU->PLLSET |= CRU_PLLSET_RESET_Msk;
}
void PLL_ResetRelease(){
CRU->PLLSET &= ~CRU_PLLSET_RESET_Msk;
}

Включить сигнал POWER DOWN можно с помощью установки в 1 битового поля PWRDN регистра PLLSET:

void PLL_PowerDown(){
CRU->PLLSET |= CRU_PLLSET_PWRDN_Msk;
}
void PLL_PowerUp(){
CRU->PLLSET &= ~CRU_PLLSET_PWRDN_Msk;
}

Включить сигнал BYPASS возможно с помощью установки в 1 битового поля BYPASS регистра PLLSET:

void PLL_BypassModeEnable(){
CRU->PLLSET |= CRU_PLLSET_BYPASS_Msk;
}

Отключить сигнал BYPASS возможно с помощью установки в 0 битового поля BYPASS регистра PLLSET:

void PLL_BypassModeDisable(){
CRU->PLLSET &= ~CRU_PLLSET_BYPASS_Msk;
}

Состояние PLL отображается в битовых полях регистра SYSSR0:

  • PLL_RFSLIP – если бит установлен в 1, то выходная частота выше ожидаемой;
  • PLL_FBSLIP – если бит установлен в 1, то выходная частота ниже ожидаемой;
  • PLL_NLOCK – если бит установлен в 1, то PLL вышла из установившегося режима, например, в случае нестабильности опорной частоты.

Чтение состояния PLL:

uint32_t PLL_RFSlipGet(){
return CRU->SYSSR0 &= CRU_SYSSR0_PLL_RFSLIP_Msk;
}
uint32_t PLL_FBSlipGet(){
return CRU->SYSSR0 &= CRU_SYSSR0_PLL_FBSLIP_Msk;
}
uint32_t PLL_NLockGet(){
return CRU->SYSSR0 &= CRU_SYSSR0_PLL_NLOCK_Msk;
}

Битовые поля PLLRFSLIP и PLL_FBSLIP отображают состояние PLL на текущий момент, в то время как PLL_NLOCK фиксирует выход PLL из установившегося режима. Битовое поле PLL_NLOCK_RSTN регистра PLLSET управляет отслеживанием выхода PLL из установившегося режима. Если PLL_NLOCK_RSTN установлен в 1, отслеживание включено, если в 0 - отслеживание отключено и битовое поле PLL_NLOCK всегда будет иметь значение 0. По умолчанию отслеживание отключено. Если в битовом поле PLL_NLOCK_RSTN будет записан 0 – сигнал PLL NLOCK будет сброшен.

Включение отслеживания:

void PLL_NLockEnable(){
CRU->PLLSET |= CRU_PLLSET_PLL_NLOCK_RSTN_Msk;
}
void PLL_NLockDisable(){
CRU->PLLSET &= CRU_PLLSET_PLL_NLOCK_RSTN_Msk;
}
void PLL_NLockReset(){
CRU->PLLSET &= CRU_PLLSET_PLL_NLOCK_RSTN_Msk;
CRU->PLLSET |= CRU_PLLSET_PLL_NLOCK_RSTN_Msk;
}

Сигнал PLL NLOCK заведен на 61-ю линию маскируемых прерываний Core 0 и Core 1. Для сброса прерывания необходимо записать 0 в битовое поле PLL_NLOCK_RSTN регистра PLLSET.

void PLL_Unlock_IrqTurnOn(){
PLL_NLockEnable(); // Настройка прерывания по выходу PLL из установившегося режима
CLIC_SetCfgNlbits(0);
CLIC_SetLevelThreshold(0);
CLIC_SetLevel(CLIC_PLL_Unlock_IRQn, 1);
CLIC_SetVectorMode(CLIC_PLL_Unlock_IRQn, CLIC_INTATTR_SHV_VECTORED);
CLIC_SetTrigType(CLIC_PLL_Unlock_IRQn, CLIC_INTATTR_TRIG_TYPE_LEVEL);
CLIC_SetTrigPolarity(CLIC_PLL_Unlock_IRQn, CLIC_INTATTR_TRIG_POL_P);
CLIC_SetPrivMode(CLIC_PLL_Unlock_IRQn, CLIC_INTATTR_MODE_MACHINE);
CLIC_EnableIRQ(CLIC_PLL_Unlock_IRQn);
__enable_irq();
}
__attribute__((interrupt))
void PLL_Unlock_IRQHandler(){
XCP_PROTO_GET(stdio)->printf("PLL Unlocked. Alarm!\n"); // Вывод сообщения о прерывании в консоль. Используется ROM XCP Protocol.
PLL_NLockReset(); // Сброс флага прерывания PLL_NLOCK

Домены тактирования подсистем микроконтроллера

Каждая подсистема тактирования (CCLKPCLK0PCLK1PCLK2HCLKTCLKCANx2CLK) состоит из:

  • Мультиплексор выбора источника синхросигнала
  • Делитель частоты сигнала PLL

Делитель частоты PLL для домена

До поступления на мультиплексор домена синхросигнал PLL проходит через делитель частоты. Делитель позволяет уменьшить частоту PLL для каждого домена до 16 раз с шагом 0,5. Управление делителем осуществляется с помощью регистров CLKCR0 и CLKCR1. На управление каждым делителем выделено по 6 бит в регистре: 5 бит отвечают за коэффициент деления и 1 бит - за включение делителя.

С помощью регистра CLKCR0 настраивается и включается делитель для доменов CCLK, PCLK0, PCLK1, PCLK2 и HCLK. Битовые поля для включения и настройки:

  • Домен CCLK: CCLKDIV – настройка, CCLKDIVEN – активация;

  • Домен PCLK0: PCLK0DIV – настройка, PCLK0DIVEN – активация;

  • Домен PCLK1: PCLK1DIV – настройка, PCLK1DIVEN – активация;

  • Домен PCLK2: PCLK2DIV – настройка, PCLK2DIVEN – активация;

  • Домен HCLK: HCLKDIV – настройка, HCLKDIVEN – активация.

С помощью регистра CLKCR1 настраивается делитель для доменов TCLK и CANx2CLK. Битовые поля для включения и настройки:

  • Домен TCLK: CCLKDIV – конфигурирование, CCLKDIVEN – активация;

  • Домен CANx2CLK: CCLKDIV – конфигурирование, CCLKDIVEN – активация.

Значение для настройки делителя рассчитывается по формуле:

DIV=(Kdiv1)×2,DIV = (K_{div} - 1) \times 2,

где:

KdivK_{div} – коэффициент деления частоты PLL,

DIV– значение, записываемое в битовое поле регистра.

# define CLK_DOMAIN_DIVIDER_MASK ((1 << 5) - 1) // Маска для делителя частоты PLL для домена
void ClkDomain_PLLDividerEnable(clk_domain_e domain){
uint32_t reg_ptr;
uint32_t first_domain;
if (domain < CLK_DOMAIN_TCLK){ // Определение в каком регистре находятся конфигурационные биты для делителя частоты PLL для домена
reg_ptr = (uint32_t )(&CRU->CLKCR0);
first_domain = CLK_DOMAIN_CCLK;
} else {
reg_ptr = (uint32_t )(&CRU->CLKCR1);
first_domain = CLK_DOMAIN_TCLK;
}
uint32_t shift_size = (domain - first_domain) 3 + 5; // Расчет позиции бита, отвечающего за включение делителя частоты PLL для выбранного домена
reg_ptr |= 1 << shift_size; // Включение делителя частоты PLL для выбранного домена
}
void ClkDomain_PLLDividerDisable(clk_domain_e domain){
uint32_t reg_ptr;
uint32_t first_domain;
if (domain < CLK_DOMAIN_TCLK){
reg_ptr = (uint32_t )(&CRU->CLKCR0);
first_domain = CLK_DOMAIN_CCLK;
} else {
reg_ptr = (uint32_t )(&CRU->CLKCR1);
first_domain = CLK_DOMAIN_TCLK;
}
uint32_t shift_size = (domain - first_domain) 3 + 5;
reg_ptr &= ~(1 << shift_size);
}
void ClkDomain_PLLDividerSet(clk_domain_e domain, float divider){
uint32_t reg_ptr;
uint32_t first_domain;
if (domain < CLK_DOMAIN_TCLK){
reg_ptr = (uint32_t )(&CRU->CLKCR0);
first_domain = CLK_DOMAIN_CCLK;
} else {
reg_ptr = (uint32_t )(&CRU->CLKCR1);
first_domain = CLK_DOMAIN_TCLK;
}
uint32_t shift_size = (domain - first_domain) 3; // Расчет позиции битов в регистре, отвечающее за значение делителя частоты PLL для выбранного домена
uint32_t clk_cr = reg_ptr;
clk_cr &= ~(CLK_DOMAIN_DIVIDER_MASK << shift_size); // Сброс предыдущего значения делителя
uint32_t div_val = (uint32_t)((divider + 0.25 - 1) 2); // Расчет нового значения делителя с округлением до 0,5 и преобразование данного значения в значение битов для записи в регистр
clk_cr |= div_val << shift_size; // Запись нового значения делителя частоты PLL для выбранного домена в регистр

*reg_ptr = clk_cr;
}

Выбор источника тактового сигнала для домена

Мультиплексор в каждом домене позволяет выбрать источник синхросигнала: SYS CLKALT CLK или PLL. Управление мультиплексорами осуществляется с помощью битовых полей регистра CLKSEL:

  • CCLKSEL – управление доменом CCLK;
  • PCLK0SEL – управление доменом PCLK0;
  • PCLK1SEL – управление доменом PCLK1;
  • PCLK2SEL – управление доменом PCLK2;
  • HCLKSEL – управление доменом HCLK;
  • TCLKSEL – управление доменом TCLK;
  • CANX2CLKSEL – управление доменами CANx2CLK, CANCLK,

Выбор источника определяется следующими значениями:

  • 0b00 – синхросигнал SYS CLK;
  • 0b01 – выходной сигнал PLL, пропущенный через делитель;
  • 0b10 – синхросигнал ALT CLK;
  • 0b11 – зарезервировано.
typedef enum{ // Список источников тактового сигнала для доменов тактирования
CLK_SRC_C0 = 0,
CLK_SRC_PLL = 1,
CLK_SRC_C1 = 2,
CLK_SRC_MASK = 3
}clk_src_e;
typedef enum{ // Список доменов тактирования микроконтроллера
CLK_DOMAIN_CCLK = 1,
CLK_DOMAIN_PCLK0 = 3,
CLK_DOMAIN_PCLK1 = 5,
CLK_DOMAIN_PCLK2 = 7,
CLK_DOMAIN_HCLK = 9,
CLK_DOMAIN_TCLK = 11,
CLK_DOMAIN_CANX2CLK = 13
}clk_domain_e;
void ClkDomain_SourceSet(clk_domain_e domain, clk_src_e src){
uint32_t clk_sel = CRU->CLKSEL;
clk_sel &= ~(CLK_SRC_MASK << domain);
clk_sel |= (src & CLK_SRC_MASK) << domain;
CRU->CLKSEL = clk_sel;

После снятия сигнала NRESET все домены тактируются от синхросигнала SYS CLK, кроме случая старта в режиме «USB».

Управление тактированием периферии

Отдельно можно управлять подачей тактового синхросигнала каждого периферийного модуля в частотных доменах PCLK0PCLK1 и PCLK2. Для этого используются регистры PCLK0ENPCLK1EN и PCLK2EN. Даже если модуль не тактируется, его регистровое пространство доступно для обращения к нему.

Поскольку периферия в доменах PCLK0 и PCLK1 идентична, то и регистры PCLK0EN и PCLK1EN имеют одинаковые битовые поля.

Битовые поля [устройство]CLKEN отвечают за включение тактирования.

  • QSPI0 и QSPI1 – QSPI0CLKEN и QSPI1CLKEN

  • SPI0/1 и SPI2/3 – SPI[0/1]CLKEN и SPI[0/1]CLKEN

  • I2C0/1 и I2C2/3 – I2C[0/1]CLKEN и I2C[2/3]CLKEN

  • TIMER0 и TIMER1 – TIMER0CLKEN и TIMER1CLKEN

  • WDT0 и WDT1 – WDT0CLKEN и WDT1CLKEN

  • PWMG0 и PWMG1 – PWMG0CLKEN и PWMG1CLKEN

  • CAN0 и CAN1 – CAN0CLKEN и CAN1CLKEN

При помощи регистра PCLK2EN можно настроить тактирование следующей периферии:

  • ADC0 – ADC0CLKEN
  • ADC1 – ADC0CLKEN
  • ADC2 – ADC0CLKEN
  • PWMA0 – PWMA0CLKEN
  • PWMA1 – PWMA1CLKEN
  • PWMA2 – PWMA2CLKEN
  • PWMA3 – PWMA3CLKEN
typedef enum{ // Список периферии микроконтроллера на шинах PCLK
PERIPH_QSPI0 = 0,
PERIPH_SPI0 = 1,
PERIPH_SPI1 = 2,
PERIPH_UART0 = 3,
PERIPH_UART1 = 4,
PERIPH_UART2 = 5,
PERIPH_I2C0 = 6,
PERIPH_I2C1 = 7,
PERIPH_I2S0 = 8,
PERIPH_TIMER0 = 9,
PERIPH_WDT0 = 10,
PERIPH_GPIO0 = 11,
PERIPH_PWMG0 = 12,
PERIPH_CAN0 = 13,
PERIPH_QSPI1 = 32,
PERIPH_SPI2 = 33,
PERIPH_SPI3 = 34,
PERIPH_UART3 = 35,
PERIPH_UART4 = 36,
PERIPH_UART5 = 37,
PERIPH_I2C2 = 38,
PERIPH_I2C3 = 39,
PERIPH_I2S1 = 40,
PERIPH_TIMER1 = 41,
PERIPH_WDT1 = 42,
PERIPH_GPIO1 = 43,
PERIPH_PWMG1 = 44,
PERIPH_CAN1 = 45,
PERIPH_ADC0 = 64,
PERIPH_ADC1 = 65,
PERIPH_ADC2 = 66,
PERIPH_PWMA0 = 67,
PERIPH_PWMA1 = 68,
PERIPH_PWMA2 = 69,
PERIPH_PWMA3 = 70,
PERIPH_GPIO2 = 71,
PERIPH_UART6 = 72,
PERIPH_UART7 = 73,
}periph_e;

void PERIPH_ClockEnable(periph_e periph){
switch(periph){
case PERIPH_UART0:
case PERIPH_UART1:
case PERIPH_UART3:
case PERIPH_UART4:
case PERIPH_UART5:
case PERIPH_UART6:
case PERIPH_UART7:
case PERIPH_I2S0:
case PERIPH_I2S1:
case PERIPH_GPIO0:
case PERIPH_GPIO1:
case PERIPH_GPIO2:
break; // У перечисленной периферии отсутствует возможность управлять тактовым сигналом
default: {
uint32_t pclk_en_regs = (uint32_t )(&CRU->PCLK0EN);
pclk_en_regs[periph / 32] |= (1 << (periph % 32));
break;
}
}
}
void PERIPH_ClockDisable(periph_e periph){
switch(periph){
case PERIPH_UART0:
case PERIPH_UART1:
case PERIPH_UART3:
case PERIPH_UART4:
case PERIPH_UART5:
case PERIPH_UART6:
case PERIPH_UART7:
case PERIPH_I2S0:
case PERIPH_I2S1:
case PERIPH_GPIO0:
case PERIPH_GPIO1:
case PERIPH_GPIO2:
break;
default: {
uint32_t pclk_en_regs = (uint32_t )(&CRU->PCLK0EN);
pclk_en_regs[periph / 32] &= ~(1 << (periph % 32)); // Поскольку регистры управления тактированием и сбросом периферии располагаются друг за другом, то обращаться к ним можно как к массиву
break;
}
}
}

Сброс

Для некоторых периферийных модулей предусмотрен сигнал программного сброса. Сброс осуществляется при помощи регистров PCLK0EN, PCLK1EN, и PCLK2EN. Установка сброса – 0, снятие сброса – 1.

Для сброса периферии используются следующие битовые поля регистров PCLK0EN, PCLK1EN:

  • QSPI0 и QSPI1 – QSPI0RSTN и QSPI1RSTN

  • SPI0/1 и SPI2/3 – SPI[0/1]RSTN и SPI[0/1]RSTN

  • I2S0 и I2S1 – I2S0RSTN и I2S1RSTN

  • I2C0/1 и I2C2/3 – I2C[0/1]RSTN и I2C[2/3]RSTN

  • UART0/1/2 и UART3/4/5 – UART[0/1/2]RSTN и UART[3/4/5]RSTN

  • TIMER0 и TIMER1 – TIMER0RSTN и TIMER1RSTN

  • WDT0 и WDT1 – WDT0RSTN и WDT1RSTN

  • PWMG0 и PWMG1 – PWMG0RSTN и PWMG1RSTN

  • CAN0 и CAN1 – CAN0RSTN и CAN1RSTN

  • GPIO0 и GPIO1 – GPIO0RSTN и GPIO1RSTN

Используются следующие битовые поля регистра PCLK2EN:

  • ADC0/1/2 – ADC[0/1/2]RSTN

  • PWMA0/1/2/3 – PWMA[0/1/2/3]RST

  • GPIO2 – GPIO2RSTN

  • UART6 – UART[6/7]RSTN

После сброса тактирование всех модулей периферии включено, сброс снят:

void PERIPH_ResetRelease(periph_e periph){
uint32_t pclk_en_regs = (uint32_t )(&CRU->PCLK0EN);
pclk_en_regs[periph / 32] |= (1 << (periph % 32 + 16)); // Т.к. регистры управления тактированием и сбросом периферии располагаются друг за другом, то можно обращаться к ним как к массиву. Управление сбросом доступно для всей периферии. Расположение битов управления сбросом периферии идентично расположению битов управления синхросигналом периферии, но смещены на 16 бит.
}
void PERIPH_ResetSet(periph_e periph){
uint32_t pclk_en_regs = (uint32_t )(&CRU->PCLK0EN);
pclk_en_regs[periph / 32] &= ~(1 << (periph % 32 + 16));
}

Микросекундный таймер ядра

В каждом ядре (Core 0, Core 1 и Core 2) имеется 64-битный микросекундный таймер, который считает микросекунды с момента старта ядра. Для таймера Core 0 и Core 1 тактовый сигнал формируется из сигнала CCLK путем пропускания его через делитель частоты, для таймера ядра Core 2 тактовый сигнал формируется из сигнала HCLK путем пропускания его через делитель частоты. Эти делители целочисленные и конфигурируются с помощью битового поля CCLK1MHZ и HCLK1MHZ регистра CLKSEL.

Значения этих полей рассчитываются по формулам: CCLK1MHZ=Fcclk1061,HCLK1MHZ=Fhclk1061,\begin{aligned} CCLK_{1MHZ} &= \frac{F_{cclk}}{10^6} - 1, \\ HCLK_{1MHZ} &= \frac{F_{hclk}}{10^6} - 1, \end{aligned}

где:

Fcclk – частота шины CCLK в Гц,

Fhclk – частота шины HCLK в Гц.

void CCLK_1MHzDividerSet(uint32_t divider){
uint32_t clk_sel = CRU->CLKSEL;
clk_sel &= (CRU_CLKSEL_CCLK1MHZ_Msk);// Сброс предыдущего значения делителя для 1 МГц таймера
clk_sel |= ((divider - 1) << CRU_CLKSEL_CCLK1MHZ_Pos) &
CRU_CLKSEL_CCLK1MHZ_Msk; // Запись нового значения делителя в регистр
CRU->CLKSEL = clk_sel;
}
void HCLK_1MHzDividerSet(uint32_t divider){
uint32_t clk_sel = CRU->CLKSEL;
clk_sel &= (CRU_CLKSEL_HCLK1MHZ_Msk);
clk_sel |= ((divider - 1) << CRU_CLKSEL_HCLK1MHZ_Pos) &
CRU_CLKSEL_CCLK1MHZ_Msk;
CRU->CLKSEL = clk_sel;
}

Чтение значения микросекундного таймера ядра из регистров MTIMEL и MTIMEH с помощью команд rdtime и rdtimeh:

uint64_t CoreTimer_UsGet(){
volatile uint32_t msb, lsb, msb2;
do
{
msb = (volatile uint32_t )CLINT_MTIMEH; // Считывание старших 32 бит таймера
lsb = (volatile uint32_t )CLINT_MTIMEL; // Считывание младших 32 бит таймера
msb2 = (volatile uint32_t )CLINT_MTIMEH; // Повторное считывание старших 32 бит таймера
} while (msb != msb2); // В случае, если значение старших 32 бит таймера, считанных в первом и во втором случае, отличаются (произошло переполнение младших 32 бит таймера) повторить процедуру считывания.
return ((((uint64_t)msb) << 32) | lsb);
}

Примеры настройки системы тактирования

В данном разделе будет приведено два варианта настройки системы тактирования:

  • Все домены работают от внешнего синхросигнала *CLKI *;
  • Все домены работают от PLL и настроены на максимально допустимые спецификацией частоты.

Конфигурация тактирования от SYS CLK

clk_domain_e domains[] = { // Список всех доменов тактирования микроконтроллера
CLK_DOMAIN_CCLK, CLK_DOMAIN_PCLK0, CLK_DOMAIN_PCLK1, CLK_DOMAIN_PCLK2,
CLK_DOMAIN_HCLK, CLK_DOMAIN_TCLK, CLK_DOMAIN_CANX2CLK
};
int System_ConfigCLKI(){
21
for (uint32_t i = 0; i < sizeof(domains) / sizeof(domains[0]); i++){ // Настройка тактирования каждого домена тактирования от CLKI
ClkDomain_SourceSet(domains[i], CLK_SRC_C0);
}
CCLK_1MHzDividerSet(25); // Установка значения делителя для 1 МГц таймера для Core 0 и Core 1 (при условии, что частота внешнего источника синхросигнала 25 МГц)
HCLK_1MHzDividerSet(25); // Установка значения делителя для 1 МГц таймера для Core 2 (при условии, что частота внешнего источника синхросигнала 25 МГц)
PLL_NLockDisable(); // Остановка PLL: отключение схемы слежения за выходом PLL из установившегося режима
PLL_ResetSet(); // Остановка PLL: установка сигнала RESET для PLL
PLL_PowerDown(); // Остановка PLL: отключение питания аналоговой схемы PLL
return 0;
}

Конфигурация тактирования от PLL на максимальные частоты

# define PLL_TIMEOUT_US 20 // Таймаут для выхода PLL в установившийся режим
int System_ConfigMaxFreqs(){
System_ConfigCLKI(); // Переход на тактирование от CLKI
PLL_TestModeDisable(); // Конфигурирование PLL: отключение тестового режима
PLL_BypassModeDisable(); // Конфигурирование PLL: отключение режима передачи опорного сигнала
PLL_DividersSet(1, 16, 2); // Конфигурирование PLL: настройка делителей – делитель опорной частоты в 1, делитель частоты в петле обратной связи в 16, делитель выходной частоты в 2. Таким образом частота VCO равна 400 МГц, выходная частота PLL – 200 МГц
PLL_PowerUp(); // Конфигурирование PLL: включение питания аналоговой схемы
PLL_ResetRelease(); // Конфигурирование PLL: снятие ресета
uint64_t pll_start = CoreTimer_UsGet(); // Ожидание выхода PLL в установившийся режим. В случае не установки данного режима – выход из функции с ошибкой
while(CRU->SYSSR0 & (CRU_SYSSR0_PLL_RFSLIP_Msk | CRU_SYSSR0_PLL_FBSLIP_Msk)){
if (CoreTimer_UsGet() - pll_start > PLL_TIMEOUT_US){
PLL_ResetSet();
PLL_PowerDown();
return -1;
}
}
CCLK_1MHzDividerSet(200); // Настройка CCLK на тактирование от PLL: настройка делителя частоты для 1 МГц таймера Core 0 и Core 1
ClkDomain_PLLDividerSet(domains[0], 1); // Настройка CCLK на тактирование от PLL: установка делителя частоты PLL для домена CCLK в 1
ClkDomain_PLLDividerEnable(domains[0]); // Настройка CCLK на тактирование от PLL: включение делителя частоты PLL для домена CCLK
ClkDomain_SourceSet(domains[0], CLK_SRC_PLL); // Настройка CCLK на тактирование от PLL: выбор сигнала PLL в качестве тактового сигнала для домена CCLK. Домен CCLK тактируется с частотой PLL (200 МГц)
HCLK_1MHzDividerSet(100); // Настройка остальных доменов на тактирование от PLL: настройка делителя частоты для 1 МГц таймера Core 2
for (uint32_t i = 1; i < sizeof(domains) / sizeof(domains[0]); i++){
ClkDomain_PLLDividerSet(domains[i], 2); // Настройка остальных доменов на тактирование от PLL: установка делителя частоты PLL в 2
ClkDomain_PLLDividerEnable(domains[i]); // Настройка остальных доменов на тактирование от PLL: включение делителя частоты PLL
ClkDomain_SourceSet(domains[i], CLK_SRC_PLL); // Настройка остальных доменов на тактирование от PLL: выбор сигнала PLL в качестве тактового сигнала. Домены PCLK0, PCLK1, PCLK2, HCLK, TCLK и CANX2CLK тактируются с частотой в 2 раза меньшей частоты PLL - 100 МГц
}
PLL_Unlock_IrqTurnOn(); // Включение прерывания по выходу PLL из установившегося режима
return 0;
}

Скачать PDF ⬇️ AppNote_clock_v6.pdf

ВерсияДатаОписание
1.029.04.2026Начальная версия
1.213.05.2026Корректировки и уточнения
304.06.2026Корректировки и уточнения
418.06.2026Корректировки и уточнения
522.06.2026Корректировки и уточнения
606.07.2026Корректировки и уточнения