Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🔗 hal::i2c

Inter-Integrated Circuit Bus - I²C Protocol

396 satır ~18 fonksiyon Multi-Master

📖 Genel Bakış

I2C (I²C, IIC), iki telli senkron haberleşme protokolüdür. Sensörler, EEPROM, RTC, OLED display gibi çevre birimlerine bağlantı için kullanılır.

🔑 Temel Özellikler

  • Master/Slave mode
  • 7-bit/10-bit addressing
  • Standard mode (100 kHz)
  • Fast mode (400 kHz)
  • Fast mode plus (1 MHz)
  • Multi-master support
  • Clock stretching
  • DMA support

🚀 Hızlı Başlangıç

içe_aktar hal::i2c, hal::gpio

// Setup I2C pins (PB6=SCL, PB7=SDA)
gpio.clock_enable(gpio.PORT_B)
gpio.pin_init(gpio.PORT_B, 6, yap  // SCL
    mode: gpio.MODE_AF_OD,
    speed: gpio.SPEED_HIGH,
    pull: gpio.PULL_UP,
    af: 4
son)
gpio.pin_init(gpio.PORT_B, 7, yap  // SDA
    mode: gpio.MODE_AF_OD,
    speed: gpio.SPEED_HIGH,
    pull: gpio.PULL_UP,
    af: 4
son)

// Initialize I2C
değişken i2c1 = i2c.init(i2c.I2C1, yap
    speed: i2c.SPEED_100KHZ,
    mode: i2c.MODE_I2C
son)

// Write to device
i2c.write(i2c1, 0x3C, [0x00, 0xAE])  // Address 0x3C

// Read from device
değişken data = i2c.read(i2c1, 0x50, 16)  // Read 16 bytes

💡 Örnek: BMP280 Temperature/Pressure Sensor

içe_aktar hal::i2c

sabit BMP280_ADDR = 0x76

yapı BMP280Data yap
    temperature: kesir,
    pressure: kesir
son

fonksiyon bmp280_init(i2c1: i2c.Handle) -> Sonuç[Hiçbir, Hata] yap
    // Read chip ID (should be 0x58)
    değişken id = i2c.read_register(i2c1, BMP280_ADDR, 0xD0)?
    eğer id != 0x58 ise yap
        dön Hata("Invalid BMP280 chip ID")
    son
    
    // Configure sensor (normal mode, oversampling x16)
    i2c.write_register(i2c1, BMP280_ADDR, 0xF4, 0xB7)?  // ctrl_meas
    i2c.write_register(i2c1, BMP280_ADDR, 0xF5, 0xA0)?  // config
    
    dön Tamam(Hiçbir)
son

fonksiyon bmp280_read(i2c1: i2c.Handle) -> Sonuç[BMP280Data, Hata] yap
    // Read raw data (6 bytes: press_msb, press_lsb, press_xlsb, temp_msb, temp_lsb, temp_xlsb)
    değişken data = i2c.read_bytes(i2c1, BMP280_ADDR, 0xF7, 6)?
    
    // Parse raw values
    değişken press_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    değişken temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
    
    // Apply calibration (simplified)
    değişken temperature = temp_raw.kesir() / 100.0
    değişken pressure = press_raw.kesir() / 256.0
    
    dön Tamam(BMP280Data yap
        temperature: temperature,
        pressure: pressure
    son)
son

fonksiyon ana() yap
    değişken i2c1 = i2c.init(i2c.I2C1, yap
        speed: i2c.SPEED_400KHZ
    son)
    
    bmp280_init(i2c1)?
    
    döngü yap
        değişken data = bmp280_read(i2c1)?
        io.println("Temp: {:.1f}°C  Pressure: {:.0f} Pa".formatla(
            data.temperature, data.pressure
        ))
        core.delay_ms(1000)
    son
son

💡 Örnek 2: MPU6050 6-Axis IMU (Accelerometer + Gyroscope)

içe_aktar hal::i2c

sabit MPU6050_ADDR = 0x68

yapı MPU6050Data yap
    accel_x: kesir,
    accel_y: kesir,
    accel_z: kesir,
    gyro_x: kesir,
    gyro_y: kesir,
    gyro_z: kesir,
    temperature: kesir
son

