diff --git a/Robot_V2/Algorithm/FOC.cpp b/Robot_V2/Algorithm/FOC.cpp index cab1965..37c7b91 100644 --- a/Robot_V2/Algorithm/FOC.cpp +++ b/Robot_V2/Algorithm/FOC.cpp @@ -11,6 +11,7 @@ #include "adc.h" #include "ang_sensor.h" #include "config.h" +#include "flash.h" #include "motor.h" #include "pid.h" #include "systick.h" @@ -29,7 +30,7 @@ extern angle_sensor KTH7823; extern CAN_transmiter CAN0_transmiter; extern EncoderVelocityKalman Vel_kalman; // 速度滤波 extern CurrentKalman Current_kalman; -extern u16 adc_value[3]; // regular DMA缓冲区:MCU温度 / DRV温度 / VBUS +extern FlashEEPROM flashEEP; // 齿槽转矩补偿:查表 + 线性插值 // angle_el: 电角度(度) @@ -271,26 +272,30 @@ void FOC_Controller::print_cogging_c_array(const char *title, * @return void */ void calibrate_zero_position(void) { - // Set_3Pwm(.25, 0, 0); // 起劲 - // delay_1ms(1000); - // KTH7823.update(); + // 运行中禁止校准:要求控制模式为停止(0)且基本静止, + // 否则转子无法对齐到电气 0°,标定结果错误,甚至因电角度突变导致失控。 + if (FOC.control_mode != 0 || fabsf(FOC.rt_velocity) > CALIB_MAX_SPEED) { + return; + } - // // 改进校准算法:获取当前传感器角度并计算补偿值 - // float current_sensor_angle = KTH7823.getcurrent_sensorangle(); - // float electrical_angle = fmodf(current_sensor_angle * PP, 360.0f); // - // 计算当前电角度 + // 使能电机驱动器,准备对齐 + motor_resume(); - // // 计算需要的补偿值,使得当前位置对应0°电角度 - // KTH7823.calibration_angle = -electrical_angle; + // 施加固定电压矢量(A 相高电平),让转子对齐到电气 0° 位置 + Set_3Pwm(ALIGN_PWM_DUTY, 0.0f, 0.0f); + delay_1ms(ALIGN_SETTLE_MS); - // // 对补偿角进行归一化处理,确保在[-180, 180]范围内 - // KTH7823.calibration_angle = fmodf(KTH7823.calibration_angle + 180.0f, - // 360.0f); if (KTH7823.calibration_angle < 0) { KTH7823.calibration_angle - // += 360.0f; - // } - // KTH7823.calibration_angle -= 180.0f; + // 对齐稳定后重新采样传感器角度,再计算零位补偿角,使当前位置对应电气 0° + KTH7823.update(); + float current_sensor_angle = KTH7823.getcurrent_sensorangle(); + float electrical_angle = fmodf(current_sensor_angle * PP, 360.0f); + KTH7823.set_calibration(-electrical_angle); - // Set_3Pwm(0, 0, 0); // 停止 + // 停止施加电压 + Set_3Pwm(0.0f, 0.0f, 0.0f); + + // 保存校准角到 Flash,掉电不丢 + flashEEP.save_calibration_angle(KTH7823.calibration_angle); } // 通过Ia,Ib,Ic计算Iq,Id(目前仅输出Iq) @@ -722,8 +727,11 @@ void FOC_Controller::update_power(float voltage) { * @return void */ void FOC_Controller::fast_voltage_check(void) { - // 直接从DMA缓冲区读取最新ADC值(regular rank2 是 VBUS) - float current_vbus = adc_value[2] * (3.3f / 4096.0f) * 11.0f; + // ADC1 单次转换读取 VBUS(ch6,11:1 分压)。 + // 旧实现读 regular DMA 的 adc_value[2],通道顺序调整后该下标已是 DRV温度, + // 属漏改:NTC 分压值(~1.06V)按 11 倍分压换算仅 ~11.5V,逼近跌落阈值。 + float current_vbus = + adc1_read_raw(ADC_CHANNEL_6) * (3.3f / 4096.0f) * 11.0f; // 更新基准电压(峰值检测) // 电压上升时,基准电压跟随;电压下跌时,基准电压保持 @@ -778,8 +786,21 @@ float FOC_Controller::velocity_open_loop() { sampler.get_current('W'), angle_el_cog); // 因为在开环控制中,Ud 始终为 0,只控制 Uq - const float Uq = _constrain(target_velocity * REDUCTION_RATIO * 0.005f, - 2.0f, voltage_power_supply * 0.5f); + // 幅度限幅必须保留符号:原 _constrain 下限为 +2V,负速度会被钳到 +2V, + // 磁场角度反向而 Uq 仍为正,导致反转时电机原地抖动。 + float Uq = target_velocity * REDUCTION_RATIO * 0.005f; + const float UQ_MIN_MAG = 2.0f; + const float UQ_MAX_MAG = voltage_power_supply * 0.5f; + // 零速不输出,避免 2V 最小幅值导致静止时缓慢爬行 + if (Uq != 0.0f) { + float Uq_mag = fabsf(Uq); + if (Uq_mag < UQ_MIN_MAG) { + Uq_mag = UQ_MIN_MAG; + } else if (Uq_mag > UQ_MAX_MAG) { + Uq_mag = UQ_MAX_MAG; + } + Uq = (Uq < 0.0f) ? -Uq_mag : Uq_mag; + } setSVPWM_PhaseVoltage(Uq + get_cogging_compensation(angle_el_cog), 0.0f, angle_el_cog); return limit_voltage; } diff --git a/Robot_V2/device/ADC.cpp b/Robot_V2/device/ADC.cpp index 7ec12e7..6e85afe 100644 --- a/Robot_V2/device/ADC.cpp +++ b/Robot_V2/device/ADC.cpp @@ -14,7 +14,6 @@ #include "systick.h" #include #include -#include // ADC 换算常量均为编译期常量,非可变全局状态。 // 用于 ADC 原始值到电压、温度、电流的固定比例换算。 @@ -31,9 +30,15 @@ extern CAN_transmiter CAN0_transmiter; extern FOC_Controller FOC; // 全局采样器单例:系统只有一组 ADC 采样缓存,由 hardware_init() 调用 sampler.init() 初始化。 -// regular DMA 写 adc_value[];injected ADC 中断写 adc_current_value[] 并置位 adc_current_frame_ready。 +// ADC0 只跑 injected 组(PWM 同步):中断写 adc_current_value[](U/V/W)与 +// adc_mcu_temp_raw(温度,挂注入序列尾部同步转换)并置位 adc_current_frame_ready。 +// VBUS / DRV温度 由 ADC1 在慢速任务中软件触发轮询,与电流采样彻底隔离。 +// 背景:此前温度/VBUS/DRV 放在 ADC0 regular 组连续扫描,与 injected 抢占同一 ADC。 +// 温度通道提到 239.5 周期后,稳态下注入触发几乎必然打断温度采样中段: +// regular 被打断后从 rank0 重启而 DMA 计数不复位 → adc_value[] 数据错位; +// 采样电容长期挂在高内阻温度源电压上,干扰注入组首通道 → 相电流检测故障。 sampler sampler; // 采样器初始化 -u16 adc_value[3] = {0}; // regular DMA: MCU温度 / DRV温度 / VBUS +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; @@ -47,83 +52,94 @@ static inline float current_adc_to_ma(u16 raw_adc) { } /*! - \brief configure the DMA peripheral + \brief configure the ADC1 peripheral(VBUS / DRV温度 专用) \param[in] none \param[out] none \retval none + \note ADC1 独占慢速量:单通道单次软件触发,无 DMA、无中断、无连续模式, + 软件按需轮询读取,与 ADC0 的 PWM 同步电流采样互不干扰。 */ -void dma_config(void) { - /* enable DMA0 clock */ - rcu_periph_clock_enable(RCU_DMA0); - /* ADC_DMA_channel configuration */ - dma_parameter_struct dma_data_parameter; +void adc1_config(void) { + /* enable ADC1 clock(ADC 时钟分频与 ADC0 共用 RCU 配置) */ + rcu_periph_clock_enable(RCU_ADC1); - /* ADC DMA_channel configuration */ - dma_deinit(DMA0, DMA_CH0); + adc_deinit(ADC1); + adc_mode_config(ADC_MODE_FREE); + adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT); - /* initialize DMA single data mode */ - dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); - dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; - dma_data_parameter.memory_addr = (uint32_t)(adc_value); - dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; - dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; - dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT; - dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; - dma_data_parameter.number = 3; - dma_data_parameter.priority = DMA_PRIORITY_HIGH; - dma_init(DMA0, DMA_CH0, &dma_data_parameter); + /* 单通道单次转换:调用方每次配置 rank0 通道后软件触发 */ + adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1); + adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, + ADC0_1_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE); - dma_circulation_enable(DMA0, DMA_CH0); + adc_enable(ADC1); + /* ADC校准 */ + adc_calibration_enable(ADC1); +} - /* enable DMA channel */ - dma_channel_enable(DMA0, DMA_CH0); +/** + * @brief ADC1 单次软件触发转换并等待完成 + * @param[in] channel ADC 通道号(如 ADC_CHANNEL_6 / ADC_CHANNEL_9) + * @return u16 转换原始值(超时返回上次 RDATA 残留值) + * @note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发)。 + * 单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。 + */ +u16 adc1_read_raw(u8 channel) { + u16 timeout = 0; + adc_flag_clear(ADC1, ADC_FLAG_EOC); + adc_regular_channel_config(ADC1, 0, channel, ADC_SAMPLETIME_28POINT5); + adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL); + while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) { + if (++timeout > 2000U) { // 硬件异常保护,避免死等 + break; + } + } + adc_flag_clear(ADC1, ADC_FLAG_EOC); + return (u16)adc_regular_data_read(ADC1); } /*! - \brief configure the ADC peripheral + \brief configure the ADC0 peripheral(PWM 同步电流采样专用) \param[in] none \param[out] none \retval none + \note ADC0 只保留 injected 组,regular 组整体移除: + 此前 regular 连续扫描与 injected 触发抢占同一 ADC,注入打断后 + regular 从头重启而 DMA 不复位导致数据错位,且温度长采样被打断后 + 采样电容残余污染注入组首通道。移除 regular 后注入组再无打断源。 */ void adc_config(void) { /* enable ADC clock */ rcu_periph_clock_enable(RCU_ADC0); - /* config ADC clock */ + /* config ADC clock(ADC0/ADC1 共用分频:120MHz/4 = 30MHz) */ rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); /* 复位ADC */ adc_deinit(ADC0); /* ADC模式配置 */ adc_mode_config(ADC_MODE_FREE); - /* regular 组保留连续扫描,用于温度/VBUS 后台采样 */ - adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); + /* 扫描模式:注入序列内 4 通道触发后自动连续转换 */ adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); - /* injected 组用于 PWM 同步电流采样,关闭自动串联,独立由 TIMER1 触发 */ - adc_special_function_config(ADC0, ADC_INSERTED_CHANNEL_AUTO, DISABLE); /* ADC数据右对齐配置 */ adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); - /* 使能ADC温度传感器和内部参考电压 */ + /* 使能ADC温度传感器(内部通道仅连接 ADC0 的 ch16/ch17) */ adc_tempsensor_vrefint_enable(); - /* 配置ADC通道长度 */ - adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 3); - /* 配置ADC规则通道 */ - adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_16, - ADC_SAMPLETIME_41POINT5); - adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_9, ADC_SAMPLETIME_28POINT5); - adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5); - - /* regular 组继续软件触发连续转换:本组只服务低速量 */ - adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, - ADC0_1_EXTTRIG_REGULAR_NONE); - adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); - - /* injected 组改为 TIMER1 CH3 导出的 TRGO 触发,实现三相同一采样时刻锁存 */ + /* 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 触发周期。 */ 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_3, ADC_SAMPLETIME_55POINT5); - adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5); - adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_5, 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, 3, ADC_CHANNEL_16, + ADC_SAMPLETIME_239POINT5); adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL, ADC0_1_EXTTRIG_INSERTED_T1_TRGO); adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE); @@ -132,19 +148,15 @@ void adc_config(void) { adc_enable(ADC0); /* ADC校准 */ adc_calibration_enable(ADC0); - /* 使能ADC DMA功能 */ - adc_dma_mode_enable(ADC0); - /* injected 完成中断负责搬运完整三相样本 */ + /* injected 完成中断负责搬运完整三相样本与温度原始值 */ adc_interrupt_enable(ADC0, ADC_INT_EOIC); - /* 先启动 regular 组连续后台采样 */ - adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); } /** * @brief 采样类初始化 */ u8 sampler::init() { - dma_config(); // DMA采样ADC初始化 - adc_config(); // ADC初始化 + adc_config(); // ADC0:PWM 同步注入组(U/V/W + MCU温度) + adc1_config(); // ADC1:VBUS / DRV温度 慢速轮询 current_sample_head = 0; adc_current_frame_ready = 0; delay_1ms(1); @@ -154,15 +166,16 @@ u8 sampler::init() { // 初始采样 for (int i = 0; i < ADC_FILTER_SIZE; i++) { mcu_temp_samples[i] = - (1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f; + (1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f; currU_samples[i] = current_init; currV_samples[i] = current_init; currW_samples[i] = current_init; - vbus_samples[i] = adc_value[2] * ADC_SCALE * VCC_FACTOR; + vbus_samples[i] = adc1_read_raw(ADC_CHANNEL_6) * ADC_SCALE * VCC_FACTOR; - float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700; + const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9); + float R_now = drv_raw / (4096 - drv_raw) * 4700; drv_temp_samples[i] = 1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f; delay_1ms(1); @@ -243,7 +256,7 @@ float sampler::get_vbus_voltage() { void sampler::update_mcu_temp() { // 环形缓冲区写入,替代 memmove mcu_temp_samples[mcu_temp_head] = - (1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f; + (1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f; mcu_temp_head++; if (mcu_temp_head >= ADC_FILTER_SIZE) mcu_temp_head = 0; @@ -260,8 +273,10 @@ void sampler::update_mcu_temp() { * @brief 更新MCU温度 */ void sampler::update_drv_temp() { + // ADC1 单次轮询转换 ch9(NTC 分压,低阻源) + const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9); // 环形缓冲区写入,替代 memmove - float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700; + float R_now = drv_raw / (4096 - drv_raw) * 4700; drv_temp_samples[drv_temp_head] = 1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f; drv_temp_head++; @@ -300,8 +315,10 @@ void sampler::update_current() { * @brief 更新供电电压 */ void sampler::update_vbus_voltage() { + // ADC1 单次轮询转换 ch6(电阻分压,低阻源) // 环形缓冲区写入,替代 memmove - vbus_samples[temp_vbus_head] = adc_value[2] * ADC_SCALE * VCC_FACTOR; + vbus_samples[temp_vbus_head] = + adc1_read_raw(ADC_CHANNEL_6) * ADC_SCALE * VCC_FACTOR; temp_vbus_head++; if (temp_vbus_head >= ADC_FILTER_SIZE) temp_vbus_head = 0; @@ -321,11 +338,11 @@ void sampler::update_vbus_voltage() { */ extern "C" void ADC0_1_IRQHandler(void) { if (adc_interrupt_flag_get(ADC0, ADC_INT_FLAG_EOIC) != RESET) { - /* 丢弃 rank0 dummy,真实样本使用 rank1/2/3,对应 U/V/W 同一 PWM 时刻 */ - (void)adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0); - adc_current_value[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1); - adc_current_value[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2); - adc_current_value[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3); + /* rank0/1/2 = U/V/W 同一 PWM 时刻样本,rank3 = MCU温度(序列尾部) */ + adc_current_value[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0); + adc_current_value[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1); + adc_current_value[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2); + adc_mcu_temp_raw = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3); adc_current_frame_ready = 1U; adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC); diff --git a/Robot_V2/device/Can.cpp b/Robot_V2/device/Can.cpp index 30eb6c2..ce46212 100644 --- a/Robot_V2/device/Can.cpp +++ b/Robot_V2/device/Can.cpp @@ -1043,9 +1043,9 @@ u8 CAN_transmiter::CANFD_all_Send() { memset(transmit_data, 0, 32); pack_u16_to_bytes(transmit_data, 0, mcu_temp); pack_u16_to_bytes(transmit_data, 2, motor_temp); - pack_u16_to_bytes(transmit_data, 4, u16(u_current_union.f)); - pack_u16_to_bytes(transmit_data, 6, u16(v_current_union.f)); - pack_u16_to_bytes(transmit_data, 8, u16(w_current_union.f)); + pack_current_s16_to_bytes(transmit_data, 4, u_current_union.f); + pack_current_s16_to_bytes(transmit_data, 6, v_current_union.f); + pack_current_s16_to_bytes(transmit_data, 8, w_current_union.f); transmit_data[10] = (transmit_data[10] & ~0x07) | ((MotorStatus & 0x01) | ((MagStatus & 0x01) << 1) | @@ -1277,6 +1277,10 @@ void CAN_transmiter::handle_reset_command(can_receive_message_struct ¤t_me Angle_kalman.init(0); Vel_kalman.init(0); } + if (first_byte >> 5 & 0x01) { + // 零位校准:施加对齐电压、读传感器角、计算并保存校准角 + calibrate_zero_position(); + } #if FOC_DEBUG if (first_byte >> 4 & 0x01) { FOC.cogging_calib_request = true; diff --git a/Robot_V2/device/Can.h b/Robot_V2/device/Can.h index 649948d..e1d4b2c 100644 --- a/Robot_V2/device/Can.h +++ b/Robot_V2/device/Can.h @@ -38,6 +38,29 @@ static inline void pack_u16_to_bytes(u8 *buffer, u16 offset, u16 value) { buffer[offset + 1] = (u8)((value >> 8) & 0xFF); } +// 将 mA 电流值饱和转换为 int16 后按小端打包。 +// DBC 中 U/V/W current 定义为 signed int16([-32768,32767] mA); +// 相电流有正有负,此前用 u16() 打包负值被削成 0,上位机波形下半周丢失。 +/** + * @brief pack_current_s16_to_bytes 函数 + * @param[in] buffer 参数 + * @param[in] offset 参数 + * @param[in] value_mA 参数 + * @return void + */ +static inline void pack_current_s16_to_bytes(u8 *buffer, u16 offset, + float value_mA) { + short value; + if (value_mA > 32767.0f) { + value = 32767; // 饱和,避免 float→int 超范围截断环绕 + } else if (value_mA < -32768.0f) { + value = -32768; + } else { + value = (short)value_mA; + } + pack_u16_to_bytes(buffer, offset, (u16)value); +} + // 添加辅助函数用于将 float 类型数据打包到 u8 数组中 /** * @brief pack_float_to_bytes 函数 diff --git a/Robot_V2/device/Timer.cpp b/Robot_V2/device/Timer.cpp index 74719f0..4ee0d11 100644 --- a/Robot_V2/device/Timer.cpp +++ b/Robot_V2/device/Timer.cpp @@ -16,7 +16,6 @@ #include "initial.h" volatile u32 total_us100_counter = 0; // TIMER2 ISR 内部 100us 调度索引 -extern u16 adc_value[3]; extern FOC_Controller FOC; extern TaskScheduler scheduler; extern CAN_transmiter CAN0_transmiter; diff --git a/Robot_V2/device/adc.h b/Robot_V2/device/adc.h index 7581fd0..dfe2237 100644 --- a/Robot_V2/device/adc.h +++ b/Robot_V2/device/adc.h @@ -90,6 +90,19 @@ class sampler { */ void ADC0_1_IRQHandler(void); +/** + * @brief ADC1 初始化(VBUS / DRV温度 专用,单通道单次软件触发) + * @return void + */ +void adc1_config(void); + +/** + * @brief ADC1 单次软件触发转换并等待完成(仅主循环任务上下文调用) + * @param[in] channel ADC 通道号(ADC_CHANNEL_6 / ADC_CHANNEL_9) + * @return u16 转换原始值 + */ +u16 adc1_read_raw(u8 channel); + #ifdef __cplusplus } #endif diff --git a/Robot_V2/device/ang_sensor.cpp b/Robot_V2/device/ang_sensor.cpp index d7d8b15..ca7de0a 100644 --- a/Robot_V2/device/ang_sensor.cpp +++ b/Robot_V2/device/ang_sensor.cpp @@ -309,4 +309,14 @@ float angle_sensor::getRawAngle(void) const { return m_cumulativeAngle; } */ float angle_sensor::getcurrent_sensorangle(void) const{ return current_angle_deg; +} + +/** + * @brief 设置电角度零位校准角 + * @param[in] angle 校准角(度) + * @return 0 成功 + */ +u8 angle_sensor::set_calibration(float angle) { + calibration_angle = normalizeAngle360(angle); + return 0; } \ No newline at end of file diff --git a/Robot_V2/sys/config.h b/Robot_V2/sys/config.h index a60b8c6..c433c37 100644 --- a/Robot_V2/sys/config.h +++ b/Robot_V2/sys/config.h @@ -22,9 +22,9 @@ // 版本号定义 - 可根据实际项目进行调整 constexpr u16 SOFTWARE_VERSION_MAJOR = 26; -constexpr u16 SOFTWARE_VERSION_MINOR = 5; -constexpr u16 SOFTWARE_VERSION_PATCH = 25; -constexpr u16 SOFTWARE_VERSION_BUILD = 2; +constexpr u16 SOFTWARE_VERSION_MINOR = 8; +constexpr u16 SOFTWARE_VERSION_PATCH = 16; +constexpr u16 SOFTWARE_VERSION_BUILD = 7; constexpr u16 HARDWARE_VERSION_MAJOR = 25; constexpr u16 HARDWARE_VERSION_MINOR = 12; @@ -105,6 +105,10 @@ constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 当前电流传感器的零 constexpr float HOLD_PWM_DUTY = 0.1f; constexpr u16 ANGLESENS_OFFSET = 0; constexpr float ANGLESENS_CALIBRATION_ANGLE = 83.0f; +// 电角度零位校准:施加的对齐占空比与稳定等待时间 +constexpr float ALIGN_PWM_DUTY = 0.25f; +constexpr u32 ALIGN_SETTLE_MS = 1000; +constexpr float CALIB_MAX_SPEED = 5.0f; // 输出级速度阈值(deg/s),超过则拒绝校准 constexpr float KE_FACTOR = 0.0f; // 角度/速度 α-β 观测器参数 diff --git a/Robot_V2/sys/flash.cpp b/Robot_V2/sys/flash.cpp index 6e8d867..b1b0bbb 100644 --- a/Robot_V2/sys/flash.cpp +++ b/Robot_V2/sys/flash.cpp @@ -73,6 +73,16 @@ u8 FlashEEPROM::init() { // 读取系统配置 read_saved_system_config(); + // 加载电角度零位校准角(首次无存储则用默认值) + float calib = ANGLESENS_CALIBRATION_ANGLE; + if (readFloat(ADDR_CALIBRATION_ANGLE, &calib) != FLASH_EEPROM_OK) { + calib = ANGLESENS_CALIBRATION_ANGLE; + } else if (calib == 0.0f) { + // 校准值为 0 视为未校准/异常,回退默认值,避免换向角度错误 + calib = ANGLESENS_CALIBRATION_ANGLE; + } + KTH7823.set_calibration(calib); + if (EEPROMStatus == FLASH_EEPROM_OK) { return FLASH_EEPROM_OK; } else { @@ -493,6 +503,15 @@ float FlashEEPROM::read_angle() { return angle; } +/** + * @brief 保存电角度零位校准角到 Flash + * @param[in] angle 校准角(度) + * @return FlashEepromStatus 操作状态 + */ +FlashEepromStatus FlashEEPROM::save_calibration_angle(float angle) { + return writeFloat(ADDR_CALIBRATION_ANGLE, angle); +} + // ============================================================================ // 节点 ID 独立读写函数实现(使用魔数+双重备份+计数器机制) // ============================================================================ @@ -884,6 +903,8 @@ u8 OTA_SET(u32 addr, u32 data) { * @retval 无 */ void jumpToBOOT() { + // 0. 进 BOOT 前保存系统配置与运行参数(兜底,确保数据落盘) + flashEEP.save_system_config(false); // 1. 关闭所有中断 __disable_irq(); for (int i = 0; i < 8; i++) { diff --git a/Robot_V2/sys/flash.h b/Robot_V2/sys/flash.h index a8f7c98..6bde8e1 100644 --- a/Robot_V2/sys/flash.h +++ b/Robot_V2/sys/flash.h @@ -54,6 +54,7 @@ class FlashEEPROM { static constexpr u8 ADDR_VOLTAGE_LIMIT = 0x4; // 电压限制 static constexpr u8 ADDR_VEL_LIMIT = 0x05; // 角度数据存储地址 static constexpr u8 ADDR_NODE_ID = 0x06; // 节点 ID 存储地址 + static constexpr u8 ADDR_CALIBRATION_ANGLE = 0x07; // 电角度零位校准角存储地址 bool angle_saved = false; bool system_config_saved = false; @@ -124,6 +125,12 @@ class FlashEEPROM { * @return float 返回值 */ float read_angle(); + /** + * @brief 保存电角度零位校准角到 Flash + * @param[in] angle 校准角(度) + * @return FlashEepromStatus 操作状态 + */ + FlashEepromStatus save_calibration_angle(float angle); // 节点 ID 独立读写函数(使用魔数+双重备份+计数器机制) // 从 Flash 读取节点 ID(带魔数验证和双重备份回退)