이글의 전부 또는 일부, 사진, 소스프로그램 등은 저작자의 동의 없이는 상업적인 사용을 금지합니다. 또한, 비상업적인 목적이라하더라도 출처를 밝히지 않고 게시하는 것은 금지합니다.


본 글에서는 비록 초보적인 내용이지만, 필자가 터득한 내용을 이해하기 쉽도록 서술할 예정입니다. 마이크로프로세서를 전혀 다루지 않았던 사람들도 쉽게 이해할 수 있도록 기술할 예정입니다.

필자처럼 AVR을 다루다가 STM32를 익히고자 하는 경우에는 더 쉽게 이해할수 있을 것으로 예상합니다. 아울러 필자도 처음 STM32를 배우기 시작한 초보자임이기 때문에 정확하지 못한 표현 등이 있을 수 있음을 다시 한 번 밝힙니다. 혹시 잘못된 내용이나 정확하지 못한 부분을 발견하시면, 지적해 주시기 바랍니다. 학습하여 수정하도록 하겠습니다.

 

 

좀 지루하기는 하겠지만, 이글에서부터는 당분간 능력을 업그레이드 시키기 위해서 STM32CubeMx가 만들어 놓은 함수들을 분석할 예정입니다. AVR의 경우 초기화가 상당히 간단하고, 입출력 지정도 간단합니다.  하지만 STM32의 경우는 초기화 자체가 그리 녹녹하지 않은 것 같습니다. 물로 SM32CubeMx가 대부분의 초기화 코드들을 만들어 주기는 합니다만, 제대로 활용하기 위해서는 기본적인 내용을 이해하고 있어야할 것 같습니다. 예를 들어 클럭 초기화 부분만해도 상당히 복잡한 것 같습니다. 이번 글은 클럭 초기화 부분만 다루어 볼 예정입니다.




 

시스템 클럭 설정

 


시스템 클럭을 설정하는 함수는 SystemClock_Config입니다. STM32CubeMx가 만들어준 다음의 코드를 살펴보도록 하겠습니다. Project 창에서 Src 폴더 안에 있는 main.c를 더블 클릭하여 엽니다.

 

 

 

main.c 안에서 함수 void SystemClock_Config(void)를 찾습니다.

void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; /**Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /**Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 4; RCC_OscInitStruct.PLL.PLLN = 96; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Configure the Systick interrupt time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); /**Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* SysTick_IRQn interrupt configuration */ HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); }

 

 

 

 

4행은 OscInitTypeDef형 구조체 변수 RCC_OscInitStruct를 정의하였습니다. 구조체 OscInitTypeDef는 잠시 후에 자세히 살펴 보겠습니다.

 

5행은 RCC_ClkInitTypeDef 형 구조체 변수 RCC_ClkInitStruct를 정의하였습니다. 구조체 RCC_ClkInitTypeDef에 관하여도 본 글에서 살펴볼 예정입니다.

 

9행은 매크로 함수 __HAL_RCC_PWR_CLK_ENABLE();을 호출합니다. 매크로 함수 __HAL_RCC_PWR_CLK_ENABLE()은 프로젝트 창에 있는 Drivers\STM32F4xx_HAL_Driver\Inc 폴더의 stm32f4xx_hal_rcc.h에 다음과 같이 정의되어 있습니다.

 

 

#define __HAL_RCC_PWR_CLK_ENABLE()  do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                        UNUSED(tmpreg); \
                                        } while(0U)

 


위에서 사용된 매크로 함수 SET_BIT()와 READ_BIT()는 Drivers\CMSIS\Device\ST\STM324Fxx\Include\stm32f4xx.h에 다음과 같이 정의되어 있습니다. 


#define SET_BIT(REG, BIT)     ((REG) |= (BIT))
#define READ_BIT(REG, BIT)    ((REG) & (BIT))

 

SET_BIT() 매크로 함수는 REG 레지스터의 BIT를 1(high)로 설정합니다. 즉 SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);은 RCC_APB1ENR 레지스터의 PWREN 비트를 1(high)로 설정합니다. READ_BIT() 매크로는 REG 레지스터의 BIT에 해당하는 비트가 0(low)이면 0을, 1(high)이면 1을 가져옵니다. RCC는 Reset and Clock Control을 의미하며, STM32F407의 경우 RCC 레지스터는 0x4002 3800 ~ 0x4002 3BFF 번지에 있습니다. APB1ENR 레지스터의 Offset 주소는 0x40입니다. 즉, APB1ENR 레지스터는 0x4002 3840 번지에 위치합니다. 다음의 그림은 STM32F407의 APB1ENR 레지스터의 구조입니다.



 

