Comma.ai Panda Board
U1) schematic. The official commaai/panda firmware remains the primary compatibility target; this file is a CubeMX/STM32CubeH7 HAL bring-up scaffold, not a replacement protocol implementation. Preserve panda CAN safety policy, USB descriptors/protocol, boot flow, and message semantics when integrating.Table
| Function | MCU pin | HAL/CubeMX selection | Live net / device | Electrical behavior |
|---|---|---|---|---|
| FDCAN1 RX/TX | PD0 / PD1 | AF9, FDCAN1 | FDCAN1_RX/TX → U2 TCAN3404-Q1 | Channel 1 |
| FDCAN2 RX/TX | PB5 / PB6 | AF9, FDCAN2 | FDCAN2_RX/TX → IC1 | Channel 2 |
| FDCAN3 RX/TX | PD12 / PD13 | AF5, FDCAN3 | FDCAN3_RX/TX → IC2 | Channel 3 |
| CAN1 standby | PG11 | GPIO output | CAN1_STB → U2 STB | low = normal; high = standby |
| CAN2 standby | PB3 | GPIO output | CAN2_STB → IC1 STB | low = normal; high = standby; conflicts with SWO, so do not enable SWO |
| CAN3 standby | PD7 | GPIO output | CAN3_STB → IC2 STB | low = normal; high = standby |
| USB D− / D+ | PA11 / PA12 | USB_OTG_HS internal FS PHY | USB_DM/DP → D1 → J1 | USB 2.0 FS device; these pins use the dedicated USB function, not the FDCAN1 AF option |
| USB VBUS monitor | PA0 | ADC1_INP16 (or analog input per CubeMX) | 100 kΩ/100 kΩ divider, USB_VBUS_SENSE | about 2.5 V at 5 V VBUS |
| Red LED | PE4 | GPIO output | LED_RED, R11, LED1 | active-low sink |
| Green LED | PE3 | GPIO output | LED_GREEN, R12, LED2 | active-low sink |
| Blue LED | PE2 | GPIO output | LED_BLUE, R13, LED3 | active-low sink |
| SWDIO / SWCLK | PA13 / PA14 | SYS Serial Wire | J3 pins 2 / 4 | preserve after reset |
| NRST | NRST | hardware reset | J3 pin 10, R15 10 kΩ pull-up, C28 | do not remap |
| BOOT0 | BOOT0 | boot strap input | R14 10 kΩ pull-down | normal flash boot when low |
SHDN pins are hard-wired to GND. Firmware controls only STB.STM32H725ZGT6, LQFP144.arm-none-eabi-gcc; use CubeIDE generated linker/startup files for STM32H725ZG.C
/* board_io.h */ #pragma once #include "stm32h7xx_hal.h" typedef enum { BOARD_CAN1 = 0, BOARD_CAN2 = 1, BOARD_CAN3 = 2 } board_can_t; typedef enum { BOARD_LED_RED = 0, BOARD_LED_GREEN = 1, BOARD_LED_BLUE = 2 } board_led_t; void BoardIO_Init(void); void Board_CAN_Standby(board_can_t can, GPIO_PinState standby); void Board_LED_Write(board_led_t led, GPIO_PinState on); void Board_LED_Toggle(board_led_t led);
C
/* board_io.c */ #include "board_io.h" static GPIO_TypeDef *const stb_port[3] = {GPIOG, GPIOB, GPIOD}; static const uint16_t stb_pin[3] = {GPIO_PIN_11, GPIO_PIN_3, GPIO_PIN_7}; static GPIO_TypeDef *const led_port[3] = {GPIOE, GPIOE, GPIOE}; static const uint16_t led_pin[3] = {GPIO_PIN_4, GPIO_PIN_3, GPIO_PIN_2}; void BoardIO_Init(void) { GPIO_InitTypeDef g = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); /* Safe states before changing pin mode: CAN standby asserted, LEDs off. */ HAL_GPIO_WritePin(GPIOG, GPIO_PIN_11, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4, GPIO_PIN_SET); g.Mode = GPIO_MODE_OUTPUT_PP; g.Pull = GPIO_NOPULL; g.Speed = GPIO_SPEED_FREQ_LOW; g.Pin = GPIO_PIN_11; HAL_GPIO_Init(GPIOG, &g); g.Pin = GPIO_PIN_3; HAL_GPIO_Init(GPIOB, &g); g.Pin = GPIO_PIN_7; HAL_GPIO_Init(GPIOD, &g); g.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4; HAL_GPIO_Init(GPIOE, &g); } void Board_CAN_Standby(board_can_t can, GPIO_PinState standby) { if ((unsigned)can < 3U) HAL_GPIO_WritePin(stb_port[can], stb_pin[can], standby); } void Board_LED_Write(board_led_t led, GPIO_PinState on) { if ((unsigned)led < 3U) HAL_GPIO_WritePin(led_port[led], led_pin[led], on == GPIO_PIN_SET ? GPIO_PIN_RESET : GPIO_PIN_SET); } void Board_LED_Toggle(board_led_t led) { if ((unsigned)led < 3U) HAL_GPIO_TogglePin(led_port[led], led_pin[led]); }
hfdcan1, hfdcan2, hfdcan3 and their MX_FDCANx_Init() functions. The following code compiles against the STM32CubeH7 HAL and keeps transceivers in standby until every controller starts.C
#include "main.h" #include "fdcan.h" #include "usb_device.h" #include "board_io.h" static FDCAN_HandleTypeDef *const canh[3] = {&hfdcan1, &hfdcan2, &hfdcan3}; static void CAN_Fatal(void) { Error_Handler(); } static void CAN_StartAll(void) { FDCAN_FilterTypeDef f = {0}; f.IdType = FDCAN_STANDARD_ID; f.FilterIndex = 0; f.FilterType = FDCAN_FILTER_MASK; f.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; f.FilterID1 = 0; f.FilterID2 = 0; for (unsigned i = 0; i < 3; ++i) { if (HAL_FDCAN_ConfigFilter(canh[i], &f) != HAL_OK) CAN_Fatal(); if (HAL_FDCAN_ConfigGlobalFilter(canh[i], FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE) != HAL_OK) CAN_Fatal(); if (HAL_FDCAN_ActivateNotification(canh[i], FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_BUS_OFF, 0) != HAL_OK) CAN_Fatal(); if (HAL_FDCAN_Start(canh[i]) != HAL_OK) CAN_Fatal(); } Board_CAN_Standby(BOARD_CAN1, GPIO_PIN_RESET); Board_CAN_Standby(BOARD_CAN2, GPIO_PIN_RESET); Board_CAN_Standby(BOARD_CAN3, GPIO_PIN_RESET); } static HAL_StatusTypeDef CAN_Send(unsigned channel, uint32_t id, const uint8_t *data, uint32_t bytes) { static const uint32_t dlc[9] = {FDCAN_DLC_BYTES_0,FDCAN_DLC_BYTES_1,FDCAN_DLC_BYTES_2, FDCAN_DLC_BYTES_3,FDCAN_DLC_BYTES_4,FDCAN_DLC_BYTES_5,FDCAN_DLC_BYTES_6, FDCAN_DLC_BYTES_7,FDCAN_DLC_BYTES_8}; if (channel >= 3U || bytes > 8U) return HAL_ERROR; FDCAN_TxHeaderTypeDef h = {0}; h.Identifier=id; h.IdType=FDCAN_STANDARD_ID; h.TxFrameType=FDCAN_DATA_FRAME; h.DataLength=dlc[bytes]; h.ErrorStateIndicator=FDCAN_ESI_ACTIVE; h.BitRateSwitch=FDCAN_BRS_OFF; h.FDFormat=FDCAN_CLASSIC_CAN; h.TxEventFifoControl=FDCAN_NO_TX_EVENTS; h.MessageMarker=0; return HAL_FDCAN_AddMessageToTxFifoQ(canh[channel], &h, (uint8_t *)data); }
main.c:C
HAL_Init(); SystemClock_Config(); BoardIO_Init(); /* safe standby and LED states first */ MX_FDCAN1_Init(); MX_FDCAN2_Init(); MX_FDCAN3_Init(); MX_USB_DEVICE_Init(); CAN_StartAll();
FrameFormat = FDCAN_FRAME_FD_BRS, use legal FD DLC values, and set FDFormat=FDCAN_FD_CAN, BitRateSwitch=FDCAN_BRS_ON. Do not copy example timing constants without calculation.Scope and compatibility target
Live pin map
Toolchain
CubeMX configuration
Compilable board I/O module
HAL FDCAN startup and test transmit
Reset, boot, and recovery
Validation before panda integration