STM32 ve HAL Kütüphanesi ile ADC Birimini Kullanmaya Giriş

Buraya kadar genel maksatlı giriş ve çıkış birimleriyle saat özelliklerini kullanmayı az çok öğrendik. Hatta yazmaçlar üzerinde de işlem yaparak nasıl yazmaçlarla programlama yapacağımızı size göstermiş olduk. Şimdi ise mikrodenetleyicinin farklı birimlerine teker teker geçiş yaparak bölüm bölüm bunları öğreneceğiz. Klasik yolu izleyip önce en kolayı olan giriş ve çıkış birimlerini öğrendikten sonra ADC ile devam edeceğiz. Fakat Arduino’da analogRead yazmak, AVR’de birkaç 8-bit yazmacı denetlemek kadar kolay olan ADC konusu STM32’de hiç bu kadar basit ve kolay değil. Daha önceden datasheetten ADC özelliklerini ayrıntılı olarak okusak da referans kılavuzunu elimize aldığımızda ADC’nin 100 sayfadan fazla tuttuğunu görmekteyiz. Belki F0 ya da F1 serilerinde daha basit yapıda olsa da F3 serisinde daha başlangıçta oldukça karmaşık bir birim bizleri karşılıyor. İşin içinden çıkabilmek için tek dayanağımız HAL kütüphanesi gibi görünmekte. O yüzden HAL kütüphanesi ve yazılım yönüyle başlayıp sonra donanıma bakarak devam etmemiz bizim için daha kolay olacaktır. Sonuçta bu noktaya gelen birisi daha önceden muhakkak ADC kullanmıştır ve ADC hakkında temel bilgisi bulunmaktadır.

HAL kütüphanelerinin sürücü başlıkları altında birbirinden ayrıldığını önceden söylemiştik. Mikrodenetleyicinin birimlerine ait ayrı ayrı sürücü dosyası bulunduğundan biz hangisinde çalışacaksak o sürücüyü kullanıyoruz ve diğerleri ile işimiz olmuyor. Bu yönüyle tek bir konuya odaklanıp daha rahat çalışabiliriz. Bizim şu an inceleyeceğimiz sürücü HAL ADC Generic Driver olacaktır. Extension Driver denilen ve ek özelliklerden bahseden sürücüleri işin temelini öğrendikten sonra öğrenmek gerekir. Generic sürücüler bütün STM32 mikrodenetleyicilerde kullanılabilen temel fonksiyonları içermektedir.

Kütüphanenin referans kılavuzuna baktığımızda ADC çevre biriminin özellikleri madde madde verilmiştir. Daha önceden ADC birimini anlatsak da bu özellikleri burada tekrar etmemizde fayda vardır.

  • 12-bit, 10-bit, 8-bit ya da 6-bit ayarlanabilir çözünürlük.
  • Normal, enjekte edilmiş ya da analog watchdog olaylarında kesme özelliği.
  • Tekli ya da devamlı çevirim modu
  • Çoklu kanallarda sıralı tarama modu
  • Veri hizalama.
  • Programlanabilir örnekleme zamanı
  • Normal ya da enjekte grup için ADC çevirimi
  • Normal ya da enjekte gruplar için harici tetikleme
  • Normal grup için DMA talep üretimi
  • Çoklu çiftli mod
  • Çoklu çiftli modda ayarlanabilir DMA veri depolama
  • Çift karıştırma modunda ayarlanabilir bekleme
  • ADC kalibrasyonu
  • ADC kanalları tekli veya diferansiyel ölçüm yapabilir
  • 2.4 – 3.6V arası ADC besleme
  • Vref- ve Vref+ olarak referans gerilimi ayarlaması

Burada AVR mikrodenetleyicilerde çalışan birine yabancı gelecek pek çok özellik karşımıza çıkmaktadır. Arduino kullanan biri için bu saydıklarımız ve STM32 hakkında bahsettiğimiz hemen her şey yabancı gelecektir. Çünkü Arduino’da ADC analogRead fonksiyonundan başka bir şey değildir.

Belgenin devamında ADC sürücüsünün nasıl kullanılacağı bahsedilmektedir. Bizim ADC kullanmaya başlamak için yapacağımız en önemli adım bu metni okumaktır. Bu sürücü sayfası yazmaçlar ve donanımdan bahseden referans kılavuzundaki bölüm kadar uzun olmadığı için bir parça daha kolay olacaktır. Şimdi nasıl kullanılması gerektiğine bakalım.

Öncelikle ADC’nin tanımlanması ve ayarlanması gereklidir. Bunun için şu şekilde talimatlar sıralanmıştır.

  • ADC Arayüzünü Etkinleştirin

Ön koşul olarak ADC saatinin RCC’de ayarlanması gereklidir. Saat ayarlarından bahsettiğimiz için RCC’nin ne olduğunu biliyorsunuz.

