3.26. CAN sabirnica u kodu

machine.CAN omata hardverski CAN upravljač. Pokrenite ga s ID-jem sabirnice i brzinom prijenosa bitova:

from machine import CAN

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

Brzina prijenosa bitova mora se točno podudarati sa svakim drugim čvorom na sabirnici – 125_000, 250_000, 500_000 i 1_000_000 uobičajene su vrijednosti. set_filters() konfigurira koje će ID-jeve upravljač propuštati; None znači prihvati sve (korisno pri pokretanju veze, manje korisno kad je sabirnica zauzeta).

3.26.1. Unutar upravljača

Tri dijela hardvera nalaze se između Python koda kamere i sabirnice – TX sandučići, filtar prihvaćanja i RX FIFO. Svaki od njih mapira se na dio CAN API-ja korištenog u nastavku.

Blok dijagram CAN upravljača. Na TX strani (gore), can.send() spušta sličice u jedan od tri TX sandučića, a blok za arbitražu bira koji sandučić sljedeći ide na sabirnicu. Na RX strani (dolje), dolazne sličice prolaze kroz filtar prihvaćanja; prihvaćene sličice slijeću u RX FIFO i can.recv() čita FIFO s njegova najstarijeg kraja.

Upravljačevi TX sandučići, filtar prihvaćanja i RX FIFO između softvera i sabirnice.

  • TX sandučići. Mali skup hardverskih utora (obično tri) koji drže odlazne sličice koje je kamera predala putem send(), ali koje još nisu stigle na sabirnicu. Kad je sabirnica slobodna, upravljač bira sandučić s ID-jem najnižeg broja (najvišeg prioriteta) i provodi arbitražu za sabirnicu u njegovo ime. Kamera ne bira sandučić; upravljač ga dodjeljuje i vraća njegov indeks iz send().

  • Filtar prihvaćanja. Konfigurabilni hardver koji uspoređuje ID svake dolazne sličice s popisom uzoraka i odbacuje sve što se ne podudara. Sličice koje prođu nastavljaju u RX FIFO; odbijene sličice upravljač odbacuje, a da nikad ne stignu do Pythona. set_filters() konfigurira te uzorke.

  • RX FIFO. Red tipa prvi unutra, prvi van (first-in, first-out) – prva sličica koja uđe ujedno je i prva koja izađe, poput reda na prodajnom šalteru. Upravljač dodaje primljene sličice na kraj reda kako pristižu, a recv() ih izvlači s prednje strane istim redoslijedom. Red je bitan jer sabirnica hvata sličice u pozadini dok je Python zauzet drugdje; kamera ih zatim prazni jednu po jednu bez gubljenja ijedne, sve dok se FIFO nije prepunio.

3.26.2. Slanje sličice

send() stavlja sličicu u red za odašiljanje:

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

Prvi argument je ID (11-bitni cijeli broj za standardnu sličicu); drugi je korisni teret (0 do 8 bajtova za CAN Classic). Poziv vraća mali cjelobrojni indeks koji identificira hardverski sandučić u koji je sličica otišla. Upravljač provodi arbitražu s bilo kojim drugim odašiljateljima na sabirnici i ponovno odašilje po potrebi bez daljnje pomoći softvera.

Za proširene (29-bitne) ID-jeve, primijenite OR sa zastavicom CAN.FLAG_EXT_ID na trećem argumentu:

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

3.26.3. Primanje sličica

Upravljač se pokreće bez instaliranog filtra i odbacuje svaku dolaznu sličicu. Prije nego što recv() išta vrati, jednom pozovite set_filters() – najjednostavniji oblik je None, koji prihvaća svaki ID:

can.set_filters(None)   # accept every frame

recv() zatim vraća sljedeću sličicu u prijemnom FIFO-u, ili None ako ništa ne čeka:

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

Sabirnica puni RX FIFO u pozadini, pa ga glavna petlja samo prazni onoliko brzo koliko iterira. Sve dok je FIFO dublji od najduljeg razmaka između pražnjenja, nijedna sličica se ne gubi.

3.26.4. Filtri

Prava sabirnica obično je zauzeta sličicama do kojih kameri nije stalo. Hardverski filtri omogućuju upravljaču da odbaci neželjene ID-jeve prije nego što stignu do FIFO-a. set_filters() uzima popis (id, mask, flags) n-torki; sličica prolazi filtar ako se njezin ID, maskiran s mask, podudara s konfiguriranim 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)))

Nepodudarne sličice upravljač odbacuje i one se nikad ne pojavljuju na recv(), što štedi i prostor međuspremnika i vrijeme procesora.

3.26.5. Stanja pogreške i oporavak

Prava CAN sabirnica skuplja pogreške u odašiljanju – kratke spojeve prema masi, čvorove koji nedostaju, električni šum koji kvari bitove. Upravljač drži dva brojača koji prate to ponašanje: Transmit Error Counter (TEC) i Receive Error Counter (REC), od kojih se svaki povećava kad upravljač otkrije pogrešku, a smanjuje nakon uspješnog prijenosa. Vrijednosti brojača stavljaju upravljač u jedno od četiri stanja:

  • Error Active (TEC i REC oba ispod 96). Normalan rad. Kad čvor otkrije pogrešku na sabirnici, odašilje dominantnu aktivnu sličicu pogreške, koja prisiljava svaki drugi čvor da odbaci sličicu koja je u tijeku kako bi pošiljatelj mogao pokušati ponovno.

  • Error Warning (bilo koji brojač dosegne 96). Još uvijek potpuno aktivan na sabirnici – stanje upozorenja je softverski signal da se pogreške nagomilavaju, a ne promjena ponašanja.

  • Error Passive (bilo koji brojač dosegne 128). Čvor je još uvijek na sabirnici, ali prestaje slati dominantne sličice pogreške; pogreške se sada signaliziraju pasivnim (recesivnim) sličicama pogreške kako neispravan čvor ne bi mogao neprestano ometati sabirnicu za sve ostale.

  • Bus Off (TEC dosegne 256). Upravljač je odlučio da je ovaj čvor previše nepouzdan da bi sudjelovao. Odspaja se od sabirnice, prestaje odašiljati i potvrđivati te ostaje izvan dok ga softver eksplicitno ne ponovno pokrene.

Prva tri prijelaza su potpuno automatska – kako se brojači smanjuju nakon uspješnih sličica, upravljač se sam vraća prema Error Active bez intervencije.

Bus Off je jedino stanje koje zahtijeva softversku akciju. restart() resetira upravljač i vraća ga u Error Active. Tipičan obrazac je provjeravati stanje iz glavne petlje i ponovno pokrenuti nakon kratke odgode kako bi se sabirnici dalo vremena da se smiri:

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

Trenutne vrijednosti brojača dostupne su iz get_counters() za dijagnostiku – TEC koji se postojano penje na inače tihoj sabirnici obično upućuje na ožičenje, terminaciju ili pogrešno konfiguriranu brzinu prijenosa bitova.