STM32 HAL Kütüphanesi ile Saat (Clock) Ayarı -1-
Önceki yazımızda STM32CubeMX programındaki kullanıcı arayüzü vasıtasıyla oldukça kolay bir şekilde STM32 mikrodenetleyicilerin saat sinyali ayarını yapabildiğimizi görmüştük. Fakat bir geliştirici olarak her zaman bu programın arayüzüne bağlı kalıp işin derinini öğrenmemek olmaz. O yüzden STM32CubeMX’in bizim için hazırladığı kodu inceleyerek işe başlayalım.
HAL kütüphanesinde saat ayarları için RCC sürücüsü bulunmaktadır. Programda yaptığımız seçimler sonucunda kod ürettiğimizde bu HAL kütüphanesi fonksiyonları kullanılarak saat ayarlaması için gerekli kodlar program tarafından yazılır ve bize hazır proje olarak verilir. Aynı GPIO işlemlerinde olduğu gibi saat ayarları için de yapı tip tanımlamaları ve sabitler bulunmaktadır. Aslında sürücüyü incelediğimizde programın arayüzde yapılan işin aynısını görmekteyiz. Yalnız orada şema üzerinde listeden veya metin kutusundan yaptığımız ayarları burada kod yazarak yazmamız gereklidir. Öğrendikten sonra program ile hiç uğraşmadan kod yazarak da aynı ayarları yapma imkanımız olur.
STM32CubeMX’i önceden incelemek saat yapıları ve bunlar için ayar değerlerini kolayca öğrenmemizde yardımcı oldu. Bu yüzden bu komutları da artık daha kolay anlayacağız. STM32F3 Discovery kartı için üretilen hazır projede yer alan saat komutlarını biz system_clock adı verilen bir dosya oluşturmuş ve ona atmıştık. Orada Error_Handler fonksiyonu yer alsa da bu fonksiyon programlama ile alakalı olduğu için bunu görmezden geleceğiz. Şimdi SystemClock_Config fonksiyonunun iç yapısına bakalım ve komutları inceleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; /* Enable HSE Oscillator and activate PLL with HSE as source */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK) { Error_Handler(); } /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2)!= HAL_OK) { Error_Handler(); } } |
Başta baktığımızda kod yığını gibi görünse de sabitlerin ve yapı değerlerinin adlarına baktığımızda tanıdık gelmektedir. Biz eğer program kullanmak istemiyorsak burada iki yapıyı tanımlayacağız ve saat ayarlarını sürücü değişkenleri ve sabitleri üzerinden kendimiz kod yazarak yapacağız. Bunun için aynı GPIO sürücüsünde yaptığınız gibi yapı tipi tanımlamamız gereklidir.
1 2 3 |
RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; |
Burada RCC_ClkInitTypeDef ve RCC_OscInitTypeDef olmak üzere iki adet yapı tipini tanımlıyoruz. Bu tanımladığımız yapıların adları RCC_ClkInitStruct ve RCC_OscInitStruct şeklindedir. Daha anlaşılır olması için farklı bir ad verilebilir fakat biz programa dokunmadan inceleyeceğiz. Bu yapıların adlarından birinin osilatör diğerinin ise saat için ayar verilerini içerdiğini düşünebiliriz. Yine Eclipse’nin güzel bir özelliği olup Eclipse tabanlı Atollic’in TrueStudio IDE’sinde bulunan CTRL + Sol Tık özelliğini kullanarak bu yapı tipinin içine bakalım.
stm32fxx_hal_rcc.h dosyasında ilgili yere gelmiş bulunuyoruz. Örnekte olduğu gibi saatle alakalı bakmamız gereken dosyanın RCC sürücü dosyası olacağını görüyoruz. Kütüphane kılavuzuna bakmanın çok şart olmadığını buradan görebiliriz. Yukarıdaki resimde bu değerlerin açıklaması yapılmış ve bu yapı tipinin hangi değerleri aldığı gösterilmiştir. Bunlar saat tipi, sistem saat kaynağı, AHB saat bölücüsü, APB1 saat bölücüsü ve APB2 saat bölücüsüdür. Önceki makalemizi okuyanlar için pek yabancı gelmeyen bu tabirlerin ne olduğunu yine bu yapı değişkenlerinin açıklamasında yazan @ref ibaresini ile bulabiliriz. Örneğin RCC_System_Clock_Type için kullanabileceğimiz değerleri bunu yazarak aratıyoruz ve aynı dosyanın başka yerinde tanımlanmış sabitleri görüyoruz.
1 2 3 4 |
#define RCC_CLOCKTYPE_SYSCLK ((uint32_t)0x00000001) #define RCC_CLOCKTYPE_HCLK ((uint32_t)0x00000002) #define RCC_CLOCKTYPE_PCLK1 ((uint32_t)0x00000004) #define RCC_CLOCKTYPE_PCLK2 ((uint32_t)0x00000008) |
Bunlar on altılık sayısal değerleri temsil eden tanımlar olduğu için daha ilerisi yazmaçlar üzerinde işlem yapmaktır. Bu konuda referans kılavuzuna bakmamız gerekirken bu sabitleri kullanırken bu on altılık sayı değerlerini değil tanımları öğrenmek yeterli olacaktır. Yine kaynak koddan anlamadığımız durumda kütüphane referansına bakarak gerekli açıklamayı bulabiliriz.
Görüldüğü gibi bu değer ayarlanacak saatleri seçmeye yarıyor. Bu saatlerin tek tek seçilebildiğini de aslına on altılık değerden görmemiz mümkündür. Bu saat seçimi sırayla “0001”, “0010”, “0100”, “1000” diye gitmektedir. Bunların bu sırayla gitmesi rastgele değildir.
Bu yapının diğer değerlerini kodun devamında anlatacağım için ikinci yapı tipinin değerlerine bakarak devam edelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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 HSEPredivValue; /*!< The HSE predivision factor value. This parameter can be a value of @ref RCCEx_HSE_Predivision_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 calibration trimming value. 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; |
Burada osilatör ayarı için kullanılan RCC_OscInitTypeDef yapısını görmekteyiz. Hepsinde olduğu gibi bu yapının da aldığı değerler ve bu değerlerin aldığı parametreler açıklama kısmında açık bir şekilde belirtilmiştir. Burada Osilatör tipi, HSE durumu, HSE Ön bölüm değeri, LSE durumu, HSI durumu, HSI Kalibrasyon değeri ve LSI durumu belirtilmiştir. Ayrıca RCC_PLLInitTypeDef adında PLL için ayrı bir yapı tipi tanımlanmıştır. Burada bunu da göz ardı etmemek gerekir.
1 2 3 4 5 6 7 8 9 10 11 12 |
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 RCCEx_PLL_Clock_Source */ uint32_t PLLMUL; /*!< PLLMUL: Multiplication factor for PLL VCO input clock This parameter must be a value of @ref RCC_PLL_Multiplication_Factor*/ }RCC_PLLInitTypeDef; |
PLL yapısına baktığımızda PLL durumu, PLL kaynağı ve PLL çarpanı olmak üzere üç ayrı değer aldığını görüyoruz. Yapı içinde yapı olması, bütün bu yapılar ve değerler kafamızı karıştırabilse de örnek kodu incelediğimizde çözüme kavuşacağız. Eğer bu kadar fazla değer ve fonksiyondan şikayet ediyorsanız yine CubeMX programı ile bu ayarlamayı yapabilirsiniz. Fakat STM32 programlamak için tembellik etmek gibi bir lüksümüz yoktur. O yüzden tembelliğe alışmamak gereklidir.
Şimdi örnek kodu satır satır inceleyelim ve böylelikle HAL kütüphanesinin saat sürücüsünü öğrenelim.
1 |
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; |
Burada RCC_OscInitStruct yapısını yapı tipini kullanarak tanımlamıştık. Bu yapının OscillatorType adında bir değişkeni yer almakta ve buna da RCC_OSCILLATORTYPE_HSE sabiti atanmaktadır. Yukarıda gösterdiğimiz gibi OscillatorType değişkeni uint32_t yani işaretsiz 32-bit tam sayı tipinde bir değişkendi. Buna osilatör tipini belirtecek değerleri atıyorduk. Bu değerlerin ne olduğunu ise örnek değere CTRL + Sol Tık yaparak bakalım.
1 2 3 4 5 |
#define RCC_OSCILLATORTYPE_NONE ((uint32_t)0x00000000) #define RCC_OSCILLATORTYPE_HSE ((uint32_t)0x00000001) #define RCC_OSCILLATORTYPE_HSI ((uint32_t)0x00000002) #define RCC_OSCILLATORTYPE_LSE ((uint32_t)0x00000004) #define RCC_OSCILLATORTYPE_LSI ((uint32_t)0x00000008) |
Burada osilatör tipi olarak HSE, HSI, LSE ve LSI seçilebildiğini görebiliriz. Seçebileceğimiz seçenekler burada belirtilse de bu değerler hakkında açıklama ne kaynak kodunda ne de kılavuzda yapılmıştır. Bunun için ayrıntılı bilgiyi teknik veri kitapçığında arayabilirsiniz. Görüldüğü gibi burada programlama yaparken osilatörler hakkında temel bilgimizin olması gerekiyor. Biz şu an teknik veri kitapçığını açmadığımız için önceki bilgilerimize dayanarak HSE’nin yüksek hızlı harici osilatör olduğunu biliyoruz. Diğerleri ise dahili (HSI) ve düşük hızlı seçeneklerdir.
1 |
RCC_OscInitStruct.HSEState = RCC_HSE_ON; |
İkinci satıra geldiğimizde HSEState yapı değişkenine RCC_HSE_ON tanımının aktarıldığını görmekteyiz. Burada HSEState değişkeninin diğer hepsinde olduğu gibi uint32_t tipinde olduğunu tekrar etmemize gerek yok. Şimdi alabileceği değerleri görmek için yine aynı şekilde bakıyoruz.
1 2 3 |
#define RCC_HSE_OFF ((uint32_t)0x00000000) #define RCC_HSE_ON ((uint32_t)0x00000001) #define RCC_HSE_BYPASS ((uint32_t)0x00000005) |
Görüldüğü gibi açık, kapalı ve bypass durumları var. Bu osilatörün durumunu ayarlardığı için bizim de kullanacaksak bunu açmamız gerekir. O yüzden RCC_HSE_ON değerini HSEState değişkenine atıyoruz.
1 |
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; |
Burada ise yine tanımladığımız yapı tipi değişkeninin HSEPredivValue adlı değişkenine bir değer atayacağız. Bu değişkenin hangi değeri ne amaçla taşıdığını öğrenme yollarından ilki değişkenin adına bakmaktır. Pre, Div (Division) ve Value kelimeleri bize bir ipucu vermelidir. Bu kelimeler ön bölümleme değerini bulunduracağını bize anlatmaktadır. Burada da bu alandaki terimleri bilmenin önemini görmekteyiz. Şimdi ön bölme değerinin program arayüzünde hangisine karşılık geldiğini görelim.
Burada HSE kısmının çıkışı bölücüye bağlanmış durumda ve 1 ile 16 arasında bölme işlemi yapılıyor. Koddan kontrol ettiğimizde bunun bu bölme işleminin değeri olduğunu görüyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#define RCC_HSE_PREDIV_DIV1 RCC_CFGR2_PREDIV_DIV1 #define RCC_HSE_PREDIV_DIV2 RCC_CFGR2_PREDIV_DIV2 #define RCC_HSE_PREDIV_DIV3 RCC_CFGR2_PREDIV_DIV3 #define RCC_HSE_PREDIV_DIV4 RCC_CFGR2_PREDIV_DIV4 #define RCC_HSE_PREDIV_DIV5 RCC_CFGR2_PREDIV_DIV5 #define RCC_HSE_PREDIV_DIV6 RCC_CFGR2_PREDIV_DIV6 #define RCC_HSE_PREDIV_DIV7 RCC_CFGR2_PREDIV_DIV7 #define RCC_HSE_PREDIV_DIV8 RCC_CFGR2_PREDIV_DIV8 #define RCC_HSE_PREDIV_DIV9 RCC_CFGR2_PREDIV_DIV9 #define RCC_HSE_PREDIV_DIV10 RCC_CFGR2_PREDIV_DIV10 #define RCC_HSE_PREDIV_DIV11 RCC_CFGR2_PREDIV_DIV11 #define RCC_HSE_PREDIV_DIV12 RCC_CFGR2_PREDIV_DIV12 #define RCC_HSE_PREDIV_DIV13 RCC_CFGR2_PREDIV_DIV13 #define RCC_HSE_PREDIV_DIV14 RCC_CFGR2_PREDIV_DIV14 #define RCC_HSE_PREDIV_DIV15 RCC_CFGR2_PREDIV_DIV15 #define RCC_HSE_PREDIV_DIV16 RCC_CFGR2_PREDIV_DIV16 |
Görüldüğü gibi burada da birden on altıya kadar değerler sıralanmış durumdadır. Bu programı kullanmayı bilince kod yazmamız zor olmayacaktır. Çünkü programda olan her işlemin kod olarak karşılığı bulunmaktadır. İşlemin ne işe yaradığını bildikçe kod yazmakta sıkıntı çekmeyeceğiz. Yalnız saat yapısını kavramak için datasheet okuyup kafa yormak gerekecektir. Eğer önceden bir mikrodenetleyici üzerinde tecrübeniz varsa bunu anlamanız kolay olacaktır. CCS C, Arduino gibi derleyicilerin hazır fonksiyonlarını kullanmak donanım konusunda size bir tecrübe vermeyecektir. Fakat bir AVR ya da PIC datasheetini okuyup donanımı öğrendiğinizde bunu da anlamanız kolay olacaktır.
Yazı oldukça uzadığı için ikiye bölmek zorunda kaldık. O yüzden bir sonraki makalede kaldığımız yerden devam edeceğiz.
Bizi Facebook grubumuzda takip etmeyi unutmayın. Bilgili ve öğrenmeye hevesli bir topluluk oluşturmak istiyoruz.
https://www.facebook.com/groups/1233336523490761/
UYARI!!
Bu sitede yayınlanan yazılar orjinal içerik olup faydalanılan kaynaklar belirtilmiştir. Yazarın izni olmaksızın tamamen alıntı yapılamaz, kopyalanamaz. Kaynak göstermek kaydıyla (Yazının adı, yazar adı ve link) kısmen alıntı yapılabilir.
1 Response
[…] HAL kütüphanesi ile saat ayarı yapmayı anlatmaya devam ediyoruz. Önceki makaleyi buradan okuyabilirsiniz. Şimdi kaldığımız yerden devam […]