3.26. 코드로 보는 CAN 버스¶
machine.CAN은 하드웨어 CAN 컨트롤러를 감쌉니다. 버스 id와 비트 레이트로 이를 구동하십시오:
from machine import CAN
can = CAN(1, 500_000)
can.set_filters(None) # accept all incoming IDs
비트 레이트는 버스의 다른 모든 노드와 정확히 일치해야 합니다. 125_000, 250_000, 500_000, 1_000_000이 일반적인 값입니다. set_filters()는 컨트롤러가 어떤 ID를 통과시킬지 구성합니다. None은 모든 것을 받아들인다는 의미입니다(링크를 구동할 때는 유용하지만, 버스가 바빠지면 덜 유용합니다).
3.26.1. 컨트롤러 내부¶
카메라의 Python 코드와 버스 사이에는 세 가지 하드웨어가 자리합니다. TX 메일박스, 수락 필터(acceptance filter), 그리고 RX FIFO입니다. 각각은 아래에서 사용하는 CAN API의 한 부분에 대응됩니다.
소프트웨어와 버스 사이에 있는 컨트롤러의 TX 메일박스, 수락 필터, RX FIFO.¶
TX 메일박스. 카메라가
send()를 통해 넘겼지만 아직 버스에 도달하지 않은 나가는 프레임을 담는 소규모의 하드웨어 슬롯 집합(보통 세 개)입니다. 버스가 유휴 상태일 때 컨트롤러는 가장 낮은 번호의 ID(가장 높은 우선순위)를 가진 메일박스를 골라 그것을 대신해 버스를 중재합니다. 카메라가 메일박스를 고르는 것이 아니라, 컨트롤러가 하나를 할당하고 그 인덱스를send()에서 반환합니다.수락 필터. 들어오는 각 프레임의 ID를 패턴 목록과 비교하여 일치하지 않는 것은 버리는 구성 가능한 하드웨어입니다. 통과한 프레임은 RX FIFO로 계속 진행하고, 거부된 프레임은 Python에 도달하기도 전에 컨트롤러가 버립니다.
set_filters()가 이 패턴들을 구성합니다.RX FIFO. 선입선출(first-in, first-out) 큐입니다. 매표소 줄처럼 가장 먼저 들어온 프레임이 가장 먼저 나갑니다. 컨트롤러는 수신한 프레임을 도착하는 대로 큐 뒤쪽에 추가하고,
recv()가 같은 순서로 앞쪽에서 꺼냅니다. 이 큐가 중요한 이유는, Python이 다른 일로 바쁜 동안 버스가 백그라운드에서 프레임을 받아내기 때문입니다. 그런 다음 카메라는 FIFO가 오버플로되지 않는 한 어떤 것도 잃지 않고 한 번에 하나씩 비워냅니다.
3.26.2. 프레임 보내기¶
send()는 프레임을 전송 대기열에 넣습니다:
can.send(0x123, b"\x01\x02\x03\x04")
첫 번째 인수는 ID(표준 프레임의 경우 11비트 정수)이고, 두 번째는 페이로드(CAN Classic의 경우 0에서 8바이트)입니다. 이 호출은 프레임이 들어간 하드웨어 메일박스를 식별하는 작은 정수 인덱스를 반환합니다. 컨트롤러는 버스의 다른 송신기들과 중재하고 필요에 따라 재전송하며, 이때 소프트웨어의 추가적인 도움은 필요하지 않습니다.
확장(29비트) ID의 경우, CAN.FLAG_EXT_ID 플래그를 세 번째 인수에 OR 연산하십시오:
can.send(0x18FF1234, b"hello", CAN.FLAG_EXT_ID)
3.26.3. 프레임 수신하기¶
컨트롤러는 필터가 설치되지 않은 상태로 시작하며 들어오는 모든 프레임을 버립니다. recv()가 무언가를 반환하기 전에, set_filters()를 한 번 호출하십시오. 가장 단순한 형태는 모든 ID를 받아들이는 None입니다:
can.set_filters(None) # accept every frame
그러면 recv()는 수신 FIFO의 다음 프레임을 반환하거나, 대기 중인 것이 없으면 None을 반환합니다:
msg = can.recv()
if msg is not None:
can_id, data, flags, errs = msg
print("got", hex(can_id), bytes(data))
버스가 백그라운드에서 RX FIFO를 채우므로, 메인 루프는 반복하는 속도만큼 빠르게 그것을 비워내기만 하면 됩니다. FIFO가 비워내는 간격 중 가장 긴 것보다 깊은 한, 프레임은 손실되지 않습니다.
3.26.4. 필터¶
실제 버스는 보통 카메라가 신경 쓰지 않는 프레임들로 바쁩니다. 하드웨어 필터는 원치 않는 ID가 FIFO에 도달하기 전에 컨트롤러가 버리도록 합니다. set_filters()는 (id, mask, flags) 튜플의 목록을 받습니다. 프레임은 그 ID를 mask로 마스킹한 값이 구성된 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)))
일치하지 않는 프레임은 컨트롤러가 버리며 recv()에 결코 나타나지 않으므로, 버퍼 공간과 CPU 시간을 모두 절약합니다.
3.26.5. 오류 상태와 복구¶
실제 CAN 버스는 전송 오류를 겪습니다. 접지로의 단락, 사라진 노드, 비트를 손상시키는 전기적 노이즈 등입니다. 컨트롤러는 이 동작을 추적하는 두 개의 카운터를 유지합니다. 전송 오류 카운터(TEC)와 수신 오류 카운터(REC)이며, 각각 컨트롤러가 오류를 검출하면 증가하고 전송이 성공하면 감소합니다. 카운터 값은 컨트롤러를 네 가지 상태 중 하나에 둡니다:
Error Active (TEC와 REC가 모두 96 미만). 정상 동작 상태입니다. 노드가 버스 오류를 감지하면 dominant active error frame을 전송하며, 이로 인해 다른 모든 노드는 진행 중이던 프레임을 폐기하므로 송신자는 재시도할 수 있습니다.
Error Warning (어느 한쪽 카운터가 96에 도달). 여전히 버스에서 완전히 활성 상태입니다. 경고 상태는 오류가 누적되고 있음을 알리는 소프트웨어 신호일 뿐, 동작이 바뀌는 것은 아닙니다.
Error Passive (어느 한쪽 카운터가 128에 도달). 노드는 여전히 버스에 있지만 dominant error frame 전송을 중단합니다. 이제 오류는 passive(recessive) error frame으로 신호되므로, 결함이 있는 노드가 다른 모두를 위한 버스를 계속 망가뜨릴 수 없습니다.
Bus Off (TEC가 256에 도달). 컨트롤러가 이 노드는 너무 불안정하여 참여할 수 없다고 판단한 상태입니다. 버스에서 연결을 끊고 전송 및 확인 응답을 중단하며, 소프트웨어가 명시적으로 재시작할 때까지 빠져 있습니다.
처음 세 전이는 전적으로 자동입니다. 프레임 성공 이후 카운터가 감소하면서, 컨트롤러는 개입 없이 스스로 Error Active 쪽으로 되돌아갑니다.
Bus Off는 소프트웨어 조치가 필요한 유일한 상태입니다. restart()는 컨트롤러를 리셋하고 Error Active로 되돌립니다. 일반적인 패턴은 메인 루프에서 상태를 확인하고, 버스가 안정될 시간을 주기 위해 짧은 지연 후 재시작하는 것입니다:
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
현재 카운터 값은 진단을 위해 get_counters()에서 얻을 수 있습니다. 그 외에는 조용한 버스에서 TEC가 꾸준히 올라간다면 보통 배선, 종단, 또는 잘못 구성된 비트 레이트를 가리킵니다.