🔗 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ı
| Cihaz | Address | Açıklama |
|---|---|---|
| PCF8574 | 0x20-0x27 | 8-bit I/O expander |
| AT24C256 | 0x50-0x57 | 256K EEPROM |
| MPU6050 | 0x68, 0x69 | 6-axis IMU |
| BMP280 | 0x76, 0x77 | Pressure sensor |
| DS1307 | 0x68 | RTC (Real-Time Clock) |
| SSD1306 | 0x3C, 0x3D | OLED display |
| ADS1115 | 0x48-0x4B | 16-bit ADC |
| LM75 | 0x48-0x4F | Temperature 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 confighal::dma- DMA transfershal::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)