이름에서 짐작할 수 있듯이 APB1ENR 레지스터는 APB1 버스를 Enable시키는 기능을 가지고 있으며, 그중 PWREN 비트는 APB1 버스에 전원 공급 여부를 결정합니다. 이 비트를 1로 설정하여 APB1 버스에 전원을 공급합니다. 다음의 그림에서 보듯이 APB1 버스에는 Backup SRAM, TIM2 를 비롯한 여러 타이머들, USART2 ~ 5, SPI2 ~ 3, I2C, CAN, WWDG 등 많은 주변 장치가 연결되어 있습니다.

 

11행은 매크로 함수 __HAL_PWR_VOLTAGESCALING_CONFIG()를 호출하고 있습니다. 이 매크로는 Drivers\ST32_HAL_Driver\Inc 폴더 안의 stm32f4xx_hal_pwr_ex.h 파일 안에 다음과 같이 정의되어 있습니다.

 

#define PWR_REGULATOR_VOLTAGE_SCALE1         PWR_CR_VOS             /* Scale 1 mode(default value at reset): the maximum value of fHCLK = 168 MHz. */
#define __HAL_PWR_VOLTAGESCALING_CONFIG(__REGULATOR__) do {                                                     \
                                                            __IO uint32_t tmpreg = 0x00U;                        \
                                                            MODIFY_REG(PWR->CR, PWR_CR_VOS, (__REGULATOR__));   \
                                                            /* Delay after an RCC peripheral clock enabling */  \
                                                            tmpreg = READ_BIT(PWR->CR, PWR_CR_VOS);             \
                                                            UNUSED(tmpreg);                                     \
                                                          } while(0U)

 

매크로 함수 MODIFY_REG()도 stm32f4xx.h에 다음과 같이 정의되어 있습니다. 이 매크로 함수는 첫번째 매개 변수인 REG 레지스터를 상대로 두번째 매개 변수 CLEARMASK 에 해당하는 비트들은 0(low)으로 해제하고, 세번째 매개 변수 SETMASK에 해당하는 비트들은 1(high)로 설정합니다. __HAL_PWR_VOLTAGESCALING_CONFIG()에 사용된 다른 매크로들은 다음과 같은 값을 갖도록 정의되어 있습니다.


#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

#define PWR_CR_VOS_Pos         (14U)                              /* stm32f407xx.h*/
#define PWR_CR_VOS_Msk         (0x1U << PWR_CR_VOS_Pos)           /*!< 0x00004000 */
#define PWR_CR_VOS             PWR_CR_VOS_Msk                     /*!< VOS bit (Regulator voltage scaling output selection) */

#define PWR_REGULATOR_VOLTAGE_SCALE1         PWR_CR_VOS           /* Scale 1 mode(default value at reset): the maximum value of fHCLK = 168 MHz. stm32f4xx_hal_pwr.h*/

 

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);은
MODIFY_REG(PWR->CR, PWR_CR_VOS,PWR_REGULATOR_VOLTAGE_SCALE1);인데,
PWR_REGULATOR_VOLTAGE_SCALE1은 PWR_CR_VOS이므로

최종적으로는 MODIFY_REG(PWR->CR, PWR_CR_VOS, PWR_CR_VOS);가 됩니다.
왜 이 매크로 함수를 썼는지 모르겠습니다. SET_BIT(PWR->CR, PWR_CR_VOS);를 쓴 것과 뭐가 다른지 궁금합니다.


STM32F407의 PWR은 전원 관리 레지스터들로 0x4000 7000 ~ 0x4000 73FF 번지에 위치합니다. PWR->CR은 POWER Control Register로 모든 기능을 다 다룰 수는 없고 본글과 관련된 비트14 VOS 부분만 발췌하였습니다.




VOS 비트를 1로 하면 POWER SCALE 1로 설정한 것이고, 이 비트를 0으로 하면 POWER SCALE 2로 설정한 것입니다. POWER SCALE 1과 POWER SCALE 2의 차이가 무엇인지에 대해서는 매뉴얼에 별도의 언급이 없습니다. 인터넷에서 정보를 찾아봐도 확실한 정보를 찾기가 어렵습니다.