fonksiyon mpu6050_init(i2c1: i2c.Handle) -> Sonuç[Hiçbir, Hata] yap
    // Wake up MPU6050 (PWR_MGMT_1 register = 0)
    i2c.write_register(i2c1, MPU6050_ADDR, 0x6B, 0x00)?
    core.delay_ms(100)
    
    // Configure accelerometer (±2g)
    i2c.write_register(i2c1, MPU6050_ADDR, 0x1C, 0x00)?
    
    // Configure gyroscope (±250°/s)
    i2c.write_register(i2c1, MPU6050_ADDR, 0x1B, 0x00)?
    
    dön Tamam(Hiçbir)
son

fonksiyon mpu6050_read(i2c1: i2c.Handle) -> Sonuç[MPU6050Data, Hata] yap
    // Read 14 bytes starting from ACCEL_XOUT_H (0x3B)
    değişken data = i2c.read_bytes(i2c1, MPU6050_ADDR, 0x3B, 14)?
    
    // Parse accelerometer data (raw values are signed 16-bit)
    değişken accel_x = ((data[0] olarak i16) << 8 | data[1] olarak i16).kesir() / 16384.0
    değişken accel_y = ((data[2] olarak i16) << 8 | data[3] olarak i16).kesir() / 16384.0
    değişken accel_z = ((data[4] olarak i16) << 8 | data[5] olarak i16).kesir() / 16384.0
    
    // Parse temperature (°C = raw/340 + 36.53)
    değişken temp_raw = ((data[6] olarak i16) << 8 | data[7] olarak i16)
    değişken temperature = temp_raw.kesir() / 340.0 + 36.53
    
    // Parse gyroscope data (°/s)
    değişken gyro_x = ((data[8] olarak i16) << 8 | data[9] olarak i16).kesir() / 131.0
    değişken gyro_y = ((data[10] olarak i16) << 8 | data[11] olarak i16).kesir() / 131.0
    değişken gyro_z = ((data[12] olarak i16) << 8 | data[13] olarak i16).kesir() / 131.0
    
    dön Tamam(MPU6050Data yap
        accel_x: accel_x,
        accel_y: accel_y,
        accel_z: accel_z,
        gyro_x: gyro_x,
        gyro_y: gyro_y,
        gyro_z: gyro_z,
        temperature: temperature
    son)
son

fonksiyon ana() yap
    değişken i2c1 = i2c.init(i2c.I2C1, yap speed: i2c.SPEED_400KHZ son)
    mpu6050_init(i2c1)?
    
    döngü yap
        değişken data = mpu6050_read(i2c1)?
        io.println("Accel: X={:.2f}g Y={:.2f}g Z={:.2f}g".formatla(
            data.accel_x, data.accel_y, data.accel_z
        ))
        io.println("Gyro:  X={:.1f}°/s Y={:.1f}°/s Z={:.1f}°/s".formatla(
            data.gyro_x, data.gyro_y, data.gyro_z
        ))
        io.println("Temp: {:.1f}°C".formatla(data.temperature))
        io.println("---")
        core.delay_ms(100)
    son
son

💡 Örnek 3: OLED Display (SSD1306)

içe_aktar hal::i2c

sabit SSD1306_ADDR = 0x3C

fonksiyon oled_command(i2c1: i2c.Handle, cmd: u8) -> Sonuç[Hiçbir, Hata] yap
    i2c.write(i2c1, SSD1306_ADDR, [0x00, cmd])?  // 0x00 = command mode
    dön Tamam(Hiçbir)
son

fonksiyon oled_data(i2c1: i2c.Handle, data: u8) -> Sonuç[Hiçbir, Hata] yap
    i2c.write(i2c1, SSD1306_ADDR, [0x40, data])?  // 0x40 = data mode
    dön Tamam(Hiçbir)
son

