00 CAN FD nedir: Classical CAN sınırları ve FD farkları
Classical CAN, 1986'dan bu yana otomotiv ve endüstride güvenilir biçimde kullanılmaktadır. Ancak modern araçlarda ECU sayısı artıkça 1 Mbit/s hız ve 8 byte payload yetersiz kalmaktadır.
Classical CAN'ın iki temel kısıtı
CAN FD (ISO 11898-7) farkları
CAN FD (Flexible Data Rate), Bosch tarafından 2012'de tanıtıldı ve ISO 11898-7 olarak standardize edildi. İki temel yenilik sunar:
Classical CAN frame:
[SOF][ID 11-bit][RTR][IDE][DLC 4-bit][DATA 0-8 byte][CRC][ACK][EOF]
CAN FD frame:
[SOF][ID 11/29-bit][RRS][IDE][EDL=1][BRS][ESI][DLC 4-bit]
↓ BRS bit → Bitrate Switch
[DATA 0-64 byte (nominal bitrate → data bitrate)][CRC 17/21-bit][ACK][EOF]
ISO vs Non-ISO CAN FD
2012'de Bosch'un yayımladığı orijinal spec (non-ISO CAN FD) ile ISO 11898-7 arasında ince farklılıklar vardır:
| Özellik | Non-ISO CAN FD (Bosch) | ISO CAN FD (11898-7) |
|---|---|---|
| CRC hesaplama | CRC stuff count içermez | CRC'ye stuff bit sayacı eklendi |
| Donanım uyumu | Eski FD controller'lar | 2015+ üretim ECU'lar, MCP2517FD |
| SocketCAN seçeneği | fd-non-iso on | Varsayılan (fd on) |
| Kullanım | Bazı eski geliştirme araçları | Tüm yeni projeler için önerilen |
ISO ve non-ISO CAN FD node'ları aynı bus'ta birlikte çalışamaz. Donanım satın alırken ISO 11898-7 uyumlu (ISO CAN FD) olduğunu doğrulayın. MCP2517FD ve MCP2518FD ISO CAN FD destekler.
01 Elektriksel katman ve transceiver
CAN FD elektriksel katmanı Classical CAN ile aynı diferansiyel sinyal yapısını kullanır; ancak yüksek data bitrate için düşük gecikmeli transceiver seçimi kritik önem taşır.
Diferansiyel sinyal: CAN_H ve CAN_L
CAN bus bükümlü çift tel üzerinden diferansiyel sinyal iletir. Bu yaklaşım ortak mod gürültüsünü büyük ölçüde baskılar.
Dominant (logic 0): CAN_H ≈ 3.5V, CAN_L ≈ 1.5V, Vdiff ≈ +2.0V
Recessive (logic 1): CAN_H ≈ 2.5V, CAN_L ≈ 2.5V, Vdiff ≈ 0.0V
┌──────────────────────────────┐
│ CAN Controller │
│ TX ──→ Transceiver ──→ CAN_H│──┐
│ RX ←── Transceiver ←── CAN_L│──┼── 120Ω terminatör
└──────────────────────────────┘ │ (her iki ucunda)
Terminasyon
Bus'un her iki ucuna 120Ω terminatör takılmalıdır. Bu değer kablonun karakteristik empedansıyla eşleşir ve yansımayı önler. CAN FD'de yüksek data bitrate, terminasyon kalitesine daha duyarlıdır.
| Terminasyon durumu | Paralel direnç | Etkisi |
|---|---|---|
| İki uçta 120Ω + 120Ω | 60Ω | Doğru — yansıma yok |
| Tek ucta 120Ω | 120Ω | Hatalı — yüksek bitrate'de frame error |
| Terminatör yok | ∞ | Hatalı — tam yansıma, bus çalışmaz |
| Split terminasyon (60Ω+60Ω + 4.7nF) | 60Ω + filtre | Yüksek EMI ortamları için tercih edilir |
CAN FD transceiver seçimi
Classical CAN transceiver'lar (TJA1050, SN65HVD230) teorik olarak CAN FD frame'lerini iletebilir; ancak propagation delay limitleri nedeniyle yüksek data bitrate'de (>2 Mbit/s) sorunlara yol açar.
Transceiver datasheet'inde propagation delay değerini kontrol edin (TX→RX loop delay). 2 Mbit/s data bitrate için bit süresi 500 ns'dir; loop delay bu değerin %30'unu aşmamalıdır. Yüksek bitrate'de kısa kablo kullanın.
02 Bit timing detayı
CAN FD'de iki ayrı bitrate tanımlanır: arbitration fazı için nominal bitrate, veri fazı için data bitrate. Her ikisi de ayrı timing parametreleri gerektirir.
Bit timing temelleri
Her CAN bit dört bölüme ayrılır: Sync Seg, Prop Seg, Phase Seg 1 ve Phase Seg 2. Birim, Time Quantum (TQ) cinsinden ölçülür.
┌─────────┬──────────┬──────────┬──────────┐
│Sync Seg │ Prop Seg │Phase Seg1│Phase Seg2│
│ 1 TQ │ 1-8 TQ │ 1-8 TQ │ 1-8 TQ │
└─────────┴──────────┴──────────┴──────────┘
↑ Sample Point
TQ = 1 / (f_clk / BRP)
CAN FD'de dual bitrate konfigürasyonu
# Nominal: 500 kbit/s, Data: 2 Mbit/s
sudo ip link set can0 type can \
bitrate 500000 \
sample-point 0.80 \
dbitrate 2000000 \
dsample-point 0.75 \
fd on
# Nominal: 500 kbit/s, Data: 4 Mbit/s (kısa kablo)
sudo ip link set can0 type can \
bitrate 500000 \
dbitrate 4000000 \
fd on
# Mevcut timing değerlerini göster
ip -details link show can0
SP tutarsızlığı sorunu (SP mismatch)
CAN FD'de farklı node'ların data fazı sample point'leri birbirinden farklıysa iletişim hataları oluşur. Bu, klasik CAN'da göz yumulan ama FD'de kritik hale gelen bir sorundur.
| Bitrate | Önerilen nominal SP | Önerilen data SP | Kablo limiti |
|---|---|---|---|
| 500k / 2M | %80 | %75 | <30 m |
| 500k / 4M | %80 | %75 | <10 m |
| 500k / 8M | %80 | %70 | <3 m |
| 1M / 8M | %75 | %70 | <1 m |
CAN FD analyzer araçları (PEAK PCAN-View, Vector CANalyzer) bit timing hesaplayıcısı içerir. bit-timing-calculator adlı açık kaynak araç (can-utils deposunda) SocketCAN için doğrudan kullanılabilir değerler üretir.
03 Linux SocketCAN ile CAN FD yapılandırması
Linux 3.6 ile birlikte SocketCAN, CAN FD desteği kazandı. Kernel tarafında canfd_frame struct ve CAN_RAW_FD_FRAMES socket seçeneği bu desteği kullanıcı alanına sunar.
CAN FD arayüzü açma
# Kernel modüllerini yükle
sudo modprobe can
sudo modprobe can_raw
sudo modprobe can_dev
# MCP2517FD için SPI-CAN modülü (Raspberry Pi örneği)
sudo modprobe mcp251xfd
# CAN FD arayüzünü yapılandır ve aç
sudo ip link set can0 type can \
bitrate 500000 \
dbitrate 2000000 \
fd on
sudo ip link set can0 up
# Durumu doğrula — "FD ON" görünmeli
ip -details link show can0
# ...
# can <FD> state ERROR-ACTIVE restart-ms 0
# bitrate 500000 sample-point 0.800
# tq 25 prop-seg 14 phase-seg1 15 phase-seg2 8 sjw 1
# mcp251xfd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
# dbitrate 2000000 dsample-point 0.750
# dtq 25 dprop-seg 4 dphase-seg1 4 dphase-seg2 3 dsjw 1
# ISO olmayan (non-ISO) CAN FD için:
sudo ip link set can0 type can \
bitrate 500000 dbitrate 2000000 \
fd on fd-non-iso on
canfd_frame struct
/* CAN FD frame yapısı — Classical can_frame ile karşılaştırma */
struct can_frame { /* Classical CAN: 16 byte toplam */
canid_t can_id; /* 4 byte: ID + flag bitleri */
__u8 can_dlc; /* 0-8 */
__u8 __pad[3];
__u8 data[8];
}; /* sizeof = 16, CAN_MTU = 16 */
struct canfd_frame { /* CAN FD frame: 72 byte toplam */
canid_t can_id; /* 4 byte: ID + flag bitleri */
__u8 len; /* gerçek payload boyutu: 0-64 */
__u8 flags; /* CANFD_BRS | CANFD_ESI */
__u8 __res0;
__u8 __res1;
__u8 data[64];
}; /* sizeof = 72, CANFD_MTU = 72 */
/* flags bitleri */
#define CANFD_BRS 0x01 /* Bit Rate Switch etkin */
#define CANFD_ESI 0x02 /* Error State Indicator */
#define CANFD_FDF 0x04 /* FD Frame (bazı kernel sürümleri) */
CAN_RAW_FD_FRAMES socket seçeneği
#include <linux/can.h>
#include <linux/can/raw.h>
int fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
/* CAN FD frame desteğini etkinleştir */
int enable_fd = 1;
setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
&enable_fd, sizeof(enable_fd));
/* Bu noktadan itibaren:
- write() ile sizeof(struct canfd_frame) = CANFD_MTU(72) yazılırsa CAN FD
- write() ile sizeof(struct can_frame) = CAN_MTU(16) yazılırsa Classical CAN
Tek socket her iki türü de gönderebilir. */
04 Gönderme ve alma — C ve Python
CAN FD frame göndermek için canfd_frame struct doldurulur ve CANFD_MTU boyutunda write() çağrısı yapılır. MTU kontrolü, yanlışlıkla classical frame göndermeyi engeller.
C ile CAN FD gönderme
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(void) {
int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
/* Arayüzü bağla */
struct ifreq ifr;
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr = {
.can_family = AF_CAN,
.can_ifindex = ifr.ifr_ifindex,
};
bind(s, (struct sockaddr *)&addr, sizeof(addr));
/* CAN FD frame desteğini etkinleştir */
int enable_fd = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
&enable_fd, sizeof(enable_fd));
/* CAN FD frame hazırla: ID=0x123, 32 byte payload */
struct canfd_frame frame;
memset(&frame, 0, sizeof(frame));
frame.can_id = 0x123;
frame.len = 32;
frame.flags = CANFD_BRS; /* data fazında hız geçişi */
for (int i = 0; i < 32; i++)
frame.data[i] = (uint8_t)i;
/* MTU kontrolü — her zaman CANFD_MTU(72) yazılmalı */
ssize_t nbytes = write(s, &frame, CANFD_MTU);
if (nbytes != CANFD_MTU) {
perror("write");
return 1;
}
printf("CAN FD frame gönderildi: ID=0x%03X len=%d\n",
frame.can_id, frame.len);
close(s);
return 0;
}
C ile CAN FD alma ve MTU kontrolü
/* Hem Classical hem CAN FD frame alabilen alıcı */
int enable_fd = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
&enable_fd, sizeof(enable_fd));
union {
struct can_frame classic;
struct canfd_frame fd;
} buf;
while (1) {
ssize_t nbytes = read(s, &buf, sizeof(buf));
if (nbytes == CAN_MTU) { /* 16 byte: Classical */
printf("Classic: ID=0x%03X dlc=%d\n",
buf.classic.can_id & CAN_EFF_MASK,
buf.classic.can_dlc);
} else if (nbytes == CANFD_MTU) { /* 72 byte: CAN FD */
printf("FD: ID=0x%03X len=%d flags=0x%02X\n",
buf.fd.can_id & CAN_EFF_MASK,
buf.fd.len,
buf.fd.flags);
}
}
Python ile CAN FD frame
pip install python-can
import can
# CAN FD etkin arayüze bağlan
bus = can.Bus(channel='can0', interface='socketcan',
fd=True,
bitrate=500_000,
data_bitrate=2_000_000)
# CAN FD Message gönder (is_fd=True)
msg = can.Message(
arbitration_id=0x123,
data=bytes(range(32)),
is_fd=True,
bitrate_switch=True, # BRS bit
is_extended_id=False
)
bus.send(msg)
print(f"Gönderildi: {msg}")
# Alma (1 saniyelik timeout)
received = bus.recv(timeout=1.0)
if received:
print(f"FD={received.is_fd} BRS={received.bitrate_switch}")
print(f"ID=0x{received.arbitration_id:03X} len={len(received.data)}")
print(f"Data: {received.data.hex()}")
bus.shutdown()
05 Error frame ve bus-off recovery
CAN FD, Classical CAN hata mekanizmalarını miras alır. TEC ve REC sayaçları aracılığıyla node'lar Error Active → Error Passive → Bus-Off geçişi yapar.
TEC / REC sayaçları ve durum geçişleri
TEC/REC < 128 : Error Active (normal çalışma, aktif error frame gönderir)
TEC veya REC ≥ 128: Error Passive (passive error flag gönderir)
TEC ≥ 256 : Bus-Off (bus'tan kopar, sessizlik)
Bus-Off çıkışı: 128 × 11 recessive bit gözlemlenince otomatik (veya manual restart)
SocketCAN ile hata frame okuma
#include <linux/can/error.h>
/* Error frame maskesini etkinleştir */
can_err_mask_t err_mask = CAN_ERR_MASK; /* tüm hataları al */
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&err_mask, sizeof(err_mask));
/* Önemli error class bitleri (can_id içinde): */
// CAN_ERR_TX_TIMEOUT 0x00000001U TX zaman aşımı
// CAN_ERR_LOSTARB 0x00000002U arbitrasyon kaybı
// CAN_ERR_CRTL 0x00000004U controller hatası
// CAN_ERR_PROT 0x00000008U protokol ihlali
// CAN_ERR_TRX 0x00000010U transceiver hatası
// CAN_ERR_BUSOFF 0x00000040U bus-off durumu
// CAN_ERR_BUSERROR 0x00000080U bus hatası
// CAN_ERR_RESTARTED 0x00000100U arayüz yeniden başladı
struct can_frame err;
read(s, &err, CAN_MTU);
if (err.can_id & CAN_ERR_FLAG) {
if (err.can_id & CAN_ERR_BUSOFF)
printf("Bus-Off durumu tespit edildi!\n");
if (err.can_id & CAN_ERR_CRTL)
printf("Controller: TEC/REC eşiği aşıldı\n");
}
Otomatik bus-off restart
# 100 ms sonra otomatik restart (bus-off recovery)
sudo ip link set can0 type can \
bitrate 500000 dbitrate 2000000 fd on \
restart-ms 100
# Manuel restart
sudo ip link set can0 type can restart
# Hata istatistiklerini izle
ip -s link show can0
# RX: errors, dropped değerlerini takip et
06 DLC genişlemesi ve payload padding
CAN FD, 0–8 arası DLC değerlerini Classical CAN ile aynı anlamda kullanır; 9–15 arası değerler ise genişletilmiş payload boyutlarına karşılık gelir.
CAN FD DLC tablosu
| DLC (4-bit) | Payload (byte) | Not |
|---|---|---|
| 0 | 0 | Boş frame |
| 1–8 | 1–8 | Classical CAN ile aynı |
| 9 | 12 | CAN FD genişleme başlangıcı |
| 10 | 16 | |
| 11 | 20 | |
| 12 | 24 | |
| 13 | 32 | |
| 14 | 48 | |
| 15 | 64 | Maksimum payload |
Payload padding zorunluluğu
DLC 9–15 değerleri için gönderilecek veri boyutu frame'in DLC'sine karşılık gelen boyutu dolduramıyorsa padding yapılması gerekir. Örneğin 10 byte veri göndermek için DLC=10 (16 byte) seçilir ve fazladan 6 byte 0x00 ile doldurulur.
#include <linux/can.h>
/* DLC → gerçek byte boyutu dönüşümü */
static const uint8_t dlc_to_len[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8,
12, 16, 20, 24, 32, 48, 64
};
/* Veri uzunluğundan uygun DLC bul (yukarı yuvarla) */
uint8_t len_to_dlc(uint8_t len) {
if (len <= 8) return len;
if (len <= 12) return 9;
if (len <= 16) return 10;
if (len <= 20) return 11;
if (len <= 24) return 12;
if (len <= 32) return 13;
if (len <= 48) return 14;
return 15; /* 64 */
}
/* Kullanım: 10 byte veri göndermek */
struct canfd_frame frame;
memset(&frame, 0, sizeof(frame));
frame.can_id = 0x200;
frame.len = 16; /* DLC=10 → 16 byte; 6 byte padding 0x00 */
frame.flags = CANFD_BRS;
memcpy(frame.data, my_10_byte_data, 10);
/* frame.data[10..15] zaten 0x00 (memset ile temizlendi) */
write(s, &frame, CANFD_MTU);
SocketCAN'da canfd_frame.len alanı gerçek payload boyutunu tutar (DLC değil). Örneğin 10 byte veri için len = 16 ayarlanır (DLC=10'a karşılık gelen boyut). Linux kernel, frame'i bus'a yazarken doğru DLC değerini hesaplar.
07 CAN FD test araçları
can-utils paketi CAN FD için genişletilmiş destek sunar. candump ve cansend komutları FD frame'leri görüntüleyebilir ve gönderebilir.
candump ile CAN FD izleme
# Tüm frame'leri göster (CAN FD dahil)
candump can0
# CAN FD çıktı örneği (FD frame, BRS etkin):
# (1716890123.456789) can0 123 [32] 00 01 02 03 04 05 06 07 08 09 0A 0B
# 0C 0D 0E 0F 10 11 12 13 14 15 16 17
# 18 19 1A 1B 1C 1D 1E 1F
# Timestamp + arayüz adıyla kaydet
candump -l can0
# Dosya: candump-2026-04-12_120000.log
# Sadece belirli ID'yi izle
candump can0 123:7FF
cansend ile CAN FD gönderme
# Classical CAN frame (8 byte)
cansend can0 123#DEADBEEF01020304
# CAN FD frame — ## ile başlar, ardından flags byte
# ##1 → BRS etkin (flags=0x01)
cansend can0 123##1DEADBEEF01020304050607080910111213141516171819
# ##0 → BRS kapalı
cansend can0 456##0AABBCCDD
canplayer ve canlogserver
# Kaydedilmiş log dosyasını oynat
canplayer -I candump-2026-04-12_120000.log
# Belirli bir arayüze yönlendir
canplayer -I capture.log -l can0=vcan0
# canlogserver: TCP üzerinden uzaktan loglama
canlogserver -p 28700 can0
# İstemci tarafında:
candump -a remote_host:28700
socketcand: CAN over TCP/IP
# Kurulum
sudo apt install socketcand
# can0 arayüzünü TCP 29536 üzerinden sun
socketcand -i can0 -p 29536
# Python ile uzaktan bağlantı
# bus = can.Bus('can0', interface='socketcand',
# host='192.168.1.100', port=29536)
PEAK PCAN-USB FD
PCAN-USB FD, PEAK System'ın USB bağlantılı CAN FD adaptörüdür. Linux'ta peak_usb kernel modülü ile çalışır ve can0 arayüzü olarak görünür.
# Modül otomatik yüklenir; kontrol et
lsmod | grep peak_usb
dmesg | grep -i pcan
# Standart ip link komutu ile yapılandır
sudo ip link set can0 type can \
bitrate 500000 dbitrate 2000000 fd on
sudo ip link set can0 up
08 Pratik: Raspberry Pi + MCP2517FD ile CAN FD
MCP2517FD, SPI arayüzü üzerinden bağlanan ISO CAN FD controller'dır. Raspberry Pi ile birlikte düşük maliyetli CAN FD geliştirme platformu oluşturur.
Donanım bağlantısı
Raspberry Pi 4 MCP2517FD MCP2562FD
───────────── ───────── ─────────
GPIO 8 (CE0) ────────→ nCS
GPIO 11 (SCLK)────────→ SCK
GPIO 10 (MOSI)────────→ SDI
GPIO 9 (MISO)────────→ SDO
GPIO 25 ────────→ nINT
TX ──────────────→ TXD
RX ←────────────── RXD
CAN_H ──→ bus
CAN_L ──→ bus
Device Tree overlay ve kernel yapılandırması
# MCP2517FD overlay — 40 MHz SPI clock, INT pin GPIO25
dtoverlay=mcp251xfd,spi0-0,oscillator=40000000,interrupt=25
# SPI etkinleştir
dtparam=spi=on
# Boot sonrası modülü kontrol et
dmesg | grep mcp251
# mcp251xfd spi0.0 can0: MCP2517FD rev0.0 (+FIFO +CRC) at 40.000 MHz
# CAN FD arayüzünü yapılandır
sudo ip link set can0 type can \
bitrate 500000 dbitrate 2000000 fd on restart-ms 100
sudo ip link set can0 up
# Loopback testi (tek kart, kablo yok)
sudo ip link set can0 type can \
bitrate 500000 dbitrate 2000000 fd on loopback on
sudo ip link set can0 up
candump can0 &
cansend can0 123##1DEADBEEF0102030405060708
Üretim senaryosu: Firmware OTA over CAN FD
CAN FD'nin 64 byte payload kapasitesi, Classical CAN'a göre OTA güncelleme sürelerini önemli ölçüde kısaltır. Örnek hesaplama:
| Parametre | Classical CAN | CAN FD (500k/2M) |
|---|---|---|
| Payload / frame | 8 byte | 64 byte |
| 256 KB firmware süresi | ~52 saniye | ~6.5 saniye |
| Protokol overhead | Yüksek (ISO-TP katmanı) | Düşük (doğrudan FD frame) |
| CRC güvenilirliği | 15-bit CRC | 17/21-bit CRC |
import can, time, struct
BLOCK_SIZE = 64 # CAN FD max payload
OTA_CMD_ID = 0x700 # OTA komut frame ID
OTA_DATA_ID = 0x701 # OTA veri frame ID
bus = can.Bus('can0', interface='socketcan', fd=True,
bitrate=500_000, data_bitrate=2_000_000)
def send_firmware(firmware_path):
with open(firmware_path, 'rb') as f:
data = f.read()
total = len(data)
n_blocks = (total + BLOCK_SIZE - 1) // BLOCK_SIZE
# Başlangıç komutu gönder (toplam boyut + blok sayısı)
cmd = struct.pack('>HH', n_blocks, total)
bus.send(can.Message(arbitration_id=OTA_CMD_ID,
data=cmd, is_fd=True,
bitrate_switch=True))
time.sleep(0.01)
# Blokları gönder
for i in range(n_blocks):
block = data[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE]
payload = struct.pack('>H', i) + block # 2 byte seq + data
bus.send(can.Message(arbitration_id=OTA_DATA_ID,
data=payload, is_fd=True,
bitrate_switch=True))
print(ff"OTA tamamlandı: {total} byte, {n_blocks} blok")
send_firmware('/tmp/firmware.bin')
bus.shutdown()
MCP2517FD için doğru TQ değerlerini hesaplamak amacıyla Microchip'in CAN FD Bit Timing Calculator aracını kullanın. 40 MHz oscillator, 500k/2M bitrate için önerilen değer: BRP=1, TSEG1=62, TSEG2=15, SJW=15 (nominal); DBRP=1, DTSEG1=14, DTSEG2=5, DSJW=4 (data).