#include "plsr_hal_f407.h" #include "plsr_address_map.h" #include "plsr_core.h" #include "plsr_job.h" #include #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) 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 directionPositive; uint8_t abQuarter; uint8_t abCountAxis; uint8_t abStartupPriming; uint8_t abOutputPrimed; uint16_t abActiveBasePsc; uint16_t abActivePairPsc; uint16_t abActiveArr; uint16_t abPendingBasePsc; uint16_t abPendingPairPsc; uint16_t abPendingArr; uint8_t abFrequencyPending; } PLSR_HW_AXIS_STATE; static PLSR_HW_AXIS_STATE PlsrHwAxes[PLSR_HW_AXIS_COUNT]; /* 调试快照:当前上板自测只记录 Q0 的 160 ms,避免四轴 * PlsrHwTick 互相混入,同时控制临时 RAM 占用。reason=0 表示段启动, * reason=3 表示 1 ms HAL tick,reason=4 表示 AB 在 00 边界换频重定相。 */ #ifndef PLSR_HOST_TEST 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 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 PlsrHwTimerSetFrozen(uint8_t axis) { #ifdef PLSR_HOST_TEST PlsrHwTimers[axis].ccmr1 = 0x0008UL; #else PlsrHwAxisMap[axis].timer->CCMR1 = 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(低)。 */ static void PlsrHwSetDirLevel(uint8_t axis, uint8_t positive) { PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis]; state->directionPositive = (positive != 0U) ? 1U : 0U; if (state->directionPoint == PLSR_HW_DIR_POINT_NONE) { return; } #ifdef PLSR_HOST_TEST PlsrHwTimers[axis].dirLevel = (positive != 0U) ? 1U : 0U; #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 点按需配置为推挽输出(上电默认高阻=截止,安全)。 */ 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); /* 漏型输出:ON(导通)= 低电平。 */ HAL_GPIO_WritePin(pin->port, pin->pin, (positive != 0U) ? GPIO_PIN_RESET : GPIO_PIN_SET); } } #endif } /* ---- PWM 启停 ---- * ARR/CCR 使用预装载(ARPE/OC1PE):运行中调频写入延迟到更新事件生效, * 避免 ARR 变小瞬间 CNT 超调提前回绕(每段加速会多出 ~ln(f1/f0) 个假脉冲)。 * 首次启动用 EGR.UG 把预装载值加载到影子寄存器,杜绝首个周期用复位值。 */ 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; if (PlsrCalculateTimerDivider(PlsrHwAxisMap[axis].timerClockHz, frequencyHz, &psc, &arr) != PLSR_RESULT_OK) { return; } PlsrHwTimerSetPsc(axis, psc); PlsrHwTimerSetArr(axis, arr); PlsrHwTimerSetCcr(axis, (uint32_t)arr / 2UL); /* 50% 占空比 */ PlsrHwTimerSetPwmMode1(axis); PlsrHwTimerSetArpe(axis, 1UL); } /* 首次启动输出:加载影子寄存器后使能更新中断、通道与计数。 */ static void PlsrHwPwmBegin(uint8_t axis) { PlsrHwTimerSetUg(axis); /* UG 只用于加载影子寄存器,不是物理脉冲,不得计数。 */ PlsrHwTimerClearUif(axis); PlsrHwTimerClearCc1if(axis); PlsrHwDbgCapture(axis, 0U); PlsrHwTimerSetCc1ie(axis, 0UL); PlsrHwTimerSetUie(axis, 1UL); PlsrHwTimerSetCc1e(axis, 1UL); PlsrHwTimerSetCen(axis, 1UL); } static void PlsrHwStopPwmTimer(uint8_t 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; } else { state->abPendingBasePsc = basePsc; state->abPendingPairPsc = pairPsc; state->abPendingArr = arr; state->abFrequencyPending = 1U; } #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; /* 落后相从 CCR 精确起步;切回 PWM 后清 CC1IF,最后才开 CC1IE。 */ lagStart = periodTicks / 2UL; state->abCountAxis = lagAxis; state->abQuarter = 0U; state->abStartupPriming = (state->abOutputPrimed == 0U) ? 1U : 0U; #ifndef PLSR_HOST_TEST interruptState = __get_PRIMASK(); __disable_irq(); __DMB(); 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); PlsrHwTimerSetFrozen(axis); PlsrHwTimerSetFrozen(pairAxis); PlsrHwTimerSetUg(axis); PlsrHwTimerSetUg(pairAxis); PlsrHwTimerClearUif(axis); PlsrHwTimerClearUif(pairAxis); PlsrHwTimerClearCc1if(axis); PlsrHwTimerClearCc1if(pairAxis); PlsrHwTimerSetCnt(leadAxis, leadStart); PlsrHwTimerSetCnt(lagAxis, lagStart); #ifdef PLSR_HOST_TEST PlsrHwTimerSetCc1e(axis, 1UL); PlsrHwTimerSetCc1e(pairAxis, 1UL); PlsrHwTimerSetPwmMode1(axis); PlsrHwTimerSetPwmMode1(pairAxis); PlsrHwTimerClearCc1if(axis); PlsrHwTimerClearCc1if(pairAxis); PlsrHwTimerSetCen(axis, 1UL); PlsrHwTimerSetCen(pairAxis, 1UL); PlsrHwTimerClearCc1if(axis); PlsrHwTimerClearCc1if(pairAxis); PlsrHwTimerSetCc1ie(lagAxis, 1UL); #else PlsrHwTimerSetCc1e(axis, 1UL); PlsrHwTimerSetCc1e(pairAxis, 1UL); PlsrHwTimerSetPwmMode1(axis); PlsrHwTimerSetPwmMode1(pairAxis); PlsrHwTimerClearCc1if(axis); PlsrHwTimerClearCc1if(pairAxis); if (state->abStartupPriming == 0U) { /* 完成过首次预热后,段间/调频重定相均从已验证的 00 边界 * 直接交还 AF,不额外吞掉用户周期。 */ PlsrHwReleasePulsePin(axis); PlsrHwReleasePulsePin(pairAxis); } PlsrHwTimerSetCen(axis, 1UL); PlsrHwTimerSetCen(pairAxis, 1UL); PlsrHwTimerClearCc1if(axis); PlsrHwTimerClearCc1if(pairAxis); PlsrHwTimerSetCc1ie(lagAxis, 1UL); __DMB(); if (interruptState == 0UL) { __enable_irq(); } #endif PlsrHwDbgCapture(axis, debugReason); } static void PlsrHwConfigureActiveOutput(uint8_t axis, PLSR_OUTPUT_MODE outputMode, uint32_t frequencyHz) { if (outputMode == PLSR_OUTPUT_AB) { (void)PlsrHwConfigureAbPwm(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 { 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 { PlsrHwStopPwmTimer(axis); } } 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; (void)memset(PlsrHwAxes, 0, sizeof(PlsrHwAxes)); for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++) { PlsrHwAxes[axis].state = PLSR_HW_STATE_IDLE; PlsrHwAxes[axis].directionPoint = 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; /* 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(); /* 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); } #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) && (PlsrHwIsAbBaseAxis(axis) == 0U)) { return PLSR_RESULT_INVALID_AXIS; } if (params->outputMode == PLSR_OUTPUT_CW_CCW) { return PLSR_RESULT_NOT_SUPPORTED; } state = &PlsrHwAxes[axis]; if (state->state == PLSR_HW_STATE_RUNNING) { return PLSR_RESULT_BUSY; } /* 方向延时只在方向发生变化时生效(首次启动/换向/换方向点): * 段间同向衔接不再等待 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->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; if (params->outputMode == PLSR_OUTPUT_PULSE_DIR) { PlsrHwSetDirLevel(axis, params->directionPositive); } else { state->directionPositive = (params->directionPositive != 0U) ? 1U : 0U; } 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]; 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 { 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 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) { PlsrHwStopActiveOutput(axis, state->outputMode); state->abQuarter = 0U; state->abFrequencyPending = 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].emittedPulses; #else { uint32_t interruptState = __get_PRIMASK(); __disable_irq(); __DMB(); emittedPulses = 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; } void PlsrHwTick(uint8_t axis) { PLSR_HW_AXIS_STATE *state; if (axis >= PLSR_HW_AXIS_COUNT) { return; } state = &PlsrHwAxes[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; } } /* 输出定时器中断入口:PULSE/DIR 在 update 计数;AB 在落后相 * CC1 下降沿(四状态回到 00)计一个完整正交周期。 */ void PlsrHwOnTimerUpdate(uint8_t axis) { PLSR_HW_AXIS_STATE *state; uint8_t ownerAxis; uint8_t hasCc1; if (axis >= PLSR_HW_AXIS_COUNT) { return; } /* 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->abStartupPriming != 0U) { /* F407 首次切换 OC 模式时 OC1REF 初态不可直接作为物理AB相。 * GPIO 保持 00 隐藏首个内部周期;落后相下降沿是真实 00 * 边界,此时再交还 AF,且该隐藏周期绝不能计入 emitted。 */ state->abStartupPriming = 0U; state->abOutputPrimed = 1U; #ifndef PLSR_HOST_TEST PlsrHwReleasePulsePin(ownerAxis); PlsrHwReleasePulsePin(PlsrHwGetPairedAxis(ownerAxis)); #endif return; } state->emittedPulses++; if (state->emittedPulses >= state->targetPulses) { PlsrHwStopActiveOutput(ownerAxis, state->outputMode); state->abQuarter = 0U; state->abFrequencyPending = 0U; state->state = PLSR_HW_STATE_DONE; (void)PlsrPostEvent(ownerAxis, PLSR_EVENT_SEGMENT_COMPLETE); } 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 (hasCc1 != 0U) { /* 非运行态/非 AB 模式的 CC1 仅作为杂散标志消费。 */ return; } state = &PlsrHwAxes[axis]; if (PlsrHwTimerHasUif(axis) == 0U) { return; } PlsrHwTimerClearUif(axis); if (state->state != PLSR_HW_STATE_RUNNING) { return; } if (state->outputMode != PLSR_OUTPUT_PULSE_DIR) { return; } state->emittedPulses++; if (state->emittedPulses >= state->targetPulses) { /* 更新时刻 = 周期结束:关通道即完整下降沿后停止,无额外脉冲。 */ PlsrHwStopActiveOutput(axis, state->outputMode); state->state = PLSR_HW_STATE_DONE; (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; } void PlsrHwTestAdvanceAbQuarter(uint8_t axis) { PLSR_HW_AXIS_STATE *state; 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; } state->abQuarter = (uint8_t)((state->abQuarter + 1U) % PLSR_HW_AB_QUARTER_COUNT); if (state->abQuarter == 0U) { /* 模拟目标板落后相 CC1 下降沿中断,复用生产计数路径。 */ PlsrHwTimers[state->abCountAxis].sr |= PLSR_HW_TIMER_CC1_BIT; PlsrHwOnTimerUpdate(state->abCountAxis); } } void PlsrHwTestTriggerUpdate(uint8_t axis) { if (axis < PLSR_HW_AXIS_COUNT) { PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT; } PlsrHwOnTimerUpdate(axis); } #endif #ifndef PLSR_HOST_TEST void TIM1_UP_TIM10_IRQHandler(void) { PlsrHwOnTimerUpdate(0U); } void TIM8_UP_TIM13_IRQHandler(void) { PlsrHwOnTimerUpdate(1U); } void TIM1_TRG_COM_TIM11_IRQHandler(void) { PlsrHwOnTimerUpdate(2U); } void TIM8_TRG_COM_TIM14_IRQHandler(void) { PlsrHwOnTimerUpdate(3U); } #endif