언급한 김에 STM32 내부의 레귤레이터에 관한 내용을 정리해 보겠습니다. STM32F4에는 내부의 레귤레이터가 있는데, 이 레귤레이터가 backup domain과 Standby circuitry를 제외한 디지털 회로에 1.2V를 공급합니다. 이 레귤레이터가 동작하기 위해서 두 개의 외장 콘덴서가 필요합니다. STM32F407VGT6은 100pin 패키지로 49번 핀이 Vcap_1이고, 73번핀이 Vcap_2입니다. 이 두 핀에 적정 용량의 콘덴서를 달아야 합니다.
RESET 후에 소프트웨어로 활성화시키면 레귤레이터가 동작합니다. 응용 프로그램의 동작 모드에 따라 이 레귤레이터는 세가지 모드로 운영됩니다.

첫번째는 Run 모드입니다. 이 모드에서는 모든 1.2V 도메인에 full power를 공급합니다. 이 모드에서 VOS 비트 설정 여부에 따라 SCALE 1과 SCALE 2로 전원을 공급합니다. SCALE 1과 SCALE 2에 따라 최대 동작 속도가 정해지는 듯합니다. STM32F407VGT6에서는 SCALE 1 모드에서는 시스템 클럭 SYSCLK가 최고 속도인 168MHz로 동작 가능합니다만, SCALE 2에서는 144MHz까지만 동작 가능합니다.

두번째는 Stop 모드입니다. 이 모드에서는 메인 레귤레이터 혹은 저전압 레귤레이터가 내부 SRAM과 레지스터의 내용을 유지하도록 1.2V를 공급합니다. 이 모드의 voltage scale은 한 가지입니다.

세번째는 Stnadby 모드입니다. 이 모드에서는 backup domain과 Standby circuitry를 제외한 회로에 전원을 공급하지 않습니다. 이 회로를 제외한 레지스터와 SRAM의 내용은 보관되지 않습니다.

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);은 PWR->CR(0x4000 7000)의 VOS 비트를 1로 설정하여 MPU가 최대 클럭으로 동작할 수 있도록 설정합니다.

 

 

 

다음은 구조체 RCC_OscInitTypeDef에 관하여 탐구하면서 SystemConfig_Clock() 함수의 15행 이후에 AHB, APB 버스 등에 클럭을 제공하는 과정을 살펴봅니다.

.


구조체 RCC_OscInitTypeDef는 Drivers\STM32F4xx_HAL_Driver\Inc 폴더의 stm32f4xx_hal_rcc.h에 다음과 같이 정의되어 있습니다.

 

 

/* Exported types ------------------------------------------------------------*/
/** @defgroup RCC_Exported_Types RCC Exported Types
  * @{
  */

/**
  * @brief  RCC Internal/External Oscillator (HSE, HSI, LSE and LSI) configuration structure definition
  */
typedef struct
{
  uint32_t OscillatorType;       /*!< The oscillators to be configured.
                                      This parameter can be a value of @ref RCC_Oscillator_Type                   */
  uint32_t HSEState;             /*!< The new state of the HSE.
                                      This parameter can be a value of @ref RCC_HSE_Config                        */
  uint32_t LSEState;             /*!< The new state of the LSE.
                                      This parameter can be a value of @ref RCC_LSE_Config                        */
  uint32_t HSIState;             /*!< The new state of the HSI.
                                      This parameter can be a value of @ref RCC_HSI_Config                        */
  uint32_t HSICalibrationValue;  /*!< The HSI calibration trimming value (default is RCC_HSICALIBRATION_DEFAULT).
                                       This parameter must be a number between Min_Data = 0x00 and Max_Data = 0x1F */
  uint32_t LSIState;             /*!< The new state of the LSI.
                                      This parameter can be a value of @ref RCC_LSI_Config                        */
  RCC_PLLInitTypeDef PLL;        /*!< PLL structure parameters                                                    */
}RCC_OscInitTypeDef;

 

 

구조체 RCC_OscInitTypeDef의 첫 요소 OscillatorType에는 사용할 클럭 소스를 지정합니다. 여기에 들어 갈 수 있는 값은 다음과 같이 4개이며, 이 값은 역시 stm32f4xx_hal_rcc.h에 다음과 같이 정해져 있습니다.

 

#define  RCC_OSCILLATORTYPE_NONE  0x00000000U
#define  RCC_OSCILLATORTYPE_HSE   0x00000001U 
#define  RCC_OSCILLATORTYPE_HSI   0x00000002U 
#define  RCC_OSCILLATORTYPE_LSE   0x00000004U 
#define  RCC_OSCILLATORTYPE_LSI   0x00000008U 

 

