電機驅動實現插補算法之脈沖和方向接收(以stm32主控為例)

一、方案 A(推薦):編碼器模式吃脈沖(TI1 = STEP,TI2 = DIR)

核心思路

把定時器設為 Encoder TI1 模式:每個 STEP 上升沿計一次,在那個沿的瞬間用 TI2(DIR)的電平決定加/減。硬件完成“鎖存方向”的動作,不用擔心一個 FOC 周期內 DIR 翻轉多次造成錯賬。

典型接線(以 TIM2 為例)

  • STEP → TIM2_CH1(PA0,AF1)
  • DIR → TIM2_CH2(PA1,AF1)
    -(可選)給 STEP、DIR 做差分/光耦/RC 小濾波;靠近 MCU 端各串 ~100 Ω + 100 pF 抑毛刺。

HAL 初始化代碼(TIM2和TIM5)

// ========== GPIO: PA0/PA1 復用為 TIM2_CH1/CH2 ==========
static void StepDir_GPIO_Init_TIM2(void) {__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitTypeDef G = {0};G.Mode      = GPIO_MODE_AF_PP;G.Pull      = GPIO_PULLDOWN;              // 依現場電路定:上拉或下拉二選一,空閑低更穩G.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;G.Alternate = GPIO_AF1_TIM2;// STEP -> PA0 (TIM2_CH1)G.Pin = GPIO_PIN_0;HAL_GPIO_Init(GPIOA, &G);// DIR -> PA1 (TIM2_CH2)G.Pin = GPIO_PIN_1;HAL_GPIO_Init(GPIOA, &G);
}// ========== TIM2: 編碼器模式(TI1計步,TI2判方向) ==========
void StepDir_Encoder_Init_TIM2(void) {__HAL_RCC_TIM2_CLK_ENABLE();StepDir_GPIO_Init_TIM2();htim2.Instance = TIM2;htim2.Init.Prescaler         = 0;                     // 1:1htim2.Init.CounterMode       = TIM_COUNTERMODE_UP;    // 編碼器模式下無實際影響htim2.Init.Period            = 0xFFFFFFFF;            // 32位滿量程htim2.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;htim2.Init.RepetitionCounter = 0;TIM_Encoder_InitTypeDef enc = {0};enc.EncoderMode = TIM_ENCODERMODE_TI1; // 用TI1為時鐘,TI2提供方向// TI1 = STEPenc.IC1Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 計上升沿(與上位保持一致)enc.IC1Selection = TIM_ICSELECTION_DIRECTTI;enc.IC1Prescaler = TIM_ICPSC_DIV1;enc.IC1Filter    = 8;  // 數字濾波:8~12 先試;若高頻步進需減小該值// TI2 = DIR(作為方向電平被鎖存)enc.IC2Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 極性對電平鎖存無本質影響enc.IC2Selection = TIM_ICSELECTION_DIRECTTI;enc.IC2Prescaler = TIM_ICPSC_DIV1;enc.IC2Filter    = 8;HAL_TIM_Encoder_Init(&htim2, &enc);TIM_MasterConfigTypeDef master = {0};master.MasterOutputTrigger = TIM_TRGO_RESET;master.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&htim2, &master);__HAL_TIM_SET_COUNTER(&htim2, 0);HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
}

在 FOC 周期讀取步數增量

// 每個 FOC 控制中斷(例如 10 kHz)調用一次
static uint32_t s_last_cnt = 0;int32_t StepDir_ReadDelta_EncoderMode(void) {uint32_t now = __HAL_TIM_GET_COUNTER(&htim2);// 利用無符號溢出特性做差,再轉有符號即為帶方向的增量int32_t  d   = (int32_t)(now - s_last_cnt);s_last_cnt   = now;return d; // 單位:步
}// 例:把步→機械角度(或直線位移)
/*
const float THETA_PER_STEP = 2.0f * (float)M_PI / (steps_per_rev * microstep * gear_ratio);
pos_ref += StepDir_ReadDelta_EncoderMode() * THETA_PER_STEP;
*/

要點

  • DIR 建立時間必須由上游(A 板)滿足:變向后延時 ≥2–5 μs 再發第一步。
  • STEP 極性不對時改 IC1Polarity 或對端極性。
  • 該方案不需要 EXTI,不會因 DIR 多次翻轉出錯,最省心

二、方案 B:外部時鐘模式(STEP)+ DIR GPIO(帶 EXTI 結清)

