夜雨聆风学习资料网

ARTICLE · 1130650

ODrive V3.x源码分析之Firmware/Board

ODrive V3.x源码分析之Firmware/Board

ODrive 固件板级目录分析(Firmware/Board)

分析对象:ODrive\Firmware\Board

分析日期:2026-10-05 基准提交:3a2e4dd1(fw-v0.5.6-5-g3a2e4dd1) 


1. 目录总览

Board目录下只有一个v3子目录

(v4.0-56V / v4.1-58V 只是在 Tupfile.lua 中占位,指向已移除的 Private/v4,无法编译,也就是说ODrive没有v4版本的源码)。

Board/└── v3/    ├── board.cpp                 ★板级对象实例化、初始化、中断服务(手写核心)    ├── Inc/                      头文件目录    │   ├── board.h               ★板级公共契约(宏/extern/函数原型)    │   ├── main.h                ★PWM时序常量 + 引脚宏(按板型分支)    │   ├── mxconstants.h         CubeMX导出旧命名引脚宏    │   ├── FreeRTOSConfig.h      ★FreeRTOS内核配置    │   ├── adc.h / tim.h / gpio.h / dma.h / spi.h / usart.h / i2c.h / can.h    │   ├── usb_device.h / usbd_cdc_if.h / usbd_conf.h / usbd_desc.h    │   ├── stm32f4xx_hal_conf.h  HAL模块开关    │   ├── stm32f4xx_it.h    │   └── prev_board_ver/       main_V3_2.h / main_V3_4.h 历史板型引脚    ├── Src/                      CubeMX生成外设初始化C文件    │   └── prev_board_ver/       adc_V3_2.c / gpio_V3_2.c ...    ├── Makefile                  CubeMX导出清单,构建不使用    ├── Odrive.ioc                CubeMX工程文件    ├── startup_stm32f405xx.s     ★向量表 + Reset_Handler    ├── STM32F405RGTx_FLASH.ld    ★链接脚本(内存布局)    └── STM32F40x.svd             CMSIS-SVD调试寄存器描述

注意

:Board/v3/Makefile 中的 C_SOURCES 指向 Drivers/、Middlewares/ 等目录,这些目录在 Board/v3/ 下并不存在——它是 CubeMX 导出的模板,真正的构建由 Firmware/Tupfile.lua 完成(Firmware/Makefile 仅是 tup 入口)。


2. 仓库版本

2.1 该仓库可编译的版本(Firmware/Tupfile.lua:315-328)

板型
编译宏
说明
v3.1-DHW_VERSION_MINOR=1 -DHW_VERSION_VOLTAGE=24
v3.2MINOR=2VOLTAGE=24
v3.3MINOR=3VOLTAGE=24
分流电阻 675µΩ 的最后一版
v3.4-24VMINOR=4VOLTAGE=24
v3.4-48VMINOR=4VOLTAGE=48
v3.5-24VMINOR=5VOLTAGE=24
GPIO 5/6/7/8 完整可用
v3.5-48VMINOR=5VOLTAGE=48
v3.6-24VMINOR=6VOLTAGE=24
v3.6-56VMINOR=6VOLTAGE=56
56V 版
v4.0-56V
 / v4.1-58V
-DSTM32F722xx -DHW_VERSION_MAJOR=4
依赖已移除的 Private/v4,不可编译

HW_VERSION_MAJOR 由 board_v3 分组统一给出(=3),与 OTP 中字节 [3..5] 比对。

2.2 由编译器宏派生的硬件差异

宏/位置
条件
影响
SHUNT_RESISTANCEHW_VERSION_MINOR <= 3
 → 675e-6f,否则 500e-6f(Inc/board.h:25-29)
相电流换算
DEFAULT_BRAKE_RESISTANCEMINOR>=5 && VOLTAGE>=48
 → 2.0f,否则 0.47f(Inc/board.h:40-44)
制动电阻
VBUS_S_DIVIDER_RATIOVOLTAGE>=48
 → 19.0f,==24 → 11.0f(Inc/board.h:120-126)
母线电压换算
FET 温度 ADC 通道
MINOR>=3
 → 通道 4,否则通道 1(board.cpp:63-67)
板载温度
M1 step/dir 引脚
MINOR>=5
 → GPIO7/8,否则 GPIO3/4(board.cpp:139-145)
