STM32H7 - configuración del reloj sin HAL

No hace mucho tiempo, la compañía STM lanzó al mercado una línea de cristales STM32H7 muy poderosa, según los estándares de los microcontroladores. Lo que me atrajo de ella:

  • aumento de la frecuencia del núcleo hasta 400 MHz
  • RAM aumentada, hasta 1 MB
  • ADC de 16 bits
  • compatibilidad pin a pin con la serie F7

Pensé perfectamente, solde el cristal STM32H743IIT6 en el tablero en lugar del STM32F746IGT6 y comencé un nuevo proyecto en SW4STM32.

Es conveniente utilizar la pestaña Configuración de reloj del programa STM32CubeMX para calcular los coeficientes de los divisores y los factores del sistema de reloj del microcontrolador.

Configuraciones de reloj:

  • cuarzo externo - 8 MHz
  • fuente de frecuencia para PLL1 - cuarzo externo (HSE)
  • divisor para PLL1 - 4 (DIVM1)
  • multiplicador PLL1 - 400 (DIVN1)
  • divisores de salida - 2 (DIVP1, DIVQ1, DIVR1)

En consecuencia, la frecuencia central (SYSCLK) es de 400 MHz.



Además del STM32CubeMX, también está el paquete de firmware STM32CubeH7, que contiene una gran cantidad de ejemplos para trabajar con periféricos para el STM32H7. Fue de él que se tomó la secuencia de inicialización del sistema de reloj del microcontrolador.

La información y los comentarios se toman de las siguientes fuentes:

  • SystemClock_Config del paquete de firmware STM32CubeH7
  • Manual de referencia STM32H743 / 753 y STM32H750 MCU avanzadas de 32 bits basadas en ARM
  • - Hoja de datos STM32H743xI

Entonces comencemos.

1. Encender el cuarzo externo y esperar la preparación.

// Enable HSE RCC->CR |= RCC_CR_HSEON; // Wait till HSE is ready while((RCC->CR & RCC_CR_HSERDY) == 0); 

2. Indicación de la fuente de frecuencia para PLL1 - cuarzo externo.

 //RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC -> PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSE; 

3. El valor del divisor se establece en 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. El factor N y los 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. Divisor de frecuencia fraccional PLL (si es necesario)

 // /* Configure PLL PLL1FRACN */ //__HAL_RCC_PLLFRACN_CONFIG(RCC_OscInitStruct->PLL.PLLFRACN); RCC -> PLL1FRACR = 0; 

6. Indicación del rango de frecuencia de entrada PLL1

  /* Select PLL1 input reference frequency range: VCI */ //__HAL_RCC_PLL_VCIRANGE(RCC_OscInitStruct->PLL.PLLRGE) ; RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_3; 

7. Indicación del rango de frecuencia de salida PLL1

  /* Select PLL1 output frequency range : VCO */ //__HAL_RCC_PLL_VCORANGE(RCC_OscInitStruct->PLL.PLLVCOSEL) ; RCC->PLLCFGR &= ~RCC_PLLCFGR_PLL1VCOSEL; 

8. Conexión de divisores de salida 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. La inclusión del divisor fraccional.

  /* Enable PLL1FRACN . */ //__HAL_RCC_PLLFRACN_ENABLE(); RCC->PLLCFGR |= RCC_PLLCFGR_PLL1FRACEN; 

10. Inicie PLL1 y espere la preparación

  /* Enable the main PLL. */ //__HAL_RCC_PLL_ENABLE(); RCC->CR |= RCC_CR_PLLON; while((RCC->CR & RCC_CR_PLL1RDY) == 0); 

PLL1 está configurado y ejecutándose. Ahora seleccione la fuente de frecuencia SYSCLK y configure los divisores de bus.

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. Sin división 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. Establezca PLL1 como fuente SYSCLK y espere la preparación

  //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 asegurarnos de que la configuración y el inicio fueron exitosos, utilizamos la salida del microcontrolador MCO2. Esta salida debe tener una frecuencia de 26.666 MHz con un divisor de salida de 15.



Genial La frecuencia está presente, entonces todo se hace correctamente.

Source: https://habr.com/ru/post/es427435/


All Articles