|
- #include "plsr_hal_f407.h"
- #include "plsr_address_map.h"
- #include "plsr_build_config.h"
- #include "plsr_core.h"
- #include "plsr_job.h"
- #include <string.h>
-
- #ifndef PLSR_HOST_TEST
- #include "stm32f4xx.h"
- #include "stm32f4xx_hal.h"
- #endif
-
- #define PLSR_HW_TIMER_CHANNEL1_BIT (0x0001U)
- #define PLSR_HW_TIMER_UPDATE_BIT (0x0001U)
- #define PLSR_HW_TIMER_CC1_BIT (0x0002U)
- #define PLSR_HW_OUTPUT_POINT_COUNT (21U)
- #define PLSR_HW_DBG_SNAPSHOT_COUNT (160U)
- #define PLSR_HW_AB_QUARTER_COUNT (4U)
- #define PLSR_HW_COUNTER_COUNT (2U)
- #define PLSR_HW_COUNTER_NONE (0xFFU)
- #define PLSR_HW_COUNTER_BLOCK_PULSES (UINT64_C(65536))
-
- typedef struct
- {
- uint32_t timerClockHz;
- uint8_t directionPoint; /* 0xFF = 无 */
- #ifndef PLSR_HOST_TEST
- TIM_TypeDef *timer;
- GPIO_TypeDef *gpioPort;
- uint16_t gpioPin;
- uint8_t afMode;
- IRQn_Type irq;
- #endif
- } PLSR_HW_AXIS_MAP;
-
- #ifndef PLSR_HOST_TEST
- /* 输出点(Y 点号)→ GPIO 引脚:XDM-60T4-E 原理图。
- * 点号 8/9/18/19 不存在(资源层掩码 0x0013FCFF 已约束)。 */
- typedef struct
- {
- GPIO_TypeDef *port;
- uint16_t pin;
- } PLSR_HW_OUTPUT_PIN;
-
- static const PLSR_HW_OUTPUT_PIN PlsrHwOutputPins[PLSR_HW_OUTPUT_POINT_COUNT] =
- {
- {GPIOF, GPIO_PIN_6}, /* Y0 */
- {GPIOF, GPIO_PIN_8}, /* Y1 */
- {GPIOF, GPIO_PIN_7}, /* Y2 */
- {GPIOF, GPIO_PIN_9}, /* Y3 */
- {GPIOI, GPIO_PIN_8}, /* Y4 */
- {GPIOE, GPIO_PIN_6}, /* Y5 */
- {GPIOE, GPIO_PIN_5}, /* Y6 */
- {GPIOE, GPIO_PIN_4}, /* Y7 */
- {NULL, 0U}, /* Y8 */
- {NULL, 0U}, /* Y9 */
- {GPIOG, GPIO_PIN_7}, /* Y10 */
- {GPIOG, GPIO_PIN_6}, /* Y11 */
- {GPIOH, GPIO_PIN_9}, /* Y12 */
- {GPIOH, GPIO_PIN_8}, /* Y13 */
- {GPIOH, GPIO_PIN_7}, /* Y14 */
- {GPIOH, GPIO_PIN_6}, /* Y15 */
- {GPIOF, GPIO_PIN_11}, /* Y16 */
- {GPIOB, GPIO_PIN_0}, /* Y17 */
- {NULL, 0U}, /* Y18 */
- {NULL, 0U}, /* Y19 */
- {GPIOH, GPIO_PIN_5} /* Y20 */
- };
- #endif
-
- /* Q0~Q3 定时器:XDM-60T4-E。
- * PF6=TIM10_CH1(AF3)、PF7=TIM11_CH1(AF3)、PF8=TIM13_CH1(AF9)、PF9=TIM14_CH1(AF9)。
- * 定时器时钟由 RCC 实际配置计算(APB2 分频≠1 时定时器时钟×2)。 */
- static const PLSR_HW_AXIS_MAP PlsrHwAxisMap[PLSR_HW_AXIS_COUNT] =
- {
- #ifndef PLSR_HOST_TEST
- {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM10, GPIOF, GPIO_PIN_6, 3U, TIM1_UP_TIM10_IRQn},
- {84000000UL, PLSR_HW_DIR_POINT_NONE, TIM13, GPIOF, GPIO_PIN_8, 9U, TIM8_UP_TIM13_IRQn},
- {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM11, GPIOF, GPIO_PIN_7, 3U, TIM1_TRG_COM_TIM11_IRQn},
- {84000000UL, PLSR_HW_DIR_POINT_NONE, TIM14, GPIOF, GPIO_PIN_9, 9U, TIM8_TRG_COM_TIM14_IRQn}
- #else
- {168000000UL, PLSR_HW_DIR_POINT_NONE},
- {84000000UL, PLSR_HW_DIR_POINT_NONE},
- {168000000UL, PLSR_HW_DIR_POINT_NONE},
- {84000000UL, PLSR_HW_DIR_POINT_NONE}
- #endif
- };
-
- #ifndef PLSR_HOST_TEST
- static const uint8_t PlsrHwPulsePinIndex[PLSR_HW_AXIS_COUNT] =
- {
- 6U, 8U, 7U, 9U
- };
-
- /* 重定相期间由 GPIO 直接保持物理输出低电平。AFR 配置保持不变,
- * 只切换 MODER,因此恢复定时器复用功能只需一次寄存器写入。 */
- static void PlsrHwHoldPulsePinLow(uint8_t axis)
- {
- GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
- uint32_t shift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
- uint32_t moder;
-
- port->BSRR = (uint32_t)PlsrHwAxisMap[axis].gpioPin << 16U;
- moder = port->MODER;
- moder &= ~(3UL << shift);
- moder |= 1UL << shift;
- port->MODER = moder;
- __DMB();
- }
-
- static void PlsrHwReleasePulsePin(uint8_t axis)
- {
- GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
- uint32_t shift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
- uint32_t moder = port->MODER;
-
- moder &= ~(3UL << shift);
- moder |= 2UL << shift;
- port->MODER = moder;
- __DMB();
- }
- #endif
-
- /* host 测试:模拟定时器寄存器。 */
- #ifdef PLSR_HOST_TEST
- typedef struct
- {
- uint32_t cr1;
- uint32_t dier;
- uint32_t sr;
- uint32_t psc;
- uint32_t arr;
- uint32_t ccr1;
- uint32_t cnt;
- uint32_t ccmr1;
- uint32_t ccer;
- uint8_t dirLevel;
- } PLSR_HW_TIMER_REGS;
-
- static PLSR_HW_TIMER_REGS PlsrHwTimers[PLSR_HW_AXIS_COUNT];
- #endif
-
- typedef struct
- {
- PLSR_HW_STATE state;
- PLSR_OUTPUT_MODE outputMode;
- uint32_t currentFrequencyHz;
- int64_t targetPulses;
- int64_t emittedPulses;
- uint16_t directionDelayRemainingMs;
- uint8_t directionPoint;
- uint8_t configuredDirectionPoint;
- uint8_t directionPositive;
- uint8_t directionNegativeLogic;
- uint8_t directionTerminalOn;
- uint8_t directionOutputPending;
- uint8_t abQuarter;
- uint8_t abCountAxis;
- uint8_t abStartupPriming;
- uint16_t abActiveBasePsc;
- uint16_t abActivePairPsc;
- uint16_t abActiveArr;
- uint16_t abPendingBasePsc;
- uint16_t abPendingPairPsc;
- uint16_t abPendingArr;
- uint8_t abFrequencyPending;
- uint8_t abStopArmed;
- uint8_t abFastGated;
- uint8_t abPausePending;
- uint8_t abPauseGated;
- uint8_t abCompletionDeferred;
- uint8_t counterSourceAxis;
- uint32_t abCounterBoundaryCnt;
- uint8_t cwActiveAxis;
- uint8_t cwStopPending;
- uint8_t counterIndex;
- uint8_t hardwareCounterActive;
- uint8_t hardwareCounterConfigured;
- uint64_t counterBlockPulses;
- } PLSR_HW_AXIS_STATE;
-
- static PLSR_HW_AXIS_STATE PlsrHwAxes[PLSR_HW_AXIS_COUNT];
- static uint8_t PlsrHwDirectionBatchActive;
- static uint8_t PlsrHwCounterOwners[PLSR_HW_COUNTER_COUNT];
- static volatile uint32_t PlsrHwMaxOutputIsrCycles;
- static volatile uint32_t PlsrHwMaxCounterIsrCycles;
- static volatile uint32_t PlsrHwMaxControlIsrCycles;
- static volatile uint32_t PlsrHwMaxAbGateCycles;
- #ifndef PLSR_HOST_TEST
- static volatile uint8_t PlsrHwAbGateMeasurePending;
- #endif
- static volatile uint64_t PlsrHwTotalIsrCycles;
- static volatile uint32_t PlsrHwIsrBusyStarted;
- static volatile uint8_t PlsrHwIsrNesting;
-
- #ifndef PLSR_HOST_TEST
- static TIM_TypeDef * const PlsrHwCounters[PLSR_HW_COUNTER_COUNT] =
- {
- TIM9, TIM12
- };
- #endif
-
- static void PlsrHwCounterBegin(uint8_t axis);
- static void PlsrHwCounterSuspend(uint8_t axis);
- static void PlsrHwFinishDeferredAbWork(uint8_t axis);
-
- #ifdef PLSR_HOST_TEST
- static uint8_t PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
- static uint32_t PlsrHwTestAbFullGateCount;
- #endif
-
- #ifndef PLSR_HOST_TEST
- static uint32_t PlsrHwCycleBegin(void)
- {
- uint32_t started = DWT->CYCCNT;
-
- if (PlsrHwIsrNesting == 0U)
- {
- PlsrHwIsrBusyStarted = started;
- }
- PlsrHwIsrNesting++;
- return started;
- }
-
- static void PlsrHwRecordMaxCycles(volatile uint32_t *maximum,
- uint32_t started)
- {
- uint32_t finished = DWT->CYCCNT;
- uint32_t elapsed = finished - started;
-
- if (elapsed > *maximum)
- {
- *maximum = elapsed;
- }
- if (PlsrHwIsrNesting > 0U)
- {
- PlsrHwIsrNesting--;
- if (PlsrHwIsrNesting == 0U)
- {
- uint32_t busyStarted = PlsrHwIsrBusyStarted;
- uint64_t totalCycles = PlsrHwTotalIsrCycles;
-
- /* Count a nested TIM6/high-speed interrupt window once. */
- totalCycles += finished - busyStarted;
- PlsrHwTotalIsrCycles = totalCycles;
- }
- }
- }
- #endif
-
- /* 调试快照:当前上板自测只记录 Q0 的 160 ms,避免四轴
- * PlsrHwTick 互相混入,同时控制临时 RAM 占用。reason=0 表示段启动,
- * reason=3 表示 1 ms HAL tick,reason=4 表示 AB 在 00 边界换频重定相。 */
- #if !defined(PLSR_HOST_TEST) && (PLSR_ENABLE_HW_TRACE != 0U)
- typedef struct
- {
- uint8_t reason; /* 0=PwmBegin(UG后) 3=PlsrHwTick(每1ms) */
- uint32_t psc;
- uint32_t arr;
- uint32_t ccr;
- uint32_t cnt;
- uint32_t frequencyHz;
- int64_t emittedPulses;
- } PLSR_HW_DBG_SNAP;
-
- static PLSR_HW_DBG_SNAP PlsrHwDbgSnap[PLSR_HW_DBG_SNAPSHOT_COUNT];
- static volatile uint16_t PlsrHwDbgCount;
-
- static void PlsrHwDbgCapture(uint8_t axis, uint8_t reason)
- {
- if (axis != 0U)
- {
- return;
- }
- if (reason == 0U)
- {
- PlsrHwDbgCount = 0U;
- }
- if (PlsrHwDbgCount < PLSR_HW_DBG_SNAPSHOT_COUNT)
- {
- PLSR_HW_DBG_SNAP *snap = &PlsrHwDbgSnap[PlsrHwDbgCount++];
- snap->reason = reason;
- snap->psc = PlsrHwAxisMap[axis].timer->PSC;
- snap->arr = PlsrHwAxisMap[axis].timer->ARR;
- snap->ccr = PlsrHwAxisMap[axis].timer->CCR1;
- snap->cnt = PlsrHwAxisMap[axis].timer->CNT;
- snap->frequencyHz = PlsrHwAxes[axis].currentFrequencyHz;
- snap->emittedPulses = PlsrHwAxes[axis].emittedPulses;
- }
- }
- #else
- #define PlsrHwDbgCapture(axis, reason) ((void)0)
- #endif
-
- /* ---- 定时器寄存器访问抽象(host 模拟 / 生产真实) ---- */
-
- static void PlsrHwTimerSetArr(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].arr = value;
- #else
- PlsrHwAxisMap[axis].timer->ARR = value;
- #endif
- }
-
- static void PlsrHwTimerSetPsc(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].psc = value;
- #else
- PlsrHwAxisMap[axis].timer->PSC = value;
- #endif
- }
-
- static void PlsrHwTimerSetCcr(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].ccr1 = value;
- #else
- PlsrHwAxisMap[axis].timer->CCR1 = value;
- #endif
- }
-
- static uint32_t PlsrHwTimerGetCcr(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- return PlsrHwTimers[axis].ccr1;
- #else
- return PlsrHwAxisMap[axis].timer->CCR1;
- #endif
- }
-
- static void PlsrHwTimerSetCnt(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].cnt = value;
- /* F407 实测语义:CNT 写到活动 CCR1 比较值会置 CC1IF。 */
- if (value == PlsrHwTimers[axis].ccr1)
- {
- PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_CC1_BIT;
- }
- #else
- PlsrHwAxisMap[axis].timer->CNT = value;
- #endif
- }
-
- static void PlsrHwTimerSetCen(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0001UL) | value;
- #else
- if (value != 0UL)
- {
- PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_CEN;
- }
- else
- {
- PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_CEN;
- }
- #endif
- }
-
- static void PlsrHwTimerSetCc1e(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].ccer = (PlsrHwTimers[axis].ccer & ~0x0001UL) | value;
- #else
- if (value != 0UL)
- {
- PlsrHwAxisMap[axis].timer->CCER |= TIM_CCER_CC1E;
- }
- else
- {
- PlsrHwAxisMap[axis].timer->CCER &= ~TIM_CCER_CC1E;
- }
- #endif
- }
-
- /* 通道 1 输出模式 = PWM 模式 1(OC1M=110)+ CCR 预装载(OC1PE)。
- * 上电复位后 CCMR1=0(冻结),通道输出恒定电平、无方波,必须显式配置。 */
- static void PlsrHwTimerSetPwmMode1(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].ccmr1 = 0x0068UL;
- #else
- PlsrHwAxisMap[axis].timer->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
- | TIM_CCMR1_OC1PE;
- #endif
- }
-
- /* AB 启动和重定相时先把 OC1REF 钳到低电平,再切到 frozen 保持 00。
- * 两路 CNT 就位后从 frozen 切到 PWM1,硬件会按当前 CNT/CCR 重新计算输出,
- * 避免 UG 后残留的 OC1REF 高电平经 CC1E 暴露为窄脉冲。 */
- static void PlsrHwTimerSetForcedInactive(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].ccmr1 = 0x0048UL;
- #else
- PlsrHwAxisMap[axis].timer->CCMR1 = TIM_CCMR1_OC1M_2
- | TIM_CCMR1_OC1PE;
- #endif
- }
-
- static void PlsrHwTimerSetUie(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].dier = (PlsrHwTimers[axis].dier & ~0x0001UL) | value;
- #else
- if (value != 0UL)
- {
- PlsrHwAxisMap[axis].timer->DIER |= TIM_DIER_UIE;
- }
- else
- {
- PlsrHwAxisMap[axis].timer->DIER &= ~TIM_DIER_UIE;
- }
- #endif
- }
-
- static void PlsrHwTimerSetCc1ie(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].dier =
- (PlsrHwTimers[axis].dier & ~PLSR_HW_TIMER_CC1_BIT)
- | ((value != 0UL) ? PLSR_HW_TIMER_CC1_BIT : 0UL);
- if ((value != 0UL)
- && ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_CC1_BIT) != 0UL))
- {
- PlsrHwOnTimerUpdate(axis);
- }
- #else
- if (value != 0UL)
- {
- PlsrHwAxisMap[axis].timer->DIER |= TIM_DIER_CC1IE;
- }
- else
- {
- PlsrHwAxisMap[axis].timer->DIER &= ~TIM_DIER_CC1IE;
- }
- #endif
- }
-
- static void PlsrHwTimerClearUif(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_UPDATE_BIT;
- #else
- PlsrHwAxisMap[axis].timer->SR &= ~TIM_SR_UIF;
- #endif
- }
-
- static uint8_t PlsrHwTimerHasUif(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- return ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_UPDATE_BIT) != 0UL)
- ? 1U
- : 0U;
- #else
- return ((PlsrHwAxisMap[axis].timer->SR & TIM_SR_UIF) != 0UL) ? 1U : 0U;
- #endif
- }
-
- static void PlsrHwTimerClearCc1if(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_CC1_BIT;
- #else
- PlsrHwAxisMap[axis].timer->SR &= ~TIM_SR_CC1IF;
- #endif
- }
-
- static uint8_t PlsrHwTimerHasCc1if(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- return ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_CC1_BIT) != 0UL)
- ? 1U
- : 0U;
- #else
- return ((PlsrHwAxisMap[axis].timer->SR & TIM_SR_CC1IF) != 0UL) ? 1U : 0U;
- #endif
- }
-
- /* ---- DIR 输出 ----
- * XDM 为晶体管(NPN 漏型)输出:ON(导通)= 引脚低电平。
- * 正逻辑:正向=ON;负逻辑:正向=OFF。逻辑运动方向始终单独保存,
- * 不能因电气极性反转而改变位置符号、AB相序或SM方向标志。 */
-
- static void PlsrHwApplyDirLevel(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- if (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
- {
- return;
- }
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].dirLevel = state->directionTerminalOn;
- state->configuredDirectionPoint = state->directionPoint;
- #else
- if (state->directionPoint < PLSR_HW_OUTPUT_POINT_COUNT)
- {
- const PLSR_HW_OUTPUT_PIN *pin =
- &PlsrHwOutputPins[state->directionPoint];
- GPIO_InitTypeDef gpio;
-
- if (pin->port != NULL)
- {
- /* DIR 点按需配置为推挽输出(上电默认高阻=截止,安全)。 */
- pin->port->BSRR = (state->directionTerminalOn != 0U)
- ? ((uint32_t)pin->pin << 16U)
- : (uint32_t)pin->pin;
- if (state->configuredDirectionPoint != state->directionPoint)
- {
- gpio.Pin = pin->pin;
- gpio.Mode = GPIO_MODE_OUTPUT_PP;
- gpio.Pull = GPIO_NOPULL;
- gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- HAL_GPIO_Init(pin->port, &gpio);
- state->configuredDirectionPoint = state->directionPoint;
- }
- /* 漏型输出:ON(导通)= 低电平。 */
- }
- }
- #endif
- }
-
- /* ---- PWM 启停 ----
- * ARR/CCR 使用预装载(ARPE/OC1PE):运行中调频写入延迟到更新事件生效,
- * 避免 ARR 变小瞬间 CNT 超调提前回绕(每段加速会多出 ~ln(f1/f0) 个假脉冲)。
- * 首次启动用 EGR.UG 把预装载值加载到影子寄存器,杜绝首个周期用复位值。 */
-
- static void PlsrHwSetDirLevel(uint8_t axis,
- uint8_t positive,
- uint8_t negativeLogic)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- state->directionPositive = (positive != 0U) ? 1U : 0U;
- state->directionNegativeLogic =
- (negativeLogic != 0U) ? 1U : 0U;
- state->directionTerminalOn =
- (uint8_t)(state->directionPositive
- ^ state->directionNegativeLogic);
- if (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
- {
- return;
- }
- if ((PlsrHwDirectionBatchActive != 0U)
- && (state->configuredDirectionPoint == state->directionPoint))
- {
- state->directionOutputPending = 1U;
- return;
- }
- PlsrHwApplyDirLevel(axis);
- }
-
- void PlsrHwBeginDirectionBatch(void)
- {
- PlsrHwDirectionBatchActive = 1U;
- }
-
- void PlsrHwEndDirectionBatch(void)
- {
- uint8_t axis;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- #endif
- PlsrHwDirectionBatchActive = 0U;
- for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++)
- {
- if (PlsrHwAxes[axis].directionOutputPending != 0U)
- {
- PlsrHwAxes[axis].directionOutputPending = 0U;
- PlsrHwApplyDirLevel(axis);
- }
- }
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- static void PlsrHwTimerSetArpe(uint8_t axis, uint32_t value)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0080UL)
- | ((value != 0UL) ? 0x0080UL : 0UL);
- #else
- if (value != 0UL)
- {
- PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_ARPE;
- }
- else
- {
- PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_ARPE;
- }
- #endif
- }
-
- /* 生成更新事件:立即加载 ARR/CCR/PSC 影子寄存器(启动时用)。 */
- static void PlsrHwTimerSetUg(uint8_t axis)
- {
- #ifdef PLSR_HOST_TEST
- PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT;
- #else
- PlsrHwAxisMap[axis].timer->EGR = TIM_EGR_UG;
- #endif
- }
-
- /* 配置 PWM 定时器(预装载写入;启动/调频共用,不触碰使能位)。 */
- static void PlsrHwConfigurePwm(uint8_t axis, uint32_t frequencyHz)
- {
- uint16_t psc;
- uint16_t arr;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState;
- #endif
-
- if (PlsrCalculateTimerDivider(PlsrHwAxisMap[axis].timerClockHz,
- frequencyHz,
- &psc,
- &arr) != PLSR_RESULT_OK)
- {
- return;
- }
- #ifndef PLSR_HOST_TEST
- interruptState = __get_PRIMASK();
- __disable_irq();
- __DMB();
- #endif
- PlsrHwTimerSetPsc(axis, psc);
- PlsrHwTimerSetArr(axis, arr);
- PlsrHwTimerSetCcr(axis, (uint32_t)arr / 2UL); /* 50% 占空比 */
- PlsrHwTimerSetPwmMode1(axis);
- PlsrHwTimerSetArpe(axis, 1UL);
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- /* 首次启动输出:加载影子寄存器后使能更新中断、通道与计数。 */
- static void PlsrHwPwmBegin(uint8_t axis)
- {
- /* Stop the slave before changing the source OCREF phase. This is also
- * required when a paused hardware-counted segment is resumed. */
- PlsrHwCounterSuspend(axis);
- PlsrHwTimerSetUg(axis);
- if (PlsrHwAxes[axis].hardwareCounterActive != 0U)
- {
- /* PWM1 is inactive when CNT >= CCR1. Arm the ITR slave from that
- * known-low OCREF phase, then expose the first complete terminal high
- * half-cycle through CC1E. Starting at CNT=0 leaves OCREF high while
- * SMS is enabled; TIM9/TIM12 count that internal startup level as one
- * event even though no complete terminal pulse has occurred. */
- PlsrHwTimerSetCnt(axis, PlsrHwTimerGetCcr(axis));
- }
- /* UG 只用于加载影子寄存器,不是物理脉冲,不得计数。 */
- PlsrHwTimerClearUif(axis);
- PlsrHwTimerClearCc1if(axis);
- /* The slave trigger was selected while OCREF was forced low. Enable its
- * external-clock mode only after the source PWM and startup UG are stable. */
- PlsrHwCounterBegin(axis);
- PlsrHwDbgCapture(axis, 0U);
- PlsrHwTimerSetCc1ie(axis, 0UL);
- /* TIM9/TIM12 count OC events in hardware. Only the two fallback axes
- * retain a per-period output-timer interrupt. */
- PlsrHwTimerSetUie(axis,
- (PlsrHwAxes[axis].hardwareCounterActive != 0U)
- ? 0UL
- : 1UL);
- PlsrHwTimerSetCc1e(axis, 1UL);
- PlsrHwTimerSetCen(axis, 1UL);
- }
-
- static void PlsrHwStopPwmTimer(uint8_t axis)
- {
- /* Freeze the ITR slave before changing OCREF/CC1E so a stop or pause
- * transition cannot be mistaken for a physical pulse boundary. */
- PlsrHwCounterSuspend(axis);
- PlsrHwTimerSetCc1e(axis, 0UL);
- PlsrHwTimerSetUie(axis, 0UL);
- PlsrHwTimerSetCc1ie(axis, 0UL);
- PlsrHwTimerSetCen(axis, 0UL);
- PlsrHwTimerClearUif(axis);
- PlsrHwTimerClearCc1if(axis);
- }
-
- static uint8_t PlsrHwIsAbBaseAxis(uint8_t axis)
- {
- return ((axis == 0U) || (axis == 2U)) ? 1U : 0U;
- }
-
- static uint8_t PlsrHwGetPairedAxis(uint8_t axis)
- {
- return (uint8_t)(axis + 1U);
- }
-
- /* 为 168MHz/84MHz 配对定时器选择相同 ARR,并让前者的 PSC 分频
- * 始终是后者的 2 倍。两路获得完全相同的计数时钟与周期,避免
- * 独立取整造成 AB 相位随运行时间漂移。 */
- static uint8_t PlsrHwCalculateAbDividers(uint8_t axis,
- uint32_t frequencyHz,
- uint16_t *basePsc,
- uint16_t *pairPsc,
- uint16_t *arr)
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- uint64_t baseClock = PlsrHwAxisMap[axis].timerClockHz;
- uint64_t pairClock = PlsrHwAxisMap[pairAxis].timerClockHz;
- uint64_t ratio;
- uint64_t pairDivider;
- uint64_t baseDivider;
- uint64_t periodTicks;
-
- if ((frequencyHz == 0UL) || (basePsc == NULL) || (pairPsc == NULL)
- || (arr == NULL) || (pairClock == 0UL)
- || ((baseClock % pairClock) != 0UL))
- {
- return 0U;
- }
- ratio = baseClock / pairClock;
- if (ratio == 0UL)
- {
- return 0U;
- }
- pairDivider = (pairClock
- + (uint64_t)frequencyHz * UINT64_C(65536) - 1UL)
- / ((uint64_t)frequencyHz * UINT64_C(65536));
- if (pairDivider == 0UL)
- {
- pairDivider = 1UL;
- }
- baseDivider = pairDivider * ratio;
- if ((pairDivider > UINT64_C(65536))
- || (baseDivider > UINT64_C(65536)))
- {
- return 0U;
- }
- periodTicks = (pairClock
- + ((uint64_t)frequencyHz * pairDivider) / 2UL)
- / ((uint64_t)frequencyHz * pairDivider);
- if ((periodTicks < 4UL) || (periodTicks > UINT64_C(65536)))
- {
- return 0U;
- }
- *basePsc = (uint16_t)(baseDivider - 1UL);
- *pairPsc = (uint16_t)(pairDivider - 1UL);
- *arr = (uint16_t)(periodTicks - 1UL);
- return 1U;
- }
-
- static void PlsrHwLoadAbPwm(uint8_t axis,
- uint16_t basePsc,
- uint16_t pairPsc,
- uint16_t arr)
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- uint32_t compare;
-
- compare = ((uint32_t)arr + 1UL) / 2UL;
- PlsrHwTimerSetPsc(axis, basePsc);
- PlsrHwTimerSetPsc(pairAxis, pairPsc);
- PlsrHwTimerSetArr(axis, arr);
- PlsrHwTimerSetArr(pairAxis, arr);
- PlsrHwTimerSetCcr(axis, compare);
- PlsrHwTimerSetCcr(pairAxis, compare);
- PlsrHwTimerSetPwmMode1(axis);
- PlsrHwTimerSetPwmMode1(pairAxis);
- PlsrHwTimerSetArpe(axis, 1UL);
- PlsrHwTimerSetArpe(pairAxis, 1UL);
- PlsrHwAxes[axis].abActiveBasePsc = basePsc;
- PlsrHwAxes[axis].abActivePairPsc = pairPsc;
- PlsrHwAxes[axis].abActiveArr = arr;
- }
-
- static uint8_t PlsrHwConfigureAbPwm(uint8_t axis, uint32_t frequencyHz)
- {
- uint16_t basePsc;
- uint16_t pairPsc;
- uint16_t arr;
-
- if (PlsrHwCalculateAbDividers(axis,
- frequencyHz,
- &basePsc,
- &pairPsc,
- &arr) == 0U)
- {
- return 0U;
- }
- PlsrHwLoadAbPwm(axis, basePsc, pairPsc, arr);
- return 1U;
- }
-
- /* 运行中的 AB 调频不能直接写两路 ARR 预装载:两路定时器相差 1/4 周期,
- * 各自的 update 时刻也相差 1/4 周期,会短暂使用不同周期并永久积累相位误差。
- * 任务上下文只计算并发布最新参数,真正装载由 00 周期边界中断完成。 */
- static uint8_t PlsrHwQueueAbFrequency(uint8_t axis, uint32_t frequencyHz)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint16_t basePsc;
- uint16_t pairPsc;
- uint16_t arr;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState;
- #endif
-
- if (PlsrHwCalculateAbDividers(axis,
- frequencyHz,
- &basePsc,
- &pairPsc,
- &arr) == 0U)
- {
- return 0U;
- }
- #ifndef PLSR_HOST_TEST
- interruptState = __get_PRIMASK();
- __disable_irq();
- __DMB();
- #endif
- if ((basePsc == state->abActiveBasePsc)
- && (pairPsc == state->abActivePairPsc)
- && (arr == state->abActiveArr))
- {
- /* 量化后的分频参数未变化时取消旧请求,避免匀速段每 1ms 重定相。 */
- state->abFrequencyPending = 0U;
- if ((state->hardwareCounterActive != 0U)
- && (state->abStopArmed == 0U)
- && (state->abPausePending == 0U))
- {
- PlsrHwTimerSetCc1ie(state->abCountAxis, 0UL);
- }
- }
- else
- {
- state->abPendingBasePsc = basePsc;
- state->abPendingPairPsc = pairPsc;
- state->abPendingArr = arr;
- state->abFrequencyPending = 1U;
- if (state->hardwareCounterActive != 0U)
- {
- /* Hardware counting keeps the per-cycle CC interrupt disabled.
- * Wake it as a one-shot to apply this request at the next 00. */
- PlsrHwTimerClearCc1if(state->abCountAxis);
- PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
- }
- }
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- return 1U;
- }
-
- static void PlsrHwBeginAbOutput(uint8_t axis, uint8_t debugReason)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- uint8_t leadAxis = (state->directionPositive != 0U) ? axis : pairAxis;
- uint8_t lagAxis = (state->directionPositive != 0U) ? pairAxis : axis;
- uint32_t periodTicks;
- uint32_t leadStart;
- uint32_t lagStart;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState;
- #else
- (void)debugReason;
- #endif
-
- #ifdef PLSR_HOST_TEST
- periodTicks = PlsrHwTimers[axis].arr + 1UL;
- #else
- periodTicks = PlsrHwAxisMap[axis].timer->ARR + 1UL;
- #endif
- leadStart = (periodTicks * 3UL) / 4UL + 1UL;
- if (leadStart >= periodTicks)
- {
- leadStart = periodTicks - 1UL;
- }
- /* Both counters must be strictly beyond CCR while GPIO is handed back to
- * AF. CNT==CCR can leave the compare/OCREF state implementation-defined
- * at the mux boundary and previously exposed one simultaneous A/B edge. */
- lagStart = periodTicks / 2UL + 1UL;
- if (lagStart >= periodTicks)
- {
- lagStart = periodTicks - 1UL;
- }
- state->abCountAxis = lagAxis;
- state->counterSourceAxis = (axis == 0U) ? axis : pairAxis;
- state->abCounterBoundaryCnt =
- (state->counterSourceAxis == leadAxis)
- ? leadStart - 1UL
- : periodTicks / 2UL;
- state->abQuarter = 0U;
- state->abStartupPriming = 0U;
- state->abFastGated = 0U;
-
- #ifndef PLSR_HOST_TEST
- interruptState = __get_PRIMASK();
- __disable_irq();
- __DMB();
- PlsrHwCounterSuspend(axis);
- PlsrHwHoldPulsePinLow(axis);
- PlsrHwHoldPulsePinLow(pairAxis);
- #endif
- PlsrHwTimerSetCen(axis, 0UL);
- PlsrHwTimerSetCen(pairAxis, 0UL);
- PlsrHwTimerSetCc1e(axis, 0UL);
- PlsrHwTimerSetCc1e(pairAxis, 0UL);
- PlsrHwTimerSetUie(axis, 0UL);
- PlsrHwTimerSetUie(pairAxis, 0UL);
- PlsrHwTimerSetCc1ie(axis, 0UL);
- PlsrHwTimerSetCc1ie(pairAxis, 0UL);
- PlsrHwTimerSetForcedInactive(axis);
- PlsrHwTimerSetForcedInactive(pairAxis);
- PlsrHwTimerSetUg(axis);
- PlsrHwTimerSetUg(pairAxis);
- PlsrHwTimerClearUif(axis);
- PlsrHwTimerClearUif(pairAxis);
- PlsrHwTimerClearCc1if(axis);
- PlsrHwTimerClearCc1if(pairAxis);
- PlsrHwTimerSetCnt(leadAxis, leadStart);
- PlsrHwTimerSetCnt(lagAxis, lagStart);
- /* Enable the forced-inactive channels while GPIO still owns the pins.
- * AF handoff and the later PWM1 selection therefore preserve the same 00
- * electrical level at every mux point. */
- PlsrHwTimerSetCc1e(axis, 1UL);
- PlsrHwTimerSetCc1e(pairAxis, 1UL);
- #ifndef PLSR_HOST_TEST
- __DMB();
- /* CC1E is enabled, but forced-inactive drives the same idle level as
- * the GPIO hold. Hand the pins to AF now, before either timer can run. */
- PlsrHwReleasePulsePin(axis);
- PlsrHwReleasePulsePin(pairAxis);
- #endif
- #ifdef PLSR_HOST_TEST
- PlsrHwTimerSetPwmMode1(axis);
- PlsrHwTimerSetPwmMode1(pairAxis);
- PlsrHwTimerClearCc1if(axis);
- PlsrHwTimerClearCc1if(pairAxis);
- PlsrHwCounterBegin(axis);
- PlsrHwTimerSetCen(axis, 1UL);
- PlsrHwTimerSetCen(pairAxis, 1UL);
- PlsrHwTimerClearCc1if(axis);
- PlsrHwTimerClearCc1if(pairAxis);
- PlsrHwTimerSetCc1ie(
- lagAxis,
- ((state->hardwareCounterActive != 0U)
- && (state->abStopArmed == 0U))
- ? 0UL
- : 1UL);
- #else
- PlsrHwTimerSetPwmMode1(axis);
- PlsrHwTimerSetPwmMode1(pairAxis);
- PlsrHwTimerClearCc1if(axis);
- PlsrHwTimerClearCc1if(pairAxis);
- PlsrHwCounterBegin(axis);
- PlsrHwTimerSetCen(axis, 1UL);
- PlsrHwTimerSetCen(pairAxis, 1UL);
- PlsrHwTimerClearCc1if(axis);
- PlsrHwTimerClearCc1if(pairAxis);
- PlsrHwTimerSetCc1ie(
- lagAxis,
- ((state->hardwareCounterActive != 0U)
- && (state->abStopArmed == 0U))
- ? 0UL
- : 1UL);
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- PlsrHwDbgCapture(axis, debugReason);
- }
-
- static void PlsrHwConfigureCwCcwPwm(uint8_t axis, uint32_t frequencyHz)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint8_t activeAxis = state->cwActiveAxis;
- uint16_t psc;
- uint16_t arr;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState;
- #endif
-
- if (PlsrCalculateTimerDivider(PlsrHwAxisMap[activeAxis].timerClockHz,
- frequencyHz,
- &psc,
- &arr) != PLSR_RESULT_OK)
- {
- return;
- }
- #ifndef PLSR_HOST_TEST
- interruptState = __get_PRIMASK();
- __disable_irq();
- __DMB();
- #endif
- /* The compare ISR may arm final-pulse shutdown while TIM6 is calculating
- * a new divider. Recheck under the same short critical section as the
- * preload writes so the tail period can no longer be changed afterwards. */
- if (state->cwStopPending == 0U)
- {
- PlsrHwTimerSetPsc(activeAxis, psc);
- PlsrHwTimerSetArr(activeAxis, arr);
- PlsrHwTimerSetCcr(activeAxis, (uint32_t)arr / 2UL);
- PlsrHwTimerSetPwmMode1(activeAxis);
- PlsrHwTimerSetArpe(activeAxis, 1UL);
- }
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- static void PlsrHwBeginCwCcwOutput(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- uint8_t activeAxis = state->cwActiveAxis;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- #endif
- /* Keep both pins in timer AF. On this output chain, switching a channel
- * to GPIO-low is observable as an asserted Q edge. CC1E=0 is the tested
- * inactive level and avoids the extra start/end edge. */
- PlsrHwStopPwmTimer(axis);
- PlsrHwStopPwmTimer(pairAxis);
- state->cwStopPending = 0U;
- PlsrHwTimerSetUg(activeAxis);
- PlsrHwTimerClearUif(activeAxis);
- PlsrHwTimerClearCc1if(activeAxis);
- PlsrHwTimerSetCnt(activeAxis, 0UL);
- PlsrHwTimerSetUie(activeAxis, 0UL);
- PlsrHwTimerSetCc1ie(activeAxis, 1UL);
- PlsrHwTimerSetCc1e(activeAxis, 1UL);
- PlsrHwTimerSetCen(activeAxis, 1UL);
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- static void PlsrHwStopCwCcwOutput(uint8_t axis)
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- #endif
- /* Disable both compare outputs while retaining AF mode; do not force
- * either pin through GPIO during the direction handover. */
- PlsrHwStopPwmTimer(axis);
- PlsrHwStopPwmTimer(pairAxis);
- PlsrHwAxes[axis].cwStopPending = 0U;
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- static void PlsrHwConfigureActiveOutput(uint8_t axis,
- PLSR_OUTPUT_MODE outputMode,
- uint32_t frequencyHz)
- {
- if (outputMode == PLSR_OUTPUT_AB)
- {
- (void)PlsrHwConfigureAbPwm(axis, frequencyHz);
- }
- else if (outputMode == PLSR_OUTPUT_CW_CCW)
- {
- PlsrHwConfigureCwCcwPwm(axis, frequencyHz);
- }
- else
- {
- PlsrHwConfigurePwm(axis, frequencyHz);
- }
- }
-
- static void PlsrHwBeginActiveOutput(uint8_t axis,
- PLSR_OUTPUT_MODE outputMode)
- {
- if (outputMode == PLSR_OUTPUT_AB)
- {
- PlsrHwBeginAbOutput(axis, 0U);
- }
- else if (outputMode == PLSR_OUTPUT_CW_CCW)
- {
- PlsrHwBeginCwCcwOutput(axis);
- }
- else
- {
- PlsrHwPwmBegin(axis);
- }
- }
-
- static void PlsrHwStopActiveOutput(uint8_t axis,
- PLSR_OUTPUT_MODE outputMode)
- {
- if ((outputMode == PLSR_OUTPUT_AB) && (PlsrHwIsAbBaseAxis(axis) != 0U))
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- /* DONE/STOP 后继续由 GPIO 保持 00,禁止已关闭 timer 泄漏残余边沿。 */
- PlsrHwHoldPulsePinLow(axis);
- PlsrHwHoldPulsePinLow(pairAxis);
- #endif
- PlsrHwStopPwmTimer(axis);
- PlsrHwStopPwmTimer(pairAxis);
- PlsrHwAxes[axis].abStartupPriming = 0U;
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
- else if ((outputMode == PLSR_OUTPUT_CW_CCW)
- && (PlsrHwIsAbBaseAxis(axis) != 0U))
- {
- PlsrHwStopCwCcwOutput(axis);
- }
- else
- {
- PlsrHwStopPwmTimer(axis);
- }
- }
-
- static uint8_t PlsrHwCounterIndexForAxis(uint8_t axis)
- {
- return (uint8_t)(axis & 1U);
- }
-
- static uint8_t PlsrHwCounterTryAcquire(uint8_t axis,
- PLSR_OUTPUT_MODE outputMode)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint8_t counterIndex;
- uint8_t acquired = 0U;
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState;
- #endif
-
- state->counterIndex = PLSR_HW_COUNTER_NONE;
- state->hardwareCounterActive = 0U;
- state->hardwareCounterConfigured = 0U;
- state->counterBlockPulses = 0UL;
- if ((outputMode != PLSR_OUTPUT_PULSE_DIR)
- && (outputMode != PLSR_OUTPUT_AB))
- {
- return 0U;
- }
- if (outputMode == PLSR_OUTPUT_AB)
- {
- /* One counter per fixed AB pair: Q0/Q1 -> TIM9, Q2/Q3 -> TIM12.
- * Very short jobs retain the existing per-cycle ISR because a
- * target-1 guard compare cannot be armed at raw count zero. */
- if ((PlsrHwIsAbBaseAxis(axis) == 0U) || (state->targetPulses < 2))
- {
- return 0U;
- }
- counterIndex = (uint8_t)(axis >> 1U);
- }
- else
- {
- counterIndex = PlsrHwCounterIndexForAxis(axis);
- }
- #ifndef PLSR_HOST_TEST
- interruptState = __get_PRIMASK();
- __disable_irq();
- __DMB();
- #endif
- if (PlsrHwCounterOwners[counterIndex] == PLSR_HW_COUNTER_NONE)
- {
- PlsrHwCounterOwners[counterIndex] = axis;
- state->counterIndex = counterIndex;
- state->hardwareCounterActive = 1U;
- acquired = 1U;
- }
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- return acquired;
- }
-
- static void PlsrHwCounterRelease(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- #endif
-
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- #ifndef PLSR_HOST_TEST
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
-
- counter->CR1 = 0UL;
- counter->DIER = 0UL;
- counter->SMCR = 0UL;
- counter->SR = 0UL;
- #endif
- if (PlsrHwCounterOwners[state->counterIndex] == axis)
- {
- PlsrHwCounterOwners[state->counterIndex] =
- PLSR_HW_COUNTER_NONE;
- }
- }
- state->counterIndex = PLSR_HW_COUNTER_NONE;
- state->hardwareCounterActive = 0U;
- state->hardwareCounterConfigured = 0U;
- state->counterBlockPulses = 0UL;
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- static void PlsrHwCounterConfigure(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- state->counterBlockPulses = 0UL;
- #ifndef PLSR_HOST_TEST
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
- uint32_t triggerSelection = ((axis & 2U) == 0U)
- ? TIM_SMCR_TS_1
- : (TIM_SMCR_TS_1 | TIM_SMCR_TS_0);
-
- uint64_t comparePulses = (uint64_t)state->targetPulses;
-
- /* PULSE/DIR owns one source timer and can force it inactive here. AB
- * owns a pair; its atomic startup routine establishes both OCREF lows
- * immediately before CounterBegin instead. */
- if (state->outputMode == PLSR_OUTPUT_PULSE_DIR)
- {
- PlsrHwTimerSetCen(axis, 0UL);
- PlsrHwTimerSetCc1e(axis, 0UL);
- PlsrHwTimerSetForcedInactive(axis);
- PlsrHwTimerSetUg(axis);
- PlsrHwTimerClearUif(axis);
- PlsrHwTimerClearCc1if(axis);
- }
- else
- {
- /* Wake the lag-CC1 one complete cycle before the target boundary.
- * It verifies raw>=target at 00, avoiding ISR-latency overshoot. */
- comparePulses--;
- }
- counter->CR1 = 0UL;
- counter->DIER = 0UL;
- counter->SMCR = 0UL;
- counter->PSC = 0UL;
- counter->ARR = 0xFFFFUL;
- counter->CCR1 = (uint32_t)(comparePulses & UINT64_C(0xFFFF));
- counter->CNT = 0UL;
- counter->EGR = TIM_EGR_UG;
- counter->SR = 0UL;
- counter->SMCR = triggerSelection;
- counter->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE;
- }
- #endif
- }
-
- static void PlsrHwCounterBegin(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- if (state->hardwareCounterActive == 0U)
- {
- return;
- }
- if (state->hardwareCounterConfigured == 0U)
- {
- return;
- }
- #ifndef PLSR_HOST_TEST
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
-
- counter->SMCR |= TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0;
- counter->CR1 |= TIM_CR1_CEN;
- }
- #endif
- }
-
- static void PlsrHwCounterSuspend(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- if ((state->hardwareCounterActive == 0U)
- || (state->hardwareCounterConfigured == 0U))
- {
- return;
- }
- #ifndef PLSR_HOST_TEST
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
-
- counter->CR1 &= ~TIM_CR1_CEN;
- counter->SMCR &= ~(TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0);
- }
- #endif
- }
-
- static void PlsrHwCounterRebase(uint8_t axis, uint64_t pulses)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- if ((state->hardwareCounterActive == 0U)
- || (state->hardwareCounterConfigured == 0U))
- {
- return;
- }
- #ifdef PLSR_HOST_TEST
- state->counterBlockPulses = pulses;
- #else
- state->counterBlockPulses =
- pulses & ~(PLSR_HW_COUNTER_BLOCK_PULSES - UINT64_C(1));
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
-
- counter->CNT = (uint16_t)pulses;
- counter->SR = 0UL;
- }
- #endif
- }
-
- static uint64_t PlsrHwCounterRawSnapshot(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint64_t pulses = state->counterBlockPulses;
-
- #ifndef PLSR_HOST_TEST
- if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
- {
- TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
-
- pulses += (uint16_t)counter->CNT;
- /* Cover the short window after wrap and before the block ISR. */
- if ((counter->SR & TIM_SR_UIF) != 0UL)
- {
- pulses += PLSR_HW_COUNTER_BLOCK_PULSES;
- }
- }
- #else
- pulses = (state->outputMode == PLSR_OUTPUT_AB)
- ? state->counterBlockPulses
- : (uint64_t)state->emittedPulses;
- #endif
- return pulses;
- }
-
- static uint64_t PlsrHwCounterSnapshot(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
- uint64_t pulses = PlsrHwCounterRawSnapshot(axis);
-
- #ifndef PLSR_HOST_TEST
- if ((state->outputMode == PLSR_OUTPUT_AB) && (pulses > 0UL)
- && (state->counterSourceAxis < PLSR_HW_AXIS_COUNT))
- {
- uint64_t verifiedPulses;
- uint32_t sourceCnt;
- uint8_t attempt;
-
- /* The ITR source rises inside an AB cycle, before the following 00
- * boundary. A stable raw/CNT/raw snapshot identifies that interval
- * and publishes only complete four-state cycles. */
- for (attempt = 0U; attempt < 2U; attempt++)
- {
- pulses = PlsrHwCounterRawSnapshot(axis);
- sourceCnt = PlsrHwAxisMap[state->counterSourceAxis].timer->CNT;
- verifiedPulses = PlsrHwCounterRawSnapshot(axis);
- if (pulses == verifiedPulses)
- {
- if ((sourceCnt < state->abCounterBoundaryCnt)
- && (pulses > 0UL))
- {
- pulses--;
- }
- break;
- }
- pulses = verifiedPulses;
- }
- }
- #else
- if ((state->outputMode == PLSR_OUTPUT_AB) && (pulses > 0UL))
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
- uint8_t leadAxis = (state->directionPositive != 0U)
- ? axis
- : pairAxis;
- uint8_t sourceQuarter = (state->counterSourceAxis == leadAxis)
- ? 1U
- : 2U;
-
- if ((state->abQuarter >= sourceQuarter)
- && (state->abQuarter != 0U))
- {
- pulses--;
- }
- }
- #endif
- if (pulses > (uint64_t)state->targetPulses)
- {
- pulses = (uint64_t)state->targetPulses;
- }
- return pulses;
- }
-
- /* AB terminal and pause IRQs only freeze the two phase timers at a verified
- * 00 boundary. GPIO handoff, counter release/rebase and event publication
- * are deliberately deferred to PlsrHwTick so an equal-priority second AB
- * boundary can be serviced before its next quarter-period transition. */
- static void PlsrHwFinishDeferredAbWork(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
-
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return;
- }
- state = &PlsrHwAxes[axis];
- if (state->abPauseGated != 0U)
- {
- uint64_t completedPulses =
- (state->hardwareCounterActive != 0U)
- ? PlsrHwCounterSnapshot(axis)
- : (uint64_t)state->emittedPulses;
-
- state->emittedPulses = (int64_t)completedPulses;
- PlsrHwStopActiveOutput(axis, state->outputMode);
- if (state->hardwareCounterActive != 0U)
- {
- PlsrHwCounterRebase(axis, completedPulses);
- }
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abPauseGated = 0U;
- state->abStopArmed =
- (completedPulses
- >= (uint64_t)(state->targetPulses - 1))
- ? 1U
- : 0U;
- }
- if (state->abCompletionDeferred != 0U)
- {
- state->emittedPulses = state->targetPulses;
- PlsrHwStopActiveOutput(axis, state->outputMode);
- PlsrHwCounterRelease(axis);
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abStopArmed = 0U;
- state->abFastGated = 0U;
- state->abCompletionDeferred = 0U;
- (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
- }
- }
-
- uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber)
- {
- /* 与资源层一致的合法输出点掩码(Q0~Q7、Q10~Q17、Q20)。 */
- const uint32_t validOutputMask = 0x0013FCFFUL;
-
- if (pointNumber >= PLSR_HW_OUTPUT_POINT_COUNT)
- {
- return 0U;
- }
- if ((validOutputMask & (1UL << pointNumber)) == 0UL)
- {
- return 0U;
- }
- #ifndef PLSR_HOST_TEST
- if (PlsrHwOutputPins[pointNumber].port == NULL)
- {
- return 0U;
- }
- #endif
- return 1U;
- }
-
- PLSR_RESULT PlsrHwInit(void)
- {
- uint8_t axis;
- uint8_t counterIndex;
-
- (void)memset(PlsrHwAxes, 0, sizeof(PlsrHwAxes));
- PlsrHwDirectionBatchActive = 0U;
- PlsrHwMaxOutputIsrCycles = 0UL;
- PlsrHwMaxCounterIsrCycles = 0UL;
- PlsrHwMaxControlIsrCycles = 0UL;
- PlsrHwMaxAbGateCycles = 0UL;
- #ifndef PLSR_HOST_TEST
- PlsrHwAbGateMeasurePending = 0U;
- #endif
- PlsrHwTotalIsrCycles = 0UL;
- PlsrHwIsrBusyStarted = 0UL;
- PlsrHwIsrNesting = 0U;
- #ifdef PLSR_HOST_TEST
- PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
- PlsrHwTestAbFullGateCount = 0UL;
- #endif
- for (counterIndex = 0U;
- counterIndex < PLSR_HW_COUNTER_COUNT;
- counterIndex++)
- {
- PlsrHwCounterOwners[counterIndex] = PLSR_HW_COUNTER_NONE;
- }
- for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++)
- {
- PlsrHwAxes[axis].state = PLSR_HW_STATE_IDLE;
- PlsrHwAxes[axis].directionPoint = PLSR_HW_DIR_POINT_NONE;
- PlsrHwAxes[axis].counterIndex = PLSR_HW_COUNTER_NONE;
- PlsrHwAxes[axis].configuredDirectionPoint =
- PLSR_HW_DIR_POINT_NONE;
- #ifdef PLSR_HOST_TEST
- (void)memset(&PlsrHwTimers[axis], 0, sizeof(PlsrHwTimers[axis]));
- #else
- PlsrHwTimerSetCc1e(axis, 0UL);
- PlsrHwTimerSetUie(axis, 0UL);
- PlsrHwTimerSetCc1ie(axis, 0UL);
- PlsrHwTimerSetCen(axis, 0UL);
- #endif
- }
-
- #ifndef PLSR_HOST_TEST
- {
- GPIO_InitTypeDef gpio;
- uint32_t tim6ClockHz;
- uint16_t tim6Psc;
- uint16_t tim6Arr;
-
- CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
- DWT->CYCCNT = 0UL;
- DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
-
- /* 1. 输出点 GPIO 时钟(DIR 点按需配置时使用)。 */
- __HAL_RCC_GPIOF_CLK_ENABLE();
- __HAL_RCC_GPIOI_CLK_ENABLE();
- __HAL_RCC_GPIOE_CLK_ENABLE();
- __HAL_RCC_GPIOG_CLK_ENABLE();
- __HAL_RCC_GPIOH_CLK_ENABLE();
- __HAL_RCC_GPIOB_CLK_ENABLE();
-
- /* 2. 上电安全:输出点保持复位默认高阻(漏型输出 = 截止 = OFF)。
- * 不驱动任何 Y 点,DIR 点仅在 PlsrHwSetDirLevel 时按需配置。 */
-
- /* 3. 定时器时钟。 */
- __HAL_RCC_TIM10_CLK_ENABLE();
- __HAL_RCC_TIM11_CLK_ENABLE();
- __HAL_RCC_TIM13_CLK_ENABLE();
- __HAL_RCC_TIM14_CLK_ENABLE();
- __HAL_RCC_TIM6_CLK_ENABLE();
- __HAL_RCC_TIM9_CLK_ENABLE();
- __HAL_RCC_TIM12_CLK_ENABLE();
-
- /* Independent 10kHz control clock for S2 refreshCode=2. */
- tim6ClockHz = HAL_RCC_GetPCLK1Freq();
- if ((RCC->CFGR & RCC_CFGR_PPRE1) != RCC_CFGR_PPRE1_DIV1)
- {
- tim6ClockHz *= 2UL;
- }
- if (PlsrCalculateTimerDivider(tim6ClockHz,
- 10000UL,
- &tim6Psc,
- &tim6Arr) != PLSR_RESULT_OK)
- {
- return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
- }
- TIM6->CR1 = 0UL;
- TIM6->DIER = 0UL;
- TIM6->PSC = tim6Psc;
- TIM6->ARR = tim6Arr;
- TIM6->EGR = TIM_EGR_UG;
- TIM6->SR = 0UL;
- TIM6->DIER = TIM_DIER_UIE;
- HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 2U, 0U);
- HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
- TIM6->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
-
- /* 4. 脉冲点切定时器复用(PF6/7=AF3、PF8/9=AF9)。
- * 定时器通道尚未使能(CC1E=0),输出级断开,无毛刺。 */
- gpio.Mode = GPIO_MODE_AF_PP;
- gpio.Pull = GPIO_NOPULL;
- gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
- gpio.Alternate = 3U;
- HAL_GPIO_Init(GPIOF, &gpio);
- gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9;
- gpio.Alternate = 9U;
- HAL_GPIO_Init(GPIOF, &gpio);
-
- /* 5. 更新中断 NVIC:高速计数/尾脉冲层(P3b 统一规划优先级表)。 */
- HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
- HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
- HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM11_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM11_IRQn);
- HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
- HAL_NVIC_SetPriority(TIM1_BRK_TIM9_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
- HAL_NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 1U, 0U);
- HAL_NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
- }
- #endif
- return PLSR_RESULT_OK;
- }
-
- PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params)
- {
- PLSR_HW_AXIS_STATE *state;
- uint8_t directionChanged = 0U;
-
- if ((axis >= PLSR_HW_AXIS_COUNT) || (params == NULL))
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- if (params->targetPulses <= 0)
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- if ((uint32_t)params->outputMode > (uint32_t)PLSR_OUTPUT_CW_CCW)
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- if (((params->outputMode == PLSR_OUTPUT_AB)
- || (params->outputMode == PLSR_OUTPUT_CW_CCW))
- && (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return PLSR_RESULT_INVALID_AXIS;
- }
- state = &PlsrHwAxes[axis];
- if ((state->state == PLSR_HW_STATE_RUNNING)
- || (state->abCompletionDeferred != 0U)
- || (state->abPauseGated != 0U))
- {
- return PLSR_RESULT_BUSY;
- }
- /* A caller may replace a prepared-but-not-started segment. Return its
- * counter lease first, otherwise the paired axis would fall back forever. */
- if (state->hardwareCounterActive != 0U)
- {
- PlsrHwCounterRelease(axis);
- }
-
- /* 方向延时只在方向发生变化时生效(首次启动/换向/换方向点):
- * 段间同向衔接不再等待 10ms,直接进入 PWM 待启动。 */
- if (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
- {
- directionChanged =
- (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
- || (state->directionPoint != params->directionPoint)
- || (state->directionPositive != params->directionPositive)
- || (state->directionNegativeLogic
- != params->directionNegativeLogic);
- }
-
- state->outputMode = params->outputMode;
- state->targetPulses = params->targetPulses;
- state->emittedPulses = 0;
- state->currentFrequencyHz = params->frequencyHz;
- state->directionPoint =
- (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
- ? params->directionPoint
- : PLSR_HW_DIR_POINT_NONE;
- state->directionDelayRemainingMs =
- ((params->outputMode == PLSR_OUTPUT_PULSE_DIR)
- && (directionChanged != 0U))
- ? params->directionDelayMs
- : 0U;
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abStopArmed = 0U;
- state->abFastGated = 0U;
- state->abPausePending = 0U;
- state->abPauseGated = 0U;
- state->abCompletionDeferred = 0U;
- if (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
- {
- PlsrHwSetDirLevel(axis,
- params->directionPositive,
- params->directionNegativeLogic);
- }
- else
- {
- state->directionPositive =
- (params->directionPositive != 0U) ? 1U : 0U;
- state->directionNegativeLogic = 0U;
- }
- if (params->outputMode == PLSR_OUTPUT_CW_CCW)
- {
- state->cwActiveAxis = (state->directionPositive != 0U)
- ? axis
- : PlsrHwGetPairedAxis(axis);
- }
- (void)PlsrHwCounterTryAcquire(axis, params->outputMode);
- if (state->hardwareCounterActive != 0U)
- {
- PlsrHwCounterConfigure(axis);
- state->hardwareCounterConfigured = 1U;
- }
- state->state = (state->directionDelayRemainingMs > 0U)
- ? PLSR_HW_STATE_DIR_SETTLING
- : PLSR_HW_STATE_PWM_PENDING;
- return PLSR_RESULT_OK;
- }
-
- PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz)
- {
- PLSR_HW_AXIS_STATE *state;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- state = &PlsrHwAxes[axis];
- if ((state->outputMode == PLSR_OUTPUT_CW_CCW)
- && (state->cwStopPending != 0U))
- {
- /* Preserve the final physical high width until its natural update
- * boundary; no later profile write may move that boundary. */
- return PLSR_RESULT_OK;
- }
- if ((state->state == PLSR_HW_STATE_RUNNING)
- && (frequencyHz == state->currentFrequencyHz))
- {
- /* Cruise ticks commonly request the same frequency for every axis.
- * Recomputing PSC/ARR performs two 64-bit divisions and rewrites the
- * same preload registers without changing the waveform. */
- return PLSR_RESULT_OK;
- }
- state->currentFrequencyHz = frequencyHz;
- if (state->state == PLSR_HW_STATE_RUNNING)
- {
- if (frequencyHz > 0UL)
- {
- if (state->outputMode == PLSR_OUTPUT_AB)
- {
- if (PlsrHwQueueAbFrequency(axis, frequencyHz) == 0U)
- {
- return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
- }
- }
- else
- {
- /* PULSE/DIR 仍由单定时器在自身 update 边界加载预装值。 */
- PlsrHwConfigureActiveOutput(axis,
- state->outputMode,
- frequencyHz);
- }
- }
- else
- {
- if (state->outputMode == PLSR_OUTPUT_AB)
- {
- #ifndef PLSR_HOST_TEST
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- #endif
- /* PAUSE is a controlled AB stop. Keep both timers running
- * until the lag compare reaches the next real 00 boundary;
- * forcing GPIO low here would discard and later re-emit an
- * already-started cycle, adding one terminal edge. */
- state->abFrequencyPending = 0U;
- state->abPausePending = 1U;
- PlsrHwTimerClearCc1if(state->abCountAxis);
- PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
- #ifndef PLSR_HOST_TEST
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
- else
- {
- PlsrHwStopActiveOutput(axis, state->outputMode);
- }
- }
- }
- else if ((state->state == PLSR_HW_STATE_PWM_PENDING)
- && (frequencyHz > 0UL))
- {
- PlsrHwConfigureActiveOutput(axis, state->outputMode, frequencyHz);
- /* 必须先发布 RUNNING,避免启用 timer IRQ 后观察到 PWM_PENDING。 */
- state->state = PLSR_HW_STATE_RUNNING;
- PlsrHwBeginActiveOutput(axis, state->outputMode);
- }
- return PLSR_RESULT_OK;
- }
-
- PLSR_RESULT PlsrHwResumePulse(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- state = &PlsrHwAxes[axis];
- if ((state->state != PLSR_HW_STATE_RUNNING)
- || (state->currentFrequencyHz != 0UL)
- || (state->abPausePending != 0U)
- || (state->abPauseGated != 0U)
- || (PlsrHwGetEmittedPulses(axis) >= state->targetPulses))
- {
- return PLSR_RESULT_INVALID_STATE;
- }
-
- /* The pause boundary worker stopped the physical timers at 00 and kept
- * the completed-cycle count. PWM_PENDING makes the next non-zero control
- * tick use the clean-start path without resetting that count. */
- state->state = PLSR_HW_STATE_PWM_PENDING;
- return PLSR_RESULT_OK;
- }
-
- PLSR_RESULT PlsrHwStopPulse(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return PLSR_RESULT_INVALID_ARGUMENT;
- }
- state = &PlsrHwAxes[axis];
- if (state->state != PLSR_HW_STATE_IDLE)
- {
- if (state->hardwareCounterActive != 0U)
- {
- state->emittedPulses =
- (int64_t)PlsrHwCounterSnapshot(axis);
- }
- PlsrHwStopActiveOutput(axis, state->outputMode);
- PlsrHwCounterRelease(axis);
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abStopArmed = 0U;
- state->abFastGated = 0U;
- state->abPausePending = 0U;
- state->abPauseGated = 0U;
- state->abCompletionDeferred = 0U;
- state->state = PLSR_HW_STATE_IDLE;
- }
- return PLSR_RESULT_OK;
- }
-
- uint8_t PlsrHwIsPulseActive(uint8_t axis)
- {
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return 0U;
- }
- return (PlsrHwAxes[axis].state == PLSR_HW_STATE_RUNNING) ? 1U : 0U;
- }
-
- PLSR_HW_STATE PlsrHwGetState(uint8_t axis)
- {
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return PLSR_HW_STATE_IDLE;
- }
- return PlsrHwAxes[axis].state;
- }
-
- uint32_t PlsrHwGetTimerClockHz(uint8_t axis)
- {
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return 0UL;
- }
- return PlsrHwAxisMap[axis].timerClockHz;
- }
-
- uint32_t PlsrHwGetCurrentFrequencyHz(uint8_t axis)
- {
- if ((axis >= PLSR_HW_AXIS_COUNT)
- || (PlsrHwAxes[axis].state != PLSR_HW_STATE_RUNNING))
- {
- return 0UL;
- }
- return PlsrHwAxes[axis].currentFrequencyHz;
- }
-
- /* 硬件已发出的脉冲数(profile 虚拟计数校准用,中断内递增)。 */
- int64_t PlsrHwGetEmittedPulses(uint8_t axis)
- {
- int64_t emittedPulses;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return 0;
- }
- #ifdef PLSR_HOST_TEST
- emittedPulses = (PlsrHwAxes[axis].hardwareCounterActive != 0U)
- ? (int64_t)PlsrHwCounterSnapshot(axis)
- : PlsrHwAxes[axis].emittedPulses;
- #else
- {
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- emittedPulses = (PlsrHwAxes[axis].hardwareCounterActive != 0U)
- ? (int64_t)PlsrHwCounterSnapshot(axis)
- : PlsrHwAxes[axis].emittedPulses;
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- }
- #endif
- return emittedPulses;
- }
-
- uint8_t PlsrHwIsAbStartupPriming(uint8_t axis)
- {
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return 0U;
- }
- return PlsrHwAxes[axis].abStartupPriming;
- }
-
- uint8_t PlsrHwUsesHardwareCounter(uint8_t axis)
- {
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return 0U;
- }
- return PlsrHwAxes[axis].hardwareCounterActive;
- }
-
- uint32_t PlsrHwGetMaxOutputIsrCycles(void)
- {
- return PlsrHwMaxOutputIsrCycles;
- }
-
- uint32_t PlsrHwGetMaxCounterIsrCycles(void)
- {
- return PlsrHwMaxCounterIsrCycles;
- }
-
- uint32_t PlsrHwGetMaxControlIsrCycles(void)
- {
- return PlsrHwMaxControlIsrCycles;
- }
-
- uint32_t PlsrHwGetMaxAbGateCycles(void)
- {
- return PlsrHwMaxAbGateCycles;
- }
-
- void PlsrHwGetCycleSnapshot(uint32_t *cycleCount,
- uint64_t *plsrIsrCycles)
- {
- #ifdef PLSR_HOST_TEST
- if (cycleCount != NULL)
- {
- *cycleCount = 0UL;
- }
- if (plsrIsrCycles != NULL)
- {
- *plsrIsrCycles = 0UL;
- }
- #else
- uint32_t interruptState = __get_PRIMASK();
-
- __disable_irq();
- __DMB();
- if (cycleCount != NULL)
- {
- *cycleCount = DWT->CYCCNT;
- }
- if (plsrIsrCycles != NULL)
- {
- *plsrIsrCycles = PlsrHwTotalIsrCycles;
- }
- __DMB();
- if (interruptState == 0UL)
- {
- __enable_irq();
- }
- #endif
- }
-
- void PlsrHwTick(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return;
- }
- state = &PlsrHwAxes[axis];
- if ((state->abPauseGated != 0U)
- || (state->abCompletionDeferred != 0U))
- {
- PlsrHwFinishDeferredAbWork(axis);
- }
- /* 调试:每 tick 记录定时器实况(CNT 演化定位第一周期压缩)。 */
- PlsrHwDbgCapture(axis, 3U);
- switch (state->state)
- {
- case PLSR_HW_STATE_DIR_SETTLING:
- if (state->directionDelayRemainingMs > 0U)
- {
- state->directionDelayRemainingMs--;
- }
- if (state->directionDelayRemainingMs == 0U)
- {
- state->state = PLSR_HW_STATE_PWM_PENDING;
- }
- break;
-
- case PLSR_HW_STATE_PWM_PENDING:
- if (state->currentFrequencyHz > 0UL)
- {
- PlsrHwConfigureActiveOutput(axis,
- state->outputMode,
- state->currentFrequencyHz);
- state->state = PLSR_HW_STATE_RUNNING;
- PlsrHwBeginActiveOutput(axis, state->outputMode);
- }
- break;
-
- default:
- break;
- }
- }
-
- static void PlsrHwFastGateAbPair(uint8_t axis)
- {
- uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
-
- PlsrHwTimerSetCen(axis, 0UL);
- PlsrHwTimerSetCen(pairAxis, 0UL);
- PlsrHwCounterSuspend(axis);
- }
-
- static void PlsrHwDeferAbCompletion(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- state->emittedPulses = state->targetPulses;
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abStopArmed = 0U;
- state->abPausePending = 0U;
- state->abPauseGated = 0U;
- state->abCompletionDeferred = 1U;
- state->state = PLSR_HW_STATE_DONE;
- }
-
- static void PlsrHwRecordFullAbGateTime(void)
- {
- #ifndef PLSR_HOST_TEST
- /* The wrapper records the endpoint after the common ISR accounting, just
- * before exception return. Measuring here would omit that equal-priority
- * blocking tail and could understate the 2.5us near-simultaneous window. */
- PlsrHwAbGateMeasurePending = 1U;
- #else
- PlsrHwTestAbFullGateCount++;
- #endif
- }
-
- #ifndef PLSR_HOST_TEST
- static void PlsrHwFinishAbGateMeasurement(uint32_t started)
- {
- if (PlsrHwAbGateMeasurePending != 0U)
- {
- uint32_t finished;
- uint32_t elapsed;
-
- PlsrHwAbGateMeasurePending = 0U;
- finished = DWT->CYCCNT;
- elapsed = finished - started;
- if (elapsed > PlsrHwMaxAbGateCycles)
- {
- PlsrHwMaxAbGateCycles = elapsed;
- }
- }
- }
- #endif
-
- static uint8_t PlsrHwGateArmedAbOutputs(void)
- {
- static const uint8_t baseAxes[2] = {0U, 2U};
- uint8_t gated = 0U;
- uint8_t index;
-
- /* Both AB pairs use equal-priority IRQs. Scan and gate every pair before
- * doing any event publication so two simultaneous 100kHz completions
- * cannot make the second pair run an extra quarter while its IRQ waits. */
- for (index = 0U; index < 2U; index++)
- {
- uint8_t axis = baseAxes[index];
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
-
- if ((state->state == PLSR_HW_STATE_RUNNING)
- && (state->outputMode == PLSR_OUTPUT_AB)
- && (state->abStopArmed != 0U)
- && (state->abFastGated == 0U)
- && (PlsrHwTimerHasCc1if(state->abCountAxis) != 0U)
- && (PlsrHwCounterRawSnapshot(axis)
- >= (uint64_t)state->targetPulses))
- {
- /* This entry is the verified 00 boundary. Stop both counters
- * first, but keep CC1E driving the frozen 00 until the slower path
- * hands the pins to GPIO. */
- PlsrHwFastGateAbPair(axis);
- state->abFastGated = 1U;
- gated = 1U;
- }
- }
- return gated;
- }
-
- /* 输出定时器中断入口:PULSE/DIR 在 update 计数;AB 在落后相
- * CC1 下降沿(四状态回到 00)计一个完整正交周期。 */
- void PlsrHwOnTimerUpdate(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
- uint8_t ownerAxis;
- uint8_t hasCc1;
- uint8_t abGated;
-
- if (axis >= PLSR_HW_AXIS_COUNT)
- {
- return;
- }
- abGated = PlsrHwGateArmedAbOutputs();
- #ifdef PLSR_HOST_TEST
- /* Model the second equal-priority lag flag arriving after the first scan
- * but before any completion bookkeeping. */
- if (PlsrHwTestLateAbFlagAxis < PLSR_HW_AXIS_COUNT)
- {
- PlsrHwTimers[PlsrHwTestLateAbFlagAxis].sr |=
- PLSR_HW_TIMER_CC1_BIT;
- PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
- abGated = 1U;
- }
- #endif
- /* Completion cleanup is deferred. This second scan closes the injected
- * arrival window; a still-later flag gets CPU back before its next jump. */
- if (abGated != 0U)
- {
- (void)PlsrHwGateArmedAbOutputs();
- }
-
- /* CC1IF 无论当前状态如何都必须先清除;否则启动窗口中的杂散
- * compare 标志会让共享 IRQ 持续重入,主线程无法完成 CEN 配置。 */
- hasCc1 = PlsrHwTimerHasCc1if(axis);
- if (hasCc1 != 0U)
- {
- PlsrHwTimerClearCc1if(axis);
- }
- ownerAxis = (uint8_t)(axis & 0xFEU);
- state = &PlsrHwAxes[ownerAxis];
- if ((state->state == PLSR_HW_STATE_RUNNING)
- && (state->outputMode == PLSR_OUTPUT_AB))
- {
- if (PlsrHwTimerHasUif(axis) != 0U)
- {
- PlsrHwTimerClearUif(axis);
- }
- if ((hasCc1 == 0U) || (axis != state->abCountAxis))
- {
- return;
- }
- if (state->abFastGated != 0U)
- {
- PlsrHwDeferAbCompletion(ownerAxis);
- PlsrHwRecordFullAbGateTime();
- return;
- }
- if (state->abPausePending != 0U)
- {
- uint8_t targetReached;
-
- targetReached =
- (state->hardwareCounterActive != 0U)
- ? ((PlsrHwCounterRawSnapshot(ownerAxis)
- >= (uint64_t)state->targetPulses)
- ? 1U
- : 0U)
- : (((uint64_t)state->emittedPulses + 1UL
- >= (uint64_t)state->targetPulses)
- ? 1U
- : 0U);
- PlsrHwFastGateAbPair(ownerAxis);
- if (targetReached != 0U)
- {
- PlsrHwDeferAbCompletion(ownerAxis);
- }
- else
- {
- if (state->hardwareCounterActive == 0U)
- {
- state->emittedPulses++;
- }
- state->abQuarter = 0U;
- state->abFrequencyPending = 0U;
- state->abPausePending = 0U;
- state->abPauseGated = 1U;
- }
- PlsrHwRecordFullAbGateTime();
- return;
- }
- if (state->hardwareCounterActive != 0U)
- {
- /* One-shot 00 interrupt for a queued frequency change or for the
- * target guard. Counting itself remains entirely in TIM9/12. */
- if (state->abFrequencyPending != 0U)
- {
- uint16_t basePsc = state->abPendingBasePsc;
- uint16_t pairPsc = state->abPendingPairPsc;
- uint16_t arr = state->abPendingArr;
- uint8_t pairAxis = PlsrHwGetPairedAxis(ownerAxis);
-
- /* This CC1 is 00. Gate first; divider calculation and timer
- * reloading are intentionally outside the 2.5us edge window. */
- PlsrHwTimerSetCen(ownerAxis, 0UL);
- PlsrHwTimerSetCen(pairAxis, 0UL);
- PlsrHwCounterSuspend(ownerAxis);
- state->abFrequencyPending = 0U;
- PlsrHwLoadAbPwm(ownerAxis, basePsc, pairPsc, arr);
- PlsrHwBeginAbOutput(ownerAxis, 4U);
- }
- else if (state->abStopArmed == 0U)
- {
- PlsrHwTimerSetCc1ie(state->abCountAxis, 0UL);
- }
- return;
- }
- state->emittedPulses++;
- if (state->emittedPulses >= state->targetPulses)
- {
- PlsrHwFastGateAbPair(ownerAxis);
- PlsrHwDeferAbCompletion(ownerAxis);
- PlsrHwRecordFullAbGateTime();
- }
- else if (state->abFrequencyPending != 0U)
- {
- uint16_t basePsc = state->abPendingBasePsc;
- uint16_t pairPsc = state->abPendingPairPsc;
- uint16_t arr = state->abPendingArr;
-
- state->abFrequencyPending = 0U;
- PlsrHwLoadAbPwm(ownerAxis, basePsc, pairPsc, arr);
- /* 落后相刚下降,AB=00;两路从同一个完整周期边界重定相。 */
- PlsrHwBeginAbOutput(ownerAxis, 4U);
- }
- return;
- }
-
- if ((state->state == PLSR_HW_STATE_RUNNING)
- && (state->outputMode == PLSR_OUTPUT_CW_CCW))
- {
- uint8_t hasUif = PlsrHwTimerHasUif(axis);
-
- if (hasUif != 0U)
- {
- PlsrHwTimerClearUif(axis);
- }
- if (axis != state->cwActiveAxis)
- {
- return;
- }
- if (hasCc1 != 0U)
- {
- state->emittedPulses++;
- if (state->emittedPulses >= state->targetPulses)
- {
- /* The board output is inverted relative to OC1REF: CC1 is
- * the physical rising edge. Arm the tail here, then stop on
- * the following update (physical falling edge). */
- state->cwStopPending = 1U;
- PlsrHwTimerSetCc1ie(axis, 0UL);
- PlsrHwTimerClearUif(axis);
- PlsrHwTimerSetUie(axis, 1UL);
- }
- return;
- }
- if ((hasUif != 0U) && (state->cwStopPending != 0U))
- {
- PlsrHwStopActiveOutput(ownerAxis, state->outputMode);
- state->state = PLSR_HW_STATE_DONE;
- (void)PlsrPostEvent(ownerAxis, PLSR_EVENT_SEGMENT_COMPLETE);
- }
- return;
- }
-
- state = &PlsrHwAxes[axis];
- if ((hasCc1 != 0U)
- && !((state->state == PLSR_HW_STATE_RUNNING)
- && (state->outputMode == PLSR_OUTPUT_PULSE_DIR)))
- {
- /* 非运行态或其他模式的 CC1 仅作为杂散标志消费。PULSE/DIR
- * 的 CC1IE 关闭,但半周期比较仍会置 CC1IF;更新 IRQ 必须
- * 在同一次入口继续消费 UIF,不能留下 UIF 再触发第二次 ISR。 */
- return;
- }
- if (PlsrHwTimerHasUif(axis) == 0U)
- {
- return;
- }
- PlsrHwTimerClearUif(axis);
- if (state->state != PLSR_HW_STATE_RUNNING)
- {
- return;
- }
- if (state->outputMode != PLSR_OUTPUT_PULSE_DIR)
- {
- return;
- }
- #ifndef PLSR_HOST_TEST
- if (state->hardwareCounterActive != 0U)
- {
- /* A hardware-counted axis has UIE disabled. Ignore any stale update
- * flag rather than counting the same OC event in software as well. */
- return;
- }
- #endif
-
- state->emittedPulses++;
- if (state->emittedPulses >= state->targetPulses)
- {
- /* 更新时刻 = 周期结束:关通道即完整下降沿后停止,无额外脉冲。 */
- PlsrHwStopActiveOutput(axis, state->outputMode);
- state->state = PLSR_HW_STATE_DONE;
- PlsrHwCounterRelease(axis);
- (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
- }
- }
-
- #ifdef PLSR_HOST_TEST
- uint32_t PlsrHwTestGetArr(uint8_t axis)
- {
- return PlsrHwTimers[axis].arr;
- }
-
- uint32_t PlsrHwTestGetCcr(uint8_t axis)
- {
- return PlsrHwTimers[axis].ccr1;
- }
-
- uint32_t PlsrHwTestGetCnt(uint8_t axis)
- {
- return PlsrHwTimers[axis].cnt;
- }
-
- uint32_t PlsrHwTestGetCcmr1(uint8_t axis)
- {
- return PlsrHwTimers[axis].ccmr1;
- }
-
- uint32_t PlsrHwTestGetCr1(uint8_t axis)
- {
- return PlsrHwTimers[axis].cr1;
- }
-
- uint32_t PlsrHwTestGetPsc(uint8_t axis)
- {
- return PlsrHwTimers[axis].psc;
- }
-
- uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis)
- {
- return ((PlsrHwTimers[axis].ccer & PLSR_HW_TIMER_CHANNEL1_BIT) != 0UL)
- ? 1U
- : 0U;
- }
-
- uint8_t PlsrHwTestGetDirLevel(uint8_t axis)
- {
- return PlsrHwTimers[axis].dirLevel;
- }
-
- uint8_t PlsrHwTestGetAbPhaseA(uint8_t axis)
- {
- const PLSR_HW_AXIS_STATE *state;
-
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return 0U;
- }
- state = &PlsrHwAxes[axis];
- if (state->directionPositive != 0U)
- {
- return ((state->abQuarter == 1U) || (state->abQuarter == 2U))
- ? 1U
- : 0U;
- }
- return ((state->abQuarter == 2U) || (state->abQuarter == 3U))
- ? 1U
- : 0U;
- }
-
- uint8_t PlsrHwTestGetAbPhaseB(uint8_t axis)
- {
- const PLSR_HW_AXIS_STATE *state;
-
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return 0U;
- }
- state = &PlsrHwAxes[axis];
- if (state->directionPositive != 0U)
- {
- return ((state->abQuarter == 2U) || (state->abQuarter == 3U))
- ? 1U
- : 0U;
- }
- return ((state->abQuarter == 1U) || (state->abQuarter == 2U))
- ? 1U
- : 0U;
- }
-
- uint8_t PlsrHwTestGetAbQuarter(uint8_t axis)
- {
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return 0U;
- }
- return PlsrHwAxes[axis].abQuarter;
- }
-
- uint32_t PlsrHwTestGetAbFullGateCount(void)
- {
- return PlsrHwTestAbFullGateCount;
- }
-
- void PlsrHwTestAdvanceAbQuarter(uint8_t axis)
- {
- PLSR_HW_AXIS_STATE *state;
- uint8_t countAxis;
- uint8_t leadAxis;
- uint8_t sourceQuarter;
-
- if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
- {
- return;
- }
- state = &PlsrHwAxes[axis];
- if ((state->state != PLSR_HW_STATE_RUNNING)
- || (state->outputMode != PLSR_OUTPUT_AB))
- {
- return;
- }
-
- countAxis = state->abCountAxis;
- leadAxis = (state->directionPositive != 0U)
- ? axis
- : PlsrHwGetPairedAxis(axis);
- sourceQuarter = (state->counterSourceAxis == leadAxis) ? 1U : 2U;
- state->abQuarter = (uint8_t)((state->abQuarter + 1U)
- % PLSR_HW_AB_QUARTER_COUNT);
- if ((state->hardwareCounterActive != 0U)
- && (state->abQuarter == sourceQuarter))
- {
- state->counterBlockPulses++;
- if ((state->abStopArmed == 0U)
- && (state->counterBlockPulses
- >= (uint64_t)(state->targetPulses - 1)))
- {
- /* Host model of the target-1 TIM9/TIM12 compare. */
- state->abStopArmed = 1U;
- PlsrHwTimerClearCc1if(countAxis);
- PlsrHwTimerSetCc1ie(countAxis, 1UL);
- }
- }
- if (state->abQuarter == 0U)
- {
- /* 模拟目标板落后相 CC1 下降沿中断,复用生产计数路径。 */
- /* Model the lag CC IRQ only while it is enabled. This detects a
- * regression that accidentally restores one interrupt per AB cycle. */
- if ((PlsrHwTimers[countAxis].dier & PLSR_HW_TIMER_CC1_BIT) != 0UL)
- {
- PlsrHwTimers[countAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
- PlsrHwOnTimerUpdate(countAxis);
- }
- }
- }
-
- void PlsrHwTestSignalDualAbFinalBoundary(uint8_t firstAxis)
- {
- static const uint8_t baseAxes[2] = {0U, 2U};
- uint8_t index;
- uint8_t firstCountAxis;
-
- if ((firstAxis != 0U) && (firstAxis != 2U))
- {
- return;
- }
- for (index = 0U; index < 2U; index++)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
-
- if ((state->state != PLSR_HW_STATE_RUNNING)
- || (state->outputMode != PLSR_OUTPUT_AB)
- || (state->hardwareCounterActive == 0U))
- {
- return;
- }
- }
- for (index = 0U; index < 2U; index++)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
-
- state->counterBlockPulses = (uint64_t)state->targetPulses;
- state->abQuarter = 0U;
- state->abStopArmed = 1U;
- PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
- PlsrHwTimers[state->abCountAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
- }
- firstCountAxis = PlsrHwAxes[firstAxis].abCountAxis;
- PlsrHwOnTimerUpdate(firstCountAxis);
- }
-
- void PlsrHwTestSignalDualAbStaggeredFinalBoundary(uint8_t firstAxis)
- {
- static const uint8_t baseAxes[2] = {0U, 2U};
- uint8_t firstCountAxis;
- uint8_t secondAxis;
- uint8_t secondCountAxis;
- uint8_t index;
-
- if ((firstAxis != 0U) && (firstAxis != 2U))
- {
- return;
- }
- secondAxis = (firstAxis == 0U) ? 2U : 0U;
- for (index = 0U; index < 2U; index++)
- {
- PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
-
- if ((state->state != PLSR_HW_STATE_RUNNING)
- || (state->outputMode != PLSR_OUTPUT_AB)
- || (state->hardwareCounterActive == 0U))
- {
- return;
- }
- state->counterBlockPulses = (uint64_t)state->targetPulses;
- state->abQuarter = 0U;
- state->abStopArmed = 1U;
- PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
- }
- firstCountAxis = PlsrHwAxes[firstAxis].abCountAxis;
- secondCountAxis = PlsrHwAxes[secondAxis].abCountAxis;
- PlsrHwTimers[firstCountAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
- PlsrHwTestLateAbFlagAxis = secondCountAxis;
- PlsrHwOnTimerUpdate(firstCountAxis);
- }
-
- void PlsrHwTestTriggerUpdate(uint8_t axis)
- {
- if (axis < PLSR_HW_AXIS_COUNT)
- {
- PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT;
- }
- PlsrHwOnTimerUpdate(axis);
- }
-
- void PlsrHwTestTriggerUpdateAndCompare(uint8_t axis)
- {
- if (axis < PLSR_HW_AXIS_COUNT)
- {
- PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT
- | PLSR_HW_TIMER_CC1_BIT;
- }
- PlsrHwOnTimerUpdate(axis);
- }
-
- void PlsrHwTestTriggerCompare(uint8_t axis)
- {
- if (axis < PLSR_HW_AXIS_COUNT)
- {
- PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_CC1_BIT;
- }
- PlsrHwOnTimerUpdate(axis);
- }
- #endif
-
- #ifndef PLSR_HOST_TEST
- static void PlsrHwOnCounterInterrupt(uint8_t counterIndex)
- {
- TIM_TypeDef *counter;
- PLSR_HW_AXIS_STATE *state;
- uint32_t flags;
- uint64_t pulses;
- uint8_t axis;
-
- if (counterIndex >= PLSR_HW_COUNTER_COUNT)
- {
- return;
- }
- counter = PlsrHwCounters[counterIndex];
- flags = counter->SR & (TIM_SR_UIF | TIM_SR_CC1IF);
- counter->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
- axis = PlsrHwCounterOwners[counterIndex];
- if ((flags == 0UL) || (axis >= PLSR_HW_AXIS_COUNT))
- {
- return;
- }
- state = &PlsrHwAxes[axis];
- if ((state->hardwareCounterActive == 0U)
- || (state->counterIndex != counterIndex)
- || (state->state != PLSR_HW_STATE_RUNNING))
- {
- return;
- }
- if ((flags & TIM_SR_UIF) != 0UL)
- {
- state->counterBlockPulses += PLSR_HW_COUNTER_BLOCK_PULSES;
- }
- pulses = state->counterBlockPulses + (uint16_t)counter->CNT;
- if (state->outputMode == PLSR_OUTPUT_AB)
- {
- uint64_t guard = (uint64_t)state->targetPulses - 1UL;
-
- if (((flags & TIM_SR_CC1IF) != 0UL) && (pulses >= guard))
- {
- /* Wake the lag-CC1 one cycle early. It remains enabled until a
- * 00 boundary observes raw>=target and fast-gates both phases. */
- state->abStopArmed = 1U;
- PlsrHwTimerClearCc1if(state->abCountAxis);
- PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
- }
- return;
- }
- if (((flags & TIM_SR_CC1IF) != 0UL)
- && (pulses >= (uint64_t)state->targetPulses))
- {
- /* TIMx_OC rises at the PWM update boundary. On this board that is
- * the physical falling edge, so the target pulse is already complete
- * and both the counter and PWM may be stopped without truncation. */
- state->emittedPulses = state->targetPulses;
- PlsrHwStopActiveOutput(axis, state->outputMode);
- state->state = PLSR_HW_STATE_DONE;
- PlsrHwCounterRelease(axis);
- (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
- }
- }
-
- void TIM1_UP_TIM10_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnTimerUpdate(0U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
- PlsrHwFinishAbGateMeasurement(started);
- }
-
- void TIM8_UP_TIM13_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnTimerUpdate(1U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
- PlsrHwFinishAbGateMeasurement(started);
- }
-
- void TIM1_TRG_COM_TIM11_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnTimerUpdate(2U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
- PlsrHwFinishAbGateMeasurement(started);
- }
-
- void TIM8_TRG_COM_TIM14_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnTimerUpdate(3U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
- PlsrHwFinishAbGateMeasurement(started);
- }
-
- void TIM1_BRK_TIM9_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnCounterInterrupt(0U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxCounterIsrCycles, started);
- }
-
- void TIM8_BRK_TIM12_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- PlsrHwOnCounterInterrupt(1U);
- PlsrHwRecordMaxCycles(&PlsrHwMaxCounterIsrCycles, started);
- }
-
- void TIM6_DAC_IRQHandler(void)
- {
- uint32_t started = PlsrHwCycleBegin();
-
- if ((TIM6->SR & TIM_SR_UIF) != 0UL)
- {
- TIM6->SR &= ~TIM_SR_UIF;
- PlsrControlTick100us();
- }
- PlsrHwRecordMaxCycles(&PlsrHwMaxControlIsrCycles, started);
- }
-
- #endif
|