STM32F30x ve F33x aygıtları için iki muhtemel saat kaynağı bulunmaktadır. Bunlardan biri senkron saat olup AHB saatinden alınır öteki ise asenkron saat olup ADC’nin bağımsız PLL sinyalidir ve hızı 72MHz’dir. Şimdi merakımızdan STM32CubeMX programını açıyoruz ve bu senkron ve asenkron saatin nasıl çalıştığına bakıyoruz.

ADC biriminin ayarlarında bu saat seçimi mevcuttur. Asenkron moda aldığımızda PLL sinyalini pek çok değere bölüp istediğimiz frekansta ADC birimini çalıştırabildiğimizi fark ediyoruz. Eğer senkrona alırsak belli bölme seçenekleri gelse de doğrudan sinyali PLL çıkışından alıyor.

Görüldüğü gibi ADC’ye giden saat sinyalini 256’ya kadar bölüp ADC saat sinyalini belirleyebiliyoruz. Asenkron çalışmada böyle iken senkron ayarı seçersek ön derecelendirici ayarını yapamıyoruz.

Kılavuza devam edersek senkron saatin ADC çekirdek saati olarak kullanıldığında gerekli olduğunu söylemekte. Senkron saat aynı zamanda ADC çevirim saati olarak da kullanılabilir. Senkron saat __ADCx_CLK_ENABLE() makrosu ile ayarlanabilir. Asenkron saat de ayrıca ADC çevirim saati olarak kullanılabilir. Asenkron saat ise HAL_RCCEx_PeriphCLKConfig() fonksiyonu ile ayarlanır.

Burada ADC birimine giden saati etkinleştirme fonksiyonuna benzer bir fonksiyonu __GPIOx_CLK_ENABLE() diye genel giriş ve çıkış birimlerinin saatini etkinleştirmede kullanmıştık. Önceden kütüphanenin kurallarını ve yapısını açıkladığımız için bu benzerliklerin tesadüf olmadığını bilmeniz gerekir. Bu kurallı yapıdan dolayı kütüphane fonksiyonlarını öğrenmemiz kolaylaşacaktır.

Örnek olarak tek bir ADC birimi bulunduran bir aygıtta HAL_ADC_MspInit() fonksiyonuna parametreler yollanır. Önceki sürücülerde olduğu gibi Init fonksiyonuna burada da parametre yolluyoruz.

__HAL_RCC_ADC1_CLK_ENABLE() fonksiyonu ile ADC birimi etkinleştirilir. Bu aynı GPIO’da olduğu gibi zorunludur.

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC komutu eğer ADC çevirimi asenkron saatten yapılacaksa kullanılır. PeriphClkInit.Adc1ClockSelection = RCC_ADC1PLLCLK_DIV1 komutu da kılavuzda çevirim asenkron saatten yapılacaksa kullanılır diye bahsediyor. Aynı şekilde HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInitStructure) komutu da asenkron saat için gereken ayarları yapan fonksiyon olduğu söyleniyor.

Geri kalan ayrıntıları bir kenara bırakarak şunu özetleyebiliriz. ADC birimini kullanmak için öncelikle saat ayarını yapmak lazımdır ve bu saat ayarı da senkron ve asenkron saate göre farklılık göstermektedir. Şimdi bir sonraki adıma geçelim.

  • ADC Ayaklarını Tanımlayın

ADC kullanabilmek için temel giriş ve çıkış sürücüsü olan GPIO sürücüsünü iyi bilmek gerekiyor. Burada da karşımıza bu ayakların analog olarak tanımlanması için GPIO sürücüsü çıkmakta. Bu GPIO birimini etkinleştirmek için yine GPIO saatini etkinleştirmek gerekiyor. Bunun için __HAL_RCC_GPIOx_CLK_ENABLE() fonksiyonunu kullanıyoruz. Bundan sonra HAL_GPIO_Init() fonksiyonu ile Analog giriş olarak kullanmak istediğimiz ayakları Analog olarak seçip tanımlayacağız. Bunu önceden yapmasak da nasıl yapılacağını size anlatmıştık.

  • ADC Kesme Ayarlarını Yapın

Eğer ADC birimi kesmelerle beraber kullanılacaksa NVIC biriminin ayarlarının yapılması gereklidir. Bunun için HAL_NVIC_EnableIRQ(ADCx_IRQn) fonksiyonu kullanılır. ADC kesme fonksiyonu için de HAL_ADC_IRQHandler() fonksiyonunu tanımlayıp kesme vektörlerini kullanırız. Şu an kesmelerle işimiz olmadığı için ileri seviyelerde buna bakabiliriz.

  • DMA Ayarlarını Yapın

