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