sl-firmware 5e82878083 feat: bxCAN integration for VESC motor control and Orin comms (Issue #674)
- can_driver: add filter bank 15 (all ext IDs → FIFO1) and widen bank 14
  to accept all standard IDs; add can_driver_send_ext/std and ext/std
  frame callbacks (can_driver_set_ext_cb / can_driver_set_std_cb)
- vesc_can: VESC 29-bit extended CAN protocol driver — send RPM to IDs 56
  and 68 (FSESC 6.7 Pro Mini Dual), parse STATUS/STATUS_4/STATUS_5
  big-endian payloads, alive timeout, JLINK_TLM_VESC_STATE at 1 Hz
- orin_can: Orin↔FC standard CAN protocol — HEARTBEAT/DRIVE/MODE/ESTOP
  commands in, FC_STATUS + FC_VESC broadcast at 10 Hz
- jlink: add JLINK_TLM_VESC_STATE (0x8E), jlink_tlm_vesc_state_t (22 bytes),
  jlink_send_vesc_state_tlm()
- main: wire vesc_can_init/orin_can_init; replace can_driver_send_cmd with
  vesc_can_send_rpm; inject Orin CAN speed/steer into balance PID; add
  Orin CAN estop/clear handling; add orin_can_broadcast at 10 Hz
- test: 56-test host-side suite for vesc_can; test/stubs/stm32f7xx_hal.h
  minimal HAL stub for all future host-side tests

Safety: balance PID runs independently on Mamba — if Orin CAN link drops
(orin_can_is_alive() == false) the robot continues balancing in-place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 21:41:19 -04:00

118 lines
4.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef VESC_CAN_H
#define VESC_CAN_H
#include <stdint.h>
#include <stdbool.h>
/*
* 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 */