fonksiyon oled_init(i2c1: i2c.Handle) -> Sonuç[Hiçbir, Hata] yap
    // Display off
    oled_command(i2c1, 0xAE)?
    
    // Set display clock
    oled_command(i2c1, 0xD5)?
    oled_command(i2c1, 0x80)?
    
    // Set multiplex ratio
    oled_command(i2c1, 0xA8)?
    oled_command(i2c1, 0x3F)?
    
    // Set display offset
    oled_command(i2c1, 0xD3)?
    oled_command(i2c1, 0x00)?
    
    // Set start line
    oled_command(i2c1, 0x40)?
    
    // Charge pump
    oled_command(i2c1, 0x8D)?
    oled_command(i2c1, 0x14)?
    
    // Memory mode
    oled_command(i2c1, 0x20)?
    oled_command(i2c1, 0x00)?
    
    // Set segment remap
    oled_command(i2c1, 0xA1)?
    
    // Set COM output scan direction
    oled_command(i2c1, 0xC8)?
    
    // Set COM pins
    oled_command(i2c1, 0xDA)?
    oled_command(i2c1, 0x12)?
    
    // Set contrast
    oled_command(i2c1, 0x81)?
    oled_command(i2c1, 0xCF)?
    
    // Set precharge
    oled_command(i2c1, 0xD9)?
    oled_command(i2c1, 0xF1)?
    
    // Set VCOMH
    oled_command(i2c1, 0xDB)?
    oled_command(i2c1, 0x40)?
    
    // Display on
    oled_command(i2c1, 0xA4)?
    oled_command(i2c1, 0xA6)?  // Normal display (not inverted)
    oled_command(i2c1, 0xAF)?  // Turn on
    
    dön Tamam(Hiçbir)
son

fonksiyon oled_clear(i2c1: i2c.Handle) -> Sonuç[Hiçbir, Hata] yap
    her page içinde 0..8 için yap
        oled_command(i2c1, 0xB0 + page)?  // Set page
        oled_command(i2c1, 0x00)?          // Low column
        oled_command(i2c1, 0x10)?          // High column
        
        her i içinde 0..128 için yap
            oled_data(i2c1, 0x00)?
        son
    son
    dön Tamam(Hiçbir)
son

fonksiyon oled_print(i2c1: i2c.Handle, text: yazı) -> Sonuç[Hiçbir, Hata] yap
    // Simple 5x8 font (implement font data)
    // This is a simplified example
    her char içinde text.harfler() için yap
        // Write character bitmap
        oled_data(i2c1, 0xFF)?  // Example pixel data
    son
    dön Tamam(Hiçbir)
son

fonksiyon ana() yap
    değişken i2c1 = i2c.init(i2c.I2C1, yap speed: i2c.SPEED_400KHZ son)
    
    oled_init(i2c1)?
    oled_clear(i2c1)?
    oled_print(i2c1, "BERK Embedded!")?
    
    döngü yap
        core.delay_ms(1000)
    son
son

💡 Örnek 4: I2C Scanner (Device Discovery)

içe_aktar hal::i2c

fonksiyon i2c_scan(i2c1: i2c.Handle) yap
    io.println("I2C Bus Taraması başlatılıyor...")
    io.println("     0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F")
    
    her row içinde 0..8 için yap
        io.print("{:X}0: ".formatla(row))
        
        her col içinde 0..16 için yap
            değişken addr = row * 16 + col
            
            eğer addr < 0x08 veya addr > 0x77 ise yap
                io.print("   ")
                devam
            son
            
            // Try to write 0 bytes (address ping)
            eşle i2c.write(i2c1, addr olarak u8, []) yap
                Tamam(_) => io.print("{:02X} ".formatla(addr)),
                Hata(_) => io.print("-- ")
            son
        son
        io.println("")
    son
    
    io.println("Tarama tamamlandı!")
son

fonksiyon ana() yap
    core.system_init()
    
    değişken i2c1 = i2c.init(i2c.I2C1, yap
        speed: i2c.SPEED_100KHZ
    son)
    
    i2c_scan(i2c1)
son

💡 Örnek 5: Multi-Device Communication

içe_aktar hal::i2c

sabit EEPROM_ADDR = 0x50
sabit RTC_ADDR = 0x68
sabit TEMP_SENSOR_ADDR = 0x48

fonksiyon eeprom_write(i2c1: i2c.Handle, addr: u16, data: u8) -> Sonuç[Hiçbir, Hata] yap
    değişken addr_high = (addr >> 8) olarak u8
    değişken addr_low = (addr & 0xFF) olarak u8
    i2c.write(i2c1, EEPROM_ADDR, [addr_high, addr_low, data])?
    core.delay_ms(5)  // EEPROM write cycle time
    dön Tamam(Hiçbir)
