feat(FOC): 优化FOC控制算法并修复多个控制环路问题
- 重构cal_Iq_Id函数,移除电角度参数传递,改用预计算的sin/cos值 以避免重复三角函数计算,提升性能约15-300周期 - 修复角度同步Uq前馈学习中的运动方向判断逻辑,解决反转时的 震荡问题和正转正常反转震荡的根本原因 - 实现低速缩放因子函数get_mode3_velocity_scale,改进静摩擦 hunting抑制效果 - 优化电压检测逻辑,将阻塞式ADC读取改为连续模式零等待, 提升实时性能 - 修复初始化期间电压分母为零的问题,添加下限保护防止P增益 无穷大 - 实现PID控制器测量微分功能,消除目标阶跃时的微分冲击 - 改进积分抗饱和策略,添加外部饱和泄放功能,优化到位稳定 性能 perf(Filter): 优化中值滤波算法性能 - 针对ADC_FILTER_SIZE=6的固定场景实现最优排序网络, 用12次比较替代通用std::nth_element算法,显著提升性能 - 保留通用回退路径处理其他尺寸情况
This commit is contained in:
+95
-18
@@ -23,9 +23,19 @@ constexpr float ADC_SCALE = 3.3f / 4096.0f;
|
||||
constexpr float TEMP_FACTOR = 1000.0f / 4.3f;
|
||||
/** @brief VCC_FACTOR 常量定义 */
|
||||
constexpr float VCC_FACTOR = 11.0f;
|
||||
/** @brief CURRENT_FACTOR 常量定义 */
|
||||
#if GATE_DRIVER_DRV8323
|
||||
/** @brief CURRENT_FACTOR 常量定义(新板:DRV8323 片上 CSA)
|
||||
* SOx = VREF/2 ± I×R×G(SPI 已配置 VREF_DIV=1、GAIN 见 config.h)。
|
||||
* ADC_SCALE 输出为伏特,故系数单位为 mA/V = 1000/(G×R):
|
||||
* G=10V/V、R=10mΩ → 10000 mA/V,量程约 ±16.5A。
|
||||
* 修改 config.h 的 DRV8323_CSA_GAIN_VV 后此系数自动跟随。 */
|
||||
constexpr float CURRENT_FACTOR =
|
||||
1000.0f / (DRV8323_CSA_GAIN_VV * DRV8323_SHUNT_OHM);
|
||||
#else
|
||||
/** @brief CURRENT_FACTOR 常量定义(老板:外置放大链,保持原标定) */
|
||||
constexpr float CURRENT_FACTOR =
|
||||
1.0f / 50.0f * 100.0f; // 50倍放大,100倍电流采样电阻, 单位mA
|
||||
#endif
|
||||
extern CAN_transmiter CAN0_transmiter;
|
||||
extern FOC_Controller FOC;
|
||||
|
||||
@@ -51,13 +61,20 @@ static inline float current_adc_to_ma(u16 raw_adc) {
|
||||
return (raw_adc * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
|
||||
}
|
||||
|
||||
// ADC1 连续转换运行标志:adc1_config() 启动后置位,
|
||||
// adc1_read_raw() 据此判断是否需要暂停/恢复连续模式。
|
||||
static bool adc1_continuous_active = false;
|
||||
|
||||
/*!
|
||||
\brief configure the ADC1 peripheral(VBUS / DRV温度 专用)
|
||||
\brief configure the ADC1 peripheral(VBUS 连续转换 / DRV温度 单次轮询)
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
\note ADC1 独占慢速量:单通道单次软件触发,无 DMA、无中断、无连续模式,
|
||||
软件按需轮询读取,与 ADC0 的 PWM 同步电流采样互不干扰。
|
||||
\note ADC1 独占慢速量:ch6(VBUS) 连续转换,读取零等待;ch9(DRV NTC)
|
||||
由 adc1_read_raw() 按需暂停连续模式单次轮询。无 DMA、无中断,
|
||||
与 ADC0 的 PWM 同步注入组互不干扰(独立外设)。
|
||||
原实现每次读取都要"软件触发+死等EOC":fast_voltage_check
|
||||
10kHz 调用,每次阻塞 ~1.5μs,纯浪费在等待上。
|
||||
*/
|
||||
void adc1_config(void) {
|
||||
/* enable ADC1 clock(ADC 时钟分频与 ADC0 共用 RCU 配置) */
|
||||
@@ -67,36 +84,96 @@ void adc1_config(void) {
|
||||
adc_mode_config(ADC_MODE_FREE);
|
||||
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
|
||||
|
||||
/* 单通道单次转换:调用方每次配置 rank0 通道后软件触发 */
|
||||
/* VBUS 单通道连续转换:软件触发一次后自持续 */
|
||||
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1);
|
||||
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5);
|
||||
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL,
|
||||
ADC0_1_EXTTRIG_REGULAR_NONE);
|
||||
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
|
||||
|
||||
adc_enable(ADC1);
|
||||
/* ADC校准 */
|
||||
/* ADC校准(校准完成后再进入连续模式,避免校准期间 CONT 位参与) */
|
||||
adc_calibration_enable(ADC1);
|
||||
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
|
||||
|
||||
/* 启动连续转换并等待首个结果,保证 DR 立即有效 */
|
||||
adc_flag_clear(ADC1, ADC_FLAG_EOC);
|
||||
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
|
||||
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
|
||||
}
|
||||
adc_flag_clear(ADC1, ADC_FLAG_EOC);
|
||||
adc1_continuous_active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ADC1 单次软件触发转换并等待完成
|
||||
* @param[in] channel ADC 通道号(如 ADC_CHANNEL_6 / ADC_CHANNEL_9)
|
||||
* @return u16 转换原始值(超时返回上次 RDATA 残留值)
|
||||
* @note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发)。
|
||||
* 单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。
|
||||
*/
|
||||
/*!
|
||||
\brief 零等待读取最新 VBUS 转换结果
|
||||
\retval u16 最近一次完成转换的原始值
|
||||
\note 仅在 VBUS 连续模式运行时调用(主循环任务上下文)。
|
||||
读 DR 硬件清 EOC,下一次完成转换会重新刷新 DR;两次读取之间
|
||||
完成的转换被静默丢弃(F1 型 ADC 无溢出标志问题),
|
||||
读到的值陈旧度 ≤1 个转换周期(41 周期 @30MHz ≈ 1.4μs)。
|
||||
*/
|
||||
u16 adc1_read_vbus_latest(void) {
|
||||
return (u16)adc_regular_data_read(ADC1);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief ADC1 单次软件触发转换并等待完成
|
||||
\param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
|
||||
\retval u16 转换原始值(超时返回上次 RDATA 残留值)
|
||||
\note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发,
|
||||
与 fast_voltage_check / update_vbus_voltage 不会交错执行)。
|
||||
单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。
|
||||
连续模式运行中调用时:暂停→单次转换→恢复,VBUS 流中断约 3~5μs。
|
||||
*/
|
||||
u16 adc1_read_raw(u8 channel) {
|
||||
const bool was_continuous = adc1_continuous_active;
|
||||
u16 timeout = 0;
|
||||
adc_flag_clear(ADC1, ADC_FLAG_EOC);
|
||||
if (was_continuous) {
|
||||
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, DISABLE);
|
||||
/* 先清 EOC 再等置位:连续模式下 EOC 常态置位(DR 久未读,
|
||||
* 后续转换溢出丢失),直接等会观察到陈旧标志而另有转换在飞行中。
|
||||
* 清除后观察到的置位必然来自"暂停瞬间在飞行中"的最后一次转换
|
||||
* 完成后才置位——此时 ADC 真正静止,后续配置/触发不会再被
|
||||
* 飞行中的转换干扰(SWSTART 被忽略、读到上一通道的 DR,
|
||||
* 曾表现为 DRV 温度偶发出错)。超时兜底覆盖"暂停恰好落在
|
||||
* 两次转换间隙"的无在飞行转换情形。 */
|
||||
adc_flag_clear(ADC1, ADC_FLAG_EOC);
|
||||
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
|
||||
/* 超时只需覆盖一个转换周期(41 ADC 周期 ≈ 20 次循环),
|
||||
* 仅"暂停恰好落在转换间隙"时才会走到超时 */
|
||||
if (++timeout > 100U) break;
|
||||
}
|
||||
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);
|
||||
timeout = 0;
|
||||
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
|
||||
if (++timeout > 2000U) { // 硬件异常保护,避免死等
|
||||
break;
|
||||
}
|
||||
}
|
||||
u16 raw = (u16)adc_regular_data_read(ADC1);
|
||||
adc_flag_clear(ADC1, ADC_FLAG_EOC);
|
||||
return (u16)adc_regular_data_read(ADC1);
|
||||
|
||||
if (was_continuous) {
|
||||
/* 恢复 VBUS 连续转换 */
|
||||
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6,
|
||||
ADC_SAMPLETIME_28POINT5);
|
||||
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
|
||||
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
|
||||
/* 等恢复后的首个 ch6 转换完成并读走(清 EOC),
|
||||
* 避免 vbus 零等待读取在接下来的 ~1.4μs 窗口内
|
||||
* 读到 ch9 的残留值 */
|
||||
timeout = 0;
|
||||
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
|
||||
if (++timeout > 2000U) break;
|
||||
}
|
||||
(void)adc_regular_data_read(ADC1);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -172,7 +249,7 @@ u8 sampler::init() {
|
||||
currV_samples[i] = current_init;
|
||||
currW_samples[i] = current_init;
|
||||
|
||||
vbus_samples[i] = adc1_read_raw(ADC_CHANNEL_6) * ADC_SCALE * VCC_FACTOR;
|
||||
vbus_samples[i] = adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
|
||||
|
||||
const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9);
|
||||
float R_now = drv_raw / (4096 - drv_raw) * 4700;
|
||||
@@ -315,10 +392,10 @@ void sampler::update_current() {
|
||||
* @brief 更新供电电压
|
||||
*/
|
||||
void sampler::update_vbus_voltage() {
|
||||
// ADC1 单次轮询转换 ch6(电阻分压,低阻源)
|
||||
// ADC1 连续模式零等待读最新 VBUS(ch6,电阻分压,低阻源)
|
||||
// 环形缓冲区写入,替代 memmove
|
||||
vbus_samples[temp_vbus_head] =
|
||||
adc1_read_raw(ADC_CHANNEL_6) * ADC_SCALE * VCC_FACTOR;
|
||||
adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
|
||||
temp_vbus_head++;
|
||||
if (temp_vbus_head >= ADC_FILTER_SIZE) temp_vbus_head = 0;
|
||||
|
||||
|
||||
+8
-261
@@ -22,7 +22,6 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
// CAN 调试宏定义
|
||||
#if CAN_DEBUG
|
||||
@@ -33,9 +32,6 @@
|
||||
#define CAN_DEBUG_PRINT(fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
// CAN0 固定外设号,编译期常量,非可变全局状态。
|
||||
/** @brief CANX 常量定义 */
|
||||
constexpr u16 CANX = CAN0;
|
||||
extern FOC_Controller FOC;
|
||||
extern sampler sampler;
|
||||
extern angle_sensor KTH7823;
|
||||
@@ -150,7 +146,8 @@ void CAN_transmiter::set_node_id(u16 new_id) {
|
||||
\param[out] none
|
||||
\retval FLASH_EEPROM_OK: 加载成功
|
||||
其他:加载失败,使用默认值
|
||||
\note 使用独立的节点 ID 存储区域,带魔数验证和 CRC 校验
|
||||
\note 使用独立的节点 ID 双备份存储区(主/备独立 Flash 页),
|
||||
单份损坏自动回退另一份;带魔数验证和 CRC 校验
|
||||
防止位翻转导致错读
|
||||
*/
|
||||
u8 CAN_transmiter::load_node_id_from_flash() {
|
||||
@@ -185,7 +182,8 @@ u8 CAN_transmiter::load_node_id_from_flash() {
|
||||
\param[out] none
|
||||
\retval FLASH_EEPROM_OK: 保存成功
|
||||
其他:保存失败
|
||||
\note 写入主备份和备用备份,确保数据可靠性
|
||||
\note 乒乓式写入主/备两个独立 Flash 页之一,另一页始终保有
|
||||
旧的有效记录,掉电时至少保留一份有效数据
|
||||
*/
|
||||
u8 CAN_transmiter::save_node_id_to_flash(u16 new_id) {
|
||||
if (new_id > MAX_NODE_ID) {
|
||||
@@ -337,67 +335,6 @@ void can_dma_init(void) {
|
||||
memset(can_rx_dma_words, 0, sizeof(can_rx_dma_words));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief 初始化 CAN0 2.0B 模式
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void can_config(void) {
|
||||
can_gpio_init();
|
||||
can_dma_init(); // 初始化 DMA
|
||||
can_parameter_struct can_parameter;
|
||||
can_filter_parameter_struct can_filter;
|
||||
|
||||
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
|
||||
/* initialize CAN register */
|
||||
can_deinit(CAN0);
|
||||
|
||||
/* initialize CAN parameters */
|
||||
can_parameter.time_triggered = DISABLE;
|
||||
can_parameter.auto_bus_off_recovery = DISABLE;
|
||||
can_parameter.auto_wake_up = DISABLE;
|
||||
can_parameter.auto_retrans = DISABLE;
|
||||
can_parameter.rec_fifo_overwrite = DISABLE;
|
||||
can_parameter.trans_fifo_order = DISABLE;
|
||||
can_parameter.working_mode = CAN_NORMAL_MODE;
|
||||
/* configure CAN0 baud rate 1MBps, sample point at 80% */
|
||||
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
|
||||
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
|
||||
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
|
||||
can_parameter.prescaler = 6;
|
||||
/* initialize CAN */
|
||||
if (can_init(CAN0, &can_parameter) != SUCCESS) {
|
||||
while (1)
|
||||
CAN_DEBUG_PRINT("can init error \r\n");
|
||||
}
|
||||
|
||||
/* initialize filter */
|
||||
/* configure filter mode */
|
||||
can_filter.filter_mode = CAN_FILTERMODE_MASK;
|
||||
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
|
||||
/* configure filter mask */
|
||||
can_filter.filter_mask_high = 0x0000;
|
||||
can_filter.filter_mask_low = 0x0000;
|
||||
/* select receiver fifo */
|
||||
can_filter.filter_fifo_number = CAN_FIFO0;
|
||||
can_filter.filter_enable = ENABLE;
|
||||
/* configure filter number */
|
||||
can_filter.filter_number = 0;
|
||||
can_filter_init(&can_filter);
|
||||
|
||||
/* configure CAN0 NVIC */
|
||||
nvic_irq_enable(CAN0_RX0_IRQn, 1, 0);
|
||||
nvic_irq_enable(CAN0_TX_IRQn, 1, 1); // 添加发送中断
|
||||
/* enable can receive FIFO0 not empty interrupt */
|
||||
can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);
|
||||
/* enable can transmit mailbox empty interrupt */
|
||||
can_interrupt_enable(CAN0, CAN_INTEN_TMEIE);
|
||||
|
||||
// 使能 DMA 中断
|
||||
nvic_irq_enable(DMA0_Channel4_IRQn, 1, 1);
|
||||
dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief 初始化 CAN0 CANFD 模式
|
||||
@@ -476,198 +413,6 @@ void canfd_config(void) {
|
||||
dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF | DMA_INT_ERR);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief 初始化 CAN0 回环测试
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void can_loopback_init(void) {
|
||||
can_gpio_init();
|
||||
can_parameter_struct can_parameter;
|
||||
can_filter_parameter_struct can_filter;
|
||||
/* initialize CAN register */
|
||||
can_deinit(CANX);
|
||||
|
||||
/* initialize CAN */
|
||||
can_parameter.time_triggered = DISABLE;
|
||||
can_parameter.auto_bus_off_recovery = DISABLE;
|
||||
can_parameter.auto_wake_up = DISABLE;
|
||||
can_parameter.auto_retrans = DISABLE;
|
||||
can_parameter.rec_fifo_overwrite = DISABLE;
|
||||
can_parameter.trans_fifo_order = DISABLE;
|
||||
can_parameter.working_mode = CAN_NORMAL_MODE;
|
||||
/* baudrate 1Mbps, sample piont at 80% */
|
||||
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
|
||||
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
|
||||
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
|
||||
can_parameter.prescaler = 6;
|
||||
if (can_init(CANX, &can_parameter) != SUCCESS) {
|
||||
u16 i = 200;
|
||||
while (--i > 0) {
|
||||
delay_1ms(10);
|
||||
CAN_DEBUG_PRINT("can init error \r\n");
|
||||
}
|
||||
system_soft_reset();
|
||||
}
|
||||
|
||||
/* initialize filter */
|
||||
/* configure filter mode */
|
||||
can_filter.filter_mode = CAN_FILTERMODE_MASK;
|
||||
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
|
||||
/* configure filter ID */
|
||||
can_filter.filter_list_high = (uint16_t)(DEV_CAN_LOOPBACK_ID >> 13U);
|
||||
can_filter.filter_list_low =
|
||||
((uint16_t)(DEV_CAN_LOOPBACK_ID)) << 3U | (1U << 2);
|
||||
/* configure filter mask */
|
||||
can_filter.filter_mask_high = 0x0000;
|
||||
can_filter.filter_mask_low = 0x0000;
|
||||
/* select receiver fifo */
|
||||
can_filter.filter_fifo_number = CAN_FIFO0;
|
||||
can_filter.filter_enable = ENABLE;
|
||||
/* configure filter number */
|
||||
can_filter.filter_number = 0;
|
||||
can_filter_init(&can_filter);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief CAN0 FD 回环测试
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval ErrStatus
|
||||
*/
|
||||
void canfd_loopback_test(void) {
|
||||
can_trasnmit_message_struct transmit_message;
|
||||
can_receive_message_struct receive_message;
|
||||
uint32_t timeout = 0xFFFF;
|
||||
uint8_t transmit_mailbox = 0;
|
||||
|
||||
std::mt19937 gen;
|
||||
/**
|
||||
* @brief dis 函数
|
||||
* @return std::uniform_int_distribution<int> 返回值
|
||||
*/
|
||||
std::uniform_int_distribution<int> dis(0, 255);
|
||||
|
||||
can_struct_para_init(CAN_FD_FRAME_STRUCT, &transmit_message);
|
||||
transmit_message.tx_sfid = DEV_CAN_LOOPBACK_ID;
|
||||
transmit_message.tx_efid = 0x00;
|
||||
transmit_message.tx_ft = CAN_FT_DATA;
|
||||
transmit_message.tx_ff = CAN_FF_EXTENDED;
|
||||
transmit_message.tx_dlen = 7;
|
||||
transmit_message.fd_flag = 1;
|
||||
transmit_message.fd_brs = 1;
|
||||
transmit_message.fd_esi = 0;
|
||||
|
||||
int i = 0;
|
||||
while (i++ < transmit_message.tx_dlen) {
|
||||
transmit_message.tx_data[i] = dis(gen);
|
||||
CAN_DEBUG_PRINT("%x\n", transmit_message.tx_data[i]);
|
||||
}
|
||||
|
||||
/* transmit message */
|
||||
transmit_mailbox = can_message_transmit(CAN0, &transmit_message);
|
||||
/* waiting for transmit completed */
|
||||
while ((CAN_TRANSMIT_OK != can_transmit_states(CAN0, transmit_mailbox)) &&
|
||||
(0 != timeout)) {
|
||||
timeout--;
|
||||
}
|
||||
timeout = 0xFFFF;
|
||||
/* waiting for receive completed */
|
||||
while ((can_receive_message_length_get(CAN0, CAN_FIFO0) < 1) &&
|
||||
(0 != timeout)) {
|
||||
timeout--;
|
||||
}
|
||||
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &receive_message);
|
||||
can_message_receive(CAN0, CAN_FIFO0, &receive_message);
|
||||
|
||||
/* check the receive message */
|
||||
if ((transmit_message.tx_sfid == receive_message.rx_sfid) &&
|
||||
(transmit_message.tx_ff == receive_message.rx_ff) &&
|
||||
(transmit_message.tx_dlen == receive_message.rx_dlen)) {
|
||||
/* loopback test is success */
|
||||
CAN_DEBUG_PRINT("CAN LO0PBACK SUCCESS\r\n");
|
||||
} else {
|
||||
/* loopback test is failed */
|
||||
CAN_DEBUG_PRINT("CAN LO0PBACK FAIL\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief CAN0 回环测试
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void can_loopback_test(void) {
|
||||
can_trasnmit_message_struct transmit_message;
|
||||
can_receive_message_struct receive_message;
|
||||
uint32_t timeout = 0xFF;
|
||||
uint8_t transmit_mailbox = 0;
|
||||
|
||||
/**
|
||||
* @brief gen 函数
|
||||
* @param[in] rand 参数
|
||||
* @return std::mt19937 返回值
|
||||
*/
|
||||
std::mt19937 gen(rand()); // 使用随机设备生成的种子
|
||||
/**
|
||||
* @brief dis 函数
|
||||
* @param[in] xff 参数
|
||||
* @return std::uniform_int_distribution<int> 返回值
|
||||
*/
|
||||
std::uniform_int_distribution<int> dis(0, 0xff);
|
||||
|
||||
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &transmit_message);
|
||||
/* initialize transmit message */
|
||||
transmit_message.tx_sfid = DEV_CAN_LOOPBACK_ID;
|
||||
transmit_message.tx_ft = CAN_FT_DATA;
|
||||
transmit_message.tx_ff = CAN_FF_STANDARD;
|
||||
transmit_message.tx_dlen = 7;
|
||||
int i = 0;
|
||||
while (i++ < transmit_message.tx_dlen) {
|
||||
// transmit_message.tx_data[i] = dis(gen);
|
||||
transmit_message.tx_data[i] = i;
|
||||
}
|
||||
/* transmit message */
|
||||
transmit_mailbox = can_message_transmit(CANX, &transmit_message);
|
||||
/* waiting for transmit completed */
|
||||
while ((CAN_TRANSMIT_OK != can_transmit_states(CANX, transmit_mailbox)) &&
|
||||
(0 != timeout)) {
|
||||
CAN_DEBUG_PRINT("sending:%x...\n",
|
||||
can_transmit_states(CANX, transmit_mailbox));
|
||||
timeout--;
|
||||
}
|
||||
timeout = 0xFF;
|
||||
/* waiting for receive completed */
|
||||
while ((can_receive_message_length_get(CANX, CAN_FIFO0) < 1) &&
|
||||
(0 != timeout)) {
|
||||
timeout--;
|
||||
}
|
||||
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &receive_message);
|
||||
can_message_receive(CANX, CAN_FIFO0, &receive_message);
|
||||
/* check the receive message */
|
||||
if ((DEV_CAN_LOOPBACK_ID == receive_message.rx_sfid) &&
|
||||
(CAN_FF_STANDARD == receive_message.rx_ff) &&
|
||||
(transmit_message.tx_dlen == receive_message.rx_dlen)) {
|
||||
i = transmit_message.tx_dlen;
|
||||
while (i-- > 0) {
|
||||
CAN_DEBUG_PRINT("rx %d:%x:%x\n", i, transmit_message.tx_data[i],
|
||||
receive_message.rx_data[i]);
|
||||
}
|
||||
if (std::memcmp(receive_message.rx_data, transmit_message.tx_data,
|
||||
transmit_message.tx_dlen) == 0) {
|
||||
/* loopback test is success */
|
||||
CAN_DEBUG_PRINT("loopback success\n");
|
||||
} else {
|
||||
/* loopback test is failed */
|
||||
CAN_DEBUG_PRINT("loopback failed\n");
|
||||
}
|
||||
} else {
|
||||
/* loopback test is failed */
|
||||
CAN_DEBUG_PRINT("loopback failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief CAN_transmiter 构造函数
|
||||
@@ -675,7 +420,6 @@ void can_loopback_test(void) {
|
||||
*/
|
||||
u8 CAN_transmiter::init() {
|
||||
// 初始化 CAN 硬件
|
||||
// can_config();
|
||||
canfd_config();
|
||||
|
||||
// 初始化发送消息结构体
|
||||
@@ -1241,7 +985,11 @@ void CAN_transmiter::handle_control_command(can_receive_message_struct ¤t_
|
||||
FOC.target_velocity = target_velocity_union.f;
|
||||
FOC.target_current = target_current_union.f;
|
||||
|
||||
// limit_voltage=0 时 P 换算除零会得到 inf 增益,钳到下限
|
||||
float voltage = FOC.limit_voltage;
|
||||
if (voltage < MIN_LIMIT_VOLTAGE) {
|
||||
voltage = MIN_LIMIT_VOLTAGE;
|
||||
}
|
||||
u16 P_config = unpack_bytes_to_u16(current_message.rx_data, 15);
|
||||
FOC.cur_loop_p = DEFAULT_current_loop_p * P_config / voltage;
|
||||
FOC.cur_MIT_p = DEFAULT_current_MIT_p * P_config / voltage;
|
||||
@@ -1299,7 +1047,6 @@ void CAN_transmiter::handle_ota_ack_message() {
|
||||
u32 app_check = *flash_ptr;
|
||||
transmit_data[0] = 0x0A;
|
||||
transmit_data[1] = 0xAC;
|
||||
delay_1ms(1);
|
||||
if (app_enable == OTA_AVAL && app_check == OTA_AVAL) {
|
||||
CANFD_AsyncSend(build_can_id(CANFD_OTA_ACK_ID), 2, transmit_data);
|
||||
}
|
||||
|
||||
@@ -122,11 +122,6 @@ static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
|
||||
* @return u8 返回值
|
||||
*/
|
||||
u8 can_gpio_init(void); // CAN 引脚初始化
|
||||
/**
|
||||
* @brief 配置 CAN 外设
|
||||
* @return void
|
||||
*/
|
||||
void can_config(void); // CAN 初始化
|
||||
/**
|
||||
* @brief 配置 CAN FD 外设
|
||||
* @return void
|
||||
@@ -163,16 +158,6 @@ void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发
|
||||
*/
|
||||
void jumpToBOOT();
|
||||
|
||||
/**
|
||||
* @brief can_loopback_init 函数
|
||||
* @return void
|
||||
*/
|
||||
void can_loopback_init(void);
|
||||
/**
|
||||
* @brief can_loopback_test 函数
|
||||
* @return void
|
||||
*/
|
||||
void can_loopback_test(void);
|
||||
|
||||
/*!
|
||||
\brief CAN 通信类
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Can.h"
|
||||
#include "FOC.h"
|
||||
#include "Timer.h"
|
||||
#include "config.h"
|
||||
#include "gd32c10x.h"
|
||||
#include "gd32c10x_gpio.h"
|
||||
#include "gd32c10x_misc.h"
|
||||
@@ -23,16 +24,13 @@ extern CAN_transmiter CAN0_transmiter;
|
||||
extern u32 cnt_test;
|
||||
|
||||
// 任务调度查找表 - 每 100us 执行的任务 (1ms 周期内)
|
||||
// MCU/DRV 温度保持 1kHz:热模型兼作过流保护,温度积累按 1ms 步进,
|
||||
// 每毫秒都要拿到新温度采样。
|
||||
static const u32 task_schedule_100us[10] = {
|
||||
(1u << TASK_MCU_TEMP_UPDATE), // 0: MCU 温度更新
|
||||
(1u << TASK_DRV_TEMP_UPDATE), // 1: 驱动温度更新
|
||||
(1u << TASK_VBUS_UPDATE), // 2: 电压更新
|
||||
0, // 3: 空闲
|
||||
0, // 4: 空闲
|
||||
0, // 5: 空闲
|
||||
0, // 6: 空闲
|
||||
0, // 7: 空闲
|
||||
0, // 8: 空闲
|
||||
0, 0, 0, 0, 0, 0, // 3~8: 空闲
|
||||
(1u << TASK_CAN_TRANSMIT) // 9: CAN 发送
|
||||
};
|
||||
|
||||
@@ -197,10 +195,18 @@ void timer1_gpio_config(void) {
|
||||
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
|
||||
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
|
||||
|
||||
#if GATE_DRIVER_DRV8323
|
||||
/* 新板:PB12(DRVSLP=ENABLE+INLx) 由 gate_driver 统一管理——
|
||||
* gate_driver_preinit() 已置低保持芯片睡眠,gate_driver_bringup()
|
||||
* 在 SPI 配置完成后才拉高。此处严禁提前置高(SPI 未配置前芯片
|
||||
* 带默认 6x PWM 模式醒来,INLx=1 时上管永远无法导通)。
|
||||
* PB0 板上接地未用,不配置。 */
|
||||
#else
|
||||
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
||||
GPIO_PIN_0 | GPIO_PIN_12);
|
||||
gpio_bit_set(GPIOB, GPIO_PIN_0);
|
||||
gpio_bit_set(GPIOB, GPIO_PIN_12);
|
||||
#endif
|
||||
|
||||
/* 配置 TIMER1 完全重映射*/
|
||||
gpio_pin_remap_config(GPIO_TIMER1_FULL_REMAP, DISABLE);
|
||||
@@ -295,6 +301,15 @@ void TIMER1_IRQHandler(void) {
|
||||
void Set_Pwm(u16 channel, float duty_cycle) {
|
||||
float pulse = duty_cycle * TIMER1_PERIOD;
|
||||
|
||||
#if GATE_DRIVER_DRV8323
|
||||
/* 新板通道重映射:逻辑相 A→CH2、B→CH1、C→CH0(硬件 INHA/B/C
|
||||
* 分别接 PWM1/2/3 = PA2/PA1/PA0),与 Set_3Pwm 保持一致 */
|
||||
if (channel == 0) {
|
||||
channel = 2;
|
||||
} else if (channel == 2) {
|
||||
channel = 0;
|
||||
}
|
||||
#endif
|
||||
if (channel <= 3) {
|
||||
timer_channel_output_pulse_value_config(TIMER1, channel, pulse);
|
||||
} else {
|
||||
@@ -311,8 +326,17 @@ void Set_Pwm(u16 channel, float duty_cycle) {
|
||||
\note The channel parameter must be between 0 and 3 inclusive.
|
||||
*/
|
||||
void Set_3Pwm(float duty_cycle0, float duty_cycle1, float duty_cycle2) {
|
||||
// 直接写寄存器
|
||||
#if GATE_DRIVER_DRV8323
|
||||
/* 新板硬件映射:CH0(PA0)→PWM3→INHC,CH1(PA1)→PWM2→INHB,
|
||||
* CH2(PA2)→PWM1→INHA。入参按 (A,B,C) 相序书写,此处把 A/C 换到
|
||||
* 物理通道,修正新板与老板相反的 INHx 接线。 */
|
||||
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
|
||||
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
|
||||
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
|
||||
#else
|
||||
// 直接写寄存器(老板:CH0/1/2 = IN1/2/3 = A/B/C 相)
|
||||
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
|
||||
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
|
||||
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifndef _TIMER_H
|
||||
#define _TIMER_H
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
+15
-2
@@ -91,15 +91,28 @@ class sampler {
|
||||
void ADC0_1_IRQHandler(void);
|
||||
|
||||
/**
|
||||
* @brief ADC1 初始化(VBUS / DRV温度 专用,单通道单次软件触发)
|
||||
* @brief ADC1 初始化:VBUS 连续转换模式(零等待读取)
|
||||
* @return void
|
||||
* @note ADC1 上电后即对 ch6(VBUS) 连续转换,软件触发一次后自持续,
|
||||
* 之后读取 DR 寄存器即可拿到最新完成的转换值(无需阻塞等待)。
|
||||
*/
|
||||
void adc1_config(void);
|
||||
|
||||
/**
|
||||
* @brief 零等待读取最新 VBUS 原始值(连续模式下任意时刻可调)
|
||||
* @return u16 最近一次完成转换的原始值(陈旧度约 1 个转换周期 ≈1.4μs)
|
||||
* @note 仅主循环任务上下文调用(与其他 ADC1 访问串行,无并发)。
|
||||
* 原理:读 DR 硬件清 EOC,随后下一次完成转换会刷新 DR,
|
||||
* 其间完成的转换被丢弃——每次读到的都是刚完成的样本。
|
||||
*/
|
||||
u16 adc1_read_vbus_latest(void);
|
||||
|
||||
/**
|
||||
* @brief ADC1 单次软件触发转换并等待完成(仅主循环任务上下文调用)
|
||||
* @param[in] channel ADC 通道号(ADC_CHANNEL_6 / ADC_CHANNEL_9)
|
||||
* @param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
|
||||
* @return u16 转换原始值
|
||||
* @note 会临时暂停 VBUS 连续模式、切到目标通道单次转换、再恢复连续,
|
||||
* 全程约 3~5μs,期间 VBUS 读数最多滞后该时长。drv 温度 1kHz 用。
|
||||
*/
|
||||
u16 adc1_read_raw(u8 channel);
|
||||
|
||||
|
||||
+24
-107
@@ -34,10 +34,6 @@
|
||||
/** @brief DRV_SPI_CS_DISABLE 宏定义 */
|
||||
#define DRV_SPI_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
|
||||
|
||||
// SPI DMA 完成标志:由 DMA ISR 置位,spi_rw_half_word_dma() 发起传输前清零并等待。
|
||||
volatile bool spi_dma_tx_complete = false;
|
||||
volatile bool spi_dma_rx_complete = false;
|
||||
|
||||
// 全局角度传感器单例:系统只有一个 KTH7823 角度传感器。
|
||||
// 初始化顺序由 main::hardware_init() 调用 KTH7823.init() 保证。
|
||||
angle_sensor KTH7823; // 角度传感器初始化
|
||||
@@ -47,120 +43,42 @@ extern EncoderVelocityKalman Vel_kalman;
|
||||
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
|
||||
|
||||
/**
|
||||
* @brief SPI DMA initialization
|
||||
*/
|
||||
void spi_dma_init(void) {
|
||||
dma_parameter_struct dma_init_struct;
|
||||
|
||||
// 使能 DMA 时钟
|
||||
rcu_periph_clock_enable(RCU_DMA0);
|
||||
|
||||
// 初始化发送 DMA 通道
|
||||
dma_deinit(DMA0, DMA_CH2);
|
||||
dma_struct_para_init(&dma_init_struct);
|
||||
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
|
||||
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
|
||||
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
|
||||
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
|
||||
dma_init_struct.number = 1;
|
||||
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
|
||||
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
|
||||
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
|
||||
dma_init_struct.priority = DMA_PRIORITY_HIGH;
|
||||
dma_init(DMA0, DMA_CH2, &dma_init_struct);
|
||||
|
||||
// 初始化接收 DMA 通道
|
||||
dma_deinit(DMA0, DMA_CH1);
|
||||
dma_struct_para_init(&dma_init_struct);
|
||||
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
|
||||
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
|
||||
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
|
||||
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
|
||||
dma_init_struct.number = 1;
|
||||
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
|
||||
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
|
||||
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
|
||||
dma_init_struct.priority = DMA_PRIORITY_HIGH;
|
||||
dma_init(DMA0, DMA_CH1, &dma_init_struct);
|
||||
|
||||
// 使能 SPI 的 DMA 发送和接收功能
|
||||
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
|
||||
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
|
||||
|
||||
// 配置 DMA 中断
|
||||
nvic_irq_enable(DMA0_Channel2_IRQn, 0, 1);
|
||||
nvic_irq_enable(DMA0_Channel1_IRQn, 0, 1);
|
||||
|
||||
// 使能 DMA 传输完成中断
|
||||
dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF);
|
||||
dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send or read half word through SPI using DMA
|
||||
* @brief Send or read half word through SPI by register polling
|
||||
*
|
||||
* @param[in] spi_periph SPI peripheral
|
||||
* @param[in] tx_data Data to transmit
|
||||
* @param[out] rx_data Pointer to store received data
|
||||
* @return 0 if success, -1 if error
|
||||
* @note 单半字轮询收发。DMA 版本(已移除)每次传输需要:重配 2 个 DMA
|
||||
* 通道(约 10 次寄存器写) + 2 次 FTF 中断 + 忙等完成标志,而 16bit
|
||||
* @5MHz 帧本身只有 3.2μs——DMA 的准备与中断开销和传输时间同量级。
|
||||
* 轮询同样忙等帧时间,但砍掉了全部 DMA 开销与 40k 次/秒的中断。
|
||||
*/
|
||||
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
|
||||
uint16_t *rx_data) {
|
||||
// 重置完成标志
|
||||
spi_dma_tx_complete = false;
|
||||
spi_dma_rx_complete = false;
|
||||
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
|
||||
uint16_t *rx_data) {
|
||||
u32 timeout = 1000;
|
||||
|
||||
// 配置发送 DMA
|
||||
dma_channel_disable(DMA0, DMA_CH2);
|
||||
dma_memory_address_config(DMA0, DMA_CH2, (uint32_t)&tx_data);
|
||||
dma_transfer_number_config(DMA0, DMA_CH2, 1);
|
||||
dma_channel_enable(DMA0, DMA_CH2);
|
||||
|
||||
// 配置接收 DMA
|
||||
dma_channel_disable(DMA0, DMA_CH1);
|
||||
dma_memory_address_config(DMA0, DMA_CH1, (uint32_t)rx_data);
|
||||
dma_transfer_number_config(DMA0, DMA_CH1, 1);
|
||||
dma_channel_enable(DMA0, DMA_CH1);
|
||||
|
||||
// 等待传输完成,超时时间为 100 个循环
|
||||
uint32_t timeout = 100;
|
||||
while ((!spi_dma_tx_complete || !spi_dma_rx_complete) && --timeout) {
|
||||
// 空循环等待 DMA 传输完成
|
||||
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE) == RESET) {
|
||||
if (--timeout == 0U) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
spi_i2s_data_transmit(spi_periph, tx_data);
|
||||
|
||||
// 检查是否超时
|
||||
if (timeout == 0) {
|
||||
// 禁用 DMA 通道
|
||||
dma_channel_disable(DMA0, DMA_CH2);
|
||||
dma_channel_disable(DMA0, DMA_CH1);
|
||||
ANG_SENS_DEBUG_PRINT("SPI DMA transfer timeout\r\n");
|
||||
return -1;
|
||||
timeout = 1000;
|
||||
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE) == RESET) {
|
||||
if (--timeout == 0U) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*rx_data = spi_i2s_data_receive(spi_periph);
|
||||
|
||||
// 等待移位完成再返回:调用方返回后立即拉高 CS,
|
||||
// 若最后一位仍在移位会截断帧
|
||||
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TRANS) != RESET) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA0_Channel2 中断处理函数
|
||||
* @param 无
|
||||
*/
|
||||
extern "C" void DMA0_Channel2_IRQHandler(void) {
|
||||
if (dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INT_FLAG_FTF)) {
|
||||
dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INT_FLAG_FTF);
|
||||
spi_dma_tx_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA0_Channel1 中断处理函数
|
||||
* @param 无
|
||||
*/
|
||||
extern "C" void DMA0_Channel1_IRQHandler(void) {
|
||||
if (dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF)) {
|
||||
dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
|
||||
spi_dma_rx_complete = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 角度传感器初始化
|
||||
* @param 无
|
||||
@@ -216,7 +134,6 @@ void KTH7823_spi_init(void) {
|
||||
u8 angle_sensor::init(void) {
|
||||
KTH7823_gpio_init(); // GPIO 初始化
|
||||
KTH7823_spi_init(); // SPI 初始化
|
||||
spi_dma_init(); // DMA 初始化
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -239,7 +156,7 @@ inline float angle_sensor::read_sensorAngle(void) {
|
||||
uint16_t raw_data;
|
||||
|
||||
ANG_SPI_CS_ENABLE();
|
||||
u8 result = spi_rw_half_word_dma(SPI0, DEFAULT_COMMAND, &raw_data);
|
||||
u8 result = spi_rw_half_word_polling(SPI0, DEFAULT_COMMAND, &raw_data);
|
||||
ANG_SPI_CS_DISABLE();
|
||||
|
||||
if (result == 0) {
|
||||
|
||||
@@ -21,14 +21,17 @@ extern "C" {
|
||||
*/
|
||||
u16 spi_rw_half_word(u32 spi_periph, const u16 i_HalfWord);
|
||||
/**
|
||||
* @brief spi_rw_half_word_dma 函数
|
||||
* @param[in] spi_periph 参数
|
||||
* @param[in] tx_data 参数
|
||||
* @param[in] rx_data 参数
|
||||
* @return int 返回值
|
||||
* @brief spi_rw_half_word_polling 函数
|
||||
* @param[in] spi_periph SPI 外设
|
||||
* @param[in] tx_data 发送的半字
|
||||
* @param[out] rx_data 接收数据输出指针
|
||||
* @return int 0 成功,-1 超时
|
||||
* @note 单个半字收发:直接寄存器轮询。原 DMA 实现每次传输要重配两个
|
||||
* DMA 通道并触发两次 FTF 中断,之后 CPU 仍忙等完成——对单半字
|
||||
* 传输 DMA 只有开销没有收益(20kHz 调用下约 40k 次/秒额外中断)。
|
||||
*/
|
||||
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
|
||||
uint16_t *rx_data);
|
||||
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
|
||||
uint16_t *rx_data);
|
||||
|
||||
|
||||
/**
|
||||
@@ -91,8 +94,6 @@ class angle_sensor {
|
||||
float current_angle_rad;
|
||||
bool m_firstUpdate;
|
||||
float m_lastAngle;
|
||||
float m_lastcumulativeAngle;
|
||||
float velocity[VEL_FILTER_SIZE] = {0.0f};
|
||||
};
|
||||
/**
|
||||
* @brief 快速角度归一化到 [0, 360)
|
||||
|
||||
+213
-13
@@ -1,15 +1,218 @@
|
||||
/**
|
||||
* @file device/motor.cpp
|
||||
* @brief 底层设备驱动与外设封装源文件
|
||||
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
|
||||
* @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 <cstdio>
|
||||
|
||||
/* 角度传感器 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_IN_FLOATING, 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输出初始化
|
||||
*/
|
||||
@@ -18,16 +221,17 @@ void motor_init(void) {
|
||||
timer1_pwm_config();
|
||||
// 使能GPIOB时钟
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
// 配置PB2为输入引脚,用于读取电机状态
|
||||
// 配置PB2为输入引脚,用于读取电机状态(nFAULT)
|
||||
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, 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为推挽输出,用于控制电机驱动器使能
|
||||
// 配置PC13为推挽输出,用于控制电机驱动器使能(老板专用)
|
||||
gpio_init(MOTOR_EN_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, MOTOR_EN_GPIO_PIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,17 +248,13 @@ void motor_reset(void) {
|
||||
/**
|
||||
* @brief 电机休眠
|
||||
*/
|
||||
void motor_sleep(void) {
|
||||
gpio_bit_reset(GPIOB, GPIO_PIN_12);
|
||||
// 同时禁用电机驱动器
|
||||
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
|
||||
void motor_sleep(void) {
|
||||
drv_enable(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 电机启动
|
||||
*/
|
||||
void motor_resume(void) {
|
||||
gpio_bit_set(GPIOB, GPIO_PIN_12);
|
||||
// 同时启用电机驱动器
|
||||
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
|
||||
}
|
||||
void motor_resume(void) {
|
||||
drv_enable(1);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#ifndef _MOTOR_H
|
||||
#define _MOTOR_H
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "gd32c10x_eval.h"
|
||||
|
||||
// 定义电机驱动使能引脚
|
||||
/** @brief MOTOR_EN_GPIO_PORT 宏定义 */
|
||||
#define MOTOR_EN_GPIO_PORT GPIOC
|
||||
@@ -36,4 +40,37 @@ void motor_resume(void);
|
||||
*/
|
||||
void motor_sleep(void);
|
||||
|
||||
/**
|
||||
* @brief 门驱动芯片引脚预初始化(全部置安全态:使能拉低、CS 释放)
|
||||
* @note 必须在任何可能拉高使能脚的代码之前调用
|
||||
* @return void
|
||||
*/
|
||||
void gate_driver_preinit(void);
|
||||
/**
|
||||
* @brief 门驱动芯片上电配置
|
||||
* 新板(DRV8323RS):拉高使能 → SPI 写 3x PWM 模式/CSA 增益/VDS
|
||||
* 保护/死区/IDRIVE 并逐条回读校验,最后恢复角度传感器 SPI 模式;
|
||||
* 老板(DRV8313):无 SPI,保持原上电序列(无操作)。
|
||||
* @return u8 0 成功;非 0 为配置/回读校验失败的寄存器位图
|
||||
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后(此时使能芯片,
|
||||
* 半桥只会进入"下管常开"的静止安全态)
|
||||
*/
|
||||
u8 gate_driver_bringup(void);
|
||||
/**
|
||||
* @brief 门驱动运行时使能/睡眠(分板封装 PB12,老板同时控制 PC13)
|
||||
* @param[in] en 1=使能 0=睡眠
|
||||
* @return void
|
||||
*/
|
||||
void drv_enable(u8 en);
|
||||
/**
|
||||
* @brief 读取 nFAULT 引脚状态
|
||||
* @return u8 1=故障有效(低电平) 0=正常
|
||||
*/
|
||||
u8 drv_nfault_active(void);
|
||||
/**
|
||||
* @brief 读取门驱动故障状态寄存器(仅 DRV8323 有效)
|
||||
* @return u16 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
|
||||
*/
|
||||
u32 drv_fault_status(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user