步进接口
gpios[]
 表
MINOR
 1/2、3/4、5/6 三套(board.cpp:159-227)
引脚映射
alternate_functions[]MINOR>=3
 才给 GPIO1-3 复用项(board.cpp:232-240)
UART/PWM 复用
pwm0_input
 通道
MINOR<=2
 → {0,0,0,4}(仅通道 4),否则 {1,2,3,4}(board.cpp:257-261)
PWM 输入
旧引脚头
MINOR 1/2
 → prev_board_ver/main_V3_2.h;3/4 → main_V3_4.h(Inc/main.h:59-65)
引脚定义

3. Inc/board.h —— 板级公共契约

3.1 预处理宏(C/C++ 通用)

宏
行
值 / 含义
SHUNT_RESISTANCE
26 / 28
675e-6f
 / 500e-6f
AXIS_COUNT
31
(2)
GPIO_COUNT
36
(17)
(含占位 GPIO0 与编码器/CAN 引脚)
CAN_FREQ
38
(2000000UL)
 —— CAN 位时间量子基准,见 §5.4
DEFAULT_BRAKE_RESISTANCE
41 / 43
2.0f
 / 0.47f Ω
DEFAULT_ERROR_PIN
46
0
DEFAULT_MIN_DC_VOLTAGE
47
8.0f
 V
DEFAULT_GPIO_MODES
49-66
17 项 GPIO 默认模式序列(DIGITAL/UART_A/ANALOG_IN/ENC0/ENC1/CAN_A…)
TIM_TIME_BASE
68
TIM14
(HAL 时间基准)
CONTROL_TIMER_PERIOD_TICKS
71
2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)
TIM1_INIT_COUNT
73
TIM_1_8_PERIOD_CLOCKS / 2 - 128
(M0/M1 采样相位差)
MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA
77
TIM_1_8_PERIOD_CLOCKS / 2 + 128
CURRENT_MEAS_PERIOD
113
采样周期(秒),static const float current_meas_period(:114)
CURRENT_MEAS_HZ
117
采样频率(Hz),static const int current_meas_hz(:118)
VBUS_S_DIVIDER_RATIO
121 / 123
19.0f
 / 11.0f
CURRENT_SENSE_MIN_VOLT
130
0.3f
 V(DRV8301 运放线性下限)
CURRENT_SENSE_MAX_VOLT
131
3.0f
 V(相对 1.65V 中点对称)

3.2 C++ 类型别名与包含(board.h:79-90)

#include <Drivers/DRV8301/drv8301.hpp>#include <Drivers/STM32/stm32_gpio.hpp>#include <Drivers/STM32/stm32_spi_arbiter.hpp>#include <MotorControl/pwm_input.hpp>#include <MotorControl/thermistor.hpp>using TGateDriver = Drv8301;   // board.h:86using TOpAmp      = Drv8301;   // board.h:87  同一颗芯片兼任运放配置

3.3 对外变量声明(board.h:92-109)

声明
行
类型
axes
92
std::array<Axis, AXIS_COUNT>
motors
93
Motor[AXIS_COUNT]
fet_thermistors
94
OnboardThermistorCurrentLimiter[AXIS_COUNT]
encoders
95
Encoder[AXIS_COUNT]
gpios
96
Stm32Gpio[GPIO_COUNT]
GpioFunction
(结构)
98
struct { int mode; uint8_t alternate_function; }
alternate_functions
99
std::array<GpioFunction, 3>[GPIO_COUNT]
usb_dev_handle
101
USBD_HandleTypeDef&
ext_spi_arbiter
103
Stm32SpiArbiter&
uart_a
 / uart_b / uart_c
105-107
UART_HandleTypeDef*
pwm0_input
109
PwmInput

3.4 板级配置读写桩(board.h:134-137)

static inline bool  board_read_config()  { return true; }static inline bool  board_write_config() { return true; }static inline void  board_clear_config() {}static inline bool  board_apply_config() { return true; }// 注释原文:This board has no board-specific user configurations

3.5 对外函数原型(board.h:139-141)

void system_init();   // HAL_Init + 时钟 + OTP 板型校验(board.cpp:272)bool board_init();    // 外设初始化 + 中断优先级 + DRV 复位(board.cpp:292)void start_timers();  // TIM1/TIM8/TIM13 同步启动 + ADC 触发使能(board.cpp:380)

