در ادامه آموزش قبلی که در مورد درخت کلاک و نحوه تخصیص کلاک سیستم صحبت کردم، میخواهم کدهای مربوط به این بخش را کامل کنم و در یک فایل مجزا (و قابل تغییر) به پروژه اضافه کنم.
در بررسی درخت کلاک تصمیم گرفتیم که فرکانس کلاک SYSCLK برابر با ۷۲ مگاهرتز تنظیم بشه و کلاک سایر پریفرالها در حداکثر مقدار ممکن قرار بگیره.
/*
* Clock Configuration parameters
*
* HSE = 8MHz
* PLL_M = 9
* USB prescaler = 1.5
* AHB prescaler = 1
* Cortex prescaler =1 -->72MHz System Clock
*
* APB1 prescaler = 2 --> 36MHz for APB1 peripherals, 72MHz for Timer Clock
* APB2 prescaler = 1 --> 72MHz for APB1 peripherals, 72MHz for Timer Clock
* ADC prescaler = 6 --> 12MHz for ADC Clock
*
*/
برای این منظور در محیط STM32CubeIDE و در پروژه ای که ساختیم، در دایرکتوری Peripherals دو ساب دایرکتوری Inc و Src میسازیم (در صورتیکه قبلا ساخته شده از این مرحله عبور کنید).
در ادامه در فولدر Inc یک فایل هدر تنظیمات با نام rcc.h و در فولدر Src یک فایل برنامه با نام rcc.c اضافه میکنیم. از این فایلها برای نوشتن توابع مربوط به تنظیم کلاک و منبع کلاک سیستم استفاده خواهیم کرد.
در فایل main.h هدر فایل stdio.h و stdbool.h را اضافه میکنیم.
#include <stdio.h>
#include <stdbool.h>
در فایل rcc.h ابتدا هدرفایل "main.h" را اضافه میکنیم:
#include "main.h"
سپس یک تابع برای انجام تنظیمات کلاک ایجاد کرده و Declaration تابع را انجام میدهیم:
/*
* @brief RCC system clock configuration
*/
bool rcc_systemclockconfig(void);
تابع rcc_systemclockconfig یک تابع با مقدار بازگشتیاز نوع bool است که مقادیر false یا true را برمیگرداند و به برنامه صحت تنظیمات یا اشکال در تنظیمات را اطلاع میدهد.
در نهایت فایل rcc.h بصورت زیر خواهد بود:
#ifndef INC_RCC_H_
#define INC_RCC_H_
#include "main.h"
/*
* @brief RCC system clock configuration
*/
bool rcc_systemclockconfig(void);
#endif /* INC_RCC_H_ */
در فایل rcc.c تنظیمات به شرح زیر است:
فایلهای stm32f1xx_hal_rcc.h و stm32f1xx_hal_rcc_ex.h در دایرکتوری HAL و ساب دایرکتوری STM32F1xx_HAL_Driver و فولدر Inc قرار دارد. (در قسمت اول آموزش stm32f1 از ابتدا نحوه اضافه کردن فایلها و ایجاد دایرکتوری های مناسب بیان شد.) در این فایلها داده ساختار مورد استفاده برای آدرس دهی رجیسترهای RCC معرفی شده اند. با استفاده از این ساختارها (Structure) دسترسی به رجیسترها و تغییرات مناسب را اعمال خواهیم کرد.
با وجود اینکه با داشتن آدرس رجیسترها به راحتی امکان تغییر در آنها میسر است، ولی بجهت راحتی کار در آینده درباره ایجاد ساختمان داده برای تغییر رجیسترها توضیحات مبسوط و کامل ارائه خواهد شد.
استراکچر مورد استفاده در فایل stm32f1xx_hal_rcc_ex.h در خطوط ۲۲۵ امکان معرفی نوع منبع کلاک سیستم (HSE, HSI, LSE و LSI) را فراهم میکند و بصورت زیر معرفی شده است:
/**
* @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 */
#if defined(STM32F105xC) || defined(STM32F107xC)
uint32_t Prediv1Source; /*!< The Prediv1 source value.
This parameter can be a value of @ref RCCEx_Prediv1_Source */
#endif /* STM32F105xC || STM32F107xC */
uint32_t HSEState; /*!< The new state of the HSE.
This parameter can be a value of @ref RCC_HSE_Config */
uint32_t HSEPredivValue; /*!< The Prediv1 factor value (named PREDIV1 or PLLXTPRE in RM)
This parameter can be a value of @ref RCCEx_Prediv1_Factor */
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 */
#if defined(STM32F105xC) || defined(STM32F107xC)
RCC_PLL2InitTypeDef PLL2; /*!< PLL2 structure parameters */
#endif /* STM32F105xC || STM32F107xC */
} RCC_OscInitTypeDef;
این استراکچر با تعریف اعضایی از نوع متغیرهای unsigned int با طول ۳۲ بیت در داخل خود امکان تنظیم حالات مختلف اسیلاتور را فراهم میاورد. در ادامه با استفاده از این داده ساختار آشنا میشویم.
همچنین استراکچر زیر در خط ۶۱ فایل stm32f1xx_hal_rcc.h جهت تنظیم کلاک سیستم، کلاک باس های سیستم (AHB, APB) استفاده میشود:
/**
* @brief RCC System, AHB and APB busses clock configuration structure definition
*/
typedef struct
{
uint32_t ClockType; /*!< The clock to be configured.
This parameter can be a value of @ref RCC_System_Clock_Type */
uint32_t SYSCLKSource; /*!< The clock source (SYSCLKS) used as system clock.
This parameter can be a value of @ref RCC_System_Clock_Source */
uint32_t AHBCLKDivider; /*!< The AHB clock (HCLK) divider. This clock is derived from the system clock (SYSCLK).
This parameter can be a value of @ref RCC_AHB_Clock_Source */
uint32_t APB1CLKDivider; /*!< The APB1 clock (PCLK1) divider. This clock is derived from the AHB clock (HCLK).
This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
uint32_t APB2CLKDivider; /*!< The APB2 clock (PCLK2) divider. This clock is derived from the AHB clock (HCLK).
This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
} RCC_ClkInitTypeDef;
از این استراکچر هم برای تنظیمات کلاک سیستم استفاده خواهیم کرد.
در فایل rcc.c ابتدا هدر فایل rcc.h را اضافه میکنیم:
#include "rcc.h"
در ادامه تابعی که در هدر معرفی کرده بودیم (rcc_systemclockconfig) را مینویسیم.
bool rcc_systemclockconfig(void)
{
/*
* Clock Configuration parameters
*
* HSE = 8MHz
* PLL_M = 9
* USB prescaler = 1.5
* AHB prescaler = 1
* Cortex prescaler =1 -->72MHz System Clock
*
* APB1 prescaler = 2 --> 36MHz for APB1 peripherals, 72MHz for Timer Clock
* APB2 prescaler = 1 --> 72MHz for APB1 peripherals, 72MHz for Timer Clock
* ADC prescaler = 6 --> 12MHz for ADC Clock
*
*/
RCC_OscInitTypeDef oscInitStruct = {0}; //from stm32f1xx_hal_rcc_ex.h
RCC_ClkInitTypeDef clkInitStruct = {0}; //from stm32f1xx_hal_rcc.h
//Oscillator initialization
oscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // from oscillator type in stm32f1xx_hal_rcc.h
oscInitStruct.HSEState = RCC_HSE_ON; // from oscillator type in stm32f1xx_hal_rcc.h
oscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; // from oscillator type in stm32f1xx_hal_rcc.h
oscInitStruct.PLL.PLLState = RCC_PLL_ON;
oscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
oscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if(HAL_RCC_OscConfig(&oscInitStruct) != HAL_OK)
{
return false;
}
//Clock Initialization
clkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
clkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
clkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
//0 - 24MHz --> 0
//24 - 48MHz --> 1
//48 - 72MHz --> 2
if(HAL_RCC_ClockConfig(&clkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
return false;
}
return true;
}
شرح تابع :
ابتدا از استراکچر های معرفی شده در بالا دو متغیر میسازیم:
RCC_OscInitTypeDef oscInitStruct = {0}; //from stm32f1xx_hal_rcc_ex.h
RCC_ClkInitTypeDef clkInitStruct = {0}; //from stm32f1xx_hal_rcc.h
ابتدا به ساکن با استفاده از انتساب {0} به استراکچرها، تمامی اعضای آن را 0 میکنیم.
سپس تنظیم و مقدار دهی اولیه اسیلاتور را انجام میدهیم. با نوشتن نام متغیرو سپس «.» امکان دسترسی به اعضای داخل استراکچر و تغییر و مقدار دهی آنها مهیا میگردد.
*- ابتدا نوع نوسان ساز را انتخاب میکنیم:
oscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // from oscillator type in stm32f1xx_hal_rcc.h
نوع متغیر هایی که OscillatorType میتواند دریافت کند به شرح زیر است. با قرار دادن کرسر موس بر روی عبارت OscillatorType و زدن کلید F3 به جایی که این متغیرها تعریف شده اند هدایت میشویم (فایل stm32f1xx_hal_rcc.h). متغیرهای در دسترس به شرح زیر است:
/** @defgroup RCC_Oscillator_Type Oscillator Type
* @{
*/
#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
بجای کریستال خارجی میتوان منبع HSI یا LSE یا LSI را انتخاب کرد.
- HSI به معنی High-Speed Internal Oscillator یا اسیلاتور پر سرعت داخلی است. (مدار نوسان ساز RC)
- HSE به معنی High-Speed External Oscillator یا اسیلاتور پر سرعت خارجی است. (کریستال یا سرامیک خارجی)
- LSE به معنی Low Speed External یا استفاده از منبع کلاک کم سرعت (32.768KHz) خارجی است.
- LSI به معنی Low Speed Internal یا استفاده از منبع کلاک کم سرعت (32.768KHz) داخلی است.
- RCC_HSE_OFF: بارگذاری HSEState با این مقدار باعث خاموش شدن اسیلاتور و Low شدن فلگ HSERDY بعد از ۶ کلاک سیستم میشود.
- RCC_HSE_ON: همانگونه که گفته شد این مقدار باعث روشن شدن اسیلاتور HSE میگردد.
- RCC_HSE_BYPASS: این گزینه در هنگام استفاده از یک مولد سیگنال خارجی بجز کریستال استفاده میشود. به این صورت که با این انتخاب خروجی پایه نوسانگر (XTAL Loop Output) خاموش میشود.
/**
* @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 */
#if defined(STM32F105xC) || defined(STM32F107xC)
uint32_t Prediv1Source; /*!< The Prediv1 source value.
This parameter can be a value of @ref RCCEx_Prediv1_Source */
#endif /* STM32F105xC || STM32F107xC */
uint32_t HSEState; /*!< The new state of the HSE.
This parameter can be a value of @ref RCC_HSE_Config */
uint32_t HSEPredivValue; /*!< The Prediv1 factor value (named PREDIV1 or PLLXTPRE in RM)
This parameter can be a value of @ref RCCEx_Prediv1_Factor */
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 */
#if defined(STM32F105xC) || defined(STM32F107xC)
RCC_PLL2InitTypeDef PLL2; /*!< PLL2 structure parameters */
#endif /* STM32F105xC || STM32F107xC */
} RCC_OscInitTypeDef;
/**
* @brief RCC PLL configuration structure definition
*/
typedef struct
{
uint32_t PLLState; /*!< PLLState: The new state of the PLL.
This parameter can be a value of @ref RCC_PLL_Config */
uint32_t PLLSource; /*!< PLLSource: PLL entry clock source.
This parameter must be a value of @ref RCC_PLL_Clock_Source */
uint32_t PLLMUL; /*!< PLLMUL: Multiplication factor for PLL VCO input clock
This parameter must be a value of @ref RCCEx_PLL_Multiplication_Factor */
} RCC_PLLInitTypeDef;
/** @defgroup RCC_PLL_Config PLL Config
* @{
*/
#define RCC_PLL_NONE 0x00000000U /*!< PLL is not configured */
#define RCC_PLL_OFF 0x00000001U /*!< PLL deactivation */
#define RCC_PLL_ON 0x00000002U /*!< PLL activation */
/** @defgroup RCC_PLL_Clock_Source PLL Clock Source
* @{
*/
#define RCC_PLLSOURCE_HSI_DIV2 0x00000000U /*!< HSI clock divided by 2 selected as PLL entry clock source */
#define RCC_PLLSOURCE_HSE RCC_CFGR_PLLSRC /*!< HSE clock selected as PLL entry clock source */
/** @defgroup RCCEx_PLL_Multiplication_Factor PLL Multiplication Factor
* @{
*/
#if defined(STM32F105xC) || defined(STM32F107xC)
#else
#define RCC_PLL_MUL2 RCC_CFGR_PLLMULL2
#define RCC_PLL_MUL3 RCC_CFGR_PLLMULL3
#endif /* STM32F105xC || STM32F107xC */
#define RCC_PLL_MUL4 RCC_CFGR_PLLMULL4
#define RCC_PLL_MUL5 RCC_CFGR_PLLMULL5
#define RCC_PLL_MUL6 RCC_CFGR_PLLMULL6
#define RCC_PLL_MUL7 RCC_CFGR_PLLMULL7
#define RCC_PLL_MUL8 RCC_CFGR_PLLMULL8
#define RCC_PLL_MUL9 RCC_CFGR_PLLMULL9
#if defined(STM32F105xC) || defined(STM32F107xC)
#define RCC_PLL_MUL6_5 RCC_CFGR_PLLMULL6_5
#else
#define RCC_PLL_MUL10 RCC_CFGR_PLLMULL10
#define RCC_PLL_MUL11 RCC_CFGR_PLLMULL11
#define RCC_PLL_MUL12 RCC_CFGR_PLLMULL12
#define RCC_PLL_MUL13 RCC_CFGR_PLLMULL13
#define RCC_PLL_MUL14 RCC_CFGR_PLLMULL14
#define RCC_PLL_MUL15 RCC_CFGR_PLLMULL15
#define RCC_PLL_MUL16 RCC_CFGR_PLLMULL16
#endif /* STM32F105xC || STM32F107xC */
if(HAL_RCC_OscConfig(&oscInitStruct) != HAL_OK)
{
return false;
}
/**
* @brief HAL Status structures definition
*/
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
/**
* @brief RCC System, AHB and APB busses clock configuration structure definition
*/
typedef struct
{
uint32_t ClockType; /*!< The clock to be configured.
This parameter can be a value of @ref RCC_System_Clock_Type */
uint32_t SYSCLKSource; /*!< The clock source (SYSCLKS) used as system clock.
This parameter can be a value of @ref RCC_System_Clock_Source */
uint32_t AHBCLKDivider; /*!< The AHB clock (HCLK) divider. This clock is derived from the system clock (SYSCLK).
This parameter can be a value of @ref RCC_AHB_Clock_Source */
uint32_t APB1CLKDivider; /*!< The APB1 clock (PCLK1) divider. This clock is derived from the AHB clock (HCLK).
This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
uint32_t APB2CLKDivider; /*!< The APB2 clock (PCLK2) divider. This clock is derived from the AHB clock (HCLK).
This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
} RCC_ClkInitTypeDef;
clkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
/** @defgroup RCC_System_Clock_Type System Clock Type
* @{
*/
#define RCC_CLOCKTYPE_SYSCLK 0x00000001U /*!< SYSCLK to configure */
#define RCC_CLOCKTYPE_HCLK 0x00000002U /*!< HCLK to configure */
#define RCC_CLOCKTYPE_PCLK1 0x00000004U /*!< PCLK1 to configure */
#define RCC_CLOCKTYPE_PCLK2 0x00000008U /*!< PCLK2 to configure */
/** @defgroup RCC_System_Clock_Source System Clock Source
* @{
*/
#define RCC_SYSCLKSOURCE_HSI RCC_CFGR_SW_HSI /*!< HSI selected as system clock */
#define RCC_SYSCLKSOURCE_HSE RCC_CFGR_SW_HSE /*!< HSE selected as system clock */
#define RCC_SYSCLKSOURCE_PLLCLK RCC_CFGR_SW_PLL /*!< PLL selected as system clock */
/** @defgroup RCC_AHB_Clock_Source AHB Clock Source
* @{
*/
#define RCC_SYSCLK_DIV1 RCC_CFGR_HPRE_DIV1 /*!< SYSCLK not divided */
#define RCC_SYSCLK_DIV2 RCC_CFGR_HPRE_DIV2 /*!< SYSCLK divided by 2 */
#define RCC_SYSCLK_DIV4 RCC_CFGR_HPRE_DIV4 /*!< SYSCLK divided by 4 */
#define RCC_SYSCLK_DIV8 RCC_CFGR_HPRE_DIV8 /*!< SYSCLK divided by 8 */
#define RCC_SYSCLK_DIV16 RCC_CFGR_HPRE_DIV16 /*!< SYSCLK divided by 16 */
#define RCC_SYSCLK_DIV64 RCC_CFGR_HPRE_DIV64 /*!< SYSCLK divided by 64 */
#define RCC_SYSCLK_DIV128 RCC_CFGR_HPRE_DIV128 /*!< SYSCLK divided by 128 */
#define RCC_SYSCLK_DIV256 RCC_CFGR_HPRE_DIV256 /*!< SYSCLK divided by 256 */
#define RCC_SYSCLK_DIV512 RCC_CFGR_HPRE_DIV512 /*!< SYSCLK divided by 512 */
برای تنظیم مقسم فرکانسی APB2 Prescaler از عضو APB2CLKDivider در ساختار RCC_ClkInitTypeDef استفاده میکنیم:
uint32_t APB2CLKDivider; /*!< The APB2 clock (PCLK2) divider. This clock is derived from the AHB clock (HCLK).
This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
/** @defgroup RCC_APB1_APB2_Clock_Source APB1 APB2 Clock Source
* @{
*/
#define RCC_HCLK_DIV1 RCC_CFGR_PPRE1_DIV1 /*!< HCLK not divided */
#define RCC_HCLK_DIV2 RCC_CFGR_PPRE1_DIV2 /*!< HCLK divided by 2 */
#define RCC_HCLK_DIV4 RCC_CFGR_PPRE1_DIV4 /*!< HCLK divided by 4 */
#define RCC_HCLK_DIV8 RCC_CFGR_PPRE1_DIV8 /*!< HCLK divided by 8 */
#define RCC_HCLK_DIV16 RCC_CFGR_PPRE1_DIV16 /*!< HCLK divided by 16 */
if(HAL_RCC_ClockConfig(&clkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
return false;
}
return true;
}
//0 - 24MHz --> 0 cycle
//24 - 48MHz --> 1 cycle
//48 - 72MHz --> 2 cycle
/** @defgroup FLASH_Latency FLASH Latency
* @{
*/
#define FLASH_LATENCY_0 0x00000000U /*!< FLASH Zero Latency cycle */
#define FLASH_LATENCY_1 FLASH_ACR_LATENCY_0 /*!< FLASH One Latency cycle */
#define FLASH_LATENCY_2 FLASH_ACR_LATENCY_1 /*!< FLASH Two Latency cycles */
هیچ نظری موجود نیست:
ارسال یک نظر