核心思路

  • STEP 接到 定時器外部時鐘(ETR 或 TI1),只向上計數;不進中斷
  • DIR 接到 GPIO,并配置 EXTI 上/下沿觸發。每次 DIR 翻轉,立即把當前計數器的步數“按舊方向結清并清零”,隨后更新方向標志。
  • 在 FOC 周期里再把“從上次事件到現在”的剩余步數按“當前方向”結清,得到本周期凈步數。
    這就等價于“硬件按步沿鎖存方向”,避免“一個周期多次翻轉”的錯賬。

典型接線(以 TIM2 外部時鐘2為例)

  • STEP → TIM2_ETR(PA15,AF1) (或選擇 TI1 外部時鐘1:PA0)
  • DIR → 任意 GPIO(例:PB3)

若你更傾向 TI1 模式:把 STEP 接 TIM2_CH1,將 TIM2 配置為 External Clock Mode 1(SMCR.TS=TI1FP1 + SMS=ECM1)。其余邏輯一致。

HAL 初始化代碼(ETR 版本)

// ----- 全局狀態 -----
TIM_HandleTypeDef htim2;
volatile int      g_dir_sign = +1;     // 當前方向(+1/-1)
volatile int32_t  g_step_accum = 0;    // 已結清的步(帶符號)// ====== 1) GPIO ======
static void StepDir_GPIO_Init_ExternalClock(void) {__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();// STEP -> PA15 (TIM2_ETR, AF1)GPIO_InitTypeDef G = {0};G.Pin       = GPIO_PIN_15;G.Mode      = GPIO_MODE_AF_PP;G.Pull      = GPIO_PULLUP;G.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;G.Alternate = GPIO_AF1_TIM2;HAL_GPIO_Init(GPIOA, &G);// DIR -> PB3 (普通輸入 + EXTI)G.Pin  = GPIO_PIN_3;G.Mode = GPIO_MODE_IT_RISING_FALLING; // 上/下沿都觸發G.Pull = GPIO_PULLUP;                  // 依現場決定HAL_GPIO_Init(GPIOB, &G);// NVIC for EXTI3 (PB3 對應 EXTI3)HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0);HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}// ====== 2) TIM2 外部時鐘(ETR) ======
void StepDir_Init_ExternalClock_ETR(void) {__HAL_RCC_TIM2_CLK_ENABLE();StepDir_GPIO_Init_ExternalClock();// 初始化當前方向(按現有電平)g_dir_sign = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) ? +1 : -1;htim2.Instance = TIM2;htim2.Init.Prescaler         = 0;htim2.Init.CounterMode       = TIM_COUNTERMODE_UP;htim2.Init.Period            = 0xFFFFFFFF;  // 32位htim2.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;HAL_TIM_Base_Init(&htim2);TIM_ClockConfigTypeDef clk = {0};clk.ClockSource = TIM_CLOCKSOURCE_ETRMODE2;   // ETR 外部時鐘模式2HAL_TIM_ConfigClockSource(&htim2, &clk);// 如需設置 ETR 濾波/極性/預分頻,可用 LL 接口更細化(見下)__HAL_TIM_SET_COUNTER(&htim2, 0);HAL_TIM_Base_Start(&htim2);
}// (可選)用 LL 細化 ETR 濾波/極性(HAL 有的庫不暴露這些)
/*
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
// ETR濾波 ETF,ETP極性,ETPS預分頻
LL_TIM_ConfigETR(TIM2, LL_TIM_ETR_POLARITY_NONINVERTED,LL_TIM_ETR_PRESCALER_DIV1,LL_TIM_ETR_FILTER_FDIV32_N8); // 例:較強濾波
LL_TIM_EnableExternalClock(TIM2); // ECM2
*/// ====== 3) DIR EXTI 中斷:翻轉即結清 ======
void EXTI3_IRQHandler(void) {if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_3) != RESET) {__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3);// 讀并清 STEP 計數器(這一段屬于“舊方向”)uint32_t steps = __HAL_TIM_GET_COUNTER(&htim2);__HAL_TIM_SET_COUNTER(&htim2, 0);g_step_accum += (int32_t)steps * g_dir_sign;// 更新為新方向(當前電平)g_dir_sign = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) ? +1 : -1;}
}

FOC 周期里合并“剩余步數”并清零

注意與 EXTI 的并發:用關鍵段確保讀-清-累加的原子性,避免 EXTI 在中途打斷造成重復/漏記。

int32_t StepDir_ReadDelta_ExternalClock(void) {uint32_t primask = __get_PRIMASK(); // 備份中斷狀態__disable_irq();                    // 進關鍵段(短)uint32_t steps = __HAL_TIM_GET_COUNTER(&htim2);__HAL_TIM_SET_COUNTER(&htim2, 0);int32_t total = g_step_accum + (int32_t)steps * g_dir_sign;g_step_accum = 0;if (!primask) __enable_irq();return total; // 本周期凈步(帶符號)
}