4. board.cpp —— 板级实例化、初始化与中断

4.1 文件级全局

符号
行
说明
TaskTimer::enabled
19
bool
 静态成员定义(属 task_timer.hpp,注释说明不想为一行建文件)
SystemClock_Config
21
extern "C"
 前向声明,实现在 Src/main.c
ControlLoop_IRQHandler
 / ControlLoop_IRQn
23-24
宏:映射到 OTG_HS_IRQHandler / OTG_HS_IRQn(复用未用的 HS USB 中断跑控制环)
fake_otp[...]
30
__attribute__((section(".testdata")))
,放在 RAM 起始 0x20000000;内容 {0,0,0, HW_VERSION_MAJOR, HW_VERSION_MINOR, HW_VERSION_VOLTAGE},OTP 未烧录时用于生产测试/兜底
timestamp_
474
volatile uint32_t
,控制环时间戳(TIM8 更新累加)
counting_down_
475
volatile bool
,用于检测 TIM8 更新是否丢失

4.2 板级对象实例(board.cpp)

对象
行
初始化内容
spi3_arbiter
32
Stm32SpiArbiter{&hspi3}
ext_spi_arbiter
33
引用别名 → spi3_arbiter(供外部编码器共享)
uart_a
35
&huart4
uart_b
36
&huart2
(注释:v3.6 或可用 USART2 支持)
uart_c
37
nullptr
(v3 板无第三个 UART)
m0_gate_driver
39-44
Drv8301{&spi3_arbiter, {PC13,nCS}, {}, {PD2,nFAULT}}
(EN/nFAULT 两轴共享,故 EN 传空)
m1_gate_driver
46-51
同上,nCS = PC14
fet_thermistor_poly_coeffs
53-54
4 阶多项式 {363.93910201, -462.15369634, 307.55129571, -27.72569531}
fet_thermistor_num_coeffs
55
= 4
fet_thermistors[2]
57-71
ADC 通道 15(M0)、通道 4/1(M1,按板型)
motor_thermistors[2]
73
外接电机温度,默认构造(由配置填充)
motors[2]
75-94
TIM1/TIM8、current_sensor_mask=0b110、1/SHUNT_RESISTANCE、gate driver + opamp + 两个温度计
encoders[2]
96-113
TIM3/TIM4、Z/A/B 引脚、&spi3_arbiter
endstops[4]
116
2 * AXIS_COUNT
,注释:无硬件依赖,应按配置动态分配
mechanical_brakes[2]
117
sensorless_estimators[2]
119
controllers[2]
120
trap[2]
121
TrapezoidalTrajectory
axes
123-155
轴 0:step=GPIO1、dir=GPIO2、优先级 osPriorityHigh+1;轴 1:step/dir 按板型(GPIO7/8 或 3/4)、优先级 osPriorityHigh
gpios[]
160-224
见 §4.3,三套板型表
alternate_functions[GPIO_COUNT]
229-255
每脚最多 3 个 {模式, AF}
pwm0_input
257-261
PwmInput{&htim5, {1,2,3,4}}
(v3.1/3.2 为 {0,0,0,4})
usb_dev_handle
263-264
= hUsbDeviceFS
(extern 自 usbd_conf.c)

4.3 gpios[] 引脚表

v3.5 / v3.6(board.cpp:204-224,GPIO_COUNT = 17):

序号
名称
端口/引脚
序号
名称
端口/引脚
0
占位
{nullptr,0}
9
ENC0_A
PB4
1
GPIO1
PA0
10
ENC0_B
PB5
2
GPIO2
PA1
11
ENC0_Z
PC9
3
GPIO3
PA2
12
ENC1_A
PB6
4
GPIO4
PA3
13
ENC1_B
PB7
5
GPIO5
PC4
14
ENC1_Z
PC15
6
GPIO6
PB2
15
CAN_R
PB8
7
GPIO7
PA15
16
CAN_D
PB9
8
GPIO8
PB3

v3.3 / v3.4(:182-202):GPIO1-4 = PA0-PA3,GPIO5 = PB2,GPIO6-8 = 不存在(nullptr),ENC0_Z = PA15,ENC1_Z = PB3。

