STM32H7 — настройка тактирования без HAL

Не так давно компания 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 МГц.

gnob_rftv_katjplg7v42hfavjc.jpeg

Кроме 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.

b62wdfa3oyu5myzx7-dacvqofxy.jpeg

Отлично. Частота присутствует, значит все сделано правильно.

© Habrahabr.ru