STM32F407 SPI KULLANIMI


STM32F407 SPI KULLANIMI

          Bazı sensörler veri transferi için SPI(Serial Peripheral Interface) protokolünü kullanırlar. Bir mikroişlemci ile ivmeölçer sensörü arasında SPI arabirimi kullanan bir örnek inceleyeceğiz.
Basit olarak bir SPI protokolü 4 (dört) sinyal ve bir ortak ground sinyalinden oluşur. Bir Master cihaz aktif ettiği veri yolu üzerinde sadece bir Slave cihazla iletişim kurabilir. Slave cihaz sadece “Slave Select (SS)” bazı dokümanlarda “Chip Select (CS)” olarak isimlendirilen sinyal ile aktif hale getirilir. Bu sinyali sadece Master cihaz üretir. Birden fazla Slave cihaz SPI veri yoluna Figür 1’de gösterildiği gibi bağlanabilir. Veri transferi seri bir şekilde bit-bit yapılır. Veri sinyalleri 2 (iki) sinyal ile taşınır. Bunlar;
MOSI (Master Output Slave Input) Master cihazdan Slave cihaza sinyal taşıyan hattır.
MISO (Master Input Slave Output) Slave cihazdan Master cihaza sinyal taşıyan hattır.
SCLK (Serial Clock) sinyali senkron olarak bu iki MOSI ve MISO sinyalinin taşınmasında kullanılır. Bu sinyal sadece Master cihaz tarafından sürülür.
Veri transfer hızı I2C veri yolundan daha hızlıdır. Slave cihaz donanımsal olarak seçildiği için (SS veya CS pini üzerinden) Slave cihaza I2C veri iletişimindeki gibi adres gönderilmez. Fakat birden fazla Slave cihazın SPI veri yoluna bağlanması için birden çok Slave Select sinyali kullanılır.

Figür 1. SPI veri yolu Master ve Slave cihazlar arasında veri transferi için 4 (dört) sinyal kullanır.

Birçok firma tarafından SPI protokolü oluşturulmuştur. Protokoller arasındaki fark clock polaritesi, kenar(edge) senkronizasyonu, her bitin transferinde kullanılan even number’dır. Burada kullanacağımız mikroişlemci standart varyantların bazılarını destekler. Kullanacağımız ivmeölçerin varyantlarını desteklememektedir. İvmeölçerin SPI iletişimi için bit-banging yazma ve okuma yapan fonksiyon kullanacağız. İvmeölçer olarak LI3LV02DL kullanılmıştır.
Figür 2’de zamanlama diyagramı verilmiştir. Diyagramda verilen yazma işlemiyle başlayalım. Slave Select sinyali Master cihaz tarafından Low yapılmalı ve Master cihazın 2 kez 8 saat (SCK) darbesi bitene kadar Low konumunda tutulmalıdır. Daha sonra SCK sinyalinin ilk High konuma yükselmesi ile Slave Select sinyali geçerli olur. MOSI sinyal değeri SCK saat sinyalinin pozitif kenarında clock’lanır, ve MOSI sinyali saat sinyali kenarından önce veya sonra değiştirilir. Bu bilgiler için Slave cihazın teknik dokümanlarından setup ve hold zamanlarına bakınız. ilk gönderilen 8-bit’in ilk biti Slave adrese yazma işlemi için Low veya okuma işlemi için High’dır. Bu durumda bu bit Low olacak. Bir sonraki bit sıfır olarak sabitlenir ve geriye kalan 6-bit adres bitleridir. Yazma için Slave cihazın içerisinde bulunan registerlara yazmak için kullanılan adrestir. I2C deki gibi veri yolundaki Slave cihazı seçmek için kullanılan adresler gibi değildir. Burada SPI veri yolu için cihaz seçiminde Slave Select sinyali kullanılır. Bir sonraki 8 (sekiz) bit ise Slave cihazın desteklediği veri byte’ıdır. Diyagramda 0x20(Hex)deki adrese , 0x40(Hex) değeri yazılmaktadır. MISO sinyali yazma sırasında gösterilememiştir. Çünkü bu sinyal yazma işleminde önemli değildir.

