Mikrodenetleyici Programlama: I2C / SPI / UART Haberleşme
Bu ders; ARM Cortex-M tabanlı mikrodenetleyicilerde (STM32, NXP) GPIO, kesme ve DMA kullanımını, UART, SPI ve I2C haberleşmesini kod örnekleri ve bir sıcaklık sensörü okuma uygulamasıyla anlatır.
Öne Çıkanlar
- Birçok MCU ARM Cortex-M0/M0+/M3/M4/M7 çekirdeği kullanır; bunlar 32 bit RISC işlemcilerdir. ESP32 gibi bazı aileler Xtensa veya RISC-V çekirdeği kullanır.
- Olaya bağlı işler (buton, haberleşme, zamanlayıcı) çoğunlukla kesmeyle ele alınır; kesme rutini kısa tutulur, ağır iş ana döngüye bırakılır.
- DMA, çevre birimi ile bellek arasındaki veri taşımayı CPU'dan alır.
- UART asenkron ve nokta-nokta, SPI senkron ve tam çift yönlü, I2C ise iki hatlı, çok cihazlı ve yarı çift yönlü bir bus'tır.
Mikrodenetleyici Mimarisi
Bir mikrodenetleyici; CPU çekirdeği (ARM Cortex-M), Flash, RAM, çevre birimleri (GPIO, timer, ADC, UART, SPI, I2C, CAN, USB) ve saat sistemini tek çip halinde barındırır. Endüstriyel gömülü sistemlerde en yaygın seri STM32'dir (STMicroelectronics). Örnek konfigürasyon: STM32F411, ARM Cortex-M4F, 100 MHz, 512 KB Flash, 128 KB RAM, 3 UART, 5 SPI, 3 I2C, 1 ADC, USB, timer. Endüstriyel cihazlarda UART çıkışı çoğunlukla bir RS-485 sürücüsüyle sahaya taşınır ve PLC ve SCADA otomasyon sistemlerinde Modbus RTU haberleşmesinin fiziksel temelini oluşturur.
GPIO Kontrolü: Register Tabanlı
LED'in PA5 pinine bağlı olduğunu düşünelim (STM32). Yapılan işlemler:
- Clock aktif: RCC → AHB1ENR → GPIOAEN = 1
- Pin modu: GPIOA → MODER[11:10] = 01 (output)
- Çıkış tipi: OTYPER[5] = 0 (push-pull)
- Hız: OSPEEDR[11:10] = 00 (low speed)
- Pull-up/down: PUPDR[11:10] = 00 (none)
- Çıkış: ODR[5] = 1 (LED yak), = 0 (söndür)
HAL kütüphanesiyle: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
Kesme (Interrupt) ile Buton
Buton PA0 ile GND arasına bağlı, PA0'da dahili pull-up açık olsun. Butona basıldığında pin 1'den 0'a düşer; bu nedenle kesme düşen kenarda tetiklenir:
// CubeMX ile PA0 = GPIO_EXTI0, Falling Edge, Pull-up
void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
static volatile uint32_t last_ms = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_0) {
uint32_t now = HAL_GetTick();
if (now - last_ms >= 30) { // 30 ms içindeki sıçramaları yok say
last_ms = now;
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
}
Mekanik buton kontağı sıçradığı için tek basış birkaç kesme üretebilir. Kesme rutininde bekleme (HAL_Delay) yapılmaz; yukarıdaki gibi zaman damgasıyla veya bir zamanlayıcıyla filtreleme yapılır. Donanımda RC filtre ve Schmitt tetikleyici de kullanılabilir.
UART: Seri Haberleşme
UART (Universal Asynchronous Receiver-Transmitter), iki cihaz arasında asenkron seri haberleşme sağlar. Tipik parametreler: 9600 bit/s, 8 veri biti, paritesiz, 1 stop biti (8N1). MCU pinlerindeki UART sinyali TTL/CMOS seviyesindedir (3,3 V veya 5 V) ve yalnızca kart içinde ya da çok kısa bağlantılarda kullanılır. Daha uzun mesafede bir sürücüyle başka bir fiziksel katmana çevrilir: RS-232'de sinyal seviyeleri ±3 V ile ±15 V arasındadır. RS-485'te ise diferansiyel sürücü çıkışı en az ±1,5 V'tur ve tek hat üzerinde çok sayıda cihaz bulunabilir.
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello\r\n", 7, 100);
// DMA ile:
HAL_UART_Transmit_DMA(&huart1, buf, len);
SPI: Yüksek Hızlı Senkron
SPI (Serial Peripheral Interface), 4 hatlı (MOSI, MISO, SCLK, CS) senkron bir protokoldür. Aynı bus'ta birden fazla cihaz olabilir; her cihaz kendi CS (Chip Select) hattıyla seçilir. Hız, cihaza ve kart yerleşimine bağlı olarak onlarca MHz'e çıkabilir. Örnek uygulama: SPI Flash, OLED ekran, SD kart, ADC (MAX11040 gibi).
uint8_t tx[2] = {0x01, 0x02};
uint8_t rx[2];
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 100);
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
I2C: Çoklu Cihaz Bus'ı
I2C (Inter-Integrated Circuit), yalnızca 2 hat (SDA, SCL) kullanan çok cihazlı bir bus'tır. Hız modları: Standard 100 kHz, Fast 400 kHz, Fast-mode Plus 1 MHz. Hatlar açık-drenajdır ve pull-up direnci gerektirir. Direnç değeri bus kapasitansına ve hıza göre seçilir; tipik aralık 2,2-10 kΩ'dur. 7 bit adresleme 128 adres verir; bunların bir kısmı ayrılmış olduğu için kullanılabilir adres sayısı yaklaşık 112'dir. Örnek uygulama: EEPROM (24LC256), sıcaklık sensörü (LM75, BMP280), IMU (MPU6050), RTC (DS3231).
// LM75 sıcaklık sensörü örneği
uint8_t reg = 0x00; // Temp register
uint8_t data[2];
HAL_I2C_Master_Transmit(&hi2c1, 0x90, ®, 1, 100);
HAL_I2C_Master_Receive(&hi2c1, 0x90, data, 2, 100);
int16_t raw = (int16_t)((data[0] << 8) | data[1]); // işaretli 16 bit
raw >>= 5; // 11 bit, 0,125 °C/bit (LM75B)
float celsius = raw * 0.125f;
UART / SPI / I2C Karşılaştırma
| Protokol | Tel | Hız | Mesafe | Cihaz | Yaygın kullanım |
|---|---|---|---|---|---|
| UART | 2 (TX, RX) + GND | 9,6-921,6 kbit/s | TTL: kart içi; RS-232: ~15 m; RS-485: 1200 m'ye kadar | 2 (nokta-nokta); RS-485 ile çok noktalı | Debug, GPS, Modbus RTU |
| SPI | 4 + cihaz başına CS | 1-50 MHz | Kart içi (~30 cm) | Her CS hattı bir cihaz | SPI Flash, SD, OLED, ADC |
| I2C | 2 | 100 kHz-1 MHz | Kart içi; bus kapasitansı en fazla 400 pF | ~112 (7 bit adres) | Sensör, EEPROM, RTC |
DMA (Direct Memory Access)
DMA, çevre biriminden RAM'e (veya tersi) CPU müdahalesi olmadan blok halinde veri taşır. CPU sadece transfer başladığında ve bittiğinde meşgul olur; arada başka işleri yapabilir. Sürekli ADC örneklemesinde, UART ile değişken uzunlukta veri alırken ve SPI/I2C toplu transferlerinde özellikle yararlıdır.
Debug Araçları
- ST-Link V2/V3: STM32 için standart debug + programlama
- J-Link: Segger'ın çoklu MCU destekli profesyonel debugger
- Saleae Logic Analyzer: UART/SPI/I2C sinyal analizi
- Osiloskop: Fiziksel sinyal kalitesi ve timing
- RTT (Real-Time Transfer): printf tabanlı hızlı debug
Sıradaki Konu
Bu orta seviye dersi tamamladıktan sonra:
- İleri seviye: PCB Tasarımı ve EMI/EMC Uyumluluk
- Uygulamalı: RC/RL filtre hesabı ve yüklü gerilim bölücü
Bu içerik eğitim amaçlıdır. Gerçek ürün tasarımında; MCU seçimi, güvenlik, sertifika ve üretim testleri için yetkili mühendis çalışması gereklidir.