۱۴۰۳ مهر ۱۱, چهارشنبه

برنامه نویسی میکروکنترلر STM32 از ابتدا (From scratch) - تنظیمات کلاک قسمت دوم

 

در ادامه آموزش قبلی که در مورد درخت کلاک و نحوه تخصیص کلاک سیستم صحبت کردم، میخواهم کدهای مربوط به این بخش را کامل کنم و در یک فایل مجزا (و قابل تغییر) به پروژه اضافه کنم.

در بررسی درخت کلاک تصمیم گرفتیم که فرکانس کلاک 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_OSCILLATORTYPE_HSE انتخاب شده است. ( استفاده از کریستال ۸ مگاهرتزی موجود بر روی برد Blue Pill).

*- در مرحله بعد اقدام به روشن کردن اسیلاتور HSE میکنیم. برای این منظور به عضو HSEState مقدار مناسب را تخصیص میدهیم:
oscInitStruct.HSEState = RCC_HSE_ON; // from oscillator type in stm32f1xx_hal_rcc.h
مقادیر دیگر شامل موارد زیر است:
  • RCC_HSE_OFF: بارگذاری HSEState با این مقدار باعث خاموش شدن اسیلاتور و Low شدن فلگ HSERDY بعد از ۶ کلاک سیستم میشود.
  • RCC_HSE_ON: همانگونه که گفته شد این مقدار باعث روشن شدن اسیلاتور HSE میگردد.
  • RCC_HSE_BYPASS: این گزینه در هنگام استفاده از یک مولد سیگنال خارجی بجز کریستال استفاده می‌شود. به این صورت که با این انتخاب خروجی پایه نوسانگر (XTAL Loop Output) خاموش می‌شود.
*- متغیر بعدی برای مقداردهی، عضو HSEPredivValue است.
oscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; // from oscillator type in stm32f1xx_hal_rcc.h
این عضو در استراکچر مقدار اولین مقسم فرکانسی بعد از ورود کلاک کریستال را تعیین می‌کند:



در صورتیکه از تراشه STM32f103 استفاده می‌کنید، تنها دو مقدار قابل انتخاب در دسترس شما وجود دارد. با استفاده از تراشه های دیگر خانواده STM میتوانید ضریب مقسم فرکانسی متفاوتی را استفاده نمایید.
در این مرحله تعیین میکنیم که فرکانس ورودی از کریستال خارجی بدون تغییر به PLL وارد بشود یا فرکانس نصف شده سپس به PLL وارد شود. با انتخاب ضریب ۱ فرکانس ورودی بدون تغییر به PLL میرسد.

*- در مرحله بعد ضرایب PLL تعیین میگردد:
از بررسی استراکچر RCC_OscInitTypeDef مشخص میگردد که عضو PLL خود یک استراکچر است که مقادیر اعضاء آن باید به نحو مناسبی مقدار دهی بشوند. (خط مربوطه با رنگ نارنجی مشخص شده است.)

/**

* @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;


با قرار دادن کرسر موس بر روی عضو (استراکچر) PLL و زدن F3 مشخص می‌شود که تعاریف مربوطه در فایل stm32f1xx_hal_rcc.h قرار دارد.

/**

* @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;

استراکچر فوق بصورت زیر مقدار دهی میشود:
PLLState: 

/** @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 */

PLLSource:

/** @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 */

PLLMUL:

/** @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 */

تعریف مربوط به PLLMUL در فایل stm32f1xx_hal_rcc_ex.h قرار دارد و مقادیر PLLState و PLLSource در فایل stm32f1xx_hal_rcc.h تعریف شده اند.
با توجه به تعاریف استراکچر PLLمیتوانیم مقادیر مناسب را برای تنظیم و فعال کردم PLL انتخاب کنیم.
ابتدا PLL را روشن می‌کنیم:
oscInitStruct.PLL.PLLState = RCC_PLL_ON;
سپس منبع پالس ورودی PLL را کریستال خارجی (HSE) قرار می‌دهیم. در صورت استفاده از HSI، ابتدا فرکانس اسیلاتور نصف می‌شود سپس به PLL اعمال می‌گردد.
oscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
در انتها مقدار ضریب PLLMUL را تعیین میکنیم. برای رسیدن به فرکانس ۷۲ مگاهرتز باید فرکانس اسیلاتور ۸ مگاهرتزی در ۹ ضرب شود، ضریب PLLMUL را ۹ درنظر میگیریم:
oscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
در نهایت در صورتیکه راه اندازی منبع کلاک و PLL با مشکل مواجه شود، اجرای کد در همین مرحله متوقف شده و BUS های سیستم تنظیم نمیشوند.

if(HAL_RCC_OscConfig(&oscInitStruct) != HAL_OK)

{

return false;

}

مقدار HAL_OK در یک enum با نام HAL_StatusTypeDef تعریف شده است که برابر با مقدار 0 می‌باشد:

/**

* @brief HAL Status structures definition

*/

typedef enum

{

HAL_OK = 0x00U,

HAL_ERROR = 0x01U,

HAL_BUSY = 0x02U,

HAL_TIMEOUT = 0x03U

} HAL_StatusTypeDef;