如果改用 TI1 外部時鐘模式(非 ETR)

把 STEP 接 TIM2_CH1(PA0),改成 External Clock Mode 1。示意 LL 配置(HAL 有版本差異):

// 關鍵點:SMCR.SMS = External Clock Mode 1,SMCR.TS = TI1FP1
LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1);
LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_EXTERNAL1);
// CH1 輸入濾波/極性
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV32_N8);
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
LL_TIM_EnableCounter(TIM2);

DIR 部分與上文 EXTI 邏輯相同。

要點

  • A 板必須滿足 t_dir_setup:變向→延時≥2–5 μs→再發第一步;
  • DIR EXTI 可做軟件去抖:若檢測到相鄰中斷時間 < 1–2 μs,可判定毛刺丟棄;
  • STEP 最高頻受限于信號質量/濾波設置。數字濾波數值越大,抗干擾越強但最小脈寬要求也更高;可從 8~12 試起,再按需要減小。

工程級細節與建議(兩方案通用)

  1. 計數位寬:用 TIM2/TIM5(32 位) 把溢出顧慮降到最低;若被占用,用 16 位計數器也行,但要在 FOC 周期里讀增量避免溢出。

  2. 單位換算

    // 步 -> 機械角度rad
    theta_per_step = 2π / (motor_steps_per_rev * microstep * gear_ratio);
    pos_ref += delta_steps * theta_per_step;// 或換成速度(rad/s)
    vel_ref = (delta_steps * theta_per_step) * FOC_rate; // FOC_rate = 控制周期頻率
    
  3. 極性/方向:若方向與期望相反,優先改上游 DIR 極性;或在本地把 g_dir_sign 取反/交換 TI 極性。

  4. 毛刺與布線:長線優先差分、近端 RC、上/下拉一致;數字濾波盡量啟用(ICxF/ETF)。

  5. 并發安全:方案 B 中合并步數使用短關鍵段即可,不會影響 FOC 實時性;DIR 翻轉頻率遠低于 STEP,不會成為瓶頸。


選型結論

  • 優先:方案 A(編碼器模式)——硬件在每個 STEP 沿鎖存 DIR,邏輯最干凈、最接近工業伺服。
  • 備選:方案 B(外部時鐘 + DIR EXTI 結清)——當引腳受限或只能用 ETR/TI1 時,依然穩。

三、用STM32F405實現方案A

1) 硬件接線(以 TIM2 為例,32 位計數器,推薦)

  • STEPTIM2_CH1(PA0,AF1)
  • DIRTIM2_CH2(PA1,AF1)
  • 建議:STEP、DIR 近 MCU 端各串 ~100 Ω + 100 pF 小 RC;長線優先差分/光耦隔離;MCU 端上拉/下拉擇一(和對端一致)。

若 TIM2 被占用,可用 TIM5(同為 32 位):
STEP→TIM5_CH1(PA0, AF2),DIR→TIM5_CH2(PA1, AF2);代碼里把 TIM2 全部替換為 TIM5 并改 AF 號。


2) 工作原理(編碼器 TI1 模式)

把定時器設為 Encoder TI1

  • STEP(TI1)上升沿每來一次,計數器 ±1;
  • DIR(TI2)電平在該沿被硬件鎖存,決定是加還是減;
  • 你在 FOC 周期里讀 CNT 的增量就是凈步數(帶符號)
    這與工業伺服“Step/Dir 口 + 硬件方向鎖存”一致,不用擔心周期內 DIR 多次翻轉

3) 關鍵參數(按51200 步/圈)

// ---- 機械參數(按需修改)----
#define STEPS_PER_REV     51200.0f   // 每圈步數(細分后)
#define GEAR_RATIO        1.0f       // 減速比(電機軸到負載),有減速箱就寫>1
// 方向是否取反(接線導致方向反了就置1)
#define INVERT_DIR        0// ---- 控制參數(示例)----
#define FOC_RATE_HZ       10000.0f   // 你的FOC控制中斷頻率(例:10 kHz)// ---- 換算 ----
#define STEP_TO_RAD  (2.0f * 3.14159265358979f / (STEPS_PER_REV * GEAR_RATIO))
#define STEPS_TO_RAD(steps)  ((steps) * STEP_TO_RAD)
#define STEPS_TO_RAD_PER_SEC(steps_per_tick)  ((steps_per_tick) * STEP_TO_RAD * FOC_RATE_HZ)

4) HAL 初始化代碼(GPIO + TIM2 編碼器模式)

放到你的 xxx_init.c 或者專用 stepdir.c 里即可;GPIO 的 MSP 也可集中放在 stm32f4xx_hal_msp.c

