Há pouco tempo, a empresa STM lançou no mercado uma muito poderosa, pelos padrões dos microcontroladores, uma linha de cristais STM32H7. O que me atraiu para ela:
- frequência de núcleo aumentada até 400 MHz
- RAM aumentada, até 1 MB
- ADC de 16 bits
- compatibilidade pin-to-pin com a série F7
Pensei perfeitamente: soldei o cristal STM32H743IIT6 na placa em vez do STM32F746IGT6 e iniciei um novo projeto no SW4STM32.
É conveniente usar a guia Clock Configuration do programa STM32CubeMX para calcular os coeficientes dos divisores e fatores do sistema de clock do microcontrolador.
Configurações do relógio:
- quartzo externo - 8 MHz
- fonte de frequência para PLL1 - quartzo externo (HSE)
- divisor para PLL1 - 4 (DIVM1)
- multiplicador PLL1 - 400 (DIVN1)
- divisores de saída - 2 (DIVP1, DIVQ1, DIVR1)
Por conseguinte, a frequência de núcleo (SYSCLK) é de 400 MHz.

Além do STM32CubeMX, também há o pacote de firmware STM32CubeH7, que contém um grande número de exemplos para trabalhar com periféricos para o STM32H7. Foi a partir dele que a sequência de inicialização do sistema de relógio do microcontrolador foi realizada.
Informações e comentários são obtidos das seguintes fontes:
- SystemClock_Config do pacote de firmware STM32CubeH7
- Manual de referência MCUs avançados de 32 bits baseados em ARM STM32H743 / 753 e STM32H750
- - Folha de dados STM32H743xI
Então, vamos começar.
1. Ligar quartzo externo e aguardar prontidão.
// Enable HSE RCC->CR |= RCC_CR_HSEON; // Wait till HSE is ready while((RCC->CR & RCC_CR_HSERDY) == 0);
2. Indicação da fonte de frequência para PLL1 - quartzo externo.
3. O valor do divisor está definido como 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. O fator N e os divisores 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 do divisor de frequência fracionária (se necessário)
// /* Configure PLL PLL1FRACN */ //__HAL_RCC_PLLFRACN_CONFIG(RCC_OscInitStruct->PLL.PLLFRACN)
6. Indicação da faixa de frequência de entrada PLL1
7. Indicação da faixa de frequência de saída PLL1
8. Ligar os divisores de saída 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. A inclusão do divisor fracionário.
10. Inicie o PLL1 e aguarde a prontidão
PLL1 está configurado e em execução. Agora selecione a fonte de frequência SYSCLK e configure os divisores de barramento.
11. Divisor por 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. Sem divisão 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. Defina PLL1 como fonte SYSCLK e aguarde a disponibilidade
//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. Divisor por 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. Divisor por 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. Divisor por 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. Divisor por 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;
Para garantir que a configuração e o início tenham sido bem-sucedidos, usamos a saída do microcontrolador MCO2. Esta saída deve ter uma frequência de 26.666 MHz com um divisor de saída de 15.

Ótimo. A frequência está presente, então tudo é feito corretamente.