HSE는 High Speed External의 약자입니다. 외부에서 공급하는 고속의 클럭 소스를 의미합니다. 앞글 STM32F407로 ESP8266 제어 프로젝트 만들기에서 보드에 달려 있는 8MHz 크리스털을 클럭 소스로 사용하도록 설정했기 때문에 위의 소스 프로그램에서 OscillatorType으로 RCC_OSCILLATORTYPE_HSE를 사용하도록 되어 있습니다.

HSI는 High Speed Internal의 약자입니다. STM32F4xx 내부에 RC 발진회로가 있습니다. 이 발진회로는 8MHz 혹은 16MHz로 동작하는데, 전원을 투입하면 일단 이 발진회로로 동작하다가 일정 시간이 경과한 후에 프로그램이 지정한 클럭으로 바뀝니다. AVR의 경우 클럭 소스를 잘못 지정했을 때에 MPU 자체가 동작하지 않아서 외부 클럭을 임시로 공급하는 소위 인공 호흡을 시켜야 했습니다. STM32는 소스 지정과 상관 없이 일단 내부 발진회로로 동작을 시작하기 때문에 인공 호흡을 시킬 일은 없습니다.

LSE는 Low Speed External의 약자이고, LSI는 Low Speed Internal의 약자입니다.

 

다음은 STM32F407의 Reference Manual에 있는 Clock tree입니다.

위의 회로를 보면 HSE, HSI, PLL Clock이 SW를 통해 SYSCLK이 되고, SYSCLK이 AHB Pres를 통해 주 클럭인 HCLK, system timer clock, APB 버스의 클럭이 되는 것을 파악할 수 있습니다. PLL(Phase Locked Loop)은 HSI나 HSE를 소스로 입력 받아서, 주파수를 증폭하여 공급합니다.

 

구조체 RCC_OscInitTypeDef의 HSEState는 HSE의 상태를 나타내는 값을 지정합니다. 여기에 사용할 수 있는 값은 다음과 같은 세가지이며, 역시 stm32f4xx_hal_rcc.h에 정의되어 있습니다. 매크로의 이름만 보아도 어떤 용도로 사용할 지 알 수 있습니다. Bypass는 용어의 뜻 그대로 외부의 입력 소스를 그대로 전달합니다.

 

#define RCC_HSE_OFF                0x00000000U
#define RCC_HSE_ON                 RCC_CR_HSEON
#define RCC_HSE_BYPASS             ((uint32_t)(RCC_CR_HSEBYP | RCC_CR_HSEON))

 

구조체 RCC_OscInitTypeDef의 다른 요소인 LSEState, HSIState, LSIState 들도 HSEState와 비슷한 용도로 사용합니다. 각각이 가질 수 있는 값은 다음과 같으며, 이 또한 stm32f4xx_hal_rcc.h에 정의되어 있습니다. 역시 매크로의 이름만 보아도 어떤 용도로 사용할는지는 쉽게 알 수 있습니다.

 

#define RCC_LSE_OFF                0x00000000U
#define RCC_LSE_ON                 RCC_BDCR_LSEON
#define RCC_LSE_BYPASS             ((uint32_t)(RCC_BDCR_LSEBYP | RCC_BDCR_LSEON))

#define RCC_HSI_OFF                ((uint8_t)0x00)
#define RCC_HSI_ON                 ((uint8_t)0x01)

#define RCC_LSI_OFF                ((uint8_t)0x00)
#define RCC_LSI_ON                 ((uint8_t)0x01)

 

 

HSICalibrationValue는 HSI의 오류를 보정하기 위한 수치를 입력합니다. STM32는 HSI와 LSI로 불리는 두 개의 내부 RC 발진회로를 가지고 있습니다. STM32 시리즈의 HSI는 16MHz로 동작하는 HSI16과 8MHz로 동작하는 HSI8 두 종류가 있습니다. STM32F407에는 HSI16이 적용되어 있습니다.

 