Bu da yine zorunlu olmayan bir özelli olup HAL_DMA_Init() ile DMA etkinleştirilmelidir. DMA’nın doğrudan hafıza erişimi birimi olduğunu hatırlatalım. Bu birim için NVIC yani kesme birimini de ayarlamak gerekir bunun için de HAL_NVIC_EnableIRQ(DMAx_Channelx_IRQn) fonksiyonu kullanılır. Ayrıca ADC kesme fonksiyonu olarak HAL_ADC_IRQHandler() ve vektör olarak DMAx_Channelx_IRQHandler() fonksiyonu kullanılır. Bunlar ileri seviye olduğu için şimdilik kullanmayacağız. Kesmeleri ve doğrudan hafıza erişimini ve bunlara ait sürücüleri öğrenmeden uygulamaya kalkmamak gerekir.

Bundan sonra ise ADC, grupları ve kanal parametlerini ayarlama bahsedilmektedir.

  • ADC parametrelerini (çözünürlük, hizalama vs.) ve normal grup parametrelerini (çevirme tetikleyicisi, ardıştırıcı vs.) HAL_ADC_Init() fonksiyonu ile ayarlayın. 
  • Kanalları normal grup parametrelerine göre (kanal numarası, ardıştırıcıdaki kanal sıralaması vs. ) HAL_ADC_ConfigChannel() fonksiyonunu kullanarak ayarlama yapın. 
  • Opsiyonel olarak eklenmiş grup parametrelerini HAL_ADCEx_InjectedConfigChannel() fonksiyonunu kullanarak belirleyin. 
  • Opsiyonel olarak analog watchdog zamanlayıcı parametrelerini HAL_ADC_AnalogWDGConfig() fonksiyonunu kullanarak yapın. 
  • Opsiyonel olarak çoklu mod parametrelerini HAL_ADCEx_MultiModeConfigChannel() fonksiyonunu kullanarak yapın. 

Biz opsiyonel olanlarla ilgilenmeyeceğiz. Çünkü temel sürücüde değil genişletilmiş sürücüde yer aldığından temel özelliklerin üzerine ilave fonksiyonlar olarak gelmektedir. Öğrenmede öncelikle temelden başlayacağız.

Bundan sonra ADC çevirimi yapmaktan bahsedilmektedir. Buraya kadar tanımlama ve ayar konusunda anlamadığımız bir nokta kalmadı. Fakat bunu uygulamada yapmak ve incelemek farklı bir boyuttur. Yine de ADC ile ilk defa yüzleştiğimiz için bu kadar ön bilgi edinmemiz bile büyük bir gelişmedir. Şimdi ADC çeviriminin nasıl yapılacağına bakalım.

Opsiyonel olarak otomatik ADC kalibrasyonu isabetliliği artırmak için yapılabilir. Bunun için HAL_ADCEx_Calibration_Start() fonksiyonu kullanılır.

ADC sürücüsü üç ayrı modda kullanılabilir: polling (normal kod akışında), kesmeler ile ve DMA transferi ile.

Normal yani sıralı program akışında ADC şu şekilde işletilir,

Önce ADC çevre birimi etkinleştirilir ve HAL_ADC_Start() fonksiyonu ile başlatılır. Sonra ADC çevirimin bitmesi beklenir. Bunun için HAL_ADC_PollForConversion() komutu kullanılır. Bizim AVR’de kullandığımız while döngüsüne benzer bir yapıda olsa gerektir. Ondan sonra ise çevrim sonucunu almak için HAL_ADC_GetValue() fonksiyonu kullanılır. Ondan sonra ise ADC biriminde çevrimin durdurulması için HAL_ADC_Stop() fonksiyonu kullanılır.

Sırf burayı okumakla bile ADC kullanmanın aslında basit olduğunu fakat tanım ve ayarlarda biraz kalabalıklığın olduğunu anlayabiliriz. Korkarak yaklaştığımız konu açıklığa kavuşunca bir anda renk değiştiriyor. ADC’nin kesme ve doğrudan hafıza erişimi ile kullanımından şu an bahsetmeyeceğiz. Çünkü bunlar daha ileri seviye uygulamalar olup başlangıçta bizi alakadar etmeyecektir.

Bundan sonra ADC birimini normal olarak nasıl kullanabileceğimize bakacağız. Bu kadar bilgi edinmek ADC’nin basit olarak nasıl kullanılacağı hakkında bizim için yeterli oldu. Yine de oturup sıfırdan kod yazmaya çalışmayacağız ve HAL kütüphanesinin örnek kodlarını inceleyerek öğrenmeye çalışacağız.

 

Bizi Facebook grubumuzda takip etmeyi unutmayın. Bilgili ve öğrenmeye hevesli bir topluluk oluşturmak istiyoruz.

https://www.facebook.com/groups/1233336523490761/

UYARI!!

 

 

 

Gökhan Dökmetaş

"Arduino Eğitim Kitabı" ve "Arduino ve Raspberry PI ile Nesnelerin İnterneti" kitaplarının yazarı. Başkent Teknoloji ve Dedektör Merkezi'nde Ar-ge Sorumlusu. Araştırmacı-Yazar.

You may also like...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.