#include "stm32f4xx_hal.h"
#include <math.h>//二選一
TIM_HandleTypeDef htim2;
//TIM_HandleTypeDef htim5;// ========== GPIO: PA0/PA1 復用為 TIM2_CH1/CH2 ==========
static void StepDir_GPIO_Init_TIM2(void) {__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitTypeDef G = {0};G.Mode      = GPIO_MODE_AF_PP;G.Pull      = GPIO_PULLDOWN;              // 依現場電路定:上拉或下拉二選一,空閑低更穩G.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;G.Alternate = GPIO_AF1_TIM2;// STEP -> PA0 (TIM2_CH1)G.Pin = GPIO_PIN_0;HAL_GPIO_Init(GPIOA, &G);// DIR -> PA1 (TIM2_CH2)G.Pin = GPIO_PIN_1;HAL_GPIO_Init(GPIOA, &G);
}// ========== TIM2: 編碼器模式(TI1計步,TI2判方向) ==========
void StepDir_Encoder_Init_TIM2(void) {__HAL_RCC_TIM2_CLK_ENABLE();StepDir_GPIO_Init_TIM2();htim2.Instance = TIM2;htim2.Init.Prescaler         = 0;                     // 1:1htim2.Init.CounterMode       = TIM_COUNTERMODE_UP;    // 編碼器模式下無實際影響htim2.Init.Period            = 0xFFFFFFFF;            // 32位滿量程htim2.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;htim2.Init.RepetitionCounter = 0;TIM_Encoder_InitTypeDef enc = {0};enc.EncoderMode = TIM_ENCODERMODE_TI1; // 用TI1為時鐘,TI2提供方向// TI1 = STEPenc.IC1Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 計上升沿(與上位保持一致)enc.IC1Selection = TIM_ICSELECTION_DIRECTTI;enc.IC1Prescaler = TIM_ICPSC_DIV1;enc.IC1Filter    = 8;  // 數字濾波:8~12 先試;若高頻步進需減小該值// TI2 = DIR(作為方向電平被鎖存)enc.IC2Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 極性對電平鎖存無本質影響enc.IC2Selection = TIM_ICSELECTION_DIRECTTI;enc.IC2Prescaler = TIM_ICPSC_DIV1;enc.IC2Filter    = 8;HAL_TIM_Encoder_Init(&htim2, &enc);TIM_MasterConfigTypeDef master = {0};master.MasterOutputTrigger = TIM_TRGO_RESET;master.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&htim2, &master);__HAL_TIM_SET_COUNTER(&htim2, 0);HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
}// ========== GPIO: PA0/PA1 復用為 TIM5_CH1/CH2 ==========
static void StepDir_GPIO_Init_TIM5(void) {__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitTypeDef G = {0};G.Mode      = GPIO_MODE_AF_PP;G.Pull      = GPIO_PULLDOWN;              // 依現場電路定:上拉或下拉二選一,空閑低更穩G.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;G.Alternate = GPIO_AF2_TIM5;               // ★ TIM5 用 AF2// STEP -> PA0 (TIM5_CH1)G.Pin = GPIO_PIN_0;HAL_GPIO_Init(GPIOA, &G);// DIR -> PA1 (TIM5_CH2)G.Pin = GPIO_PIN_1;HAL_GPIO_Init(GPIOA, &G);
}// ========== TIM5: 編碼器模式(TI1計步,TI2判方向) ==========
void StepDir_Encoder_Init_TIM5(void) {__HAL_RCC_TIM5_CLK_ENABLE();               // ★ TIM5 時鐘StepDir_GPIO_Init_TIM5();htim5.Instance = TIM5;htim5.Init.Prescaler         = 0;                     // 1:1htim5.Init.CounterMode       = TIM_COUNTERMODE_UP;    // 編碼器模式下無實際影響htim5.Init.Period            = 0xFFFFFFFF;            // 32位滿量程htim5.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;htim5.Init.RepetitionCounter = 0;TIM_Encoder_InitTypeDef enc = {0};enc.EncoderMode = TIM_ENCODERMODE_TI1; // 用 TI1 為時鐘,TI2 提供方向// TI1 = STEPenc.IC1Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 計上升沿(與上位保持一致)enc.IC1Selection = TIM_ICSELECTION_DIRECTTI;enc.IC1Prescaler = TIM_ICPSC_DIV1;enc.IC1Filter    = 8;  // 8~12 先試;要更高步頻可適當減小// TI2 = DIR(方向電平被鎖存)enc.IC2Polarity  = TIM_INPUTCHANNELPOLARITY_RISING;   // 極性對電平鎖存影響不大enc.IC2Selection = TIM_ICSELECTION_DIRECTTI;enc.IC2Prescaler = TIM_ICPSC_DIV1;enc.IC2Filter    = 8;HAL_TIM_Encoder_Init(&htim5, &enc);TIM_MasterConfigTypeDef master = {0};master.MasterOutputTrigger = TIM_TRGO_RESET;master.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&htim5, &master);__HAL_TIM_SET_COUNTER(&htim5, 0);HAL_TIM_Encoder_Start(&htim5, TIM_CHANNEL_ALL);
}