내부 발진회로를 클럭 소스로 사용하면 회로가 간단해지고, 그만큼 비용 절감 효과를 가집니다. 다만, 내부 발진회로는 전기적 잡음의 영향을 많이 받고, 동작 온도에 따라 오차가 발생한다는 단점을 가지고 있습니다. 이런 오차를 보정하는 용도로 이 구조체 요소를 사용합니다. STM32에는 HSI를 관리하는 레지스터 HSITRIM이 있습니다. 이 레지스터는 0비트부터 4비트까지(HSITRIM[0:4]) 5개 비트를 가지고 있습니다. 즉 HSICalibrationValue는 0x00부터 0x1F의 값을 가질 수 있습니다. MPU 리셋 후에 HSITRIM은 16(0x10)의 값을 갖습니다. HSITRIM의 값이 1변화하면 약 클럭이 0.3% 변화합니다. 따라서 HSICalibrationValue 값을 바꾸어 줌으로써 클럭 값을 ±4.5% 변화시킬 수 있습니다. 다음의 그림은 HSI16의 경우 HSICalibrationValue 값에 따른 클럭 값의 변화를 보여줍니다.

 

 

 

 

이번 프로젝트에서는 클럭 소스로 HSE를 사용하기때문에 구조체의 HSICalibrationValue는 사용할 필요가 없습니다.

 

 구조체 RCC_OscInitTypeDef는 구성 요소로 또 다른 구초체인 RCC_PLLInitTypeDef를 가지고 있습니다. 구조체 RCC_PLLInitTypeDef은 Drivers\STM32F4xx_HAL_Driver\Inc\stm32f4xx_hal_rcc_ex.h에 다음과 같이 정의되어 있습니다.

typedef struct
{
  uint32_t PLLState;   /*!< The new state of the PLL.
                            This parameter can be a value of @ref RCC_PLL_Config                      */

  uint32_t PLLSource;  /*!< RCC_PLLSource: PLL entry clock source.
                            This parameter must be a value of @ref RCC_PLL_Clock_Source               */

  uint32_t PLLM;       /*!< PLLM: Division factor for PLL VCO input clock.
                            This parameter must be a number between Min_Data = 0 and Max_Data = 63    */

  uint32_t PLLN;       /*!< PLLN: Multiplication factor for PLL VCO output clock.
                            This parameter must be a number between Min_Data = 50 and Max_Data = 432 
                            except for STM32F411xE devices where the Min_Data = 192 */

  uint32_t PLLP;       /*!< PLLP: Division factor for main system clock (SYSCLK).
                            This parameter must be a value of @ref RCC_PLLP_Clock_Divider             */

  uint32_t PLLQ;       /*!< PLLQ: Division factor for OTG FS, SDIO and RNG clocks.
                            This parameter must be a number between Min_Data = 2 and Max_Data = 15    */
#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) || defined(STM32F446xx) || defined(STM32F469xx) ||\
    defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) ||\
    defined(STM32F413xx) || defined(STM32F423xx)
  uint32_t PLLR;       /*!< PLLR: PLL division factor for I2S, SAI, SYSTEM, SPDIFRX clocks.
                            This parameter is only available in STM32F410xx/STM32F446xx/STM32F469xx/STM32F479xx
                            and STM32F412Zx/STM32F412Vx/STM32F412Rx/STM32F412Cx/STM32F413xx/STM32F423xx devices. 
                            This parameter must be a number between Min_Data = 2 and Max_Data = 7     */
#endif /* STM32F410xx || STM32F446xx || STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx || STM32F413xx || STM32F423xx */ 
}RCC_PLLInitTypeDef;

 

 

 

구조체의 첫 요소인 PLLState에 사용할 수 있는 값은 stm32f4xx_hal_rcc.h에 정의되어 있는 다음의 셋입니다.


#define RCC_PLL_NONE                      ((uint8_t)0x00)
#define RCC_PLL_OFF                       ((uint8_t)0x01)
#define RCC_PLL_ON                        ((uint8_t)0x02)



RCC-CFGR(RCC clock configuration register)을 잠시 보겠습니다.




0x4002 3808 번지에 있는 이 레지스터의 하위 두 비트 SW[1:0]에 0x02를 써 넣으면 PLL이 clock source switch를 통해 PLL이 주클럭으로 사용됩니다.

구조체의 두번째 요소인 PLLSource는 PLL의 입력 소스입니다. 이 값으로 사용할 수 있는 값은 다음의 두 가지 입니다.

#define RCC_PLLSOURCE_HSI                RCC_PLLCFGR_PLLSRC_HSI
#define RCC_PLLSOURCE_HSE                RCC_PLLCFGR_PLLSRC_HSE