v3.1 / v3.2(:160-180):GPIO1 = PB2、GPIO2 = PA5、GPIO3 = PA4、GPIO4 = PA3、GPIO5-8 = 不存在,编码器/Z 同 v3.3。

4.4 alternate_functions[] 复用表(board.cpp:229-255)

GPIO
复用项(模式 → AF)
GPIO0
空
GPIO1 / GPIO2
UART_A → GPIO_AF8_UART4
、PWM → GPIO_AF2_TIM5(仅 MINOR>=3;否则空)
GPIO3
UART_B → GPIO_AF7_USART2
、PWM → GPIO_AF2_TIM5(仅 MINOR>=3)
GPIO4
UART_B → GPIO_AF7_USART2
、PWM → GPIO_AF2_TIM5
GPIO5-8
空(纯 GPIO/模拟)
ENC0_A / ENC0_B
ENC0 → GPIO_AF2_TIM3
ENC0_Z
空
ENC1_A / ENC1_B
I2C_A → GPIO_AF4_I2C1
、ENC1 → GPIO_AF2_TIM4
ENC1_Z
空
CAN_R / CAN_D
CAN_A → GPIO_AF9_CAN1
、I2C_A → GPIO_AF4_I2C1

ODrive::GPIO_MODE_* 与 ODriveIntf::GPIO_MODE_* 等价(ODrive : ODriveIntf)。

4.5 初始化函数

check_board_version(const uint8_t* otp_ptr) — board.cpp:266-270

return (otp_ptr[3] == HW_VERSION_MAJOR) &&       (otp_ptr[4] == HW_VERSION_MINOR) &&       (otp_ptr[5] == HW_VERSION_VOLTAGE);

system_init() — board.cpp:272-290

  1. HAL_Init()
  2. SystemClock_Config()
    (main.c:160)
  3. 读 FLASH_OTP_BASE;若首字节 0xff(OTP 未烧录)→ 改用 RAM 中的 fake_otp(:280-283)
  4. check_board_version()
     失败则 for (;;); 死循环(:287-289)

board_init() — board.cpp:292-378

  1. 依次调用 MX_GPIO_Init / MX_DMA_Init / MX_ADC1_Init / MX_ADC2_Init / MX_TIM1_Init / MX_TIM8_Init / MX_TIM3_Init / MX_TIM4_Init / MX_SPI3_Init / MX_ADC3_Init / MX_TIM2_Init / MX_TIM5_Init / MX_TIM13_Init(:294-306)
  2. EXTI 中断优先级 = 1
    :EXTI0/1/2/3/4/9_5/15_10(:309-322)——高于 FreeRTOS configMAX_SYSCALL_INTERRUPT_PRIORITY(5),故只能裸用、不可调 FromISR API
  3. ControlLoop_IRQn
    (= OTG_HS)优先级 5,TIM8_UP_TIM13_IRQn 优先级 0(:324-328)
  4. 按配置初始化 UART4 / USART2,并写入用户波特率(:330-338)
  5. 若 enable_i2c_a:把 GPIO3/4/5 配为上拉输入,读 3 位拨码拼出 I2C 从机地址,调 MX_I2C1_Init(addr)(:340-352)——地址 = (0xD<<3) | strap,即基址 0x68
  6. 若 enable_can_a:校验 gpio_modes[15]/[16] 必须是 GPIO_MODE_CAN_A,否则 odrv.misconfigured_ = true(:354-361)
  7. 调试停核冻结 TIM1/TIM8/TIM13(:363-366)
  8. DRV8301 硬复位
    :EN_GATE 拉低 → delay_us(40) → 拉高 → delay_us(20000)(:368-375)

start_timers() — board.cpp:380-416

在 CRITICAL_SECTION() 中:

  1. 临时关闭 ADC 触发(ADC_CR2_JEXTEN/EXTEN,:384-386)
  2. Stm32Timer::start_synchronously<3>({&htim1,&htim8,&htim13}, {TIM1_INIT_COUNT, 0, TIM1_INIT_COUNT/2})
    :
    • TIM1 三角波领先 TIM8 90°
    • TIM13 每次重装载对齐 TIM1 下计数更新事件(:388-397)
  3. 恢复 ADC 触发(注入上升沿 / 规则上升沿,:399-401)
  4. 清 JEOC/EOC/OVR 标志,使能 TIM8 更新中断(:403-414)