Figür 2. SPI veri yolu sinyalleri, Slave cihaz içerisine yazma işlemi (üst), Slave cihazdan okuma işlemi (Alt)

Figür 2’deki diyagramın alt kısmında okuma işlemi için zamanlama diyagramı verilmiştir. Slave Select sinyali saat sinyalinin 16 yükselen kenarı süresince yazma işleminin aynısı gibi Low konumunda tutulur. Sinyallerden MOSI ve MISO farklıdır. Master cihaz ilk olarak ilk biti High olacak şekilde ve yazma işleminde olduğu gibi ikinci bit Low olacak şekilde MOSI sinyalini kullanarak Slave cihaza okuma komutunu gönderir. Geriye kalan 6-bit  Slave cihazın adresidir. Burada okuma için Slave cihazın iş bloklarından 0x28(hex) adresidir. Sonraki birinci byte MOSI sinyali için önemli değildir. Figürde de görüleceği üzere “X” ile işaretlenmiştir. Fakat saat sinyali devam eder ve Slave MISO sinyali üzerinden okunan byte’ı verir. Figürde gösterilen okunan veri 0xAC(Hex) değeridir.
Her iki yazma ve okuma sıralaması için Slave Select sinyali, saat sinyali ve ilk byte’ın MOSI sinyali üzerinden gönderme benzerdir. Bu işlemler tek bir fonksiyon yazılarak yazma ve okuma işlemi yapılabilir ve kesme üzerinden okunan veri alınabilir. Ne zaman Slave’e yazma işlemi uygulanırsa kesme üzerinden MISO sinyali ile geriye dönen veri görmezden gelinir. Ne zaman slave cihazdan okuma işlemi yapılırsa MOSI sinyali üzerinden boş bir 8-bit değer gönderebiliriz. Aşağıdaki  kodda bu özellikler listelenmiştir.
char SPItransaction (int AandD) { // yaklaşık 7us
#define MOSI 0x8000
#define MISO 0x4000
#define SCK 0x2000
#define SEL 0x1000
int Ret = 0;
                         GPIO->ODR &= ~SEL;            // SPI seçilir
                         for (char k=0; k<16; k++) {   // 16 bti için for döngüsü kur.
                               if (AandD & 0x8000)       GPIOB->ODR |= MOSI; // adres ve veri gönder
                               else                               GPIOB->ODR &= ~MOSI;
                         AandD <<= 1;                       // adres ve veri için ilk bit
                         GPIOB->ODR &= ~SCK;         // SCK Low yap.
                         for (int i=0; i<10;i++) { };     // 150ns zaman gecikmesi
                         Ret <<= 1;                           // okunan bit sağa kaydır
                         GPIOB->ODR |= SEL;            // SPI tamam
                         GPIOB->ODR |= SCK;            // SCK High yap
                         if (GPIOB->IDR & MISO) Ret++;        // bit oku ve string’e ekle
                         };
                         GPIOB->ODR |= SEL;               // Slave Select pasif yap.
                         return  ((char) (Ret & 0xff));  // okunan veri ile geri dön.
}
Figür 3. SPI veri yolu kullanılarak bir byte veri okuma ve yazma fonksiyonu
Fonksiyon MISO sinyali üzerinden iletişim anında alınan değeri “char” olarak geri döner. Fonksiyon bazı bit tanımlamaları, deklerasyonlar ve “Ret” lokal değişkenin kurulumuyla başlar. Sonra Slave Select sinyali Low konumuna alınır ve fonksiyon sonuna kadar bu konumda tutulur.
Veri alışı ve verişi için ihtiyaç duyulan 16 (on altı) saat darbesi için fonksiyonun devamındaki “for” döngüsü kullanılır. Bu döngü içerisinde ilk bit-15 kontrol edilir ve MOSI sinyali bu bit ile set edilir. Sonra argüman değeri bir sonraki loop için bir bit sola kaydırılır. Saat darbesi SCK ilk Low yapılır ve tekrar High yapılır. Slave cihazla adaptasyon için biraz zaman gecikmesi eklenir. Döngü içerisinde MISO sinyali üzerinden okunan veri Ret değişkenine bit kaydırma işlemi yapılarak yazılır.
Slave cihaza yazmak amacıyla yukarıdaki fonksiyon çağırılabilir. Veri byte’ları ve adres bu argümanda birleştirilebilir. “SPIwrite” fonksiyonunun listesi Figür 4’ün üst kısmında verilmiştir. “Adr” sadece 6-bit olarak kullanılır(bu 16-bit dizisinin ilk ikisi Low durumundadır). Bunlar 8-bit sola kaydırılır ve çıkan sonuç veriye eklenir. Geri dönüş değeri göz ardı edilir.
Slave cihazdan okuma yapmak için benzer bir yöntemle yukarıdaki fonksiyon çağrılır fakat 16-bit bit dizisinin en yüksek biti 0x8000(Hex) değeri eklenerek High yapılır. Slave’den okuma durumunda geri dönüş değeri burada kullanılır.
void  SPIwrite (int Adr, int Data) {
                         SPItransaction (((Adr & 0x3f) << 8) + Data);  // 16-biti hazırla
}
char SPIread(int Adr) {
                         char Ret = SPItransaction(((Adr & 0x3f) << 8) + 0x8000);  // 16 bit
                         return (Ret);                                            // okunan sonuç ile geri dön
}
Figür 4. Yazma ve okuma için yukarıdaki fonksiyonun kurulumu
İvmeölçerden iki eksenin okumasını yapan programın tamamı figür 5'de verilmiştir. Program Slave cihazın hangi portlara bağlantısı yapılmış ise mikroişlemcinin portlarının kurulumuyla başlar. Slave cihaz ve LCD ekran kurulumuyla devam eder. Program sonsuz döngü içerisinde çalışır. Her eksen için 2 byte okur ve 16-bit dizisi halinde birleştirir. Biraz zaman gecikmesi eklenerek sonuç LCD ekranda gösterilir.
#include "stm32f4xx.h"
#include "LCD2x16.c"
void main () {
// ports init: 15-MOSI, 14-MISO, 13-SCK, 12-SEL
RCC->AHB1ENR |= 0x00000002;    // GPIOB portunun saatini aktif et
GPIOB->MODER &= ~0x80000000; // PB 15 => sadece giriş pini olarak ayarla
GPIOB->MODER |= 0x45000000;   // PB 15,13,12 => çıkışlar
GPIOB->ODR |= 0xc000;              // SEL & SCK -> high
// ivmeölçer çipinin kurulumu
SPIwrite(0x20, 0xc7);                 // CR1: power-up, tüm eksenler
SPIwrite(0x21, 0x40);                // CR2: Block data update & 12 bit mode
SPIwrite(0x22, 0x00);                // CR3: filtre yok
// LCD ekran kurulumunu yap
LCD_init();                               // LCD kurulumu
LCD_string("x= mg", 0x00);       // 1nci satırı hazırla
LCD_string("y= mg", 0x40);      // 2nci satırı hazırla
// sonsuz döngü
while (1) {
          short retX = SPIread(0x28) + (SPIread(0x29) << 8); // x ekseni, her iki byte
          LCD_sInt16(retX, 0x02, 1);                                    // displayde göster
          short retY = SPIread(0x2a) + (SPIread(0x2b) << 8); // y ekseni, her iki byte
          LCD_sInt16(retY, 0x42, 1);                                   // displayde göster
          for (int i=0; i<1000000; i++) {};                            // yaklaşık 30ms zaman gecikme
          };
}
Figür 5. SPI veri yolunu kullanımıyla sensörden veri okuyan program
Şimdilik bu kadar arkadaşlar Gülen
Sorularınız ve önerileriniz için m.hakki.kaplan@gmail.com adresinden her zaman bana ulaşabilirsiniz.
Hakkı KAPLAN



1 yorum: