Files

264 lines
10 KiB
C++
Raw Permalink Normal View History

/**
* @file device/motor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 功率级门驱动芯片抽象层:
* - 老板 DRV8313(集成 FET 三半桥,无 SPI):保持原使能时序
* - 新板 DRV8323RS(外置 MOS):使能前经 SPI 配置 3x PWM 模式、
* CSA 增益、VDS 过流门限、死区与 IDRIVE,并逐条回读校验
*/
2025-12-08 02:48:59 +08:00
#include "motor.h"
#include "Timer.h"
#include "config.h"
#include "ang_sensor.h"
2025-12-08 02:48:59 +08:00
#include "gd32c10x_eval.h"
2026-01-24 01:50:39 +08:00
#include "gd32c10x_gpio.h"
#include "gd32c10x_spi.h"
#include "systick.h"
2025-12-08 02:48:59 +08:00
#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
}
2025-12-08 02:48:59 +08:00
/**
* @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);
2025-12-08 02:48:59 +08:00
/* 配置GPIOB的PB14为推挽输出 SPICS */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_14 | GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
2026-01-24 01:50:39 +08:00
// 使能GPIOC时钟,用于电机驱动使能引脚
rcu_periph_clock_enable(RCU_MOTOR_EN_GPIO);
// 配置PC13为推挽输出,用于控制电机驱动器使能(老板专用)
2026-01-24 01:50:39 +08:00
gpio_init(MOTOR_EN_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, MOTOR_EN_GPIO_PIN);
#endif
2025-12-08 02:48:59 +08:00
}
/**
* @brief 电机复位
*/
void motor_reset(void) {
motor_sleep();
#if FOC_DEBUG
2025-12-08 02:48:59 +08:00
printf("motor reseting...\r\n");
#endif
2025-12-08 02:48:59 +08:00
motor_resume();
}
/**
* @brief 电机休眠
*/
void motor_sleep(void) {
drv_enable(0);
2026-01-24 01:50:39 +08:00
}
2025-12-08 02:48:59 +08:00
/**
* @brief 电机启动
*/
void motor_resume(void) {
drv_enable(1);
}