4.6 ADC 读取:fetch_and_reset_adcs()(static,board.cpp:418-451)

  • 等待 ADC1.JEOC、ADC2.EOC|JEOC、ADC3.EOC|JEOC 全部就绪,否则返回 false
  • vbus_sense_adc_cb(ADC1->JDR1)
    (:428)→ 更新 vbus_voltage
  • 轴 0:ADC2->JDR1 = M0_IB、ADC3->JDR1 = M0_IC(注入组),用 motors[0].phase_current_from_adcval() 换算,缺失相由 -phB - phC 补(:430-436)
  • 轴 1:ADC2->DR = M1_IB、ADC3->DR = M1_IC(规则组)(:438-444)
  • 两轴共用 ADC2/ADC3,靠注入组(M0)与规则组(M1)分流
  • 清标志后返回 true(:446-450)

4.7 中断服务程序(extern "C",board.cpp:453-593)

ISR
行
行为
HAL_SPI_TxCpltCallback
 / HAL_SPI_RxCpltCallback
455-461
转发到 HAL_SPI_TxRxCpltCallback
HAL_SPI_TxRxCpltCallback
463-467
hspi == &hspi3
 → spi3_arbiter.on_complete()
TIM5_IRQHandler
469-472
pwm0_input.on_capture()
(PWM 输入捕获)
TIM8_UP_TIM13_IRQHandler
477-516
采样节拍核心
,见下
ControlLoop_IRQHandler
(= OTG_HS_IRQHandler)
518-575
控制环核心
,见下
I2C1_EV_IRQHandler
 / I2C1_ER_IRQHandler
577-585
转 HAL_I2C_EV/ER_IRQHandler
OTG_FS_IRQHandler
588-591
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS)

TIM8_UP_TIM13_IRQHandler(采样触发)

  1. 清 TIM8 更新标志;读 TIM8->CR1 DIR 判断当前计数方向
  2. timer_update_missed = (counting_down_ == counting_down)
    ,若为真 → 两轴 disarm_with_error(ERROR_TIMER_UPDATE_MISSED)(:488-493)
  3. timestamp_ += TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)
    (:496)
  4. 上计数
    (采到 SVM 矢量 0,真实电流):TaskTimer::enabled = odrv.task_timers_armed_、odrv.sampling_cb()、NVIC->STIR = ControlLoop_IRQn 软触发控制环(:498-503)
  5. 下计数
    (SVM 矢量 7,零电流):先把 TIM1/TIM8 六路 CCR 全部暂设为 50% 占空比作为兜底,控制环若及时完成则覆盖(:504-515)

ControlLoop_IRQHandler(控制环)

  1. fetch_and_reset_adcs()
     失败 → ERROR_BAD_TIMING(:526-529)
  2. 若 TIM1/TIM8 的 BDTR.MOE 未置位(下桥未导通、无法测流)→ 该轴电流置 0(:536-541)
  3. motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, current0)
    、motors[1].current_meas_cb(timestamp, current1)(:543-544)
  4. odrv.control_loop_cb(timestamp)
    (:546)
  5. 等待 ADC2->SR & ADC_SR_EOC(计入 task_times_.dc_calib_wait,:550-552)
  6. 再次 fetch_and_reset_adcs() → motors[*].dc_calib_cb(...)(:554-560)
  7. motors[*].pwm_update_cb(timestamp + 3 * 周期 ...)
    (:562-563)
  8. 校验 timestamp_ 是否恰好推进一个周期,否则 ERROR_CONTROL_DEADLINE_MISSED(:568-571)
  9. 关闭 TaskTimer::enabled,并 task_timers_armed_ 收敛(:573-574)

5. CubeMX 生成层(Inc/ + Src/)

5.1 Src/main.c

符号
行
说明
SystemClock_Config()
 原型
73
MX_FREERTOS_Init()
 原型