關于濾波(ICxF):值越大抗毛刺越強,但可接受的最小脈寬越大(最高步頻越低)。
一般先用 8~12 驗證;若要跑更高頻(>200 kHz),逐步調小濾波,或提升電氣整形質量。


5) 讀增量 & 注入 FOC(示例)

// 累計位置(以“步”為單位),用64位避免長時間溢出
static volatile int64_t s_pos_steps_accum = 0;
static uint32_t         s_last_cnt = 0;// 每次FOC控制中斷(例:10kHz)調用
static inline int32_t StepDir_ReadDeltaSteps(void)
{uint32_t now = __HAL_TIM_GET_COUNTER(&htim2);// 無符號差處理溢出,再轉有符號就是帶方向的增量int32_t delta = (int32_t)(now - s_last_cnt);s_last_cnt = now;#if INVERT_DIRdelta = -delta; // 方向反接時一鍵取反
#endifs_pos_steps_accum += delta;return delta;
}// 示例:在你的 FOC 周期函數里使用
void FOC_Control_ISR(void)
{int32_t dsteps = StepDir_ReadDeltaSteps();// 目標位置增量(弧度)float delta_pos_rad = STEPS_TO_RAD((float)dsteps);// 目標速度(弧度/秒),如需速度環float vel_ref_rad_s = STEPS_TO_RAD_PER_SEC((float)dsteps);// 將目標注入外環(按你的控制結構)// pos_ref += delta_pos_rad;// vel_ref = vel_ref_rad_s;// ... 之后進入速度/電流環計算(你的FOC實現)
}// 歸零(回零完成后可調用)
static inline void StepDir_Zero(void)
{__HAL_TIM_SET_COUNTER(&htim2, 0);s_last_cnt = 0;s_pos_steps_accum = 0;
}

6) 常見問題與工程注意

  1. DIR 建立時間

    • 路線規劃的主控板發送變向脈沖后,務必延時 ≥2–5 μs再發第一步(行業常規要求)。編碼器模式會在STEP 的那個沿采樣 DIR 電平,有這個建立時間就穩如老狗。
  2. 極性不一致

    • 如果發現“DIR=正”卻反向增加,最快的處理:#define INVERT_DIR 1
    • 或改上位機 DIR 邏輯;或交換電機線相(若允許)。
  3. 最高步頻與濾波

    • 數字濾波值越大,允許的最小脈寬越大,從而限制最高頻;
    • 實測時從 ICxF=8~12 起步,觀測丟步/毛刺情況,再折中到你需要的最高頻;
    • 電氣上盡量用差分/光耦、良好地線、靠近 MCU 的 RC 抑毛刺,可以讓濾波開得更小從而上更高頻。
  4. 計數位寬

    • TIM2/TIM5(32 位) 基本無需擔心溢出;
    • 依然推薦只在 FOC 周期用增量,而不是長期依賴絕對 CNT
  5. 對齊到機械/電角度

    • 上面 STEP_TO_RAD機械角度換算。若你的 FOC 內部用電角度,乘以極對數即可。
  6. 自檢與診斷(可選)

    • 聯調階段可用輸入捕獲+DMA記錄若干 STEP 的時間戳,檢查上位機步間隔抖動與 t_dir_setup 是否滿足;量產時關閉。

7) 如果改用 TIM5(引腳被占時)

把 GPIO AF 改為 AF2,其余相同:

// STEP -> PA0 (TIM5_CH1, AF2)
// DIR  -> PA1 (TIM5_CH2, AF2)
G.Alternate = GPIO_AF2_TIM5; // 僅此處不同
// 然后把所有 TIM2 改成 TIM5

這樣配置后,驅動板就擁有“工業伺服式 Step/Dir 接口”

  • A 板輸出脈沖與方向;
  • B 板硬件在每個 Step 沿鎖存方向、計正負步;
  • 你只在 FOC 周期讀增量并換算成位置/速度即可。

8)IC1Filter 與 IC2Filter 介紹再調試技巧

主要就三件事:它濾的是什么、會犧牲什么、怎么按你目標步頻和脈寬倒推數值。


