OpenRTX/platform/mcu/STM32H7xx/drivers/rcc.cpp

131 wiersze
5.6 KiB
C++

/***************************************************************************
* Copyright (C) 2018 by Terraneo Federico *
* 2024 - 2025 by Silvano Seva IU2KWO *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <stm32h7xx.h>
#include "rcc.h"
/**
* Clock tree configuration:
*
* - input clock from 25MHz external crystal
* - VCO clock 800MHz
* - PLL_P clock 400MHz
* - PLL_Q clock 100MHz
* - PLL_R clock 400MHz
*
* Clocks derived from PLL_P output:
* - CPU clock 400MHz
* - AHB1/2/3 clock 200MHz
* - APB1/2/4 clock 200MHz
*/
void startPll()
{
//In the STM32H7 DVFS was introduced (chapter 6, Power control)
//The default voltage is the lowest, switch to highest to run @ 400MHz
//QUIRK: it looks like VOS can only be set by first setting SCUEN to 0.
//This isn't documented anywhere in the reference manual
PWR->CR3 &= ~PWR_CR3_SCUEN;
PWR->D3CR = PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0;
while((PWR->D3CR & PWR_D3CR_VOSRDY)==0) ; //Wait
//Enable HSE oscillator
RCC->CR |= RCC_CR_HSEON;
while((RCC->CR & RCC_CR_HSERDY)==0) ; //Wait
//Start the PLL
RCC->PLLCKSELR |= RCC_PLLCKSELR_DIVM1_0
| RCC_PLLCKSELR_DIVM1_2 //M=5 (25MHz/5)5MHz
| RCC_PLLCKSELR_PLLSRC_HSE; //HSE selected as PLL source
RCC->PLL1DIVR = (2-1) << 24 // R=2
| (8-1) << 16 // Q=8
| (2-1) << 9 // P=2
| (160-1); // N=160
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_2 // Pll ref clock between 4 and 8MHz
| RCC_PLLCFGR_DIVP1EN // Enable output P
| RCC_PLLCFGR_DIVQ1EN // Enable output Q
| RCC_PLLCFGR_DIVR1EN; // Enable output R
RCC->CR |= RCC_CR_PLL1ON;
while((RCC->CR & RCC_CR_PLL1RDY)==0) ; //Wait
//Before increasing the fequency set dividers
RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1 //CPU clock /1
| RCC_D1CFGR_D1PPRE_DIV1 //D1 APB3 /1
| RCC_D1CFGR_HPRE_DIV2; //D1 AHB /2
RCC->D2CFGR = RCC_D2CFGR_D2PPRE2_DIV1 //D2 APB2 /1
| RCC_D2CFGR_D2PPRE1_DIV1;//D2 APB1 /1
RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV1; //D3 APB4 /1
//And increase FLASH wait states
FLASH->ACR = FLASH_ACR_WRHIGHFREQ_1 //Settings for FLASH freq=200MHz
| FLASH_ACR_LATENCY_3WS;
//Finally, increase frequency
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1) ; //Wait
//Configure PLL2 to have 168MHz on P output
RCC->PLLCKSELR |= 25 << RCC_PLLCKSELR_DIVM2_Pos; // M=25, VCO input 1MHz
RCC->PLL2DIVR = (168-1); // N=168, P,Q,R = 1
RCC->PLLCFGR |= RCC_PLLCFGR_PLL2VCOSEL // Medium VCO range
| RCC_PLLCFGR_DIVP2EN;
RCC->CR |= RCC_CR_PLL2ON; // Start PLL2
while((RCC->CR & RCC_CR_PLL2RDY)==0) ; // Wait until ready
// Configure USART6 kernel clock source to use PCLK2
// USART16SEL field is bits [5:3]: 000 = pclk2
RCC->D2CCIP2R &= ~RCC_D2CCIP2R_USART16SEL;
}
uint32_t getBusClock(const uint8_t bus)
{
switch(bus) {
case PERIPH_BUS_AHB:
return 200000000; // AHB: CPU(400MHz) / HPRE_DIV2 = 200MHz
break;
case PERIPH_BUS_APB1:
return 200000000; // APB1: AHB(200MHz) / D2PPRE1_DIV1 = 200MHz
break;
case PERIPH_BUS_APB2:
return 200000000; // APB2: AHB(200MHz) / D2PPRE2_DIV1 = 200MHz
break;
case PERIPH_BUS_APB3:
return 200000000; // APB3: AHB(200MHz) / D1PPRE_DIV1 = 200MHz
break;
case PERIPH_BUS_APB4:
return 200000000; // APB4: AHB(200MHz) / D3PPRE_DIV1 = 200MHz
break;
}
return 0;
}