74
CubeMX 版 main()
91(//int main)/ 93(#if 0)
被 #if 0 禁用
——固件使用 MotorControl/main.cpp 的 main()
SystemClock_Config()
160-211
见 §5.2
HAL_TIM_PeriodElapsedCallback()
226-237
htim->Instance == TIM14
 → HAL_IncTick()
_Error_Handler(char*, int)
245-253
死循环
assert_failed()
263
仅 USE_FULL_ASSERT 时编译

时钟树(main.c:160-211):

参数
值
结果
PLLM / PLLN / PLLP / PLLQ4 / 168 / DIV2 / 7
HSE→168 MHz SYSCLK
SYSCLKSourceRCC_SYSCLKSOURCE_PLLCLK
168 MHz
AHBCLKDividerRCC_SYSCLK_DIV1
HCLK = 168 MHz
APB1CLKDividerRCC_HCLK_DIV4
PCLK1 = 42 MHz(定时器时钟 ×2 = 84 MHz)
APB2CLKDividerRCC_HCLK_DIV2
PCLK2 = 84 MHz(定时器时钟 ×2 = 168 MHz)
Flash 等待
FLASH_LATENCY_5
SysTick
HAL_RCC_GetHCLKFreq()/1000
(main.c:204),优先级 15(main.c:211)
1 ms 节拍
振荡器
HSE_ON
 + LSI_ON

5.2 Inc/main.h —— PWM 时序常量与引脚

时序常量(v3.5/3.6 分支,main.h:69-75;v3.1-v3.4 走 prev_board_ver/*.h):

宏
行
值
含义
TIM_1_8_CLOCK_HZ
69
168000000
TIM1/TIM8 定时器时钟
TIM_1_8_PERIOD_CLOCKS
70
3500
三角波周期计数
TIM_1_8_DEADTIME_CLOCKS
71
20
死区 ≈ 119 ns
TIM_APB1_CLOCK_HZ
72
84000000
APB1 定时器时钟
TIM_APB1_PERIOD_CLOCKS
73
4096
TIM2 周期
TIM_APB1_DEADTIME_CLOCKS
74
40
TIM2 死区
TIM_1_8_RCR
75
2
重复计数:每 3 个三角波出一次更新

推导:PWM 载波 = 168e6 / (2×3500) = 24 kHz;更新/采样/控制环频率 = 168e6 / (2×3500×3) = 8 kHz(= CURRENT_MEAS_HZ,Inc/board.h:117)。

引脚宏(main.h:77-156,v3.5/3.6 版,_Pin + _GPIO_Port 成对):分组如下——

组
宏前缀
端口示例
栅极驱动 SPI
M0_nCS
(PC13)、M1_nCS(PC14)、EN_GATE(PB12)、nFAULT(PD2)
相电流采样
M0_IB
(PC0)、M0_IC(PC1)、M1_IB(PC3)、M1_IC(PC2)
ADC2/3
高侧 PWM
M0_AH/BL/CH
(PA8-10)、M1_AH/BH/CH(PC6-8)
TIM1/TIM8
低侧 PWM
M0_AL/BL/CL
(PB13-15)、M1_BL/CL(PB0-1)、AUX_L/H(PB10-11)
用户 GPIO
GPIO_1..GPIO_8
PA0-3 / PC4 / PB2 / PA15 / PB3
温度/母线
M0_TEMP
(PC5)、M1_TEMP(PA4)、AUX_TEMP(PA5)、AUX_I(PA2)、AUX_V(PA6)、VBUS_S(PA6)
ADC1/2
编码器
M0_ENC_A/B
(PB4/5)、M0_ENC_Z(PC9)、M1_ENC_A/B(PB6/7)、M1_ENC_Z(PC15)
TIM3/TIM4

注意:VBUS_S 与 AUX_V 同为 PA6 —— mxconstants.h 是另一套(历史)命名,两者不同时包含。

其他:_Error_Handler 原型与 Error_Handler() 宏(main.h:172-174)。Inc/mxconstants.h:CubeMX 导出的等价引脚宏(M0_TEMP/AUX_I/AUX_V/GPIO_1.. 等),命名与 main.h 略有出入,当前代码以 main.h 为准。

5.3 外设句柄与 MX_* 接口

头文件
全局句柄
初始化函数
Inc/adc.hhadc1hadc2hadc3
(:64-66)
MX_ADC1_InitMX_ADC2_InitMX_ADC3_Init
(:74-76)
Inc/tim.hhtim1htim2htim3htim4htim5htim8htim13
(:64-70)
MX_TIM1..TIM13_Init
(:78-84)、HAL_TIM_MspPostInit(:86)
Inc/gpio.h
—
MX_GPIO_Init
(:68)
Inc/dma.h
—
MX_DMA_Init
(:72)
Inc/spi.hhspi3
(:64)
MX_SPI3_Init
(:72)
Inc/usart.hhuart4huart2
(:64-65)
MX_UART4_InitMX_USART2_UART_Init
(:73-74)
Inc/i2c.hhi2c1
(:64)
MX_I2C1_Init(uint8_t addr)
(:72)
Inc/can.hhcan1
(:64)
MX_CAN1_Init
(:72)——实现被注释,实际由 ODriveCAN::start_server() 配置
Inc/usb_device.hhUsbDeviceFS
(:82)
MX_USB_DEVICE_Init
(:94)
Inc/usbd_cdc_if.hUSBD_Interface_fops_FS
(:121)
—

MX_CAN1_Init 在 Src/can.c:73-105 整段被注释,只保留 hcan1 结构体初始化(can.c:59-73)。

5.4 各外设配置要点

文件
关键配置
行
adc.chadc1
:PCLK/4、12 位、规则通道 6 + 注入通道 6(= VBUS_S,软件触发)
81-113
hadc2
:规则 CH13(M1_IB)触发源 ADC_EXTERNALTRIGCONV_T8_TRGO;注入 CH10(M0_IB)
133-166
hadc3
:规则 CH12(M1_IC)触发 T8_TRGO;注入 CH11(M0_IC)
185-217
hdma_adc1
:DMA2_Stream0 / CHANNEL_0、半字、DMA_CIRCULAR
264-279
采样时间统一 ADC_SAMPLETIME_3CYCLES
101/112/153/164/205/216
tim.chtim1
:中心对齐 3、Period=TIM_1_8_PERIOD_CLOCKS、RCR=TIM_1_8_RCR、TRGO_UPDATE、PWM CH1-3 + OC CH4、死区 TIM_1_8_DEADTIME_CLOCKS
76-144
htim8
:同上(无 CH4),TRGO_UPDATE,死区 20
306-352
htim2
:中心对齐 3、Period=TIM_APB1_PERIOD_CLOCKS(4096)、PWM CH3/CH4(AUX 输出)
159-186
htim3
 / htim4:编码器模式、Period=0xffff、双通道 ICPSC_DIV1
201-252
htim5
:32 位、Period=0xFFFFFFFF、输入捕获 CH3/CH4(PWM 输入)
267-292
htim13
:Period = (2×3500×3)×(84M/168M) − 1 = 10499 → 8 kHz
365-368
gpio.cMX_GPIO_Init
:使能 GPIOC/H/A/B/D 时钟;`M0_nCS
M1_nCS 输出高、EN_GATE 输出低、M1_ENC_Z
dma.c
NVIC:DMA1_Stream0=4(SPI RX)、DMA1_Stream7=3(SPI TX,需高于 RX)、Stream2/4/5/6=10;DMA2_Stream0 被注释(改在 low_level.cpp 中动态使能)
75-98
spi.chspi3
:Master、双线、16 位、CPOL=LOW/CPHA=2EDGE、软 NSS、/16 分频、MSB 先行、CRC 关
69-79
;TX/RX DMA 120/145
usart.chuart4
/huart2:8N1、无流控、16 倍过采样;波特率行被注释,实际由 board_init() 用 odrv.config_.uart_*_baudrate 写入
72-97
i2c.cMX_I2C1_Init(addr)
:100 kHz、7 位地址、OwnAddress1 = addr << 1
65-76
can.chcan1
:Prescaler=8、SJW_4TQ、BS1_16TQ、BS2_4TQ、AutoBusOff/AutoWakeUp/AutoRetransmission=ENABLE
59-73

CAN 波特率推导:PCLK1 = 42 MHz,位时间 = 1(sync)+16(BS1)+4(BS2) = 21 TQ,故 CAN_FREQ = 42e6/21 = 2 MHz 是「每个 prescaler 计数对应的波特率基数」,prescaler = CAN_FREQ / baud_rate(communication/can/odrive_can.cpp:31)。默认 baud_rate = CAN_BAUD_250K(communication/can/odrive_can.hpp:25),即 prescaler = 8,位速率 = 42e6/(8×21) = 250 kbit/s,与 can.c 静态值一致。

5.5 Src/stm32f4xx_it.c 中断向量

ISR
行
所属
DMA1_Stream0_IRQHandler
219
SPI3 RX
DMA1_Stream2_IRQHandler
233
DMA1_Stream4_IRQHandler
247
DMA1_Stream5_IRQHandler
261
DMA1_Stream6_IRQHandler
275
DMA1_Stream7_IRQHandler
289
SPI3 TX
CAN1_TX_IRQHandler
303
CAN1_RX0_IRQHandler
317
CAN1_RX1_IRQHandler
331
CAN1_SCE_IRQHandler
345
USART2_IRQHandler
359
UART B
TIM8_TRG_COM_TIM14_IRQHandler
373
HAL 时间基准
SPI3_IRQHandler
388
UART4_IRQHandler
402
UART A

不在本文件的中断:OTG_HS_IRQn(控制环)、TIM8_UP_TIM13_IRQn、TIM5_IRQn、I2C1_EV/ER、OTG_FS_IRQn、EXTI* —— 它们全部在 board.cpp:453-593 或 Drivers/STM32/stm32_gpio.cpp 中实现。

5.6 Inc/FreeRTOSConfig.h

宏
行
值
configUSE_PREEMPTION
95
1
configSUPPORT_STATIC/DYNAMIC_ALLOCATION
96-97
0
 / 1
configUSE_IDLE_HOOK
98
1
configCPU_CLOCK_HZ
100
SystemCoreClock
configTICK_RATE_HZ
101
1000
configMAX_PRIORITIES
102
7
configMINIMAL_STACK_SIZE
103
128
(字)
configTOTAL_HEAP_SIZE
104
65536
(字节,ucHeap 放在 CCMRAM)
configUSE_MUTEXES
107
1
configCHECK_FOR_STACK_OVERFLOW
109
1
configUSE_PORT_OPTIMISED_TASK_SELECTION
110
1
configLIBRARY_LOWEST_INTERRUPT_PRIORITY
138
15
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
144
5
configASSERT(x)
156
关中断 + 死循环
configAPPLICATION_ALLOCATED_HEAP
170
1
(堆由应用侧 freertos.c/main.cpp 提供)

Src/freertos.c:

符号
行
StartDefaultTask
 原型
87
MX_FREERTOS_Init
125
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, stack_size_default_task / sizeof(StackType_t))
143
StartDefaultTask()
(调 MX_USB_DEVICE_Init() 后空转,实际启动流程在 main.cpp 的 rtos_main 中接管)
162
__weak vApplicationIdleHook()
97
__weak vApplicationStackOverflowHook()
112

5.7 USB 栈

文件
关键接口 / 内容
行
Src/usb_device.cMX_USB_DEVICE_Init()
:USBD_Init → USBD_RegisterClass(&USBD_CDC) → USBD_CDC_RegisterInterface(&USBD_Interface_fops_FS) → USBD_Start
93-106
Src/usbd_cdc_if.cUSBD_Interface_fops_FS
 = {CDC_Init_FS, CDC_DeInit_FS, CDC_Control_FS, CDC_Receive_FS}
156-162
CDC_Init_FS()
 / CDC_DeInit_FS() → osMessagePut(usb_event_queue, 1/2, 0)
169
 / 182
CDC_Control_FS(cmd, pbuf, len)
(SET_LINE_CODING 等)
197
CDC_Receive_FS(Buf, Len, endpoint_pair)
 → 唤醒 USB 线程
283
CDC_Transmit_FS(Buf, Len, endpoint_pair)
 → 对外发送接口
303
Inc/usbd_cdc_if.hUSB_RX_DATA_SIZE
 / USB_TX_DATA_SIZE = 64
81-83
Src/usbd_desc.cUSBD_VID = 0x1209
、USBD_PID_FS = 0x0D32、USBD_LANGID_STRING = 1033
94-97
USBD_MANUFACTURER_STRING = "ODrive Robotics"96
USBD_PRODUCT_STRING_FS
 = "ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR CDC Interface"
100
NATIVE_STRING
 = "... Native Interface"(fibre 端点用)
101

相关学习资料