/** * @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 /* 角度传感器 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); }