3.26. Sběrnice CAN v kódu

machine.CAN obaluje hardwarový řadič CAN. Uveďte jej do provozu pomocí id sběrnice a bitové rychlosti:

from machine import CAN

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

Bitová rychlost se musí přesně shodovat s každým dalším uzlem na sběrnici – 125_000, 250_000, 500_000 a 1_000_000 jsou běžné hodnoty. set_filters() konfiguruje, která ID řadič propustí; None znamená přijímat vše (užitečné při uvádění spoje do provozu, méně užitečné, jakmile je sběrnice vytížená).

3.26.1. Uvnitř řadiče

Mezi kódem kamery v Pythonu a sběrnicí se nacházejí tři hardwarové prvky – schránky TX (TX mailboxes), akceptační filtr a RX FIFO. Každý z nich odpovídá části API CAN použitého níže.

Blokové schéma řadiče CAN. Na straně TX (nahoře) can.send() vkládá rámce do jedné ze tří schránek TX (TX mailboxes) a arbitrážní blok vybírá, která schránka půjde na sběrnici jako další. Na straně RX (dole) příchozí rámce procházejí akceptačním filtrem; přijaté rámce přistanou v RX FIFO a can.recv() čte FIFO od jejího nejstaršího konce.

Schránky TX, akceptační filtr a RX FIFO řadiče mezi softwarem a sběrnicí.

  • Schránky TX (TX mailboxes). Malá sada hardwarových slotů (typicky tří), které drží odchozí rámce předané kamerou přes send(), ale které ještě nedorazily na sběrnici. Když je sběrnice volná, řadič vybere schránku s ID s nejnižším číslem (nejvyšší prioritou) a provede za ni arbitráž o sběrnici. Kamera schránku nevybírá; řadič ji přidělí a vrátí její index z send().

  • Akceptační filtr. Konfigurovatelný hardware, který porovnává ID každého příchozího rámce se seznamem vzorů a zahodí vše, co se neshoduje. Rámce, které projdou, pokračují do RX FIFO; zamítnuté rámce řadič zahodí, aniž by se kdy dostaly do Pythonu. Tyto vzory konfiguruje set_filters().

  • RX FIFO. Fronta typu first-in, first-out – první rámec dovnitř je také prvním rámcem ven, jako řada u pokladny. Řadič připojuje přijaté rámce na konec fronty, jak přicházejí, a recv() je odebírá zepředu ve stejném pořadí. Fronta je důležitá proto, že sběrnice zachytává rámce na pozadí, zatímco je Python zaneprázdněn jinde; kamera je poté vyčerpává jeden po druhém bez ztráty kteréhokoliv z nich, dokud FIFO nepřeteče.

3.26.2. Odeslání rámce

send() zařadí rámec do fronty k přenosu:

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

Prvním argumentem je ID (11bitové celé číslo u standardního rámce); druhým je užitečná zátěž (0 až 8 bajtů u CAN Classic). Volání vrací malý celočíselný index identifikující hardwarovou schránku, do které rámec putoval. Řadič provede arbitráž s libovolnými dalšími vysílači na sběrnici a podle potřeby rámec znovu vyšle bez další pomoci ze softwaru.

U rozšířených (29bitových) ID přidejte pomocí OR příznak CAN.FLAG_EXT_ID do třetího argumentu:

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

3.26.3. Příjem rámců

Řadič se spouští bez nainstalovaného filtru a zahazuje každý příchozí rámec. Než recv() cokoliv vrátí, zavolejte jednou set_filters() – nejjednodušší podobou je None, které přijímá každé ID:

can.set_filters(None)   # accept every frame

recv() poté vrátí další rámec v přijímací FIFO, nebo None, pokud nic nečeká:

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

Sběrnice plní RX FIFO na pozadí, takže hlavní smyčka ji jen vyčerpává tak rychle, jak iteruje. Dokud je FIFO hlubší než nejdelší mezera mezi vyčerpáními, neztrácejí se žádné rámce.

3.26.4. Filtry

Skutečná sběrnice je obvykle vytížená rámci, o které se kamera nezajímá. Hardwarové filtry umožňují řadiči zahodit nežádoucí ID dříve, než dorazí do FIFO. set_filters() přijímá seznam n-tic (id, mask, flags); rámec projde filtrem, pokud jeho ID, maskované pomocí mask, odpovídá nakonfigurovanému id:

# 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)))

Neodpovídající rámce řadič zahodí a nikdy se neobjeví v recv(), což šetří jak místo v bufferu, tak čas CPU.

3.26.5. Chybové stavy a zotavení

Skutečná sběrnice CAN zachytává chyby přenosu – zkraty k zemi, chybějící uzly, elektrické rušení poškozující bity. Řadič udržuje dva čítače, které toto chování sledují: čítač chyb vysílání (Transmit Error Counter, TEC) a čítač chyb příjmu (Receive Error Counter, REC), každý se inkrementuje, když řadič detekuje chybu, a dekrementuje po úspěšném přenosu. Hodnoty čítačů uvádějí řadič do jednoho ze čtyř stavů:

  • Error Active (TEC i REC pod 96). Normální provoz. Když uzel detekuje chybu na sběrnici, vyšle dominantní aktivní chybový rámec, který přinutí každý další uzel zahodit právě probíhající rámec, aby odesílatel mohl pokus zopakovat.

  • Error Warning (kterýkoliv čítač dosáhne 96). Stále plně aktivní na sběrnici – výstražný stav je softwarový signál, že se chyby hromadí, nikoliv změna chování.

  • Error Passive (kterýkoliv čítač dosáhne 128). Uzel je stále na sběrnici, ale přestává posílat dominantní chybové rámce; chyby se nyní signalizují pasivními (recesivními) chybovými rámci, aby vadný uzel nemohl neustále narušovat sběrnici všem ostatním.

  • Bus Off (TEC dosáhne 256). Řadič rozhodl, že tento uzel je příliš nespolehlivý na to, aby se účastnil. Odpojí se od sběrnice, přestane vysílat a potvrzovat a zůstane mimo, dokud jej software výslovně nerestartuje.

První tři přechody jsou zcela automatické – jak se čítače po úspěšných rámcích dekrementují, řadič se sám bez zásahu posouvá zpět směrem k Error Active.

Bus Off je jediný stav, který vyžaduje zásah softwaru. restart() resetuje řadič a vrátí jej do Error Active. Typickým vzorem je kontrolovat stav z hlavní smyčky a restartovat po krátké prodlevě, aby měla sběrnice čas se ustálit:

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

Aktuální hodnoty čítačů jsou pro diagnostiku dostupné z get_counters() – TEC, který na jinak klidné sběrnici neustále stoupá, obvykle ukazuje na kabeláž, zakončení nebo špatně nakonfigurovanou bitovou rychlost.