Files
Motor_v2/Robot_V2/device/motor.cpp
T
cbb 86ca7666fa feat(motor): 引入运行禁止机制重构电机控制安全系统
- 添加 run_inhibit.h/cpp 模块作为全局运行闸门,统一管理所有停机/唤醒逻辑
- 移除 FOC 控制器中的 5 个相互引用的开关机函数(shut_down/resume 等)
- 将 nFAULT 硬件故障检测从仅上报改为运行门控制,连续采样故障时申请禁止位
- 改进电压检测逻辑,欠压判定现在通过 run_inhibit 统一执行停机动作
- 修正 PB2(nFAULT) 引脚配置为内部上拉输入,避免浮空误读导致系统锁死
- 在 CAN 状态帧中新增 RunInhibit 位图(字节 36-39),实时反映禁止运行的故障源
- 电流采样失效、过热故障现在通过禁止位管理而非直接操作驱动状态
- 校准函数增加运行许可检查,防止在停机状态下意外唤醒驱动
2026-10-13 00:47:52 +08:00

264 lines
10 KiB
C++
Raw 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.
/**
* @file device/motor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 功率级门驱动芯片抽象层:
* - 老板 DRV8313(集成 FET 三半桥,无 SPI):保持原使能时序
* - 新板 DRV8323RS(外置 MOS):使能前经 SPI 配置 3x PWM 模式、
* CSA 增益、VDS 过流门限、死区与 IDRIVE,并逐条回读校验
*/
#include "motor.h"
#include "Timer.h"
#include "config.h"
#include "ang_sensor.h"
#include "gd32c10x_eval.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_spi.h"
#include "systick.h"
#include <cstdio>
/* 角度传感器 SPI 初始化(ang_sensor.cpp):DRV 配置完成后恢复其 SPI 模式 */
extern void KTH7823_spi_init(void);
/**
* @brief 门驱动芯片引脚预初始化
* @note 新板:DRVSLP(PB12) 置低保持 DRV8323 睡眠、片选 PB14 释放、
* PB2(nFAULT) 内部上拉输入。必须在任何可能拉高使能脚的代码之前调用;
* 老板:无操作(原时序在 timer1_gpio_config 中保持不变)。
*/
void gate_driver_preinit(void) {
#if GATE_DRIVER_DRV8323
rcu_periph_clock_enable(RCU_GPIOB);
/* 使能脚推挽输出并保持低:芯片睡眠,栅极全 Hi-Z,寄存器默认值不起作用 */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_bit_reset(GPIOB, GPIO_PIN_12);
/* SPI 片选默认释放(SCLK 空闲低时才允许 CS 翻转) */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_bit_set(GPIOB, GPIO_PIN_14);
/* nFAULT 低有效,开漏输出 → 内部上拉,避免浮空误读成故障 */
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
#endif
}
#if GATE_DRIVER_DRV8323
/* ---------------- DRV8323 SPI ----------------
* 帧格式:[15:11] 寄存器地址 [10] 1读0写 [9:0] 数据。
* 时序:SCLK 空闲低、SDI 下降沿采样(SPI 模式1),与角度传感器
* (模式3)不同:配置前重初始化 SPI0,配置完调 KTH7823_spi_init() 恢复。
* 寄存器/位定义来源:TI SLVSDJ3D(DRV832x datasheet)Register Maps。 */
#define DRV_CS_ENABLE() gpio_bit_reset(GPIOB, GPIO_PIN_14)
#define DRV_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
#define DRV8323_REG_FAULT1 0x00u /* FAULT/VDS_OCP/GDF/UVLO/OTSD/VDS_Hx,VDS_Lx */
#define DRV8323_REG_VGS2 0x01u /* SA/SB/SC_OC、OTW、CPUV、VGS_Hx,VGS_Lx */
#define DRV8323_REG_DRVCTRL 0x02u /* [6:5]PWM_MODE [2]COAST [1]BRAKE [0]CLR_FLT */
#define DRV8323_REG_GDRV_HS 0x03u /* [10]LOCK [9:6]IDRIVEP_HS [5:2]IDRIVEN_HS */
#define DRV8323_REG_GDRV_LS 0x04u /* [10]CBC [9:8]TDRIVE [7:4]IDRIVEP_LS [3:0]IDRIVEN_LS */
#define DRV8323_REG_OCPCTRL 0x05u /* [10]TRETRY [9:8]DEAD_TIME [7:6]OCP_MODE [5:4]OCP_DEG [3:0]VDS_LVL */
#define DRV8323_REG_CSACTRL 0x06u /* [10]CSA_FET [9]VREF_DIV [8]LS_REF [7:6]CSA_GAIN [1:0]SEN_LVL */
/* PWM_MODE=01b:3x PWM。INHx 低→下管开、高→上管开,死区芯片内部插入。
芯片复位默认 00b(6x PWM),6x 下 INLx 语义不同,本板 INLx=DRVSLP 常高,
必须切到 3x,否则上管永远开不了(电流全走体二极管)。 */
#define DRV8323_PWM_MODE_3X (1u << 5)
/* 栅驱动电流:源 170mA/灌 190mA(WSD6035DN33 Qg≈12.6nC,边沿约 75ns,
* 无栅电阻布局下避免过快边沿加重振铃) */
#define DRV8323_IDRIVE_HS ((0x6u << 6) | (0x7u << 2))
#define DRV8323_IDRIVE_HS_MASK ((0xFu << 6) | (0xFu << 2))
#define DRV8323_IDRIVE_LS ((0x6u << 4) | (0x7u << 0))
#define DRV8323_IDRIVE_LS_MASK ((0xFu << 4) | (0xFu << 0))
/* 死区 200ns(20kHz 大 Qg 管的安全值);OCP_MODE=10b 仅上报不动作:
* VDS 门限受 RDSon 温漂影响大(热态 20→30mΩ),先靠软件限流+热模型,
* 避免误跳闸;VDS_LVL=0.75V(默认),故障位可经 drv_fault_status() 读取 */
#define DRV8323_OCP_CFG ((0x2u << 8) | (0x2u << 6) | (0x9u << 0))
#define DRV8323_OCP_CFG_MASK ((0x3u << 8) | (0x3u << 6) | (0xFu << 0))
/** @brief CSA 增益值(V/V) → 0x06[7:6] 编码(5:00b 10:01b 20:10b 40:11b) */
constexpr u16 drv8323_gain_code(float g) {
return (g <= 5.0f) ? 0x0u : (g <= 10.0f) ? 0x1u : (g <= 20.0f) ? 0x2u : 0x3u;
}
#define DRV8323_CSA_GAIN_BITS (drv8323_gain_code(DRV8323_CSA_GAIN_VV) << 6)
/**
* @brief DRV8323 单帧 SPI 收发(片选内 16bit,帧间隔由调用节奏保证)
* @param[in] frame 完整 16bit 帧
* @return u16 从机返回的 10bit 数据
*/
static u16 drv8323_xfer(u16 frame) {
u16 rx = 0;
DRV_CS_ENABLE();
spi_rw_half_word_polling(SPI0, frame, &rx);
DRV_CS_DISABLE();
return rx & 0x03FFu;
}
/**
* @brief 读改写一个寄存器并回读校验
* @param[in] addr 寄存器地址
* @param[in] clrmask 要清零的位
* @param[in] setmask 要置一的位
* @return u8 0=回读一致 1=校验失败
*/
static u8 drv8323_write_verify(u8 addr, u16 clrmask, u16 setmask) {
u16 cur = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
u16 nv = (u16)((cur & ~clrmask) | setmask);
drv8323_xfer((u16)(((u16)addr << 11) | nv));
u16 rb = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
return (rb == nv) ? 0u : 1u;
}
#endif /* GATE_DRIVER_DRV8323 */
/**
* @brief 门驱动芯片上电配置
* @return u8 0=成功;非 0 按位指示回读校验失败的寄存器
* (bit0:DRVCTRL bit1:GDRV_HS bit2:GDRV_LS bit3:OCPCTRL bit4:CSACTRL)
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后。拉高 DRVSLP 唤醒
* 芯片的瞬间 INHx=0、INLx=1,默认 6x 模式下为"三相下管导通",
* 静止电机零电流,与 3x 模式 0% 占空比稳态一致,随后写模式无扰动。
*/
u8 gate_driver_bringup(void) {
#if GATE_DRIVER_DRV8323
/* 1. 唤醒芯片:DRVSLP 网络 200nF + 芯片内部复位,留足建立时间 */
drv_enable(1);
delay_1ms(10);
/* 2. SPI0 切到 DRV8323 时序(模式1:CPOL=0/下降沿采样,2MHz) */
spi_parameter_struct spi_init_str;
spi_struct_para_init(&spi_init_str);
spi_init_str.device_mode = SPI_MASTER;
spi_init_str.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_str.frame_size = SPI_FRAMESIZE_16BIT;
spi_init_str.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
spi_init_str.nss = SPI_NSS_SOFT;
spi_init_str.endian = SPI_ENDIAN_MSB;
spi_init_str.prescale = 60; /* 120MHz/60 = 2MHz */
spi_init(SPI0, &spi_init_str);
spi_nss_output_enable(SPI0);
spi_nssp_mode_enable(SPI0);
spi_enable(SPI0);
/* 3. 配置并逐条回读校验(读改写,保留未触碰位的出厂默认:
* VREF_DIV=1→SOx 中点 VREF/2,与 CURRENT_OFFSET 的 1.655V 一致;
* CSA_FET=0→正端 SPx,与 shunt 的 SP/SN 接法一致) */
u8 err = 0;
err |= (u8)(drv8323_write_verify(DRV8323_REG_DRVCTRL, 0x0000u,
DRV8323_PWM_MODE_3X)
<< 0);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_HS,
DRV8323_IDRIVE_HS_MASK, DRV8323_IDRIVE_HS)
<< 1);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_LS,
DRV8323_IDRIVE_LS_MASK, DRV8323_IDRIVE_LS)
<< 2);
err |= (u8)(drv8323_write_verify(DRV8323_REG_OCPCTRL, DRV8323_OCP_CFG_MASK,
DRV8323_OCP_CFG)
<< 3);
err |= (u8)(drv8323_write_verify(DRV8323_REG_CSACTRL, 0x3u << 6,
DRV8323_CSA_GAIN_BITS)
<< 4);
/* 4. CLR_FLT 脉冲清一次上电故障锁存,再恢复正常值 */
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X | 0x1u));
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X));
/* 5. 恢复角度传感器的 SPI 模式(模式3) */
KTH7823_spi_init();
return err;
#else
return 0; /* 老板 DRV8313 无 SPI,原使能时序保持不变 */
#endif
}
/**
* @brief 门驱动运行时使能/睡眠
* @note 新板:PB12 同时是 DRV8323 的 ENABLE 与 INLA/B/C 电平——
* 高=正常运转,低=芯片睡眠三相 Hi-Z(惯性滑行,兼作 MCU 复位
* 时的天然 fail-safe);老板:PB12+PC13 双脚,保持原行为。
*/
void drv_enable(u8 en) {
if (en) {
gpio_bit_set(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
} else {
gpio_bit_reset(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
}
}
/**
* @brief 读取 nFAULT 引脚状态(PB2,低有效)
* @return u8 1=故障有效 0=正常
*/
u8 drv_nfault_active(void) {
return (gpio_input_bit_get(GPIOB, GPIO_PIN_2) == RESET) ? 1u : 0u;
}
/**
* @brief 读取门驱动故障状态寄存器
* @return u32 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
*/
u32 drv_fault_status(void) {
#if GATE_DRIVER_DRV8323
u32 f1 = drv8323_xfer((u16)((DRV8323_REG_FAULT1 << 11) | (1u << 10)));
u32 f2 = drv8323_xfer((u16)((DRV8323_REG_VGS2 << 11) | (1u << 10)));
return f1 | (f2 << 10);
#else
return 0;
#endif
}
/**
* @brief driver输出初始化
*/
void motor_init(void) {
timer1_gpio_config();
timer1_pwm_config();
// 使能GPIOB时钟
rcu_periph_clock_enable(RCU_GPIOB);
// 配置PB2为输入引脚,用于读取电机状态(nFAULT)。
// 用内部上拉(GPIO_MODE_IPU)而不是浮空:nFAULT 是开漏输出,无故障时靠
// 上拉保持高。现在 PB2 电平直接决定"允不允许运行",浮空输入在接线未确认
// 或无上拉时会随机读低,把整机锁死;内部上拉(~40kΩ)与外部上拉并联无害。
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
/* 配置GPIOB的PB14为推挽输出 SPICS */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_14 | GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
// 使能GPIOC时钟,用于电机驱动使能引脚
rcu_periph_clock_enable(RCU_MOTOR_EN_GPIO);
// 配置PC13为推挽输出,用于控制电机驱动器使能(老板专用)
gpio_init(MOTOR_EN_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, MOTOR_EN_GPIO_PIN);
#endif
}
/**
* @brief 电机复位
*/
void motor_reset(void) {
motor_sleep();
#if FOC_DEBUG
printf("motor reseting...\r\n");
#endif
motor_resume();
}
/**
* @brief 电机休眠
*/
void motor_sleep(void) {
drv_enable(0);
}
/**
* @brief 电机启动
*/
void motor_resume(void) {
drv_enable(1);
}