STM32 HAL ve CubeMX ile ADC Uygulaması -1-
ADC üzerinde ilk yaptığım uygulama herhangi bir hata vermeden çalıştı. Bu hatayı almayışımızın iki büyük sebebi vardır. Birincisi HAL kütüphane kılavuzunu elimizin altında bulundurup önceden okumamızdır. İkincisi ise STM32CubeMX programını kullanmaktır. İnternette veya örnek kodlarda ADC örneklerini görebilsek de bunlar eski sürüm kütüphanelerle yazıldığı için pek çoğu işimize yaramıyordu. Standart Peripheral Library ile yazılan kodların devri geçtiği için bununla yazılmış kodlar işimize yaramazdı. Biz de ilk kodumuzu sıfırdan oturup kendimiz yazmak zorunda kaldık.
Bu kodu yazarken sadece HAL kütüphane kılavuzunu kullanıp bütün ayarları kodla kendimiz yapmamız gerektiği gibi projeyi de kendimiz düzenlemek durumundaydık. Bize hazır proje verecek ve ön ayar yapacak bir programın olması bu ayarların nasıl yapıldığını da öğrenmemizde yardımcı olacaktır. Programın yaptığı hazır projeyi ve hazırladığı kodları incelemenin bize çok katkısı olacaktır. Şimdi biz de adım adım bu şekilde ADC kullanmayı öğreneceğiz. Öncelikle STM32CubeMX programıyla hazır bir proje şablonu hazırlamamız lazım. Bunu hazırlamak arayüz vasıtasıyla oldukça kolay olacaktır. Yalnız STM32F3 Discovery kartını seçtiğimizde kart üzerinde bulunan bazı parçalar için I2C, USB ve SPI ayakları önceden ayarlanmış şekilde karşımıza çıkmaktadır. Biz kod kalabalığı olmasın diye bunları kaldırmamız gereklidir. STM32CubeMX’de nasıl proje açılacağını önceden anlattığımız için sadece bu ayakların nasıl kaldırılacağını size gösterelim.
Ayağa tıklayıp Reset_State durumuna çektiğimizde ayağa bağlı olan birim de kullanım dışı kalmış oluyor. Kullanım dışı olmasıyla kod üretilmiyor ve gereksiz kod kalabalığı ile uğraşmış olmuyoruz. Burada I2C, USB ve SPI ayaklarını devre dışı bıraksak da önemli ayaklara dokunmamak gereklidir. Bunlar programlama ve osilatör ayaklarıdır. İkinci adımda ise bir ayağı analog giriş olarak ayarlamak ve ADC birimini etkinleştirmek gereklidir. Analog giriş olarak biz A1 ayağını seçiyoruz ve bunu Analog olarak ayarlıyoruz.
Bu ayağı analog olarak ayarladığımızda koyu sarı bir renkte oluyor. Çünkü ADC birimi ile ilgili ayarlamaları tam olarak yapmadık. Bunun için sol ekranda yer alan çevre birimleri arasından ADC1 birimini seçiyoruz ve ilgili ayağı ADC birimine tanıtıyoruz.
Burada IN1 ayağı düğme olarak tanımlandığı için eflatun renkte gösterilmiştir. Yani program bu ayağı kullanamayacağımıza dair bize uyarı vermektedir. Tek kanal veya diferansiyel kullanım için iki ayrı seçeneğimiz vardır. Aşağıda ise ADC ile ilgili daha ayrıntılı ayarlar yer almaktadır. Bu ayarlara pek dokunmasak da enjekte çevirim modunu devre dışı bırakarak en basit haliyle kullanmayı hedefliyoruz. Bu yüzden saat sinyalini de senkron saat olarak seçtik. Bundan sonra kod üretip projeyi TrueStudio ile açıyoruz. TrueStudio’ya proje aktarmak için File / Open Projects from File System sekmesini kullanabiliriz. Şimdi projemizi TrueStudio’da açalım ve kodları incelemeye başlayalım.
Projemiz oldukça kalabalık olduğu için öncelikle .h yani başlık dosyalarını inceleyelim. Bizim burada inceleyeceğimiz iki dosya olacak. Bunlardan main.h dosyasında CubeMX programında yaptığımız ayak tanımlamalarına dair #define direktiflerini göreceğiz.
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 30 31 32 |
#define OSC32_IN_Pin GPIO_PIN_14 #define OSC32_IN_GPIO_Port GPIOC #define OSC32_OUT_Pin GPIO_PIN_15 #define OSC32_OUT_GPIO_Port GPIOC #define OSC_IN_Pin GPIO_PIN_0 #define OSC_IN_GPIO_Port GPIOF #define OSC_OUT_Pin GPIO_PIN_1 #define OSC_OUT_GPIO_Port GPIOF #define B1_Pin GPIO_PIN_0 #define B1_GPIO_Port GPIOA #define LD4_Pin GPIO_PIN_8 #define LD4_GPIO_Port GPIOE #define LD3_Pin GPIO_PIN_9 #define LD3_GPIO_Port GPIOE #define LD5_Pin GPIO_PIN_10 #define LD5_GPIO_Port GPIOE #define LD7_Pin GPIO_PIN_11 #define LD7_GPIO_Port GPIOE #define LD9_Pin GPIO_PIN_12 #define LD9_GPIO_Port GPIOE #define LD10_Pin GPIO_PIN_13 #define LD10_GPIO_Port GPIOE #define LD8_Pin GPIO_PIN_14 #define LD8_GPIO_Port GPIOE #define LD6_Pin GPIO_PIN_15 #define LD6_GPIO_Port GPIOE #define SWDIO_Pin GPIO_PIN_13 #define SWDIO_GPIO_Port GPIOA #define SWCLK_Pin GPIO_PIN_14 #define SWCLK_GPIO_Port GPIOA #define SWO_Pin GPIO_PIN_3 #define SWO_GPIO_Port GPIOB |
Bunlar programda ayaklara verdiğimiz isimleri HAL kütüphanesine uyumlu hale getiren tanım blokudur. Yani düğme için kullandığımız B1_Pin, GPIO_PIN_0 tanımlamasına eşitlenmiştir. Bunlara bakmadan kodu incelediğimizde ne olduğu hakkında fikrimiz olmayacaktır. O yüzden öncelikli olarak .h dosyalarını incelememiz iyi oldu. main.h dosyasında daha başka bir şey göremiyoruz o yüzden ikinci başlık dosyası olan stm32f3xx_hal_conf.h dosyasına bakıyoruz. Bu dosya HAL kütüphanesi ile ilgili ayarları yapacağımız dosya olduğundan bir çevre birimini veya sürücüyü kullanmadan önce bu ayarları yapmamız gereklidir. Örneğin biz şimdi ADC modülünü kullanacağımızdan program otomatik olarak ADC modülünü kullanmayı etkinleştirmiştir.
1 2 3 4 5 6 7 8 |
#define HAL_MODULE_ENABLED #define HAL_ADC_MODULE_ENABLED /*#define HAL_CRYP_MODULE_ENABLED */ /*#define HAL_CAN_MODULE_ENABLED */ /*#define HAL_CEC_MODULE_ENABLED */ /*#define HAL_NAND_MODULE_ENABLED */ /*#define HAL_NOR_MODULE_ENABLED */ /*#define HAL_PCCARD_MODULE_ENABLED */ |
Burada kullanılmayan modüllerin yorum haline getirildiğini görebilirsiniz. Kullanacağınız modül olduğunda /* ve */ işaretlerini kaldırarak etkinleştirebilirsiniz. Bu ayarın nasıl çalıştığını öğrenmek istiyorsak dosyada biraz aşağı bakmamız yeterli olacaktır.
1 2 3 |
#ifdef HAL_ADC_MODULE_ENABLED #include "stm32f3xx_hal_adc.h" #endif /* HAL_ADC_MODULE_ENABLED */ |
Görüldüğü gibi eğer HAL_ADC_MODULE_ENABLED tanımlanmışsa projemize stm32f3xx_hal_adc.h kütüphane dosyası eklenmektedir. Bu kütüphane dosyasını ise Drivers klasöründe görmemiz mümkündür. Eğer projemizde yok ise bunu bizim eklememiz gereklidir.
Buraya kadar güzel ilerledik ve .h dosyalarını anlamış olduk. Şimdi ise src klasöründeki projenin kaynak kodlarını inceleme vakti geldi. Burada sadece main.c dosyasını değil diğer dosyaları da incelememiz gerekli. Eğer isteseydik anlamadan da birkaç fonksiyon yazarak bunu yapabilirdik fakat anlayarak yaparsak işi şansa bırakmamış oluruz. O yüzden dosyaları inceleyerek HAL kütüphanesinde ADC’nin nasıl çalıştırıldığını anlamamız gerekli.
stm32f3xx_it.c dosyası kesmelerle alakalı kesme fonksiyonlarını içerdiği için bizi şimdilik ilgilendirmiyor. Bu dosya boş bir dosya olup kesme fonksiyonları hazır bir şekilde burada sıralanmıştır. Kesme söz konusu olduğunda belirli kesme fonksiyonunun arasına yazacağımız komutlar işletilir. Kesmeler için ayrı bir dosya olması kesmeler üzerinde derli toplu halde işlem yapmamızı sağlıyor.
Burada yeni gördüğümüz dosya ise stm32f3xx_hal_msp.c dosyasıdır. MSP’nin açılımı MCU Support Package şeklindedir. Yani mikrodenetleyici destek paketine ait komutlar burada yer almaktadır. CubeMX programı ürettiği kodun bir kısmını main.c dosyasına değil bu dosyaya atmıştır. Ana programı incelemeden önce buradaki kodları inceleyelim.
stm32f3xx_hal_msp.c dosyasını açıyoruz ve ilk fonksiyona geliyoruz.
1 2 3 4 5 6 7 8 9 |
void HAL_MspInit(void) { __HAL_RCC_SYSCFG_CLK_ENABLE(); __HAL_RCC_PWR_CLK_ENABLE(); HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0); } |
Burada HAL_MspInit() fonksiyonunun prototipini stm32f3xx_hal.h dosyasında görmekteyiz. Yalnız bu fonksiyon HAL kütüphane sürücülerinden olmayıp CubeMX programının ürettiği kullanıcı komutlarını barındırıyor.
__HAL_RCC_SYSCFG_CLK_ENABLE(); Bu fonksiyon SYSCFG saatini etkinleştiriyor. Fonksiyonun kaynak kodu ise stm32f3xx_hal_rcc.h dosyasında yer almaktadır.
__HAL_RCC_PWR_CLK_ENABLE(); Bu fonksiyon PWR saatini etkinleştirmekte ve yine fonksiyonun kaynak kodu stm32fxx_hal_rcc.h dosyasında yer almaktadır. Bunlar saat ayarı olmakla beraber biz senkron saati seçtiğimiz için bu saat yapıları etkinleştirilmiş oluyor. Saat ile ilgili temel bilgimiz olduğu için işin ayrıntısına daha ileri seviyelere geleceğiz.
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0); Bu fonksiyon kesmelerle alakalı olup kesme önceliğini ayarlamaktadır. Kesme konusuna gelmediğimiz için bunu şimdilik programa bırakıyoruz.
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 |
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hadc->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**ADC1 GPIO Configuration PA1 ------> ADC1_IN2 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } } |
ADC ile alakalı en önemli fonksiyonlardan birine gelmiş bulunuyoruz. Bu fonksiyon ADC tanımlamak ve ADC ayak ayarlarını yapmaya yarıyor. Bunun için ana programda tanımladığımız hadc yapı tipi burada denetlenmekte ve ona göre saat ayarları yapılmaktadır.
if(hadc->Instance==ADC1) Bu karar yapısı hadc yapısının Instance değişkenine erişir. Ok (->) operatörünü burada yapı değişkenine erişmede kullanılırken görmekteyiz. Instance değişkeni eğer ADC1’e eşitse yani mikrodenetleyicinin ADC1 birimi seçilmişse aşağıdaki komutlar işletilir.
__HAL_RCC_ADC12_CLK_ENABLE(); Burada RCC yani saat ayarları ile ilgili bir makroyu görmekteyiz. Bu ADC1 veya ADC2 birimlerine gelen saati etkinleştirerek bu birimleri aktif hale getirir.
__HAL_RCC_GPIOA_CLK_ENABLE(); Biz ADC ayağını A1 ayağı olarak belirlediğimiz için GPIOA biriminin saati etkinleştirilmek zorundadır. Bunun için ADC saatini etkinleştirdiğimiz gibi ayağın kullanılabilmesi için ilgili GPIO biriminin saatini etkinleştiriyoruz. Bunlar olmazsa olmaz fonksiyonlar olup unutulduğu takdirde çalışmayacaktır.
1 2 3 4 |
GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); |
Burasını artık hepimiz kolaylıkla anlayabiliyoruz. A portunun 1 numaralı yani A1 ayağını GPIO_MODE_ANALOG diyerek analog ayak olarak tanımlıyoruz. Aynı basit giriş ve çıkış ayaklarını tanımladığımız gibi tanımlayıp sadece Mode kısmını GPIO_MODE_ANALOG olarak değiştiriyoruz. GPIO sürücüsünü önceden neredeyse bütün ayrıntısıyla anlattığımız için burada tekrarlamıyoruz.
Buraya kadar harici dosyaları inceledik ve incelememiz gereken bir tek main.c dosyası kaldı. Asıl meselemiz main.c dosyası olduğu için bu dosyayı burada incelemeye başlarsak yazı oldukça uzayacaktır. O yüzden bir sonraki başlıkta main.c dosyasını inceleyip ilk uygulamamızı yapacağız.
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.
Son Yorumlar