Не так давно компания STM выпустила на рынок очень мощную, по меркам микроконтроллеров, линейку кристаллов STM32H7. Что меня в ней привлекло:
- повышенная частота ядра до 400 МГц
- увеличенный объем ОЗУ, до 1 МБ
- 16 разрядный АЦП
- pin-to-pin совместимость с серий F7
Отлично подумал я, запаял на плату кристалл STM32H743IIT6 вместо STM32F746IGT6 и начал новый проект в SW4STM32.
Для расчета коэффициентов делителей и множителей системы тактирования микроконтроллера удобно пользоваться вкладкой Clock Configuration программы STM32CubeMX.
Настройки тактирования:
- внешний кварц — 8 МГц
- источник частоты для PLL1 — внешний кварц (HSE)
- делитель для PLL1 — 4 (DIVM1)
- множитель PLL1 — 400 (DIVN1)
- выходные делители — 2 (DIVP1, DIVQ1, DIVR1)
Соответственно, частота ядра (SYSCLK)- 400 МГц.
Кроме STM32CubeMX есть еще набор «STM32CubeH7 firmware package», который содержит большое количество примеров для работы с периферией для STM32H7. Именно из него была взята последовательность инициализации системы тактирования микроконтроллера.
Информация и комментарии взяты из следующих источников:
- SystemClock_Config из STM32CubeH7 firmware package
- Reference manual STM32H743/753 and STM32H750 advanced ARM-based 32-bit MCUs
- — Datasheet STM32H743xI
Итак, начнем.
1. Включение внешнего кварца и ожидание готовности.
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait till HSE is ready
while((RCC->CR & RCC_CR_HSERDY) == 0);
2. Указание источника частоты для PLL1 — внешний кварц.
//RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC -> PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSE;
3. Значение делителя устанавливается равным 4.
//PLLM = 4
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_5; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_4; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_3; //0
RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2; //1
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_1; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_0; //0
4. Множитель N и делители P, Q, R
//PLL1DIVR bits
//DIVN1[8:0] 0 - 8 PLLN = 400
//DIVP1[6:0] 9 - 15 PLLP = 2
//DIVQ1[6:0] 16 - 22 PLLQ = 2
//DIVR1[6:0] 24 - 30 PLLR = 2
RCC -> PLL1DIVR |= 0x0101038F;
5. Дробный делитель частоты PLL (если нужен)
// /* Configure PLL PLL1FRACN */
//__HAL_RCC_PLLFRACN_CONFIG(RCC_OscInitStruct->PLL.PLLFRACN);
RCC -> PLL1FRACR = 0;
6. Указание диапазона входной частоты PLL1
/* Select PLL1 input reference frequency range: VCI */
//__HAL_RCC_PLL_VCIRANGE(RCC_OscInitStruct->PLL.PLLRGE) ;
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_3;
7. Указание диапазона выходной частоты PLL1
/* Select PLL1 output frequency range : VCO */
//__HAL_RCC_PLL_VCORANGE(RCC_OscInitStruct->PLL.PLLVCOSEL) ;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLL1VCOSEL;
8. Включение выходных делителей PLL1: P, Q, R
/* Enable PLL System Clock output. */
// __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVP);
//Bit 16 DIVP1EN: PLL1 DIVP divider output enable
RCC->PLLCFGR |= RCC_PLLCFGR_DIVP1EN;
/* Enable PLL1Q Clock output. */
//__HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ);
RCC->PLLCFGR |= RCC_PLLCFGR_DIVQ1EN;
/* Enable PLL1R Clock output. */
// __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVR);
RCC->PLLCFGR |= RCC_PLLCFGR_DIVR1EN;
9. Включение дробного делителя.
/* Enable PLL1FRACN . */
//__HAL_RCC_PLLFRACN_ENABLE();
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1FRACEN;
10. Пуск PLL1 и ожидание готовности
/* Enable the main PLL. */
//__HAL_RCC_PLL_ENABLE();
RCC->CR |= RCC_CR_PLLON;
while((RCC->CR & RCC_CR_PLL1RDY) == 0);
PLL1 настроен и запущен. Теперь выбор источника частоты SYSCLK и настройка делителей шин.
11. Делитель на 2 HPRE
//RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
// MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
//HPRE[3:0]: D1 domain AHB prescaler
//1000: rcc_hclk3 = sys_d1cpre_ck / 2
RCC -> D1CFGR |= RCC_D1CFGR_HPRE_3; //1
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_2; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_1; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_0; //0
12. Без деления D1CPRE
//RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
//MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1CPRE, RCC_ClkInitStruct->SYSCLKDivider);
//D1CPRE[3:0]: D1 domain Core prescaler
//0xxx: sys_ck not divided (default after reset)
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_3; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_2; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_1; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_0; //0
13. Задаем PLL1 как источник SYSCLK и ожидаем готовности
//RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
//MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_ClkInitStruct->SYSCLKSource);
//SW[2:0]: System clock switch
//011: PLL1 selected as system clock (pll1_p_ck)
RCC->CFGR &= ~RCC_CFGR_SW_2; //0
RCC->CFGR |= RCC_CFGR_SW_1; //1
RCC->CFGR |= RCC_CFGR_SW_0; //1
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1);
14. Делитель на 2 D1PPRE
//D1PCLK1 Configuration
//RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
//MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1PPRE, RCC_ClkInitStruct->APB3CLKDivider);
//Bits 6:4 D1PPRE[2:0]: D1 domain APB3 prescaler
//100: rcc_pclk3 = rcc_hclk3 / 2
RCC -> D1CFGR |= RCC_D1CFGR_D1PPRE_2;
RCC -> D1CFGR &= ~RCC_D1CFGR_D1PPRE_1;
RCC -> D1CFGR &= ~RCC_D1CFGR_D1PPRE_0;
15. Делитель на 2 D2PPRE1
//PCLK1 Configuration
//RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
//MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE1, (RCC_ClkInitStruct->APB1CLKDivider));
//Bits 6:4 D2PPRE1[2:0]: D2 domain APB1 prescaler
//100: rcc_pclk1 = rcc_hclk1 / 2
RCC -> D2CFGR |= RCC_D2CFGR_D2PPRE1_2;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE1_1;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE1_0;
16. Делитель на 2 D2PPRE2
//PCLK2 Configuration
//RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
//MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE2, (RCC_ClkInitStruct->APB2CLKDivider));
//Bits 10:8 D2PPRE2[2:0]: D2 domain APB2 prescaler
//100: rcc_pclk2 = rcc_hclk1 / 2
RCC -> D2CFGR |= RCC_D2CFGR_D2PPRE2_2;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE2_1;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE2_0;
17. Делитель на 2 D3PPRE
//D3PCLK1 Configuration
//RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
//MODIFY_REG(RCC->D3CFGR, RCC_D3CFGR_D3PPRE, (RCC_ClkInitStruct->APB4CLKDivider) );
//Bits 6:4 D3PPRE[2:0]: D3 domain APB4 prescaler
//100: rcc_pclk4 = rcc_hclk4 / 2
RCC -> D3CFGR |= RCC_D3CFGR_D3PPRE_2;
RCC -> D3CFGR &= ~RCC_D3CFGR_D3PPRE_1;
RCC -> D3CFGR &= ~RCC_D3CFGR_D3PPRE_0;
Для того чтобы убедиться, что конфигурирование и запуск прошли успешно используем выход микроконтроллера MCO2. На этом выходе должна быть частота 26.666 МГц при делителе выхода 15.
Отлично. Частота присутствует, значит все сделано правильно.
Автор: sanders1967