/* * canbus_driver.h * MCP2515 CAN controller driver via SPI. * Abstracts all hardware details — upper layers see only can_frame_t. */ #pragma once #include #include #ifdef __cplusplus extern "C" { #endif /* ============================================================ * CAN Frame * ============================================================ */ typedef struct { uint32_t id; /* Arbitration ID (11 or 29 bit) */ uint8_t dlc; /* Data Length Code (0-8) */ uint8_t data[8]; /* Payload */ bool extended; /* Extended (29-bit) ID */ bool rtr; /* Remote Transmission Request */ int64_t timestamp_us; /* Microsecond timestamp (esp_timer_get_time) */ } can_frame_t; /* ============================================================ * Operating Modes * ============================================================ */ typedef enum { CAN_MODE_NORMAL, /* Full TX/RX participation on bus */ CAN_MODE_LISTEN_ONLY, /* RX only, no ACK (stealth sniff) */ CAN_MODE_LOOPBACK, /* Self-test, TX frames loop back to RX */ } can_mode_t; /* ============================================================ * RX Callback * ============================================================ */ /* Called from RX task context (not ISR) — safe to call msg_data() etc. */ typedef void (*can_rx_callback_t)(const can_frame_t *frame, void *ctx); /* ============================================================ * Driver Lifecycle * ============================================================ */ /* Init SPI bus + MCP2515 reset + bit timing config */ bool can_driver_init(int bitrate, uint8_t osc_mhz); /* Set MCP2515 to operational mode, start RX task */ bool can_driver_start(can_mode_t mode); /* Set MCP2515 to config mode, kill RX task */ void can_driver_stop(void); /* Free SPI resources */ void can_driver_deinit(void); /* Check if driver is running */ bool can_driver_is_running(void); /* ============================================================ * TX / RX * ============================================================ */ /* Send a single CAN frame (blocking until TX complete or timeout) */ bool can_driver_send(const can_frame_t *frame); /* Register callback for received frames */ void can_driver_set_rx_callback(can_rx_callback_t cb, void *ctx); /* Retrieve the currently installed RX callback */ void can_driver_get_rx_callback(can_rx_callback_t *cb, void **ctx); /* ============================================================ * Hardware Filters (MCP2515 acceptance masks + filters) * ============================================================ */ /* Set one of 6 acceptance filters (0-5). Filters 0-1 use mask 0, filters 2-5 use mask 1. */ bool can_driver_set_filter(int filter_idx, uint32_t id, bool extended); /* Set one of 2 acceptance masks (0-1) */ bool can_driver_set_mask(int mask_idx, uint32_t mask, bool extended); /* Clear all filters — accept all frames */ void can_driver_clear_filters(void); /* ============================================================ * Status / Diagnostics * ============================================================ */ typedef struct { uint32_t rx_count; uint32_t tx_count; uint32_t rx_errors; /* REC from MCP2515 */ uint32_t tx_errors; /* TEC from MCP2515 */ uint32_t bus_errors; uint32_t rx_overflow; /* RX buffer overflow count */ bool bus_off; /* TEC > 255 */ bool error_passive; /* TEC or REC > 127 */ const char *state; /* "stopped"/"running"/"bus_off"/"error_passive" */ } can_status_t; void can_driver_get_status(can_status_t *out); /* ============================================================ * Replay * ============================================================ */ /* Replay recorded frames. speed_pct: 100=real-time, 0=max speed */ bool can_driver_replay(const can_frame_t *frames, int count, int speed_pct); #ifdef __cplusplus } #endif