feat(FOC): 实现电角度零位自动校准并修复ADC采样干扰问题

- 新增FlashEEPROM用于保存零位校准角,掉电不丢失
- 实现安全的零位校准算法:运行中禁止校准、对齐后重新采样、保存到Flash
- 重构ADC架构:分离ADC0(PWM同步电流采样)和ADC1(VBUS/DRV温度轮询)
- 解决ADC采样冲突:移除ADC0的regular连续扫描,避免与注入组抢占资源
- 修复VBUS电压读取错误:从DMA缓冲区改为直接ADC1单次转换
- 修正开环控制Uq幅值限幅逻辑,保留符号避免反转时抖动
- 更新CAN协议:电流数据改为有符号16位打包,支持负电流传输
- 调整版本号至v26.8.16.7
This commit is contained in:
cbb
2026-08-16 02:13:37 +08:00
parent 42c9f2f669
commit 76f7987238
10 changed files with 214 additions and 95 deletions
+42 -21
View File
@@ -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;
}
+84 -67
View File
@@ -14,7 +14,6 @@
#include "systick.h"
#include <cstdint>
#include <cstring>
#include <gd32c10x_dma.h>
// 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);
+7 -3
View File
@@ -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 &current_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;
+23
View File
@@ -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 函数
-1
View File
@@ -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;
+13
View File
@@ -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
+10
View File
@@ -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;
}
+7 -3
View File
@@ -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;
// 角度/速度 α-β 观测器参数
+21
View File
@@ -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++) {
+7
View File
@@ -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(带魔数验证和双重备份回退)