feat(adc): 添加三相电流零点配平并扩展初始化错误上报
- ADC: 新增 zero_calibrate() 函数,在上电后采集 200 帧计算各通道零点偏差 - ADC: 修改 current_adc_to_ma() 支持逐通道扣除配平后的零点偏移 - ADC: 调整电流采样引脚为模拟输入模式并优化采样时间至 13.5 周期 - CAN: STATUS 帧字节 32~35 新增 InitStatus 字段,反馈初始化错误位图 - main: 细化初始化错误检测,分别标记角度/ADC/CAN/Kalman/FOC/门驱动故障 - config.h: 新增 INIT_ERR_* 错误码定义及 CURRENT_ZERO_CAL_LIMIT 阈值
This commit is contained in:
+111
-18
@@ -13,6 +13,7 @@
|
||||
#include "math.h"
|
||||
#include "systick.h"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// ADC 换算常量均为编译期常量,非可变全局状态。
|
||||
@@ -31,6 +32,15 @@ constexpr float VCC_FACTOR = 11.0f;
|
||||
* 修改 config.h 的 DRV8323_CSA_GAIN_VV 后此系数自动跟随。 */
|
||||
constexpr float CURRENT_FACTOR =
|
||||
1000.0f / (DRV8323_CSA_GAIN_VV * DRV8323_SHUNT_OHM);
|
||||
#elif CURRENT_SENSE_INA240
|
||||
/** @brief CURRENT_FACTOR 常量定义(低边采样板:下桥臂 10mΩ + INA240A2)
|
||||
* 电流链路单位为安培:get_current() 返回 A,cal_Iq_Id/Can 上报处 ×1000 转 mA。
|
||||
* OUT = 1.65V ± I×R×G(REF1=AGND、REF2=A3V3,芯片内部分压取中点 1.65V),
|
||||
* G=50V/V、R=10mΩ → 0.5V/A → 2.0 A/V,ADC 3.3V 满量程对应约 ±3.3A。
|
||||
* 与老板外置放大链(50倍×10mΩ)标定数值恰好一致;换增益/阻值时此系数
|
||||
* 随 config.h 的 INA240_CSA_GAIN_VV / INA240_SHUNT_OHM 自动跟随。 */
|
||||
constexpr float CURRENT_FACTOR =
|
||||
1.0f / (INA240_CSA_GAIN_VV * INA240_SHUNT_OHM);
|
||||
#else
|
||||
/** @brief CURRENT_FACTOR 常量定义(老板:外置放大链,保持原标定) */
|
||||
constexpr float CURRENT_FACTOR =
|
||||
@@ -52,13 +62,23 @@ volatile u16 adc_mcu_temp_raw = 0; // ADC0 injected rank3: MCU温度原始值
|
||||
static volatile u16 adc_current_value[3] = {0}; // injected 同步采样: U / V / W
|
||||
static volatile u8 adc_current_frame_ready = 0;
|
||||
|
||||
// ---- 三相电流零点配平结果(上电一次,hardware_init 在 gate_driver_bringup
|
||||
// 之后调用 zero_calibrate 采集)。含义:各通道实测零点相对 CURRENT_OFFSET
|
||||
// 基准的偏差(V),采样换算时逐通道扣除,吸收 REF 中点偏差、AGND/PGND 地差
|
||||
// 与 INA 失调。初始 0 = 未配平(按标称零点换算)。
|
||||
static float adc_zero_offset_v[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
/**
|
||||
* @brief 将电流 ADC 原始值换算为毫安值
|
||||
* @param[in] raw_adc 参数
|
||||
* @brief 将电流 ADC 原始值换算为电流值
|
||||
* @param[in] raw_adc 注入组转换原始值
|
||||
* @param[in] ch 通道号 0/1/2 = U/V/W(逐通道扣除上电配平零点偏差)
|
||||
* @return float 返回值
|
||||
* @note 函数名沿用历史(_to_ma),实际返回单位是安培:MIN/MAX_CURRENT(±40)
|
||||
* 为安培阈值,cal_Iq_Id / CAN 上报处再 ×1000 转 mA。
|
||||
*/
|
||||
static inline float current_adc_to_ma(u16 raw_adc) {
|
||||
return (raw_adc * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
|
||||
static inline float current_adc_to_ma(u16 raw_adc, u8 ch) {
|
||||
return (raw_adc * ADC_SCALE + CURRENT_OFFSET - adc_zero_offset_v[ch]) *
|
||||
CURRENT_FACTOR;
|
||||
}
|
||||
|
||||
// ADC1 连续转换运行标志:adc1_config() 启动后置位,
|
||||
@@ -187,6 +207,15 @@ u16 adc1_read_raw(u8 channel) {
|
||||
采样电容残余污染注入组首通道。移除 regular 后注入组再无打断源。
|
||||
*/
|
||||
void adc_config(void) {
|
||||
/* 电流采样引脚配模拟输入:U/V/W = PA3/PA4/PA5(ADC0_IN3/4/5)。
|
||||
* 手册要求 ADC 引脚必须配 AIN——复位默认浮空输入态下数字输入缓冲器
|
||||
* 挂在引脚上,静态 1.65V 中点正处于线性区,开关噪声环境下会向引脚
|
||||
* 注入电荷、污染 13.5 周期短采样窗口(v1 外置放大链下凑合未暴露)。
|
||||
* 顺手关掉该三脚的数字输入缓冲(低功耗&抗扰)。 */
|
||||
rcu_periph_clock_enable(RCU_GPIOA);
|
||||
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_2MHZ,
|
||||
GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
|
||||
|
||||
/* enable ADC clock */
|
||||
rcu_periph_clock_enable(RCU_ADC0);
|
||||
/* config ADC clock(ADC0/ADC1 共用分频:120MHz/4 = 30MHz) */
|
||||
@@ -204,17 +233,21 @@ void adc_config(void) {
|
||||
adc_tempsensor_vrefint_enable();
|
||||
|
||||
/* injected 组:rank0=U, rank1=V, rank2=W, rank3=MCU温度,TIMER1 TRGO 触发。
|
||||
* 温度传感器(ch16)为高内阻源,数据手册要求采样时间≥17.1μs,保持最大档
|
||||
* 239.5 周期,且挂在序列尾部:整段转换不会被打断,每 PWM 周期同步采一次,
|
||||
* 40kHz 过采样 + 中值滤波后温度比旧架构更稳。
|
||||
* 原先的 rank0 dummy 已删除:dummy 用于吸收"注入打断 regular"产生的采样
|
||||
* 电容残余电荷,现 regular 已移除,序列空闲期电容挂在温度通道(~1.3V),
|
||||
* U 相(ch3 运放低阻输出) 的 55.5 周期采样足以完成重建。
|
||||
* 序列总时长 68×3+252 = 456 周期 = 15.2μs < 25μs 触发周期。 */
|
||||
* 电流通道用最短 13.5 周期采样(INA240 低阻输出足以驱动采样电容):
|
||||
* 每通道 26 周期,U/V/W 的采样保持窗末端仅滞后触发点 0.45/1.32/2.18µs
|
||||
* (54/158/262 个计数),三相样本整体压进下管导通窗头部,最大占空比
|
||||
* ~90% 时仍全部有效;温度通道 ch16 高内阻按手册仍需 239.5 周期,挂尾部。
|
||||
* 整段 26×3+252 = 330 周期 = 11µs。
|
||||
* 触发点紧贴计数峰顶(见 config.h):中心对齐下 CH3 每周期匹配两次
|
||||
* (上升沿 2989 / 下降沿 3011,仅差 22 计数),无论第二次触发是被 ADC
|
||||
* 忽略(注入忙)还是重启序列,帧的等效采样位置都贴着峰顶——三相下管
|
||||
* 同时导通窗的正中,帧率 20kHz 与控制环同频。
|
||||
* 序列空闲期采样电容挂在温度通道(~1.3V),U 相(ch3 运放低阻输出)的
|
||||
* 13.5 周期保持窗足以完成重建。 */
|
||||
adc_channel_length_config(ADC0, ADC_INSERTED_CHANNEL, 4);
|
||||
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
|
||||
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
|
||||
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
|
||||
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_3, ADC_SAMPLETIME_13POINT5);
|
||||
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_13POINT5);
|
||||
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_5, ADC_SAMPLETIME_13POINT5);
|
||||
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_16,
|
||||
ADC_SAMPLETIME_239POINT5);
|
||||
adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL,
|
||||
@@ -239,7 +272,7 @@ u8 sampler::init() {
|
||||
delay_1ms(1);
|
||||
|
||||
const float current_init = current_adc_to_ma(
|
||||
static_cast<u16>((-CURRENT_OFFSET / ADC_SCALE) + 0.5f));
|
||||
static_cast<u16>((-CURRENT_OFFSET / ADC_SCALE) + 0.5f), 0); // 配平前按标称零点
|
||||
// 初始采样
|
||||
for (int i = 0; i < ADC_FILTER_SIZE; i++) {
|
||||
mcu_temp_samples[i] =
|
||||
@@ -259,6 +292,66 @@ u8 sampler::init() {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// ---- 三相电流零点配平(上电一次,hardware_init 在 gate_driver_bringup 之后调用) ----
|
||||
// 必须在门驱动唤醒后进行:DRV8323 板唤醒后 INLx=DRVSLP 常高,0% 占空比下
|
||||
// 三相下管导通、采样电阻经下管接地,INA240 输入有确定共模(0V),输出即
|
||||
// 真实零点;若在芯片睡眠 Hi-Z 时采样,下管源极悬空,INA240 IN+ 浮空会把
|
||||
// 输出拉到轨,配平值无效(老板下管常通,两种时序均有效,但统一放后面)。
|
||||
// 采集 N 帧均值作为每通道零点偏差(V,相对 CURRENT_OFFSET 基准),折算
|
||||
// 电流超过 CURRENT_ZERO_CAL_LIMIT 视为电流检测初始化错误。
|
||||
|
||||
/**
|
||||
* @brief 三相电流零点配平并校验
|
||||
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
|
||||
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
|
||||
* @note 阻塞约 15ms(跳过 5ms 上电瞬态 + 采集 200 帧 @20kHz),仅限
|
||||
* hardware_init 上下文调用(看门狗在其后才初始化,无喂狗需求)。
|
||||
* main 循环尚未启动,期间 EOIC 中断只刷新 adc_current_value,
|
||||
* 不会与 update_current 并发写环形缓冲。
|
||||
*/
|
||||
u8 sampler::zero_calibrate(void) {
|
||||
delay_1ms(5); // 跳过上电瞬态(INA 建立、驱动使能稳定),≈100 帧
|
||||
|
||||
u32 zero_acc[3] = {0, 0, 0};
|
||||
const u16 ZERO_CAL_FRAMES = 200;
|
||||
for (u16 i = 0; i < ZERO_CAL_FRAMES; i++) {
|
||||
u32 timeout = 0;
|
||||
while (adc_current_frame_ready == 0U) {
|
||||
/* 帧周期 50µs,超时覆盖 ~30ms 无帧:ADC/触发链路异常 */
|
||||
if (++timeout > 600000U) {
|
||||
printf("[ADC] zero cal fail: no sample frame\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
zero_acc[0] += adc_current_value[0];
|
||||
zero_acc[1] += adc_current_value[1];
|
||||
zero_acc[2] += adc_current_value[2];
|
||||
adc_current_frame_ready = 0U;
|
||||
}
|
||||
|
||||
u8 result = 0;
|
||||
printf("[ADC] zero cal:");
|
||||
for (u8 ch = 0; ch < 3U; ch++) {
|
||||
/* 实测零点偏差(V)= 样本均值电压 + CURRENT_OFFSET 基准 */
|
||||
adc_zero_offset_v[ch] =
|
||||
((float)zero_acc[ch] / (float)ZERO_CAL_FRAMES) * ADC_SCALE +
|
||||
CURRENT_OFFSET;
|
||||
/* 配平值折算电流(A)超限 → 电流检测初始化错误 */
|
||||
float offset_a = adc_zero_offset_v[ch] * CURRENT_FACTOR;
|
||||
printf(" %c=%dmA", "UVW"[ch], (int)(offset_a * 1000.0f));
|
||||
if (offset_a > CURRENT_ZERO_CAL_LIMIT ||
|
||||
offset_a < -CURRENT_ZERO_CAL_LIMIT) {
|
||||
result = 2;
|
||||
}
|
||||
}
|
||||
if (result == 2) {
|
||||
printf(" -> over limit, current sense init error\r\n");
|
||||
} else {
|
||||
printf(" ok\r\n");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取MCU温度
|
||||
* @return float
|
||||
@@ -377,9 +470,9 @@ void sampler::update_current() {
|
||||
}
|
||||
|
||||
const u8 idx = current_sample_head;
|
||||
currU_samples[idx] = current_adc_to_ma(adc_current_value[0]);
|
||||
currV_samples[idx] = current_adc_to_ma(adc_current_value[1]);
|
||||
currW_samples[idx] = current_adc_to_ma(adc_current_value[2]);
|
||||
currU_samples[idx] = current_adc_to_ma(adc_current_value[0], 0);
|
||||
currV_samples[idx] = current_adc_to_ma(adc_current_value[1], 1);
|
||||
currW_samples[idx] = current_adc_to_ma(adc_current_value[2], 2);
|
||||
|
||||
current_sample_head++;
|
||||
if (current_sample_head >= ADC_FILTER_SIZE) {
|
||||
|
||||
+16
-3
@@ -783,8 +783,10 @@ u8 CAN_transmiter::CANFD_all_Send() {
|
||||
|
||||
u32 success_flag = 0;
|
||||
|
||||
// 打包 status 数据 - 只清零实际使用的 32 字节
|
||||
memset(transmit_data, 0, 32);
|
||||
// 打包 status 数据 - 只清零实际使用的 48 字节
|
||||
// v0.74:字节 32~35 新增 InitStatus(初始化错误码位图,0=全部正常,
|
||||
// 定义见 config.h INIT_ERR_*,含电流检测配平超限/无采样帧),36~47 预留
|
||||
memset(transmit_data, 0, 48);
|
||||
pack_u16_to_bytes(transmit_data, 0, mcu_temp);
|
||||
pack_u16_to_bytes(transmit_data, 2, motor_temp);
|
||||
pack_current_s16_to_bytes(transmit_data, 4, u_current_union.f);
|
||||
@@ -802,9 +804,10 @@ u8 CAN_transmiter::CANFD_all_Send() {
|
||||
transmit_data[29] = test_u16 & 0xFF;
|
||||
transmit_data[30] = (test_u16 >> 8) & 0xFF;
|
||||
transmit_data[31] = cpu_usage;
|
||||
pack_u32_to_bytes(transmit_data, 32, (u32)init_error_flags);
|
||||
// 使用节点 ID 构建发送 ID (高 16 位为节点 ID)
|
||||
success_flag +=
|
||||
CANFD_AsyncSend(build_can_id(CANFD_STATUS_ID), 32, transmit_data);
|
||||
CANFD_AsyncSend(build_can_id(CANFD_STATUS_ID), 48, transmit_data);
|
||||
|
||||
// 打包 config 数据 - 只清零实际使用的 24 字节
|
||||
memset(transmit_data, 0, 24);
|
||||
@@ -972,6 +975,16 @@ void CAN_transmiter::handle_node_message(can_receive_message_struct ¤t_mes
|
||||
|
||||
void CAN_transmiter::handle_control_command(can_receive_message_struct ¤t_message) {
|
||||
// 控制帧布局:模式、电压限制、目标电流/速度/角度、三组 P 参数。
|
||||
/* 收到非零控制模式即视为驱动意图:v2 板 EN 经 R17 10k 下拉、DRV8313
|
||||
* 上电默认睡眠(上电配平后也保持睡眠态),控制路径里没有其他唤醒点,
|
||||
* 不在此唤醒则所有闭环控制都落在 Hi-Z 桥臂上永远无效。
|
||||
* 模式 0(drv_hold 主动锁止,A 相恒 HOLD_PWM_DUTY)不唤醒:上位机
|
||||
* 上电先发模式 0 当"静止待机"时不至于意外通电锁止发热;驱动已在
|
||||
* 唤醒态时模式 0 仍是锁止。幂等;关机态(过流/过压/过热)不唤醒,
|
||||
* 恢复仍由 power_manage / resume_from_thermal 统一管理。 */
|
||||
if (current_message.rx_data[0] != 0U && !FOC.is_shut_down) {
|
||||
motor_resume();
|
||||
}
|
||||
FOC.control_mode = current_message.rx_data[0];
|
||||
FOC.limit_voltage = float(unpack_bytes_to_u16(current_message.rx_data, 1)) / 1000;
|
||||
target_current_union.f = unpack_bytes_to_float(current_message.rx_data, 3).f;
|
||||
|
||||
@@ -38,6 +38,18 @@ static inline void pack_u16_to_bytes(u8 *buffer, u16 offset, u16 value) {
|
||||
buffer[offset + 1] = (u8)((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pack_u32_to_bytes 函数(小端)
|
||||
* @param[in] buffer 参数
|
||||
* @param[in] offset 参数
|
||||
* @param[in] value 参数
|
||||
* @return void
|
||||
*/
|
||||
static inline void pack_u32_to_bytes(u8 *buffer, u16 offset, u32 value) {
|
||||
pack_u16_to_bytes(buffer, offset, (u16)(value & 0xFFFF));
|
||||
pack_u16_to_bytes(buffer, offset + 2, (u16)((value >> 16) & 0xFFFF));
|
||||
}
|
||||
|
||||
// 将 mA 电流值饱和转换为 int16 后按小端打包。
|
||||
// DBC 中 U/V/W current 定义为 signed int16([-32768,32767] mA);
|
||||
// 相电流有正有负,此前用 u16() 打包负值被削成 0,上位机波形下半周丢失。
|
||||
|
||||
@@ -266,7 +266,8 @@ void timer1_pwm_config(void) {
|
||||
TIMER1_DEFAULT_PULSE);
|
||||
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_2,
|
||||
TIMER1_DEFAULT_PULSE);
|
||||
// 设置 CH3 为固定 ADC 采样点,避免与三相 PWM duty 耦合
|
||||
// 设置 CH3 为固定 ADC 采样点(低边采样:必须落在三相下管同时导通窗,
|
||||
// 即中心对齐计数的峰顶附近,见 config.h TIMER1_CH3_COMPARE_VALUE 说明)
|
||||
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_3,
|
||||
TIMER1_CH3_COMPARE_VALUE);
|
||||
|
||||
|
||||
@@ -40,6 +40,12 @@ class sampler {
|
||||
* @return u8 返回值
|
||||
*/
|
||||
u8 init(void);
|
||||
/**
|
||||
* @brief 三相电流零点配平并校验(hardware_init 在 FOC.init 之后调用)
|
||||
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
|
||||
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
|
||||
*/
|
||||
u8 zero_calibrate(void);
|
||||
/**
|
||||
* @brief 更新 mcu_temp 相关状态
|
||||
* @return void
|
||||
|
||||
+55
-11
@@ -41,6 +41,9 @@ TaskScheduler scheduler = {0};
|
||||
u32 motor_runtime_50us = 0;
|
||||
// 负载检测/通信心跳计数:主循环递增,CAN 周期发送任务清零。
|
||||
u32 cnt_test = 0;
|
||||
// 初始化错误位图(INIT_ERR_*,config.h):hardware_init 按模块置位,
|
||||
// CAN STATUS 帧 InitStatus 反馈给上位机,0 表示全部初始化正常
|
||||
volatile u32 init_error_flags = 0;
|
||||
|
||||
/**
|
||||
* @brief 应用程序入口函数
|
||||
@@ -50,7 +53,11 @@ int main(void) {
|
||||
u8 init_status = hardware_init();
|
||||
|
||||
while (init_status) { // 初始化检测
|
||||
MAIN_DEBUG_PRINT("初始化失败\r\n");
|
||||
// 初始化失败:持续通过 CAN STATUS 帧上报 InitStatus 错误码
|
||||
// (含电流检测配平超限/无采样帧),上位机无需调试串口即可识别故障模块
|
||||
printf("初始化失败 InitStatus=0x%08X\r\n", init_error_flags);
|
||||
CAN0_transmiter.CANFD_all_Send(); // STATUS 帧字节 32~35 携带错误位图
|
||||
delay_1ms(100);
|
||||
}
|
||||
OTA_initial(); // OTA 初始化,A/B 分区处理
|
||||
|
||||
@@ -86,24 +93,61 @@ u8 hardware_init(void) {
|
||||
USART0_init(); // 调试串口初始化
|
||||
User_timer_init(); // 任务 Timer 初始化
|
||||
|
||||
u8 init_status = 0;
|
||||
init_status += KTH7823.init(); // 角度传感器初始化
|
||||
init_status += sampler.init(); // 采样器初始化
|
||||
// 初始化错误码按模块置位(INIT_ERR_*,位或组合;0=全部正常),
|
||||
// 经 CAN STATUS 帧 InitStatus 反馈
|
||||
init_error_flags = 0;
|
||||
if (KTH7823.init()) {
|
||||
init_error_flags |= INIT_ERR_ANGLE_SENSOR; // 角度传感器初始化
|
||||
}
|
||||
if (sampler.init()) {
|
||||
init_error_flags |= INIT_ERR_ADC; // 采样器/ADC 初始化
|
||||
}
|
||||
|
||||
// EEPROM 初始化 (必须在 CAN 初始化之前,因为 CAN 会从 Flash 读取节点 ID)
|
||||
flashEEP.init(); // EEPROM 初始化
|
||||
|
||||
// CAN 初始化 (会先设置默认节点 ID,然后从 Flash 加载已保存的节点 ID)
|
||||
init_status += CAN0_transmiter.init(); // CAN 初始化
|
||||
if (CAN0_transmiter.init()) {
|
||||
init_error_flags |= INIT_ERR_CAN; // CAN 初始化
|
||||
}
|
||||
|
||||
init_status += Angle_kalman.init(KTH7823.getAngle()); // 角度滤波初始化
|
||||
init_status += Vel_kalman.init(0.0f); // 速度滤波初始化
|
||||
init_status += FOC.init(); // FOC 初始化
|
||||
if (Angle_kalman.init(KTH7823.getAngle())) {
|
||||
init_error_flags |= INIT_ERR_ANG_KALMAN; // 角度滤波器初始化
|
||||
}
|
||||
if (Vel_kalman.init(0.0f)) {
|
||||
init_error_flags |= INIT_ERR_VEL_KALMAN; // 速度滤波器初始化
|
||||
}
|
||||
if (FOC.init()) {
|
||||
init_error_flags |= INIT_ERR_FOC; // FOC 初始化
|
||||
}
|
||||
|
||||
// 门驱动上电配置放在最后:此时 TIMER1/PWM 已启动且占空比为 0,
|
||||
// 门驱动上电配置放在 FOC 校验之后:此时 TIMER1/PWM 已启动且占空比为 0,
|
||||
// 新板(DRV8323)在此拉高 DRVSLP 唤醒芯片并经 SPI 配置 3x PWM/增益/保护,
|
||||
// 随后即使任务立即输出 0 占空比,桥臂也处于"下管常开"的静止安全态
|
||||
init_status += gate_driver_bringup();
|
||||
return init_status;
|
||||
if (gate_driver_bringup()) {
|
||||
init_error_flags |= INIT_ERR_GATE_DRIVER; // 门驱动 bringup
|
||||
}
|
||||
|
||||
// 三相电流零点配平:采样前唤醒门驱动,让 0% 占空比下三相下管导通,
|
||||
// PGNDx 与功率地等电位、共模与运行时一致,配平结果才可用。
|
||||
// 老板 bringup 是空操作、上电默认睡眠:此处临时唤醒(此时占空比 0=
|
||||
// 三相下管导通,静止安全态),配平完恢复睡眠,保持"上电未使能"的
|
||||
// 原始安全态(模式 0 的 drv_hold 恒占空比不会真实输出);新板
|
||||
// bringup 已唤醒并保持常醒,无需额外操作。配平值超限/无采样帧
|
||||
// 分别置独立错误位。
|
||||
#if !GATE_DRIVER_DRV8323
|
||||
motor_resume();
|
||||
#endif
|
||||
u8 current_cal = sampler.zero_calibrate();
|
||||
#if !GATE_DRIVER_DRV8323
|
||||
motor_sleep();
|
||||
#endif
|
||||
if (current_cal == 1) {
|
||||
init_error_flags |= INIT_ERR_CURRENT_NOFRAME; // 无采样帧
|
||||
} else if (current_cal == 2) {
|
||||
init_error_flags |= INIT_ERR_CURRENT_BAL; // 配平值超限
|
||||
}
|
||||
|
||||
return (init_error_flags != 0U) ? 1U : 0U;
|
||||
}
|
||||
|
||||
|
||||
+48
-3
@@ -102,6 +102,23 @@ constexpr u16 BOOT_VERSION_BUILD = 0;
|
||||
constexpr float DRV8323_CSA_GAIN_VV = 10.0f;
|
||||
constexpr float DRV8323_SHUNT_OHM = 0.010f;
|
||||
#endif
|
||||
|
||||
// ===================== 相电流采样前端选择(独立于门驱动芯片) =====================
|
||||
// 1 = 低边采样板(DRV8313 + 下桥臂 10mΩ + INA240A2):采样电阻串在各相下管
|
||||
// 源极(PGNDx)与功率地之间,INA240A2(50V/V)放大后接 ADC0 注入组。
|
||||
// OUT 静态中点 = (V_REF1+V_REF2)/2 = 1.65V(REF1 接 AGND、REF2 接 A3V3,
|
||||
// 由芯片内部分压取中,数据手册支持的接法)。
|
||||
// 换算 0.5V/A → 2.0 A/V,与老板外置放大链(50倍×10mΩ)数值一致,
|
||||
// 电流链路单位为安培(get_current 返回 A,上报处 ×1000 转 mA)。
|
||||
// 注意:下桥臂采样电阻只有对应下管导通(INx=0)时才有相电流流过,
|
||||
// ADC 采样点必须在三相下管同时导通的窗口内(见 TIMER1_CH3_COMPARE_VALUE)。
|
||||
// 0 = 老板(DRV8313 + 外置放大链):保持原标定系数与采样点。
|
||||
#define CURRENT_SENSE_INA240 1
|
||||
|
||||
#if CURRENT_SENSE_INA240
|
||||
constexpr float INA240_CSA_GAIN_VV = 50.0f; // INA240A2 = 50V/V(A1=20/A3=100/A4=200)
|
||||
constexpr float INA240_SHUNT_OHM = 0.010f; // 低边采样电阻 10mΩ
|
||||
#endif
|
||||
// 编译时间(AC6 编译器自动生成)
|
||||
#define APP_BUILD_DATE __DATE__
|
||||
#define APP_BUILD_TIME __TIME__
|
||||
@@ -167,7 +184,25 @@ constexpr u32 OTA_AVAL = 0x00000000; // 全零表示可用
|
||||
constexpr u8 PP = 11;
|
||||
constexpr float DIR = 1.0f; // 默认方向:-1.0f 反转,1.0f 正转
|
||||
constexpr u16 REDUCTION_RATIO = 25;
|
||||
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 当前电流传感器的零点偏移
|
||||
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 电流通道零点偏移(V),
|
||||
// 与 INA240 中点 1.65V / DRV8323 SO 中点 VREF/2 一致
|
||||
// 三相电流零点配平:配平值(各通道实测零偏折算电流)超过该限值视为
|
||||
// 电流检测初始化错误,随 init_status 阻止启动
|
||||
constexpr float CURRENT_ZERO_CAL_LIMIT = 0.1f; // 100mA
|
||||
|
||||
// ===================== 初始化错误码(CAN STATUS 帧 InitStatus 反馈) =====================
|
||||
// hardware_init 按模块置位(可位或组合),InitStatus=0 表示全部初始化正常;
|
||||
// 信号定义见 DBC v0.74 的 STATUS 帧字节 32~35
|
||||
constexpr u32 INIT_ERR_ANGLE_SENSOR = 1u << 0; // 角度传感器 KTH7823 初始化错误
|
||||
constexpr u32 INIT_ERR_ADC = 1u << 1; // 采样器/ADC 初始化错误
|
||||
constexpr u32 INIT_ERR_CAN = 1u << 2; // CAN 初始化错误
|
||||
constexpr u32 INIT_ERR_ANG_KALMAN = 1u << 3; // 角度滤波器初始化错误
|
||||
constexpr u32 INIT_ERR_VEL_KALMAN = 1u << 4; // 速度滤波器初始化错误
|
||||
constexpr u32 INIT_ERR_FOC = 1u << 5; // FOC 初始化错误
|
||||
constexpr u32 INIT_ERR_CURRENT_BAL = 1u << 6; // 电流检测:零点配平值超限(>100mA)
|
||||
constexpr u32 INIT_ERR_CURRENT_NOFRAME = 1u << 7; // 电流检测:无采样帧(触发链路异常)
|
||||
constexpr u32 INIT_ERR_GATE_DRIVER = 1u << 8; // 门驱动 bringup 错误
|
||||
extern volatile u32 init_error_flags; // 初始化错误位图,定义在 main.cpp
|
||||
constexpr float HOLD_PWM_DUTY = 0.1f;
|
||||
constexpr u16 ANGLESENS_OFFSET = 0;
|
||||
constexpr float ANGLESENS_CALIBRATION_ANGLE = 83.0f;
|
||||
@@ -336,8 +371,18 @@ constexpr float MOTOR_FREQUENCY = 1 / TIME_SLICE / MOTOR_PERIOD; // 频率
|
||||
constexpr int TIMER1_PRESCALER = 0; // 不分频,计数器时钟为 120MHz
|
||||
constexpr int TIMER1_PERIOD = 2999; // 120MHz / (0+1) / (2999+1) = 40kHz计数, 中心对齐实际PWM 20kHz
|
||||
constexpr int TIMER1_DEFAULT_PULSE = 0;
|
||||
constexpr int TIMER1_CH3_COMPARE_VALUE =
|
||||
(TIMER1_PERIOD + 1) * 0.41 - 1; // PWM 周期 41% 位置触发ADC采样
|
||||
// CH3 为固定 ADC 采样触发点(TRGO=O3CPRE)。低边采样要求触发落在"三相下管
|
||||
// 同时导通"的窗口内:中心对齐 PWM0 下 INx 高(上管导通)区间以计数谷底
|
||||
// CNT=0 为中心,INx 低(下管导通)区间以计数峰顶 CNT=PERIOD 为中心,故采样
|
||||
// 点必须贴近峰顶(在线采样时代取 41% 是任意有效点,直接沿用会采到上管
|
||||
// 导通期、三相电流恒读零)。
|
||||
// 取 PERIOD-10:紧贴峰顶。中心对齐下每周期匹配两次(上升沿 2989 / 下降沿
|
||||
// 3011,仅差 22 计数),无论第二次触发被 ADC 忽略(注入转换忙)还是重启
|
||||
// 序列,帧的等效采样位置都在峰顶附近——行为确定,不依赖芯片的触发丢弃
|
||||
// 策略;配合 ADC.cpp 电流通道 13.5 周期短采样,最大占空比 ~90% 时三相样本
|
||||
// 仍全部落在下管导通窗内。占空比更高时序列末位的 W 相先落出窗口(读数
|
||||
// 趋零、U+V+W≠0),软件侧需对占空比限幅。
|
||||
constexpr int TIMER1_CH3_COMPARE_VALUE = TIMER1_PERIOD - 10;
|
||||
|
||||
// 齿槽转矩补偿
|
||||
constexpr u16 COGGING_TABLE_SIZE = 128; // 每电周期采样点数
|
||||
|
||||
Reference in New Issue
Block a user