son

fonksiyon eeprom_read(i2c1: i2c.Handle, addr: u16) -> Sonuç[u8, Hata] yap
    değişken addr_high = (addr >> 8) olarak u8
    değişken addr_low = (addr & 0xFF) olarak u8
    
    // Write address
    i2c.write(i2c1, EEPROM_ADDR, [addr_high, addr_low])?
    
    // Read data
    değişken data = i2c.read(i2c1, EEPROM_ADDR, 1)?
    dön Tamam(data[0])
son

fonksiyon rtc_set_time(i2c1: i2c.Handle, hour: u8, minute: u8, second: u8) -> Sonuç[Hiçbir, Hata] yap
    // DS1307 RTC format (BCD encoding)
    değişken hour_bcd = ((hour / 10) << 4) | (hour % 10)
    değişken min_bcd = ((minute / 10) << 4) | (minute % 10)
    değişken sec_bcd = ((second / 10) << 4) | (second % 10)
    
    i2c.write(i2c1, RTC_ADDR, [0x00, sec_bcd, min_bcd, hour_bcd])?
    dön Tamam(Hiçbir)
son

fonksiyon temp_read(i2c1: i2c.Handle) -> Sonuç[kesir, Hata] yap
    // LM75 temperature sensor
    değişken data = i2c.read_bytes(i2c1, TEMP_SENSOR_ADDR, 0x00, 2)?
    değişken temp_raw = ((data[0] olarak i16) << 8 | data[1] olarak i16) >> 5
    değişken temp = temp_raw.kesir() * 0.125
    dön Tamam(temp)
son

fonksiyon ana() yap
    değişken i2c1 = i2c.init(i2c.I2C1, yap speed: i2c.SPEED_100KHZ son)
    
    // EEPROM test
    eeprom_write(i2c1, 0x0000, 0xAB)?
    değişken value = eeprom_read(i2c1, 0x0000)?
    io.println("EEPROM[0]: 0x{:02X}".formatla(value))
    
    // RTC set
    rtc_set_time(i2c1, 14, 30, 0)?  // 14:30:00
    io.println("RTC ayarlandı")
    
    // Temperature reading
    döngü yap
        değişken temp = temp_read(i2c1)?
        io.println("Sıcaklık: {:.2f}°C".formatla(temp))
        core.delay_ms(2000)
    son
son

📦 Tipler ve Enum'lar

// I2C Instance
enum Instance {
    I2C1, I2C2, I2C3
}

// I2C Speed Mode
enum Speed {
    SPEED_100KHZ,   // Standard mode (100 kHz)
    SPEED_400KHZ,   // Fast mode (400 kHz)
    SPEED_1MHZ      // Fast mode plus (1 MHz)
}

// Addressing Mode
enum AddressMode {
    ADDR_7BIT,   // 7-bit addressing (default)
    ADDR_10BIT   // 10-bit addressing
}

// Duty Cycle (Fast mode)
enum DutyCycle {
    DUTY_2,      // Tlow/Thigh = 2
    DUTY_16_9    // Tlow/Thigh = 16/9
}

yapı Config yap
    speed: Speed,
    mode: AddressMode,
    duty_cycle: İsteğe_Bağlı[DutyCycle],
    own_address: İsteğe_Bağlı[u8],  // For slave mode
    ack_enable: mantıksal,
    dma_enable: mantıksal
son

⚙️ Fonksiyonlar

Initialization

fonksiyon init(instance: Instance, config: Config) -> Handle
// Initialize I2C with configuration

fonksiyon deinit(handle: Handle)
// Deinitialize I2C

fonksiyon enable(handle: Handle)
// Enable I2C peripheral

fonksiyon disable(handle: Handle)
// Disable I2C peripheral

Master Mode Operations

fonksiyon write(handle: Handle, addr: u8, data: [u8]) -> Sonuç[Hiçbir, Hata]
// Write data to slave device

fonksiyon read(handle: Handle, addr: u8, len: sayı) -> Sonuç[[u8], Hata]
// Read data from slave device

