3.26. CAN busz a kódban

A machine.CAN egy hardveres CAN vezérlőt csomagol be. Hozd létre a busz azonosítójával és egy bitsebességgel:

from machine import CAN

can = CAN(1, 500_000)
can.set_filters(None)        # accept all incoming IDs

A bitsebességnek pontosan egyeznie kell a buszon lévő összes többi csomóponttal – a 125_000, 250_000, 500_000 és 1_000_000 a szokásos értékek. A set_filters() beállítja, hogy a vezérlő mely azonosítókat engedi át; a None azt jelenti, hogy mindent elfogad (hasznos egy kapcsolat felállításakor, kevésbé hasznos, ha a busz már forgalmas).

3.26.1. A vezérlő belsejében

Három hardverelem helyezkedik el a kamera Python kódja és a busz között – a TX postaládák, egy elfogadási szűrő és az RX FIFO. Mindegyik megfelel a CAN API alább használt egy-egy darabjának.

Block diagram of the CAN controller. On the TX side (top), can.send() drops frames into one of three TX mailboxes, and an arbitrate block picks which mailbox goes to the bus next. On the RX side (bottom), incoming frames pass through an acceptance filter; accepted frames land in the RX FIFO and can.recv() reads the FIFO from its oldest end.

A vezérlő TX postaládái, elfogadási szűrője és RX FIFO-ja a szoftver és a busz között.

  • TX postaládák. Hardveres rekeszek kis halmaza (jellemzően három), amelyek a kimenő képkockákat tartják, amelyeket a kamera a send() segítségével adott át, de amelyek még nem jutottak el a buszra. Amikor a busz tétlen, a vezérlő kiválasztja a legalacsonyabb számú azonosítóval (legmagasabb prioritással) rendelkező postaládát, és a nevében arbitrál a buszért. A kamera nem választja ki a postaládát; a vezérlő rendel hozzá egyet, és visszaadja annak indexét a send()-ből.

  • Elfogadási szűrő. Konfigurálható hardver, amely minden beérkező képkocka azonosítóját összehasonlítja egy mintalistával, és eldob mindent, ami nem illeszkedik. Az átjutó képkockák tovább haladnak az RX FIFO-ba; az elutasított képkockákat a vezérlő eldobja, anélkül hogy valaha is elérnék a Pythont. A set_filters() konfigurálja ezeket a mintákat.

  • RX FIFO. Egy first-in, first-out (először be, először ki) sor – az elsőként bekerült képkocka egyben az elsőként kikerülő is, mint egy sor a jegypénztárnál. A vezérlő a beérkező képkockákat a sor végéhez fűzi, ahogy megérkeznek, a recv() pedig az elejéről húzza le őket ugyanabban a sorrendben. A sor azért számít, mert a busz a háttérben kapja el a képkockákat, miközben a Python máshol van elfoglalva; a kamera ezután egyesével lecsapolja őket anélkül, hogy bármelyik elveszne, amíg a FIFO nem csordul túl.

3.26.2. Egy képkocka küldése

A send() adásra sorba állít egy képkockát:

can.send(0x123, b"\x01\x02\x03\x04")

Az első argumentum az azonosító (egy 11 bites egész szám szabványos képkocka esetén); a második a hasznos adat (0–8 bájt CAN Classic esetén). A hívás egy kis egész szám indexet ad vissza, amely azonosítja azt a hardveres postaládát, amelybe a képkocka került. A vezérlő arbitrál a buszon lévő bármely más adóval, és szükség szerint újraküld, a szoftver további segítsége nélkül.

Kiterjesztett (29 bites) azonosítók esetén OR-old a CAN.FLAG_EXT_ID jelzőt a harmadik argumentumba:

can.send(0x18FF1234, b"hello", CAN.FLAG_EXT_ID)

3.26.3. Képkockák fogadása

A vezérlő telepített szűrő nélkül indul, és minden beérkező képkockát eldob. Mielőtt a recv() bármit visszaadna, hívd meg egyszer a set_filters()-t – a legegyszerűbb forma a None, amely minden azonosítót elfogad:

can.set_filters(None)   # accept every frame

A recv() ezután a fogadási FIFO következő képkockáját adja vissza, vagy None-t, ha semmi sem vár:

