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ı.
| Versiyon | Yıl | Önemli değişiklikler |
|---|---|---|
| LIN 1.0 | 1999 | İlk yayın, temel frame yapısı |
| LIN 1.3 | 2002 | Checksum iyileştirmesi |
| LIN 2.0 | 2003 | Enhanced checksum, sleep/wake-up, diagnostics |
| LIN 2.2A | 2010 | Mevcut en yaygın versiyon, tam özellik seti |
| ISO 17987 | 2016 | LIN 2.2A'nın ISO standardı olarak yayımlanması |
Kullanım alanları
LIN vs CAN karşılaştırması
| Özellik | LIN | CAN |
|---|---|---|
| Tel sayısı | 1 (+ GND + 12V) | 2 (diferansiyel) |
| Max hız | 20 kbps | 1 Mbps (CAN FD 8 Mbps) |
| Mimari | Master/Slave | Multi-master |
| Max node | 16 (1 master + 15 slave) | ~110 |
| Transceiver maliyeti | ~$0.30 | ~$1.00 |
| Senkronizasyon | Otomatik (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
| Durum | Voltaj | Açıklama |
|---|---|---|
| Recessive (1) | VBattery − 1V diod ≈ 11V | Bus pull-up ile belirlenir |
| Dominant (0) | < 0.4 × VBattery ≈ 4.8V | Node aktif çekim ile sürülen durum |
| Uyku (sleep) | VBattery | Tam recessive, master veya slave wake-up bekler |
Hız seçenekleri
| Hız | Kullanım |
|---|---|
| 2400 bps | Düşük hızlı koltuk/ayna kontrolü |
| 4800 bps | Standart konfor elektroniği |
| 9600 bps | Genel araç içi uygulama |
| 19200 bps | Hızlı sensör okuma |
| 20000 bps | LIN maksimum hızı |
LIN transceiver çipleri
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
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:
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ür | Hesap | Kullanım |
|---|---|---|
| Classic checksum | Data byte'larının toplamının 1'in tümleyeni | LIN 1.x slave'lerle uyumluluk |
| Enhanced checksum | PID + Data byte'larının toplamının 1'in tümleyeni | LIN 2.x standart (diagnostics hariç) |
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
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ı
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-USB 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
#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;
}
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.
pip install pyserial
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
/* 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
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
| Servis | SID | Açıklama |
|---|---|---|
| AssignNAD | 0xB0 | Slave'e NAD adresi ata |
| ReadProductId | 0xB2 | Supplier ID, Function ID, Variant okuma |
| ConditionalChangNAD | 0xB3 | Koşullu NAD değiştirme |
| DataDump | 0xB4 | Ham 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ı
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ü
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ı
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)