它到底在做什么

  • ICxF 是定時器通道前端的“數字去抖濾波器”
    作用:只有當輸入在某個采樣頻率 f_sampling連續 N 次保持同一電平,才把這次變化“放行”為一個有效沿。

  • ICxF(4bit)并不是簡單的 0~15 階,而是選擇兩樣東西

    1. 采樣頻率 f_sampling = f_DTS / M(M 為某個分頻因子),
    2. 需要連續通過的樣本數 N(通常取 2、4 或 8)。
  • f_DTS 來源于定時器數字濾波時鐘(由定時器時鐘和 CR1.CKD 派生)。

  • IC1Filter 作用在 STEP 通道(TI1)IC2Filter 作用在 DIR 通道(TI2)(編碼器模式下,硬件在 STEP 的那個有效沿鎖存此刻的 DIR 電平)。

結論:ICxF 越大 → 抗毛刺越強,但輸入允許的最高頻率越低、沿的傳播延遲變大。參考手冊 RM0090 的定時器章節有完整映射表(每個 ICxF 對應的 MN 組合)。(STMicroelectronics)


你能用的兩條硬規則(很實用)

把你 A 板輸出的 STEP 脈沖看成高/低各一段時間:

  • 邊沿“能被看見”的必要條件(不丟沿):

    t_high ≥ N / f_sampling   且   t_low ≥ N / f_sampling
    

    也就是高、低最短持續時間都要夠“連過 N 個采樣”。

  • 50% 占空近似下的最高可通過頻率(方便估算):

    f_step_max ≈ f_sampling / (2N)
    

這兩條直接把“濾波強度(N)/采樣頻率(f_sampling)”跟你的最短脈寬/最高步頻綁在一起了。


怎么一步到位地“倒推”ICxF

  1. 量/定你的上限:

    • 最高步頻 f_step_target
    • 最短高/低電平寬度 t_min(若不是 50% 占空就取兩者中的較小值)。
  2. 選一個 N

    • 工程上常用 N=4 起步(穩、延遲不大);
    • 線纜很長/干擾大可上 N=8
  3. 算需要的采樣頻率

    • 按“最短脈寬”保守:f_sampling ≥ N / t_min × 安全系數(≈1.5~2)
    • 或按“最高步頻”保守:f_sampling ≥ 2N × f_step_target × 安全系數
  4. 在手冊表里挑 ICxF

    • 找到**≥ 目標 f_sampling**的那個 ICxF 組合(它對應某個分頻 MN)。
    • 同時別忘了 CR1.CKD 也會影響 f_DTS(一般保持 DIV1,讓 f_sampling 有更大余量)。(STMicroelectronics)

例子(演示思路):
假設 TIM5 時鐘 ~84 MHz,CKD=DIV1,你要撐到 200 kHz 的步頻并留一倍余量,且選 N=4
f_sampling ≥ 2×4×200k×2 ≈ 3.2 MHz
你就在表里挑一個 IC1Filterf_sampling ≥ 3.2 MHz(比如分頻到 ~8–16 MHz 量級都夠),再上機驗證。


實操建議(不看表也能調起來)

  • 編碼器模式(方案A)

    • IC1Filter(STEP)從 8~12 起步:通常能有效滅抖;若高頻運行丟步,就逐步減小(比如 6、4、2、0)。
    • IC2Filter(DIR)可與 IC1Filter 持平或略大 1~2 檔:方向線通常比步線更怕毛刺。
    • CR1.CKD 先用 DIV1,真的很吵再考慮加大 CKD(會整體放慢 f_sampling,要同步下調 ICxF)。
  • 觀察與對癥

    • 空閑時計數跳動/上電就到 0xFFFFFFFF

      • 給 STEP/DIR 設合適下拉(空閑低更穩)、增大 ICxF、按“先啟用后清零”的順序再清一次 CNT。
    • 高速下丟步

      • 減小 IC1Filter 或把 CKD 調回 DIV1;同時確認 A 板脈寬是否達到驅動器規范(不少于 2–3 μs)。
    • 偶發錯向/負跳

      • 增大 IC2Filter;檢查 t_dir_setup(變向到第一步的延時)是否 ≥ 2–5 μs。
    • 光耦尾沿回跳

      • 適當增大 ICxF 或加 RC/施密特;必要時把“有效沿”選在回跳更干凈的邊。

一個可復制的“調參流程”

  1. 先把 STEP/DIR 空閑電平穩定(多用 PULLDOWN,空閑低+正脈沖最穩)。

  2. IC1Filter=10IC2Filter=12CKD=DIV1 啟動,空閑觀察 CNT=0

  3. 帶著邏輯分析儀跑到目標最高步頻:

    • 若丟步 → 依次把 IC1Filter 降到 8、6、4… 直到滿頻不丟;
    • 若仍有毛刺/誤計 → 把 IC2Filter 再提 1–2 檔,必要時 STEP 也提一檔并稍降最高步頻或加硬件整形。
  4. 定下來的 ICxF 記錄在案,再按 t_dir_setup脈寬裕量 做一次回歸測試。


