#ifndef VESC_CAN_H #define VESC_CAN_H #include #include /* * vesc_can — VESC CAN protocol driver for FSESC 6.7 Pro Mini Dual (Issue #674). * * VESC uses 29-bit extended CAN IDs: * arbitration_id = (packet_type << 8) | vesc_node_id * * Wire format is big-endian throughout (matches VESC FW 6.x). * * Physical layer: CAN2 on PB12 (RX, AF9) / PB13 (TX, AF9) at 500 kbps. * * NOTE ON PA11/PA12 vs PB12/PB13: * PA11/PA12 carry CAN1_RX/TX (AF9) BUT are also USB_OTG_FS DM/DP (AF10). * USB CDC is active on this board, so PA11/PA12 are occupied. * PB8/PB9 (CAN1 alternate) are occupied by I2C1 (barometer). * CAN2 on PB12/PB13 is the only conflict-free choice. * If the SN65HVD230 is wired to the pads labelled RX6/TX6 on the Mamba * silkscreen, those pads connect to PB12/PB13 (SPI2/OSD, repurposed). * * VESC frames arrive in FIFO1 (extended-ID filter, bank 15). * Orin standard frames arrive in FIFO0 (standard-ID filter, bank 14). */ /* ---- VESC packet type IDs (upper byte of 29-bit arb ID) ---- */ #define VESC_PKT_SET_DUTY 0u /* int32 duty × 100000 */ #define VESC_PKT_SET_CURRENT 1u /* int32 current (mA) */ #define VESC_PKT_SET_CURRENT_BRAKE 2u /* int32 brake current (mA) */ #define VESC_PKT_SET_RPM 3u /* int32 target RPM */ #define VESC_PKT_STATUS 9u /* int32 RPM, int16 I×10, int16 duty×1000 */ #define VESC_PKT_STATUS_4 16u /* int16 T_fet×10, T_mot×10, I_in×10 */ #define VESC_PKT_STATUS_5 27u /* int32 tacho, int16 V_in×10 */ /* ---- Default VESC node IDs (configurable via vesc_can_init) ---- */ #define VESC_CAN_ID_LEFT 56u #define VESC_CAN_ID_RIGHT 68u /* ---- Alive timeout ---- */ #define VESC_ALIVE_TIMEOUT_MS 1000u /* node offline if no STATUS for 1 s */ /* ---- JLink telemetry rate ---- */ #define VESC_TLM_HZ 1u /* ---- Fault codes (VESC FW 6.6) ---- */ #define VESC_FAULT_NONE 0u #define VESC_FAULT_OVER_VOLTAGE 1u #define VESC_FAULT_UNDER_VOLTAGE 2u #define VESC_FAULT_DRV 3u #define VESC_FAULT_ABS_OVER_CURRENT 4u #define VESC_FAULT_OVER_TEMP_FET 5u #define VESC_FAULT_OVER_TEMP_MOTOR 6u #define VESC_FAULT_GATE_DRIVER_OVER_VOLTAGE 7u #define VESC_FAULT_GATE_DRIVER_UNDER_VOLTAGE 8u #define VESC_FAULT_MCU_UNDER_VOLTAGE 9u #define VESC_FAULT_WATCHDOG_RESET 10u /* ---- Telemetry state per VESC node ---- */ typedef struct { int32_t rpm; /* actual RPM (STATUS pkt, int32 BE) */ int16_t current_x10; /* phase current (A × 10; STATUS pkt) */ int16_t duty_x1000; /* duty cycle (× 1000; –1000..+1000) */ int16_t temp_fet_x10; /* FET temperature (°C × 10; STATUS_4) */ int16_t temp_motor_x10; /* motor temperature (°C × 10; STATUS_4) */ int16_t current_in_x10; /* input (battery) current (A × 10; STATUS_4) */ int16_t voltage_x10; /* input voltage (V × 10; STATUS_5) */ uint8_t fault_code; /* VESC fault code (0 = none) */ uint8_t _pad; uint32_t last_rx_ms; /* HAL_GetTick() of last received STATUS frame */ } vesc_state_t; /* ---- API ---- */ /* * vesc_can_init(id_left, id_right) — store VESC node IDs and register the * extended-frame callback with can_driver. * Call after can_driver_init(). */ void vesc_can_init(uint8_t id_left, uint8_t id_right); /* * vesc_can_send_rpm(vesc_id, rpm) — transmit VESC_PKT_SET_RPM (3) to the * target VESC. arb_id = (3 << 8) | vesc_id. Payload: int32 big-endian. */ void vesc_can_send_rpm(uint8_t vesc_id, int32_t rpm); /* * vesc_can_on_frame(ext_id, data, len) — called by can_driver when an * extended-ID frame arrives (registered via can_driver_set_ext_cb). * Parses STATUS / STATUS_4 / STATUS_5 into the matching vesc_state_t. */ void vesc_can_on_frame(uint32_t ext_id, const uint8_t *data, uint8_t len); /* * vesc_can_get_state(vesc_id, out) — copy latest telemetry snapshot. * vesc_id must match id_left or id_right passed to vesc_can_init. * Returns false if vesc_id unknown or no frame has arrived yet. */ bool vesc_can_get_state(uint8_t vesc_id, vesc_state_t *out); /* * vesc_can_is_alive(vesc_id, now_ms) — true if a STATUS frame arrived * within VESC_ALIVE_TIMEOUT_MS of now_ms. */ bool vesc_can_is_alive(uint8_t vesc_id, uint32_t now_ms); /* * vesc_can_send_tlm(now_ms) — rate-limited JLINK_TLM_VESC_STATE (0x8E) * telemetry to Orin over JLink. Safe to call every ms; internally * rate-limited to VESC_TLM_HZ (1 Hz). */ void vesc_can_send_tlm(uint32_t now_ms); #endif /* VESC_CAN_H */