در بررسی صحت عملکرد برنامه (و بهبود خوانایی و نگهداری برنامه) مناسب است از نمادگذاری استاندارد شرکت ST استفاده کنیم.
پس از اطمینان از تنظیم صحیح کلاک و PLL که SYSCLK یا کلاک سیستم را می‌سازد، اقدام به تنظیم کلاک باس های سیستم و پریفرالها مینمائیم:
همانگونه که در ابتدای این آموزش مشاهده شد، ابتدا به ساکن اقدام به تعریف ۲ متغیر از نوع استراکچر با نام های oscInitStruct (برای تنظیم منبع کلاک و PLL) و همچنین clkInitStruct برای تنظیم کلاک داخلی پریفرالها و BUS های سیستم نمودیم. در ادامه اعضای clkInitStruct را مقداردهی میکنیم:

/**

* @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;

اعضای استراکچر به شرح فوق می‌باشند که با مقداردهی مناسب پریفرال مربوطه فعال خواهد شد.
ابتدا اقدام به تنظیم ClockType مینمائیم:
این متغیر تعیین میکند کلاک کدام یک از پریفرالها تنظیم خواهد شد. در این آموزش کلاک تمام Bus های سیستم تنظیم میشوند.

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 */

پس از مشخص شدن پریفرالهای مورد نیاز برای تنظیم کلاک، در ادامه منبع کلاک سیستم را تنظیم مینمائیم:
clkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
همانگونه که در تصویر درخت کلاک مشخص شده است کلاک سیستم از منابع مختلفی میتواند تامین شود:



برای این منظور میتوان منبع کلاک سیستم (SYSCLK) را با انتخاب یکی از موارد زیر تعیین کرد، که با توجه به روند آموزش از منبع کلاک PLL استفاده میکنیم:

/** @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 */


در ادامه پالس ساعتی که CPU و حافظه رم، باس های APB1 و APB2 را تغذیه میکند تنظیم میکنیم، این کلاک میتواند ضریبی از کلاک سیستم (SYSCLK) باشد.
clkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
کلاک AHBCLKDivider در حداکثر مقدار ممکن میتواند برابر با ۷۲ مگاهرتز باشد. بر ای این منظور ضریب تقسیم فرکانسی را ۱ انتخاب میکنیم:

ضرایب قابل انتخاب به شرح زیر است:

/** @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 */


کلاک AHB برای تامین کلاک CPU، حافظه و تجهیزات جانبی نگاشت شده روی گذرگاه AHB (DMA و GPIO) استفاده می شود.

در ادامه کلاک گذرگاه (BUS) APB1 را تنظیم میکنیم. حداکثر فرکانس گذرگاه APB1 برای پریفرالهای متصل به این گذرگاه، که با PCLK1 معرفی میشود، به ۳۶ مگاهرتز محدود شده است. اما تایمر APB1 میتواند با فرکانس ۷۲ مگاهرتز کار کند. با تنظیم مقدار APB1 prescaler مقدار مقسم فرکانس میتواند با توجه به فرکانس کلاک سیستم (SYSCLK) عددی بین ۱ تا ۲،۴،۸ و ۱۶ تنظیم بشود. در صورتیکه فرکانس کلاک سیستم ۷۲ مگاهرتز تنظیم شده باشد مقدار مقسم را نمیتوان ۱ تنظیم کرد (با این کار فرکانس کلاک APB1 برابر با ۷۲ مگاهرتز میشود که علیرغم کامپایل کد بدون مشکل باعث عدم کارکرد صحیح میکروکنترلر خواهد شد.)
در فرکانس های  کمتر از ۷۲ مگاهرتز و با تنظیم مقسم فرکانسی به عدد ۱، مقدار ضرب کننده فرکانسی که وظیفه تامین کلاک برای تایمر APB1 را بر عهده دارد بصورت خودکار ۱ تعیین می‌شود. در صورتیکه مقدار مقسم فرکانسی عددی بجز ۱ (۲ و بیشتر از ۲) تعیین گردد، مقدار ضرب کننده فرکانسی تامین کننده کلاک تایمر بصورت اتوماتیک ۲ تعیین میشود.


کلاک گذرگاه PCLK2 از عبور کلاک (HCLK) از مقسم فرکانسی APB2 Prescaler بدست می‌آيد و فرکانس مورد نیاز پریفرالهای APB2 و تایمر APB2 را تامین می‌کند.

برای تنظیم مقسم فرکانسی 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 */


مقادیر قابل قبول برای این عضو بصورت مشترک با APB1CLKDivider بصورت زیر Define  شده است:

/** @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;

}

پروتوتایپ تابعی که با توجه به کدهای ما اقدام به Initialize کردن سیستم میکند بصورت زیر است:
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)

کد فوق دو پارامتر ورودی دارد. ابتدا یک آدرس از متغیر (از نوع استراکچر) را بصورت پوینتر میگیرد. و مقدار تاخیر مورد نیاز برای تثبیت تغییرات در سیستم.
با توجه به فرکانس کلاک سیستم تاخیر مورد نیاز حافظه فلش بصورت زیر تعیین میشود(صفحه ۵۹ از RM):


//0 - 24MHz --> 0 cycle

//24 - 48MHz --> 1 cycle

//48 - 72MHz --> 2 cycle

 نظر به تنظیم کلاک سیستم به ۷۲ مگاهرتز، ۲ سیکل ساعت تاخیر برای اطمینان از ثبت تغییرات بر روی رجیسترها درنظر گرفته می‌شود. در صورت عدم وجود مشکل (عدم تغییر اعضای استراکچر RCC_ClockConfig)، مقدار true و در غیر این صورت مقدار false برگردانده میشود. 
با توجه به فرکانس کلاک 
مقدار تاخیر حافظه فلش بصورت زیر define میشود:

/** @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 */


در ادامه به تنظیم باقی پریفرالهای میکرو میپردازیم.

هیچ نظری موجود نیست:

ارسال یک نظر