需要查表的時候

當你要精確算某個 ICxF 對應的 f_sampling/N,或想用 ETR 的 ETF 做同樣事情(方案B)時,直接開 RM0090 的定時器章節,里面有一張“ICxF/ETF 取值 → 采樣分頻 M & 連續樣本數 N”的對照表;把你的定時器時鐘、CKD、該表三者代入上面的兩個公式即可。(STMicroelectronics)


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94056.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94056.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94056.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[特殊字符] 潛入深淵:探索 Linux 內核源碼的奇幻之旅與生存指南

文章目錄 朋友們&#xff0c;敲黑板&#xff01;&#xff01;&#xff01;&#xff08;超級重要&#xff09;我們今天聊點硬核的——不是普通的代碼&#xff0c;而是驅動了整個數字世界心跳的Linux內核源代碼&#xff01;它藏在哪&#xff1f;就在那個傳奇倉庫&#xff1a;torv…

如何監控和管理微服務之間的調用關系

監控和管理微服務之間的調用關系需要結合分布式追蹤、服務依賴分析、實時監控和可視化工具&#xff0c;以實現對調用鏈路的可見性、問題定位和性能優化。以下是具體的實現方案和工具鏈&#xff1a;一、核心監控目標調用鏈路可視化&#xff1a;展示服務間的調用關系、方向和頻率…

3.4 縮略詞抽取

概述 縮略詞指的是一個詞或者短語的縮略形式&#xff0c;其通常由原詞中的一些組成部分構成&#xff0c;同時保持原詞的含義。縮略詞的檢測與抽取在方法上與同義詞的檢測與抽取類似&#xff0c;但是相比同義詞&#xff0c;縮略詞在文本中出現的規則往往更簡單。 不同語言縮略詞…

Lua腳本如何執行主程序的C函數

Lua Call C function Lua腳本可以和C函數靈活的結合&#xff0c;Lua腳本作為控制語言&#xff0c;使用C函數來做性能計算部分。 Lua腳本的執行器有Lua 和 Luajit。 使用Lua執行器&#xff0c;可以通過C語言注冊C函數到Lua State, 然后Lua腳本可以調用該C函數。 使用Luajit&…

農業-學習記錄

1-git上傳&#xff08;簡易版&#xff09;首先&#xff0c;記得vscode打開的項目路徑是/home/Agribrain。不然會提示上傳很多項目無關的文件。① 暫存所有更改②使用vscode終端注意&#xff1a;終端打開路徑&#xff1a;/home/Agribrain/agribrain【git項目的所在目錄】&#x…

什么是數據集成?數據集成對數據治理有什么影響

在大數據與人工智能驅動的時代&#xff0c;數據已經成為企業的“新型生產力”。然而&#xff0c;企業內部數據往往分散在不同系統、不同格式、甚至不同地域中。如果缺乏有效管理與整合&#xff0c;數據價值就無法真正釋放。這時&#xff0c;“數據集成”應運而生&#xff0c;它…

技術成長戰略是什么?

文章目錄技術成長戰略是什么&#xff1f;1. 前言2. 跟技術大牛學成長戰略2.1 系統性能專家案例2.2 從開源到企業案例2.3 技術媒體大V案例2.4 案例小結3. 學習金字塔和刻意訓練4. 戰略思維的誕生5. 建議技術成長戰略是什么&#xff1f; 1. 前言 在波波的微信技術交流群里頭&am…

從0到1打造一臺機器人走起來

聚焦仿人雙足機器人,著重解決其下肢魯棒行走中仿真到實機間隔(SimToReal gap)的誤差問題 總述 硬件:采用傅利葉智能科技一體化關節模組: 1)胯部和膝關節選用 FSA80 - 29E 電機,以承受較大扭矩; 2)大腿部分采用 FSA60 - 43E 電機,兼顧扭矩和轉速需求; 3)小腿選用 …

【Cmake】Cmake概覽

目錄 一.環境準備 1.1.Cmake安裝 1.2. VSCodeCMake插件安裝 1.3 快速樣例-helloworld?程 二. cmake的基礎命令?使用示例 2.1.文件準備 2.2.?成構建系統 2.3.編譯連接 2.4.測試Ctest模塊 2.5.測試安裝模塊 2.6.測試打包模塊 2.7 查看幫助 CMake語法簡潔清晰&…

