Seri Protokoller
TEKNİK REHBER SERİ PROTOKOLLER LIN BUS 2026

LIN Bus
otomotiv alt ağı.

Tek tel, 20 kbps, düşük maliyet — araç içi koltuk, ayna, pencere ve kapı modülleri için otomotiv standardı. Master/slave mimarisi, schedule table ve LDF dosyası ile tam kontrol.

00 LIN nedir

LIN (Local Interconnect Network) — BMW, VW, Audi ve diğer otomotiv üreticilerinin oluşturduğu konsorsiyum tarafından 1999'da tasarlanan düşük maliyetli araç içi ağ standardı.

Tarihçe ve standart

CAN bus tüm araç içi haberleşme için güçlü ancak pahalı bir çözümdür. Koltuk pozisyonu, ayna eğimi, pencere motoru, klima fan hızı gibi düşük hızlı, basit işlevler için CAN'ın karmaşıklığı ve maliyeti gereksizdir. LIN bu boşluğu doldurmak için tasarlandı.

VersiyonYılÖnemli değişiklikler
LIN 1.01999İlk yayın, temel frame yapısı
LIN 1.32002Checksum iyileştirmesi
LIN 2.02003Enhanced checksum, sleep/wake-up, diagnostics
LIN 2.2A2010Mevcut en yaygın versiyon, tam özellik seti
ISO 179872016LIN 2.2A'nın ISO standardı olarak yayımlanması

Kullanım alanları

Koltuk modülüElektrikli koltuk pozisyonu (ileri/geri, yükseklik, sırtlık açısı), ısıtma seviyesi.
Ayna modülüDış ayna eğimi (yatay/dikey), katlanma, ısıtma.
Kapı modülüElektrikli cam, kilit, iç aydınlatma kontrolü.
KlimaFan hızı, sıcaklık sensörü okuma, vana kontrolü.
DireksiyonDireksiyon üzeri buton modülü, cruise control tuşları.

LIN vs CAN karşılaştırması

ÖzellikLINCAN
Tel sayısı1 (+ GND + 12V)2 (diferansiyel)
Max hız20 kbps1 Mbps (CAN FD 8 Mbps)
MimariMaster/SlaveMulti-master
Max node16 (1 master + 15 slave)~110
Transceiver maliyeti~$0.30~$1.00
SenkronizasyonOtomatik (sync byte)Crystal/clock gerektirir

01 Fiziksel katman

LIN tek-tel (single-wire) seri bus. Dominant durum 0V, recessive durum 12V. Araç bataryası üzerinden çalışır.

Sinyal seviyeleri

  12V ────────────────────────────────────────────
         recessive (1)     recessive (1)
  GND ──────────┐                ┌─────────────────
                │   dominant(0)  │
                └────────────────┘

  LIN bus: tek tel, 12V referans, tek yönlü süreç
  Master pull-up: 1kΩ + diod → VBattery
    
DurumVoltajAçıklama
Recessive (1)VBattery − 1V diod ≈ 11VBus pull-up ile belirlenir
Dominant (0)< 0.4 × VBattery ≈ 4.8VNode aktif çekim ile sürülen durum
Uyku (sleep)VBatteryTam recessive, master veya slave wake-up bekler

Hız seçenekleri

HızKullanım
2400 bpsDüşük hızlı koltuk/ayna kontrolü
4800 bpsStandart konfor elektroniği
9600 bpsGenel araç içi uygulama
19200 bpsHızlı sensör okuma
20000 bpsLIN maksimum hızı

LIN transceiver çipleri

TJA1021NXP, en yaygın LIN transceiver, 20 kbps, uyku/uyandırma, +40V koruma.
MCP2021Microchip, LIN 2.2 uyumlu, dahili voltage regulator, SOP-8 paket.
TLIN1029Texas Instruments, geniş gerilim aralığı (5.5–40V), düşük uyku akımı (10µA).
NOT

LIN fiziksel katmanı RS-232'ye benzer (tek tel, asenkron) ancak gerilim seviyeleri çok farklıdır. UART donanımı LIN sinyalini doğrudan süremez; TJA1021 gibi bir transceiver zorunludur. 3.3V UART → LIN için seviye çevirici de gerekir.

02 Master/Slave mimarisi

LIN ağında her zaman bir master ve en fazla 15 slave bulunur. Sadece master haberleşmeyi başlatabilir.

