3.26. Bus CAN trong code¶
machine.CAN bọc một bộ điều khiển CAN phần cứng. Khởi động nó với bus id và tốc độ bit:
from machine import CAN
can = CAN(1, 500_000)
can.set_filters(None) # accept all incoming IDs
Tốc độ bit phải khớp chính xác với mọi nút khác trên bus -- 125_000, 250_000, 500_000 và 1_000_000 là các giá trị phổ biến. set_filters() cấu hình các ID mà bộ điều khiển sẽ cho qua; None có nghĩa là chấp nhận tất cả (hữu ích khi khởi động một liên kết, ít hữu ích hơn khi bus bận).
3.26.1. Bên trong bộ điều khiển¶
Ba phần phần cứng nằm giữa code Python của camera và bus -- các hộp thư TX, một bộ lọc chấp nhận và FIFO RX. Mỗi phần ánh xạ đến một phần của API CAN được sử dụng bên dưới.
Các hộp thư TX, bộ lọc chấp nhận và FIFO RX của bộ điều khiển giữa phần mềm và bus.¶
Hộp thư TX. Một tập nhỏ các khe phần cứng (thường là ba) giữ các khung gửi đi mà camera đã giao qua
send()nhưng chưa đến bus. Khi bus rảnh, bộ điều khiển chọn hộp thư có ID số thấp nhất (ưu tiên cao nhất) và tranh giành bus thay mặt nó. Camera không chọn hộp thư; bộ điều khiển gán một và trả về chỉ số của nó từsend().Bộ lọc chấp nhận. Phần cứng có thể cấu hình so sánh ID của mỗi khung đến với danh sách các mẫu và loại bỏ bất cứ thứ gì không khớp. Các khung đi qua tiếp tục vào FIFO RX; các khung bị từ chối bị bộ điều khiển loại bỏ mà không bao giờ đến Python.
set_filters()cấu hình các mẫu này.FIFO RX. Một hàng đợi vào trước, ra trước -- khung đầu tiên vào cũng là khung đầu tiên ra, như xếp hàng mua vé. Bộ điều khiển thêm các khung nhận được vào cuối hàng đợi khi chúng đến, và
recv()lấy chúng từ đầu theo cùng thứ tự. Hàng đợi quan trọng vì bus bắt các khung ở nền trong khi Python đang bận ở nơi khác; camera sau đó xả chúng từng cái một mà không mất cái nào, miễn là FIFO chưa tràn.
3.26.2. Gửi một khung¶
send() xếp hàng một khung để truyền:
can.send(0x123, b"\x01\x02\x03\x04")
Đối số đầu tiên là ID (một số nguyên 11 bit cho khung chuẩn); đối số thứ hai là tải trọng (0 đến 8 byte cho CAN Classic). Lệnh gọi trả về một chỉ số số nguyên nhỏ xác định hộp thư phần cứng mà khung đã đi vào. Bộ điều khiển tranh giành với bất kỳ máy phát nào khác trên bus và truyền lại khi cần mà không cần thêm sự trợ giúp từ phần mềm.
Đối với các ID mở rộng (29 bit), OR cờ CAN.FLAG_EXT_ID vào đối số thứ ba:
can.send(0x18FF1234, b"hello", CAN.FLAG_EXT_ID)
3.26.3. Nhận các khung¶
Bộ điều khiển khởi động mà không có bộ lọc nào được cài đặt và bỏ mọi khung đến. Trước khi recv() trả về bất cứ thứ gì, hãy gọi set_filters() một lần -- dạng đơn giản nhất là None, chấp nhận mọi ID:
can.set_filters(None) # accept every frame
recv() sau đó trả về khung tiếp theo trong FIFO nhận, hoặc None nếu không có gì đang chờ:
msg = can.recv()
if msg is not None:
can_id, data, flags, errs = msg
print("got", hex(can_id), bytes(data))
Bus điền vào FIFO RX ở nền, vì vậy vòng lặp chính chỉ xả nó nhanh như nó lặp. Miễn là FIFO sâu hơn khoảng cách dài nhất giữa các lần xả, không có khung nào bị mất.
3.26.4. Bộ lọc¶
Một bus thực thường bận với các khung mà camera không quan tâm. Các bộ lọc phần cứng cho phép bộ điều khiển bỏ các ID không mong muốn trước khi chúng đến FIFO. set_filters() nhận một danh sách các tuple (id, mask, flags); một khung đi qua bộ lọc nếu ID của nó, được che bởi mask, khớp với id đã cấu hình:
# 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)))
Các khung không khớp bị bộ điều khiển loại bỏ và không bao giờ xuất hiện tại recv(), điều này tiết kiệm cả dung lượng bộ đệm và thời gian CPU.
3.26.5. Trạng thái lỗi và khôi phục¶
Một bus CAN thực sẽ gặp lỗi truyền -- ngắn mạch xuống đất, thiếu nút, nhiễu điện làm hỏng bit. Bộ điều khiển giữ hai bộ đếm theo dõi hành vi này: Bộ đếm Lỗi Truyền (TEC) và Bộ đếm Lỗi Nhận (REC), mỗi cái được tăng khi bộ điều khiển phát hiện lỗi và giảm sau khi truyền thành công. Các giá trị bộ đếm đặt bộ điều khiển vào một trong bốn trạng thái:
Lỗi Hoạt Động (TEC và REC đều dưới 96). Hoạt động bình thường. Khi nút phát hiện lỗi bus, nó truyền một khung lỗi tích cực dominant, buộc mọi nút khác loại bỏ khung đang xử lý để người gửi có thể thử lại.
Cảnh Báo Lỗi (một trong hai bộ đếm đạt 96). Vẫn hoạt động đầy đủ trên bus -- trạng thái cảnh báo là tín hiệu phần mềm cho biết lỗi đang tích lũy, không phải thay đổi hành vi.
Lỗi Thụ Động (một trong hai bộ đếm đạt 128). Nút vẫn còn trên bus nhưng ngừng gửi khung lỗi dominant; lỗi bây giờ được báo hiệu bằng khung lỗi thụ động (recessive) để một nút lỗi không thể tiếp tục phá hoại bus cho mọi người khác.
Bus Off (TEC đạt 256). Bộ điều khiển đã quyết định nút này quá không đáng tin cậy để tham gia. Nó ngắt kết nối khỏi bus, ngừng truyền và xác nhận, và ở ngoài cho đến khi phần mềm khởi động lại nó một cách rõ ràng.
Ba chuyển đổi đầu tiên hoàn toàn tự động -- khi các bộ đếm giảm sau các khung thành công, bộ điều khiển tự chuyển về Lỗi Hoạt Động mà không cần can thiệp.
Bus Off là trạng thái duy nhất yêu cầu hành động phần mềm. restart() đặt lại bộ điều khiển và đưa nó trở lại Lỗi Hoạt Động. Mẫu điển hình là kiểm tra trạng thái từ vòng lặp chính và khởi động lại sau một khoảng trễ ngắn để bus có thời gian ổn định:
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
Các giá trị bộ đếm hiện tại có thể lấy từ get_counters() để chẩn đoán -- một TEC tăng đều đặn trên một bus yên tĩnh thường chỉ đến vấn đề về dây dẫn, kết cuối hoặc tốc độ bit được cấu hình sai.