概率核心概念學習筆記:隨機事件與樣本空間、古典概率與條件概率、全概率公式與貝葉斯公式

目錄 一、 隨機事件與樣本空間 1. 原理講解 2. 類型與關系 3. 案例計算 4. 應用場景 二、 古典概率與條件概率 1. 古典概率 (Classical Probability) 2. 條件概率 (Conditional Probability) 三、 全概率公式與貝葉斯公式 1. 全概率公式 (Law of Total Probability) …

優考試局域網系統V6.0.0版

優考試局域網系統迎來V6.0.0版本更新&#xff0c;核心在于提升功能性能與優化操作體驗。重點對學情分析、移動端考試支持、考試監控和答題體驗等方面進行了實用性更新&#xff0c;進一步提升了局域網環境下考試系統的靈活性與管理效率。 一、增加學情分析功能&#xff0c;教學…

Autosar之Com模塊

Com模塊主要實現了Signal在I-PDU中的封裝及解析功能,為RTE層提供了基于Signal的發送與接收接口,實現了基于Signal的網關功能,實現了PDU的不同發送模式,以及Signal濾波,Update bit,Pdu Counter等功能 圖 Com模塊層次圖 Com模塊處于AUTOSAR架構中的通信服務層,其下層模塊…

【iOS】NSRunLoop

目錄 概念 RunLoop與線程的關系 Runloop對外的接口 CFRunLoopSourceRef Source0 Source1 CFRunLoopTimer CFRunLoopObserver RunLoop的Mode 應用場景 Runloop的內部邏輯 Runloop應用 tableView延遲加載圖片&#xff0c;保證流暢 Timer不被ScrollView的滑動影響 A…

HTTP接口鑒權方式

幾種主流且可行的HTTP接口鑒權方式&#xff0c;從簡單到復雜&#xff0c;各有其適用場景。我將它們分為兩大類&#xff1a;傳統方式和現代方式。一、傳統方式這類方式簡單易用&#xff0c;但通常安全性較低或擴展性較差&#xff0c;適用于內部系統或簡單API。1. HTTP Basic Aut…

DIC技術極端環境案例分享:系泊鏈在海水環境下氫脆化性能測試

實驗結果的具體視頻可詳見以下鏈接&#xff1a;研索儀器DIC技術在極端條件下的應用 01 海水環境&#xff1a; DIC技術在海水環境下的應用核心挑戰在于惡劣的光學條件&#xff08;如散射、衰減、畸變&#xff09;、嚴酷的化學/生物環境&#xff08;腐蝕、生物污損&#xff09;…

DL00291-聯邦學習以去中心化鋰離子電池健康預測模型完整實現

聯邦學習在鋰離子電池健康預測中的應用&#xff1a;去中心化訓練與客戶選擇策略在鋰離子電池健康預測領域&#xff0c;隨著電池使用環境的多樣化以及電池狀態監測需求的不斷增長&#xff0c;傳統的集中式數據訓練方法逐漸顯現出局限性。為了解決數據隱私保護和大規模數據集中處…

TCP協議大全

什么是TCP&#xff1f;基本定義與屬性TCP&#xff08;傳輸控制協議&#xff09;是傳輸層的重要協議&#xff0c;具有面向連接&#xff08;傳輸前需先建立連接&#xff0c;是發送方和接收方的點對點一對一連接&#xff09;、基于字節流&#xff08;以字節流形式傳輸數據&#xf…

當硅基生命遇見碳基萌寵:Deepoc具身智能如何重新定義“寵物監護者”

在東京某高級公寓里&#xff0c;一只布偶貓正優雅地踱步到智能喂食器前。令人驚訝的是&#xff0c;這個通體雪白的喂食器突然"活"了過來——它微微傾斜身體&#xff0c;用柔和的機械音發出問候&#xff0c;同時伸出仿生機械臂輕輕撫過貓咪的背部。這不是科幻電影場景…

線上日志排查問題

1、查異常堆棧 顯示該行及其后面的50行內容&#xff0c;然后通過 less 命令進行分頁查看 grep -A 50 "NullPointerException" a.log | less參數解釋&#xff1a; grep: 文本搜索命令-A 50: After 的意思&#xff0c;顯示匹配行后面的50行“NullPointerException”: 要…

LabVIEW與CAN開發燃料電池監控

?基于 LabVIEW 與 CAN 總線技術&#xff0c;構建了一套多組質子交換膜燃料電池&#xff08;PEMFC&#xff09;堆監控系統。系統采用優質硬件設備&#xff0c;通過 LabVIEW 的圖形化編程能力實現數據采集、實時監控與多堆切換控制&#xff0c;穩定可靠&#xff0c;為燃料電池性…