Sensörün veri sayfasını açtınız. Hem I2C hem SPI destekliyor. Hangisini seçeceksiniz?
Bu soruya "ikisi de olur" diye cevap vermek teknik olarak doğrudur — ama mühendislik açısından yanlıştır. İkisi farklı sorunları çözer, farklı durumlarda bozulur ve yanlış seçildiğinde sizi projenin ortasında çıkmaz sokağa sokar. Bir TFT ekranı I2C üzerinden sürmek istediğinizde, ya da aynı I2C hattına aynı adresten iki sensör bağlamaya çalıştığınızda farkı acı biçimde öğrenirsiniz.
Gömülü sistemlerde bir sensör, bir ekran veya bir bellek çipiyle konuşmanız gerektiğinde genellikle iki protokol karşınıza çıkar: I2C ve SPI.
En belirgin fark tel sayısıdır.
I2C (Inter-Integrated Circuit), 1982'de Philips tarafından geliştirildi. Yalnızca 2 sinyal hattı kullanır:
Her iki hat da açık-kollektör (open-drain) çıkışlıdır: bir cihaz hattı 0'a çekebilir ama aktif olarak 1'e çekemez. Bunun yerine pull-up dirençleri (genellikle 4,7 kΩ) hattı yüksek tutmakla sorumludur.
MCU Sensör A Sensör B
│ │ │
SDA ─────────────────┤──────────────┤
SCL ─────────────────┤──────────────┤
[4.7kΩ pull-up]
2 tel, birden fazla cihazı aynı hat üzerinde bağlamanıza olanak tanır. Her cihazın 7-bit benzersiz bir adresi vardır; MCU önce hangi cihazla konuşacağını belirtir, sonra veri gönderir.
SPI (Serial Peripheral Interface), Motorola'nın 1980'lerde ortaya koyduğu bir standarttır. 4 sinyal hattı kullanır:
Birden fazla cihaz bağlarken MOSI, MISO ve SCK hattı paylaşılır; ama her cihaz için ayrı bir CS pini gerekir:
MCU Flash ADC DAC
│ │ │ │
MOSI ────────┤───────────┤───────────┤
MISO ────────┤───────────┤───────────┤
SCK ────────┤───────────┤───────────┤
CS1 ────────┤
CS2 ────────────────────┤
CS3 ────────────────────────────────┤
3 cihaz için 3 ayrı CS pini — 5 cihaz için 5 ayrı CS pini. Pin sayısı hızla artabilir.
Bu, pratikte en belirleyici farktır.
| Protokol | Standart mod | Hızlı mod | Yüksek hız |
|---|---|---|---|
| I2C | 100 kHz | 400 kHz | 3,4 MHz (HS) |
| SPI | — | 10–50 MHz (MCU'ya bağlı) | 50 MHz+ |
I2C, her byte için bir ACK (acknowledgement) biti bekler ve başlangıç/bitiş koşulları ekstra zaman harcatır. Bu ek yük, protokolü doğası gereği yavaşlatır.
SPI'da böyle bir yük yoktur: MCU clock pinini titretir, bit bit veri akar. Eşzamanlı tam-çift yönlü (full-duplex) iletişim yapılabilir — MCU bir byte gönderirken aynı anda bir byte alabilir.
Ne zaman önemlidir?
Bir sıcaklık sensöründen saniyede birkaç okuma yapıyorsanız I2C'nin 400 kHz'i fazlasıyla yeterlidir. Ama 240×320 piksellik bir TFT ekranı güncelleyecekseniz veya yüksek örnekleme hızında bir ADC ile çalışacaksanız, SPI'nın hızı kritik öneme sahip olur.
I2C'de her iletişim, MCU'nun hedef cihazın adresini yayınlamasıyla başlar. 7-bit adresleme şeması 128 farklı cihaz adresine izin verir (bazı adresler rezerve edilmiştir, pratikte ~112 kullanılabilir).
START | ADRES (7-bit) | R/W | ACK | VERİ | ACK | ... | STOP
Aynı türden iki sensörünüz varsa (örneğin iki BME280 nem/basınç sensörü), problem çıkar: aynı I2C adresine sahip olduklarından aynı hat üzerinde yan yana çalışamazlar. Çözüm için ya sensörün adres konfigürasyon pinini kullanmanız (varsa) ya da farklı I2C veri yolları oluşturmanız gerekir.
SPI'da cihaz seçimi yazılımsal değil, donanımsaldır: MCU, CS pinini LOW'a çekerek o cihazla iletişimi başlatır, HIGH'a döndürerek bitirir. İki aynı cihaz her zaman yan yana kullanılabilir — sadece iki ayrı CS pinine ihtiyacınız vardır.
STM32 HAL ile bir I2C cihazından veri okumak şöyle görünür:
#include "stm32f0xx_hal.h"
I2C_HandleTypeDef hi2c1;
void i2c1_init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x2000090E; /* 400 kHz @ 48 MHz */
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
/* 7-bit adresi 0x48 olan sensörden 2 byte oku */
void read_sensor(uint8_t *buf) {
uint8_t reg = 0x00; /* Okumak istediğimiz register adresi */
HAL_I2C_Master_Transmit(&hi2c1, 0x48 << 1, ®, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Receive (&hi2c1, 0x48 << 1, buf, 2, HAL_MAX_DELAY);
}
Not: HAL'de I2C adresi 8-bit olarak iletilir — 7-bit adresi 1 sola kaydırın (
0x48 << 1 = 0x90). R/W biti HAL tarafından eklenir.
STM32 HAL ile SPI haberleşmesi:
#include "stm32f0xx_hal.h"
SPI_HandleTypeDef hspi1;
void spi1_init(void) {
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES; /* Tam çift yönlü */
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; /* CPOL = 0 */
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; /* CPHA = 0 → SPI Mode 0 */
hspi1.Init.NSS = SPI_NSS_SOFT; /* CS yazılımsal */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 48 MHz / 8 = 6 MHz */
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
}
/* CS pini PA4 */
#define CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
/* Bir register oku (çoğu SPI cihazının protokolü: komut gönder, cevabı al) */
uint8_t spi_read_register(uint8_t reg_addr) {
uint8_t tx = reg_addr | 0x80; /* MSB = 1 → okuma komutu (cihaza göre değişir) */
uint8_t rx = 0;
CS_LOW();
HAL_SPI_Transmit(&hspi1, &tx, 1, HAL_MAX_DELAY);
HAL_SPI_Receive (&hspi1, &rx, 1, HAL_MAX_DELAY);
CS_HIGH();
return rx;
}
CPOL ve CPHA: SPI'ın 4 farklı çalışma modu (Mode 0–3) vardır. Veri sayfasındaki (datasheet) "SPI Mode" veya "CPOL/CPHA" tablosuna bakarak hangi modun kullanılacağını belirleyin. Yanlış mod seçilirse veri okunabilir görünür ama tutarsız olur.
Yaygın I2C cihazları: OLED ekranlar (SSD1306), ortam sensörleri (BME280, SHT31), gerçek zamanlı saat modülleri (DS3231), jiroskop/ivmeölçer (MPU-6050)
Yaygın SPI cihazları: SPI Flash bellek (W25Q serisi), yüksek çözünürlüklü ADC/DAC'lar, TFT ve e-ink ekranlar, SD kart arabirimleri, RF modüller (NRF24L01)
Bant genişliği > 1 MHz gerekiyor mu?
├── Evet → SPI
└── Hayır
├── Pin sayısı çok kısıtlı mı? → I2C (2 tel yeterli)
├── Birden fazla aynı cihaz mı?
│ ├── Evet, farklı adres var mı? → I2C
│ └── Hayır (aynı adres) → SPI
└── Genel kullanım → I2C (daha basit kablolama)
| Kriter | I2C | SPI |
|---|---|---|
| Tel sayısı | 2 (+ güç) | 4 (+ cihaz başına 1 CS) |
| Maksimum hız | ~3,4 MHz (pratik: 400 kHz) | 10–50 MHz+ |
| Çift yönlü | Yarım çift yönlü | Tam çift yönlü |
| Birden fazla cihaz | Adresle, 2 tel yeterli | CS pini artırarak |
| Adres çakışması riski | Var | Yok |
| Protokol yükü | Orta (ACK, start/stop) | Düşük |
| Tipik kullanım | Sensörler, yapılandırma, ekranlar | Flash, yüksek hızlı ADC, TFT |
Bu platform üzerindeki STM32 derslerinde her iki protokol de register seviyesinde ele alınmaktadır — USART ve SPI birimleri gerçek donanım üzerinde adım adım yapılandırılmaktadır.
Kitap serisini incele →