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:
+42
-21
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 函数
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
// 角度/速度 α-β 观测器参数
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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(带魔数验证和双重备份回退)
|
||||
|
||||
Reference in New Issue
Block a user