Master ve slave rolleri

  ┌──────────────────────────────────────────────────────┐
  │  LIN bus (tek tel, 12V/0V)                           │
  └────┬──────────┬──────────┬──────────┬───────────────┘
       │          │          │          │
  ┌────┴───┐ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐
  │ MASTER │ │Slave 1│ │Slave 2│ │Slave N│
  │(ECU)   │ │(Koltuk│ │(Ayna) │ │(Kapı) │
  └────────┘ └───────┘ └───────┘ └───────┘
  Yalnızca master HEADER gönderir.
  Slave sadece kendi PID'ini gördüğünde yanıt verir.
    

Master görevi

  • Schedule table'a göre periyodik olarak frame header (Break + Sync + PID) gönderir.
  • Saat kaynağıdır: Sync byte ile tüm slave'ler baud rate'ini senkronize eder.
  • Bus arbitrasyonu yoktur — sadece master konuşmayı başlatır, çakışma imkansızdır.
  • Sleep komutu (0x00 PID) ile tüm ağı uyku moduna alabilir.

Slave görevi

  • Sync byte'ı alarak baud rate'ini master'a kilitler (kendinde kristal gerekmez — maliyet düşer).
  • PID'ini tanırsa Response kısmını gönderir (publisher) veya alır (subscriber).
  • Master PID'ini tanımazsa sessiz kalır.

Sleep ve wake-up

Go-to-sleepMaster 0x00 PID ile 0x00 data gönderir. Tüm slave'ler uyku moduna girer.
Wake-up by masterMaster bus'a dominant pulse gönderir (≥250µs). Slave'ler uyanır.
Wake-up by slaveSlave de wake-up frame üretebilir. Master uyandıktan sonra normal schedule'a devam eder.

03 Frame yapısı

LIN frame'i beş alandan oluşur: Break, Sync, PID, Data ve Checksum. Master header'ı, slave response'u gönderir.

  ┌──────────┬──────┬──────┬──────────────────────┬──────────┐
  │  BREAK   │ SYNC │ PID  │       DATA (1-8B)     │CHECKSUM  │
  │ ≥13 bit  │ 0x55 │ 1B   │  max 8 byte           │  1B      │
  │ dominant │      │      │                        │          │
  └──────────┴──────┴──────┴──────────────────────┴──────────┘
  ←────── HEADER (master gönderir) ──────────────────────────►
                          ←──── RESPONSE (slave gönderir) ────►
    

Break field

Tüm slave'ler bir frame'in başladığını anlasın diye master minimum 13 bit dominant (0V) sinyal gönderir, ardından 1 bit recessive (delimiter). Bu uzun dominant puls, normal veri bitiyle karıştırılamaz.

Sync byte

Değeri her zaman 0x55 (01010101₂) — dönüşümlü 0 ve 1 biti sayesinde slave baud rate ölçüm yapabilir ve kendi dahili osilatörünü senkronize eder.

PID — Protected Identifier

1 byte: 6-bit frame ID + 2-bit parity. Frame ID 0-63 arasındadır. P0 ve P1 parity bitleri ID'den hesaplanır:

Python — PID hesabı
def calc_pid(frame_id: int) -> int:
    """6-bit frame ID'den PID (Protected ID) hesapla."""
    id_bits = frame_id & 0x3F
    p0 = ((id_bits >> 0) ^ (id_bits >> 1) ^ (id_bits >> 2) ^ (id_bits >> 4)) & 1
    p1 = ~((id_bits >> 1) ^ (id_bits >> 3) ^ (id_bits >> 4) ^ (id_bits >> 5)) & 1
    return id_bits | (p0 << 6) | (p1 << 7)

print(hex(calc_pid(0x01)))   # → 0x41
print(hex(calc_pid(0x10)))   # → 0x50

Checksum türleri

TürHesapKullanım
Classic checksumData byte'larının toplamının 1'in tümleyeniLIN 1.x slave'lerle uyumluluk
Enhanced checksumPID + Data byte'larının toplamının 1'in tümleyeniLIN 2.x standart (diagnostics hariç)
Python — checksum hesabı
def lin_checksum(data: bytes, pid: int = None) -> int:
    """Enhanced checksum: PID varsa dahil et, yoksa classic."""
    total = sum(data)
    if pid is not None:
        total += pid          # enhanced: PID de dahil
    # Carry propagation
    while total > 0xFF:
        total = (total & 0xFF) + (total >> 8)
    return (~total) & 0xFF   # 1'in tümleyeni

04 Schedule table

Master, hangi frame'i ne zaman göndereceğini schedule table ile belirler. Bu tablo LDF (LIN Description File) içinde tanımlanır.

Frame türleri

Unconditional frameHer schedule slot'unda koşulsuz gönderilir. En yaygın tür. Periyodik sensör verisi.
Event-triggered frameMaster header gönderir; veri değişen slave yanıt verir. Bant genişliği tasarrufu sağlar.
Sporadic frameMaster yalnızca belirli bir koşul sağlandığında (örn. kullanıcı tuşa bastı) gönderir.
Diagnostic framePID 0x3C (Master Request) ve 0x3D (Slave Response) — UDS tanılaması için.

Schedule table örneği

  Zaman (ms):  0   10   20   30   40   50   60
               |    |    |    |    |    |    |
  Slot 1:     [Koltuk konum → ID 0x01]
  Slot 2:          [Ayna durum → ID 0x02]
  Slot 3:               [Pencere → ID 0x03]
  Slot 4:                    [Klima → ID 0x04]
  Döngü tekrar →         [Koltuk konum → ID 0x01]
    

LDF (LIN Description File) yapısı

door_module.ldf
LIN_description_file;
LIN_protocol_version = "2.2";
LIN_language_version = "2.2";
LIN_speed = 9600 bps;

Nodes {
    Master: BCM, 5 ms, 0.1 ms;    /* BCM = Body Control Module */
    Slaves: SeatModule, MirrorModule, WindowModule;
}

Signals {
    SeatPos:       8, 0,   BCM,          SeatModule;
    MirrorAngleH:  8, 0,   BCM,          MirrorModule;
    WindowPos:     8, 255, WindowModule, BCM;
    WindowSwitch:  2, 0,   BCM,          WindowModule;
}

Frames {
    SeatControl:   0x01, BCM,          2 {SeatPos, 0;}
    MirrorControl: 0x02, BCM,          1 {MirrorAngleH, 0;}
    WindowStatus:  0x03, WindowModule, 2 {WindowPos, 0; WindowSwitch, 8;}
}

Schedule_tables {
    Normal_Schedule {
        SeatControl   delay 10 ms;
        MirrorControl delay 10 ms;
        WindowStatus  delay 10 ms;
    }
}

05 LIN ile Linux

Linux üzerinde LIN bus analizi için USB-LIN adaptörler ve PEAK PLIN-USB sürücüsü kullanılır.

USB-LIN adaptörler

PEAK PLIN-USBPEAK System, LIN 2.2 uyumlu, Linux sürücüsü mevcut, /dev/plin0 arayüzü.
Kvaser LIN adaptörKvaser, hem CAN hem LIN destekli, CANlib ile erişim.
Microchip LIN Bus AnalyzerAPGDT002, USB, LIN 1.x/2.x, Windows ağırlıklı ama libusb ile Linux'te çalışır.

PEAK PLIN-USB kurulumu

bash — PLIN-USB sürücü kurulumu
# PEAK Linux driver indir ve derle:
wget https://www.peak-system.com/fileadmin/media/linux/files/peak-linux-driver.tar.gz
tar xzf peak-linux-driver.tar.gz
cd peak-linux-driver-*
make
sudo make install

# PLIN-USB tak ve kontrol et:
dmesg | grep plin
# [12.345] plin: PLIN-USB attached to /dev/plin0

ls /dev/plin*
# /dev/plin0

PEAK PLinApi ile C örneği

C — PLIN-USB frame gönderme
#include <plin_api.h>
#include <stdio.h>

int main(void)
{
    TPLinHandle hClient = 0;
    TPLinHandle hHardware = 0;
    TPLinHandle hNet = 0;

    /* İstemci oluştur */
    LINRESULT res = LIN_RegisterClient("MyApp", 0, &hClient);
    if (res != TLIN_ERROR_OK) { printf("Client hatası\n"); return 1; }

    /* Donanımı bağla */
    res = LIN_ConnectClient(hClient, hHardware);

    /* Network oluştur: LIN 2.2, 9600 bps, master */
    res = LIN_CreateMasterNet(hClient, hHardware, 9600, TLIN_FRAMEVERSION_22, &hNet);

    /* Frame yapılandır: PID 0x01, 2 byte data */
    TLINFrameEntry frame = {0};
    frame.FrameId      = 0x01;
    frame.Direction    = TLIN_DIRECTION_PUBLISHER;
    frame.ChecksumType = TLIN_CHECKSUMTYPE_ENHANCED;
    frame.Length       = 2;
    LIN_SetFrameEntry(hNet, &frame);

    /* Veri yaz */
    BYTE data[2] = {0x50, 0x00};   /* koltuk pozisyon = 80, seçenek = 0 */
    LIN_UpdateByteArray(hNet, 0x01, 0, 2, data);

    printf("LIN frame gönderildi (PID 0x01)\n");

    LIN_RemoveNet(hNet);
    LIN_UnregisterClient(hClient);
    return 0;
}
NOT

SocketCAN LIN desteği deneysel ve sınırlıdır. Gerçek araç testleri için PEAK PLIN-USB veya Vector LINalyzer gibi adanmış donanım kullanmak çok daha güvenilirdir. Raspberry Pi + TJA1021 kombinasyonu prototip için uygundur.

06 Python ile LIN simülasyonu

Python ve pyserial ile LIN master simülasyonu: frame gönderme, slave yanıt bekleme, schedule table döngüsü.

Donanım bağlantısı

Raspberry Pi UART0 (ttyAMA0) → TJA1021 transceiver → LIN bus. TJA1021 TXD/RXD pinleri 3.3V uyumludur.

bash — pyserial kurulumu
pip install pyserial
Python — lin_master.py
import serial, time, struct

class LINMaster:
    def __init__(self, port: str, baudrate: int = 9600):
        self.ser = serial.Serial(port, baudrate, timeout=0.05)
        self.baudrate = baudrate

    def _calc_pid(self, frame_id: int) -> int:
        """6-bit ID → Protected Identifier."""
        i = frame_id & 0x3F
        p0 = ((i >> 0) ^ (i >> 1) ^ (i >> 2) ^ (i >> 4)) & 1
        p1 = ~((i >> 1) ^ (i >> 3) ^ (i >> 4) ^ (i >> 5)) & 1
        return i | (p0 << 6) | (p1 << 7)

    def _checksum(self, data: bytes, pid: int) -> int:
        """Enhanced checksum hesabı."""
        total = sum(data) + pid
        while total > 0xFF:
            total = (total & 0xFF) + (total >> 8)
        return (~total) & 0xFF

    def send_break(self):
        """LIN break field: geçici olarak daha düşük baud'da 0x00 gönder."""
        self.ser.baudrate = self.baudrate // 2
        self.ser.write(bytes([0x00]))   # 8 dominant + stop → 9 bit dominant
        self.ser.flush()
        self.ser.baudrate = self.baudrate

    def send_header(self, frame_id: int):
        """Break + Sync(0x55) + PID gönder."""
        self.send_break()
        pid = self._calc_pid(frame_id)
        self.ser.write(bytes([0x55, pid]))

    def send_frame(self, frame_id: int, data: bytes):
        """Tam LIN frame (header + data + checksum) gönder."""
        pid = self._calc_pid(frame_id)
        chk = self._checksum(data, pid)
        self.send_break()
        self.ser.write(bytes([0x55, pid]) + data + bytes([chk]))

    def request_response(self, frame_id: int, length: int) -> bytes:
        """Header gönder, slave response'u oku."""
        self.send_header(frame_id)
        resp = self.ser.read(length + 1)  # data + checksum
        if len(resp) == length + 1:
            return resp[:length]
        return b''

    def close(self):
        self.ser.close()

# Kullanım örneği:
master = LINMaster('/dev/ttyAMA0', baudrate=9600)

try:
    while True:
        # Slot 1: koltuk konum komutunu gönder (publisher)
        master.send_frame(0x01, bytes([0x50, 0x00]))
        time.sleep(0.01)

        # Slot 2: pencere durumunu oku (subscriber)
        data = master.request_response(0x03, 2)
        if data:
            window_pos = data[0]
            print(f'Pencere: {window_pos}% açık')
        time.sleep(0.01)

        # Slot 3: ayna açısı ayarla
        master.send_frame(0x02, bytes([0x7F]))   # merkez konum
        time.sleep(0.03)

except KeyboardInterrupt:
    master.close()

07 LIN diagnostics

ISO 17987-7 tanılaması — LIN Transport Layer (LIN-TP) üzerinden UDS servisleri ile node tanımlama, hata okuma ve yazılım güncelleme.

Diagnostic frame'leri

Master Request (MRF)PID = 0x3C (ID 60). 8 byte sabit uzunluk. Master → Slave tanılama isteği.
Slave Response (SRF)PID = 0x3D (ID 61). 8 byte sabit uzunluk. Slave → Master tanılama yanıtı.
LIN frame — Diagnostic Request yapısı
/* MRF (Master Request Frame): */
/* [NAD][PCI][SID][D1][D2][D3][D4][D5] */
/*  ↑    ↑    ↑              
/*  │    │    └─ Service ID (UDS: 0x22 = ReadDataByIdentifier)
/*  │    └────── Protocol Control Information
/*  └─────────── Node Address (1-127, 0x7F = broadcast)

UDS over LIN — ReadDataByIdentifier

Python — LIN-TP UDS okuma
def lin_read_data_by_id(master, nad: int, data_id: int) -> bytes:
    """
    UDS ReadDataByIdentifier (0x22) over LIN-TP.
    nad: node address (1-127)
    data_id: 2-byte identifier (örn. 0xF190 = VIN)
    """
    # MRF: NAD | 0x06 | 0x22 | ID_high | ID_low | 0xFF | 0xFF | 0xFF
    req = bytes([
        nad,          # NAD
        0x06,         # PCI: SF (Single Frame), uzunluk 6
        0x22,         # SID: ReadDataByIdentifier
        (data_id >> 8) & 0xFF,
        data_id & 0xFF,
        0xFF, 0xFF, 0xFF   # padding
    ])
    master.send_frame(0x3C, req)   # Master Request
    time.sleep(0.005)
    resp = master.request_response(0x3D, 8)   # Slave Response
    if resp and resp[2] == 0x62:   # pozitif yanıt
        return resp[5:]            # veri
    return b''

# Slave node'dan parça numarasını oku (DID 0xF187):
part_no = lin_read_data_by_id(master, nad=2, data_id=0xF187)
print('Parça No:', part_no.hex())

Slave node tanımlama

ServisSIDAçıklama
AssignNAD0xB0Slave'e NAD adresi ata
ReadProductId0xB2Supplier ID, Function ID, Variant okuma
ConditionalChangNAD0xB3Koşullu NAD değiştirme
DataDump0xB4Ham veri oku/yaz (üretici özel)

08 Pratik: Koltuk ve kapı modülü kontrolü

Raspberry Pi + TJA1021 transceiver ile LIN master — koltuk pozisyon kontrolü, pencere kaldırma/indirme ve LDF dosyası oluşturma.

Donanım bağlantısı

bağlantı şeması
Raspberry Pi 4              TJA1021             LIN Bus
─────────────               ──────────          ────────
GPIO14 (TXD) ──────────────► TXD (pin 1)
GPIO15 (RXD) ◄────────────── RXD (pin 4)
3.3V ─────────────────────── INH (pin 3)  ← enable
GND  ─────────────────────── GND (pin 2)
                              VBAT ─────────── 12V
                              LIN  ─────────── LIN bus (single wire)
                              GND  ─────────── chassis GND

4 slave LIN ağı kontrolü

Python — 4 slave tam ağ
import time
from lin_master import LINMaster   # önceki bölümdeki sınıf

master = LINMaster('/dev/ttyAMA0', baudrate=9600)

# Frame ID atamaları:
#   0x01 → Koltuk kontrolü (master → slave1)
#   0x02 → Ayna kontrolü  (master → slave2)
#   0x03 → Pencere durum  (slave3 → master)
#   0x04 → Klima durum    (slave4 → master)

seat_position = 0x50    # %80 ileri
mirror_h      = 0x7F    # merkez yatay

try:
    while True:
        # Slot 1: koltuk pozisyon komutu (10ms)
        master.send_frame(0x01, bytes([seat_position, 0x00]))
        time.sleep(0.010)

        # Slot 2: ayna açısı (10ms)
        master.send_frame(0x02, bytes([mirror_h]))
        time.sleep(0.010)

        # Slot 3: pencere durumu oku
        win_data = master.request_response(0x03, 2)
        if win_data:
            print(f'Pencere: sol={win_data[0]}% sağ={win_data[1]}%')
        time.sleep(0.010)

        # Slot 4: klima durumu oku
        clim_data = master.request_response(0x04, 3)
        if clim_data:
            temp = clim_data[0] - 40   # -40°C offset
            fan  = clim_data[1]        # fan hızı %
            print(f'Klima: {temp}°C  Fan: {fan}%')
        time.sleep(0.010)

except KeyboardInterrupt:
    master.close()

LDF dosyası oluşturma araçları

LINworksLIN Consortium resmi LDF düzenleyici, LIN 2.2A uyumlu, Windows.
Vector CANdb++ / LINdbProfesyonel otomotiv araç geliştirme paketi. LDF ve DBC dosyaları düzenleme.
LDF Explorer (açık kaynak)Python tabanlı LDF parser: pip install ldfparser
Python — ldfparser ile LDF okuma
import ldfparser

ldf = ldfparser.parse_ldf('door_module.ldf')

print('LIN hızı:', ldf.get_baudrate())
print('Slave'ler:', [s.name for s in ldf.get_slaves()])

for frame in ldf.get_frames():
    print(f'Frame {frame.frame_id:#04x}: {frame.name} ({frame.length}B)')

# Encoding ile sinyal decode:
seat_frame = ldf.get_frame('SeatControl')
raw_data = bytes([0x50, 0x00])
signals = seat_frame.parse_raw(raw_data)
print('Sinyal değerleri:', signals)