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.
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 asend()-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.