#ifndef CAN_DRIVER_H #define CAN_DRIVER_H #include #include /* CAN bus driver for BLDC motor controllers (Issue #597) * CAN2 on PB12 (RX, AF9) / PB13 (TX, AF9) at 500 kbps * APB1 = 54 MHz: PSC=6, BS1=13tq, BS2=4tq, SJW=1tq → 18 tq/bit = 500 kbps */ /* Node IDs */ #define CAN_NUM_MOTORS 2u #define CAN_NODE_LEFT 0u #define CAN_NODE_RIGHT 1u /* CAN frame IDs */ #define CAN_ID_VEL_CMD_BASE 0x100u /* TX: 0x100 + node_id — velocity/torque command */ #define CAN_ID_ENABLE_CMD_BASE 0x110u /* TX: 0x110 + node_id — enable/disable */ #define CAN_ID_FEEDBACK_BASE 0x200u /* RX: 0x200 + node_id — position/velocity/current */ /* Filter: accept standard IDs 0x200–0x21F */ #define CAN_FILTER_STDID 0x200u #define CAN_FILTER_MASK 0x7E0u /* Bit timing (500 kbps @ 54 MHz APB1) */ #define CAN_PRESCALER 6u /* TX rate */ #define CAN_TX_RATE_HZ 100u /* Node alive timeout */ #define CAN_NODE_TIMEOUT_MS 100u /* TX command frame (8 bytes payload, DLC=4 for vel cmd) */ typedef struct { int16_t velocity_rpm; /* target RPM (+/- = fwd/rev) */ int16_t torque_x100; /* torque limit × 100 (0 = unlimited) */ } can_cmd_t; /* RX feedback frame (DLC=8) */ typedef struct { int16_t velocity_rpm; /* actual RPM */ int16_t current_ma; /* phase current in mA */ int16_t position_x100; /* position × 100 (degrees or encoder counts) */ int8_t temperature_c; /* controller temperature °C */ uint8_t fault; /* fault flags (0 = healthy) */ uint32_t last_rx_ms; /* HAL_GetTick() at last valid frame */ } can_feedback_t; /* Bus statistics */ typedef struct { uint32_t tx_count; /* frames transmitted */ uint32_t rx_count; /* frames received */ uint16_t err_count; /* HAL-level errors */ uint8_t bus_off; /* 1 = bus-off state */ uint8_t _pad; } can_stats_t; /* Initialise CAN2 peripheral, GPIO, and filter bank 14 */ void can_driver_init(void); /* Send velocity+torque command to one node */ void can_driver_send_cmd(uint8_t node_id, const can_cmd_t *cmd); /* Send enable/disable command to one node */ void can_driver_send_enable(uint8_t node_id, bool enable); /* Copy latest feedback snapshot (returns false if node never heard from) */ bool can_driver_get_feedback(uint8_t node_id, can_feedback_t *out); /* Returns true if node has been heard within CAN_NODE_TIMEOUT_MS */ bool can_driver_is_alive(uint8_t node_id, uint32_t now_ms); /* Copy bus statistics snapshot */ void can_driver_get_stats(can_stats_t *out); /* Drain RX FIFO0; call every main-loop tick */ void can_driver_process(void); #endif /* CAN_DRIVER_H */