msg = can.recv()
if msg is not None:
    can_id, data, flags, errs = msg
    print("got", hex(can_id), bytes(data))

A busz a háttérben tölti fel az RX FIFO-t, így a fő ciklus csak olyan gyorsan csapolja le, ahogy iterál. Amíg a FIFO mélyebb, mint a lecsapolások közötti leghosszabb szünet, egyetlen képkocka sem vész el.

3.26.4. Szűrők

Egy valódi busz általában tele van olyan képkockákkal, amelyekkel a kamera nem törődik. A hardveres szűrők lehetővé teszik, hogy a vezérlő eldobja a nem kívánt azonosítókat, mielőtt elérnék a FIFO-t. A set_filters() egy (id, mask, flags) tuple-ökből álló listát vár; egy képkocka átjut a szűrőn, ha az azonosítója a mask-kal maszkolva megegyezik a beállított id-vel:

# Accept only IDs 0x100 - 0x10F (mask off the bottom 4 bits)
can.set_filters(((0x100, 0x7F0, 0),))

# Accept IDs 0x300 and 0x700 exactly
can.set_filters(((0x300, 0x7FF, 0),
                 (0x700, 0x7FF, 0)))

A nem illeszkedő képkockákat a vezérlő eldobja, és soha nem jelennek meg a recv()-nél, ami pufferhelyet és CPU-időt is megtakarít.

3.26.5. Hibaállapotok és helyreállítás

Egy valódi CAN busz adási hibákat szed fel – földzárlatokat, hiányzó csomópontokat, biteket megrontó elektromos zajt. A vezérlő két számlálót tart, amelyek ezt a viselkedést követik: egy adáshiba-számlálót (Transmit Error Counter, TEC) és egy vételihiba-számlálót (Receive Error Counter, REC), mindkettő növekszik, amikor a vezérlő hibát észlel, és csökken egy sikeres átvitel után. A számlálók értékei a vezérlőt négy állapot egyikébe helyezik:

  • Error Active (a TEC és a REC is 96 alatt van). Normál működés. Amikor a csomópont busz-hibát észlel, egy dominant aktív hibakockát ad, ami minden más csomópontot arra kényszerít, hogy eldobja a folyamatban lévő képkockát, így a küldő újra próbálkozhat.

  • Error Warning (valamelyik számláló eléri a 96-ot). Még teljesen aktív a buszon – a figyelmeztető állapot egy szoftveres jelzés, hogy a hibák halmozódnak, nem pedig viselkedésbeli változás.

  • Error Passive (valamelyik számláló eléri a 128-at). A csomópont még a buszon van, de abbahagyja a dominant hibakockák küldését; a hibákat most passzív (recessive) hibakockákkal jelzi, így egy hibás csomópont nem tudja folyamatosan szétzilálni a buszt mindenki más számára.

  • Bus Off (a TEC eléri a 256-ot). A vezérlő úgy döntött, hogy ez a csomópont túl megbízhatatlan a részvételhez. Lecsatlakozik a buszról, abbahagyja az adást és a nyugtázást, és addig kívül marad, amíg a szoftver kifejezetten újra nem indítja.

Az első három átmenet teljesen automatikus – ahogy a számlálók a sikeres képkockák után csökkennek, a vezérlő beavatkozás nélkül halad vissza az Error Active felé.

A Bus Off az egyetlen állapot, amely szoftveres beavatkozást igényel. A restart() visszaállítja a vezérlőt, és visszaadja Error Active állapotba. Tipikus minta, hogy a fő ciklusból ellenőrzöd az állapotot, és egy rövid késleltetés után újraindítod, hogy a busznak legyen ideje lecsillapodni:

import time
from machine import CAN

can = CAN(1, 500_000)
can.set_filters(None)

while True:
    if can.state() == CAN.STATE_BUS_OFF:
        time.sleep_ms(100)
        can.restart()
    # ... rest of the loop

Az aktuális számlálóértékek diagnosztikai célból a get_counters()-ből érhetők el – egy egyébként csendes buszon egyenletesen kúszó TEC általában a kábelezésre, a lezárásra vagy egy rosszul beállított bitsebességre utal.