#ifndef UART_PROTOCOL_H #define UART_PROTOCOL_H /* * uart_protocol.h — UART command protocol for Jetson-STM32 communication (Issue #629) * * Frame format: * [STX][LEN][CMD][PAYLOAD...][CRC8][ETX] * 0x02 1B 1B 0-12 B 1B 0x03 * * CRC8-SMBUS: poly=0x07, init=0x00, computed over CMD+PAYLOAD bytes. * * Physical layer: UART5 (PC12=TX / PD2=RX), GPIO_AF8_UART5, 115200 baud, no hw flow. * NOTE: Spec requested USART1 @ 115200, but USART1 is occupied by JLink @ 921600. * Implemented on UART5 instead; Jetson must connect to PC12/PD2. * * DMA: DMA1_Stream0_Channel4, circular 256-byte ring buffer. * Heartbeat: if no frame received in UART_PROT_HB_TIMEOUT_MS (500 ms), Jetson is * considered lost; caller must handle estop if needed. */ #include #include /* ── Frame delimiters ─────────────────────────────────────────────────────── */ #define UPROT_STX 0x02u #define UPROT_ETX 0x03u /* ── Command IDs (host → STM32) ───────────────────────────────────────────── */ #define UCMD_SET_VELOCITY 0x01u /* payload: int16 left_rpm, int16 right_rpm (4 B) */ #define UCMD_GET_STATUS 0x02u /* payload: none */ #define UCMD_SET_PID 0x03u /* payload: float kp, float ki, float kd (12 B) */ #define UCMD_ESTOP 0x04u /* payload: none */ #define UCMD_CLEAR_ESTOP 0x05u /* payload: none */ /* ── Response IDs (STM32 → host) ──────────────────────────────────────────── */ #define URESP_ACK 0x80u /* payload: 1 B — echoed CMD */ #define URESP_NACK 0x81u /* payload: 2 B — CMD, error_code */ #define URESP_STATUS 0x82u /* payload: sizeof(uart_prot_status_t) = 8 B */ /* ── NACK error codes ─────────────────────────────────────────────────────── */ #define UERR_BAD_CRC 0x01u #define UERR_BAD_LEN 0x02u #define UERR_BAD_ETX 0x03u #define UERR_ESTOP 0x04u /* command rejected — estop active */ #define UERR_DISARMED 0x05u /* velocity rejected — not armed */ /* ── STATUS payload (URESP_STATUS, 8 bytes packed) ───────────────────────── */ typedef struct __attribute__((packed)) { int16_t pitch_x10; /* pitch angle ×10 deg (balance controller) */ int16_t motor_cmd; /* ESC motor command -1000..+1000 */ uint16_t vbat_mv; /* battery voltage in mV */ uint8_t balance_state; /* BalanceState enum (0=DISARMED, 1=ARMED, …) */ uint8_t estop_active; /* non-zero if remote estop is latched */ } uart_prot_status_t; /* ── Shared state (read by main.c) ────────────────────────────────────────── */ typedef struct { volatile uint8_t vel_updated; /* 1 when SET_VELOCITY received */ volatile int16_t left_rpm; volatile int16_t right_rpm; volatile uint8_t pid_updated; /* 1 when SET_PID received */ volatile float pid_kp; volatile float pid_ki; volatile float pid_kd; volatile uint8_t estop_req; /* 1 on UCMD_ESTOP */ volatile uint8_t estop_clear_req; /* 1 on UCMD_CLEAR_ESTOP */ volatile uint32_t last_rx_ms; /* HAL_GetTick() of last valid frame */ } UartProtState; extern UartProtState uart_prot_state; /* ── API ───────────────────────────────────────────────────────────────────── */ /** * uart_protocol_init() — configure UART5 + DMA, start circular receive. * Must be called once during system init, before main loop. */ void uart_protocol_init(void); /** * uart_protocol_process() — drain DMA ring buffer, parse frames, dispatch commands. * Call once per main loop iteration (every ~1 ms). */ void uart_protocol_process(void); /** * uart_protocol_send_status() — build and TX a URESP_STATUS frame. * @param s Pointer to status payload to send. */ void uart_protocol_send_status(const uart_prot_status_t *s); #endif /* UART_PROTOCOL_H */