위 매크로에 사용된 매크로 RCC_PLLCFGR_PLLSRC_HSI와 RCC_PLLCFGR_PLLSRC_HSE는 Drivers\CMSIS\Device\ST\STM32F4xx\Include\stm32f407xx.h에 다음과 같이 정의되어 있습니다.

#define RCC_PLLCFGR_PLLSRC_Pos             (22U)                               
#define RCC_PLLCFGR_PLLSRC_Msk             (0x1U << RCC_PLLCFGR_PLLSRC_Pos)    /*!< 0x00400000 */
#define RCC_PLLCFGR_PLLSRC                 RCC_PLLCFGR_PLLSRC_Msk              
#define RCC_PLLCFGR_PLLSRC_HSE_Pos         (22U)                               
#define RCC_PLLCFGR_PLLSRC_HSE_Msk         (0x1U << RCC_PLLCFGR_PLLSRC_HSE_Pos) /*!< 0x00400000 */
#define RCC_PLLCFGR_PLLSRC_HSE             RCC_PLLCFGR_PLLSRC_HSE_Msk          
#define RCC_PLLCFGR_PLLSRC_HSI             0x00000000U



RCC_PLLCFGR_PLLSRC_HSE는 22번째 비트를 1로 설정합니다. RCC_PLLCFGR(RCC PLL configuration register)를 살펴 보겠습니다.


>





0x4002 3804 번지에 있는 이 레지스터의 22번째 비트(빨간 사각형으로 표시함)를 1로 하면 PLL의 소스가 HSE로 지정되며, 0으로 하면 PLL의 소스로 HSI가 지정됩니다. 구조체의 다른 요소들도 이 레지스터와 관련이 있습니다.


세번째 구성 요소인 PLLM은 초록색으로 표시한 사각형 영역 PLLM[5:0]에 기록될 값이며, 네번째 구성 요소인 PLLN은 파란색으로 표시한 사각형 영역 PLLN[14:6]에 기록될 값입니다. 또한 다섯번째 구성 요소인 PLLP는 보라색 사각형으로 표시한 PLLP[17:16]에, 여섯번째 구성 요소인 PLLQ는 갈색으로 표시한 사각형 영역 PLLQ[27:24]에 들어갈 값입니다. PLLP 값으로 사용한 RCC_PLLP_DIV2는 Drivers\STM32F4xx_HAL_Driver\Inc\stm32f4xx_hal_rcc.h에 다음과 같이 정의되어 있습니다.

#define RCC_PLLP_DIV2                  0x00000002U
#define RCC_PLLP_DIV4                  0x00000004U
#define RCC_PLLP_DIV6                  0x00000006U
#define RCC_PLLP_DIV8                  0x00000008U



각 구성 요소가 같는 값의 의미는 STM32CubeMx의 Clock Configuration 을 보면 알 수 있습니다.



PLL에 입력된 소스 주파수를 PLLM으로 나눈 후에 PLLN으로 곱한 값을 다시 PLLP로 나누어 PLLCLK로 clock switch로 보내서 SYSCLK로 사용합니다. 이 프로젝트의 경우 HSE 8MHz를 PLLM(4)로 나누어 2MHz로 만들고, PLLN(96)을 곱해서 192MHz를 만든 후에 PLLP(2)로 나누어 96MHz를 SYSCLK로 사용합니다. 이 96MHz의 SYSCLK를 AHB Prescaler가 2로 나누어 48MHz를 APB1과 APB2의 클럭으로 사용할 수 있게 보냅니다. 48MHz를 APB1과 APB2의 프리스케일러가 2 또는 1로 나누어 각각의 버스에 달린 주변장치나 타이머의 클럭으로 제공합니다.

PLLM의 값은 2이상 63이하의 값을 가질 수 있지만, PLL의 입력 주파수가 1 ~ 2MHz 범위 안에 들도록 해야합니다. PLL 클럭의 jitter를 방지하려면 2MHz로 입력 되도록 권장합니다.

PLLN의 값은 50부터 432의 값을 가지는데 결과가 100MHz 이상 432MHz 이하의 범위에 들도록 해야 합니다. PLLP은 2개의 비트이므로 0부터 3의 값을 갖는데 이 값은 각각 2, 4, 6, 8의 값으로 클럭을 분주합니다.





아직 SystemClock_Config() 함수를 다 분석하지는 못했지만 글이 너무 길어져서 다음 글로 넘겨서 계속 분석하겠습니다.



블로그 이미지

엠쿠스

Microprocessor(STM32, AVR)로 무엇인가를 만들어 보고자 학습 중입니다.

,