- Add can_driver.h / can_driver.c: CAN2 on PB12/PB13 (AF9) at 500 kbps APB1=54 MHz, PSC=6, BS1=13tq, BS2=4tq, SJW=1tq → 18tq/bit, SP 77.8% Filter bank 14 (SlaveStartFilterBank=14); 32-bit mask; FIFO0 Accept std IDs 0x200–0x21F (left/right feedback frames) TX: velocity+torque cmd (0x100+nid, DLC=4) at 100 Hz via main loop RX: velocity/current/position/temp/fault feedback (0x200+nid, DLC=8) AutoBusOff=ENABLE for HW recovery; can_driver_process() drains FIFO0 - Add JLINK_TLM_CAN_STATS (0x89, 16 bytes) + JLINK_CMD_CAN_STATS_GET (0x10) Also add JLINK_TLM_SLOPE (0x88) + jlink_tlm_slope_t missing from Issue #600 - Wire into main.c: init after jlink_init; 100Hz TX loop (differential drive speed_rpm ± steer_rpm/2); CAN enable follows arm state; 1Hz stats telemetry - Add CAN_RPM_SCALE=10 and CAN_TLM_HZ=1 to config.h Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
2.7 KiB
C
82 lines
2.7 KiB
C
#ifndef CAN_DRIVER_H
|
||
#define CAN_DRIVER_H
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
|
||
/* 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 */
|