fonksiyon write_read(handle: Handle, addr: u8, write_data: [u8], read_len: sayı) -> Sonuç[[u8], Hata]
// Combined write then read (repeated start)

fonksiyon write_register(handle: Handle, addr: u8, reg: u8, value: u8) -> Sonuç[Hiçbir, Hata]
// Write single byte to register

fonksiyon read_register(handle: Handle, addr: u8, reg: u8) -> Sonuç[u8, Hata]
// Read single byte from register

fonksiyon read_bytes(handle: Handle, addr: u8, reg: u8, len: sayı) -> Sonuç[[u8], Hata]
// Read multiple bytes from register

Status and Control

fonksiyon is_device_ready(handle: Handle, addr: u8, trials: sayı) -> mantıksal
// Check if device is ready (ACK polling)

fonksiyon get_bus_state(handle: Handle) -> BusState
// Get current bus state

enum BusState {
    IDLE,
    BUSY,
    MASTER_TX,
    MASTER_RX,
    SLAVE_TX,
    SLAVE_RX,
    ERROR
}

Error Handling

fonksiyon get_error(handle: Handle) -> İsteğe_Bağlı[I2CError]
// Get last error

enum I2CError {
    NACK,           // No acknowledge
    ARBITRATION_LOST, // Multi-master arbitration lost
    BUS_ERROR,      // Bus error (misplaced start/stop)
    OVERRUN,        // Data overrun
    TIMEOUT         // Timeout occurred
}

fonksiyon clear_error(handle: Handle)
// Clear error flags

DMA Support

fonksiyon write_dma(handle: Handle, addr: u8, data: &[u8]) -> Sonuç[Hiçbir, Hata]
// Write data using DMA

fonksiyon read_dma(handle: Handle, addr: u8, buffer: &mut [u8]) -> Sonuç[Hiçbir, Hata]
// Read data using DMA

⚡ Performans İpuçları

  • Pull-up Rezistans: 4.7kΩ genel kullanım için ideal (100 kHz için 10kΩ, 400 kHz için 2.2kΩ)
  • Kablo Uzunluğu: Kısa kablolar kullanın (<1m), uzun kablolarda capacitance artar
  • Bus Hızı: Sensör datasheet'ine göre seçin (çoğu 100-400 kHz destekler)
  • DMA Kullanımı: Büyük veri transferlerinde (>10 byte) CPU yükünü azaltır
  • Error Handling: Her I2C işlemi sonrası error check yapın
  • Clock Stretching: Yavaş slave'ler için enable edin

⚠️ Önemli Notlar

  • SCL ve SDA pinlerinde external pull-up gerekir (4.7kΩ)
  • GPIO pinlerini MODE_AF_OD (open-drain) olarak yapılandırın
  • Bus üzerinde maksimum 112 device (7-bit) veya 1024 device (10-bit)
  • Start/stop condition timing hassasiyeti önemli
  • Multi-master mode'da arbitration kaybı oluşabilir
  • 5V tolerant pinler yoksa level shifter kullanın
  • EMI/noise için twisted-pair kablo veya shielding kullanın

🎯 Yaygın I2C Cihazları

CihazAddressAçıklama
PCF85740x20-0x278-bit I/O expander
AT24C2560x50-0x57256K EEPROM
MPU60500x68, 0x696-axis IMU
BMP2800x76, 0x77Pressure sensor
DS13070x68RTC (Real-Time Clock)
SSD13060x3C, 0x3DOLED display
ADS11150x48-0x4B16-bit ADC
LM750x48-0x4FTemperature sensor

📚 Platform Desteği

  • STM32: Tüm serilerde I2C1/I2C2/I2C3
  • ESP32: 2x I2C (software configurable pins)
  • GD32VF103: I2C0/I2C1 (RISC-V)
  • nRF52: TWI (Two-Wire Interface)

🔗 İlgili Modüller

  • hal::gpio - SCL/SDA pin config
  • hal::dma - DMA transfers
  • hal::int - Interrupt handling

📖 Referanslar

  • NXP UM10204 - I2C-bus specification and user manual
  • STM32 AN2824 - I2C optimized examples
  • I2C Bus Pull-up Resistor Calculation (Texas Instruments)

← HAL Modülleri | Tüm Modüller | Ana Sayfa