No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

3522 líneas
106 KiB

  1. #include "plsr_hal_f407.h"
  2. #include "plsr_address_map.h"
  3. #include "plsr_build_config.h"
  4. #include "plsr_core.h"
  5. #include "plsr_job.h"
  6. #include <string.h>
  7. #ifndef PLSR_HOST_TEST
  8. #include "stm32f4xx.h"
  9. #include "stm32f4xx_hal.h"
  10. #endif
  11. #define PLSR_HW_TIMER_CHANNEL1_BIT (0x0001U)
  12. #define PLSR_HW_TIMER_CC1P_BIT (0x0002U)
  13. #define PLSR_HW_TIMER_UPDATE_BIT (0x0001U)
  14. #define PLSR_HW_TIMER_CC1_BIT (0x0002U)
  15. #define PLSR_HW_OUTPUT_POINT_COUNT (21U)
  16. #define PLSR_HW_DBG_SNAPSHOT_COUNT (160U)
  17. #define PLSR_HW_AB_QUARTER_COUNT (4U)
  18. #define PLSR_HW_COUNTER_COUNT (2U)
  19. #define PLSR_HW_COUNTER_NONE (0xFFU)
  20. #define PLSR_HW_COUNTER_BLOCK_PULSES (UINT64_C(65536))
  21. typedef struct
  22. {
  23. uint32_t timerClockHz;
  24. uint8_t directionPoint; /* 0xFF = 无 */
  25. #ifndef PLSR_HOST_TEST
  26. TIM_TypeDef *timer;
  27. GPIO_TypeDef *gpioPort;
  28. uint16_t gpioPin;
  29. uint8_t afMode;
  30. IRQn_Type irq;
  31. #endif
  32. } PLSR_HW_AXIS_MAP;
  33. #ifndef PLSR_HOST_TEST
  34. /* 输出点(Y 点号)→ GPIO 引脚:XDM-60T4-E 原理图。
  35. * 点号 8/9/18/19 不存在(资源层掩码 0x0013FCFF 已约束)。 */
  36. typedef struct
  37. {
  38. GPIO_TypeDef *port;
  39. uint16_t pin;
  40. } PLSR_HW_OUTPUT_PIN;
  41. static const PLSR_HW_OUTPUT_PIN PlsrHwOutputPins[PLSR_HW_OUTPUT_POINT_COUNT] =
  42. {
  43. {GPIOF, GPIO_PIN_6}, /* Y0 */
  44. {GPIOF, GPIO_PIN_8}, /* Y1 */
  45. {GPIOF, GPIO_PIN_7}, /* Y2 */
  46. {GPIOF, GPIO_PIN_9}, /* Y3 */
  47. {GPIOI, GPIO_PIN_8}, /* Y4 */
  48. {GPIOE, GPIO_PIN_6}, /* Y5 */
  49. {GPIOE, GPIO_PIN_5}, /* Y6 */
  50. {GPIOE, GPIO_PIN_4}, /* Y7 */
  51. {NULL, 0U}, /* Y8 */
  52. {NULL, 0U}, /* Y9 */
  53. {GPIOG, GPIO_PIN_7}, /* Y10 */
  54. {GPIOG, GPIO_PIN_6}, /* Y11 */
  55. {GPIOH, GPIO_PIN_9}, /* Y12 */
  56. {GPIOH, GPIO_PIN_8}, /* Y13 */
  57. {GPIOH, GPIO_PIN_7}, /* Y14 */
  58. {GPIOH, GPIO_PIN_6}, /* Y15 */
  59. {GPIOF, GPIO_PIN_11}, /* Y16 */
  60. {GPIOB, GPIO_PIN_0}, /* Y17 */
  61. {NULL, 0U}, /* Y18 */
  62. {NULL, 0U}, /* Y19 */
  63. {GPIOH, GPIO_PIN_5} /* Y20 */
  64. };
  65. #endif
  66. /* Q0~Q3 定时器:XDM-60T4-E。
  67. * PF6=TIM10_CH1(AF3)、PF7=TIM11_CH1(AF3)、PF8=TIM13_CH1(AF9)、PF9=TIM14_CH1(AF9)。
  68. * 定时器时钟由 RCC 实际配置计算(APB2 分频≠1 时定时器时钟×2)。 */
  69. static const PLSR_HW_AXIS_MAP PlsrHwAxisMap[PLSR_HW_AXIS_COUNT] =
  70. {
  71. #ifndef PLSR_HOST_TEST
  72. {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM10, GPIOF, GPIO_PIN_6, 3U, TIM1_UP_TIM10_IRQn},
  73. {84000000UL, PLSR_HW_DIR_POINT_NONE, TIM13, GPIOF, GPIO_PIN_8, 9U, TIM8_UP_TIM13_IRQn},
  74. {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM11, GPIOF, GPIO_PIN_7, 3U, TIM1_TRG_COM_TIM11_IRQn},
  75. {84000000UL, PLSR_HW_DIR_POINT_NONE, TIM14, GPIOF, GPIO_PIN_9, 9U, TIM8_TRG_COM_TIM14_IRQn}
  76. #else
  77. {168000000UL, PLSR_HW_DIR_POINT_NONE},
  78. {84000000UL, PLSR_HW_DIR_POINT_NONE},
  79. {168000000UL, PLSR_HW_DIR_POINT_NONE},
  80. {84000000UL, PLSR_HW_DIR_POINT_NONE}
  81. #endif
  82. };
  83. #ifndef PLSR_HOST_TEST
  84. static const uint8_t PlsrHwPulsePinIndex[PLSR_HW_AXIS_COUNT] =
  85. {
  86. 6U, 8U, 7U, 9U
  87. };
  88. /* 重定相期间由 GPIO 直接保持端子物理输出低电平。板上漏型输出级
  89. * 会反相,所以 MCU 必须驱动高电平才能让 Q 端子为低。AFR 配置保持不变,
  90. * 只切换 MODER,因此恢复定时器复用功能只需一次寄存器写入。 */
  91. static void PlsrHwHoldPulsePinLow(uint8_t axis)
  92. {
  93. GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
  94. uint32_t shift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
  95. uint32_t moder;
  96. port->BSRR = (uint32_t)PlsrHwAxisMap[axis].gpioPin;
  97. moder = port->MODER;
  98. moder &= ~(3UL << shift);
  99. moder |= 1UL << shift;
  100. port->MODER = moder;
  101. __DMB();
  102. }
  103. static void PlsrHwReleasePulsePin(uint8_t axis)
  104. {
  105. GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
  106. uint32_t shift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
  107. uint32_t moder = port->MODER;
  108. moder &= ~(3UL << shift);
  109. moder |= 2UL << shift;
  110. port->MODER = moder;
  111. __DMB();
  112. }
  113. /* Q0..Q3 share GPIOF. At an AB 00 boundary, transfer both pins with one
  114. * GPIO write before changing CCR/ARR. A larger downshift CCR can otherwise
  115. * assert one channel while it is still connected to the timer. */
  116. static void PlsrHwHoldAbPairLowFast(uint8_t axis)
  117. {
  118. uint8_t pairAxis = (uint8_t)(axis + 1U);
  119. GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
  120. uint32_t firstShift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
  121. uint32_t secondShift =
  122. (uint32_t)PlsrHwPulsePinIndex[pairAxis] * 2UL;
  123. uint32_t moder = port->MODER;
  124. port->BSRR = (uint32_t)PlsrHwAxisMap[axis].gpioPin
  125. | (uint32_t)PlsrHwAxisMap[pairAxis].gpioPin;
  126. moder &= ~((3UL << firstShift) | (3UL << secondShift));
  127. moder |= (1UL << firstShift) | (1UL << secondShift);
  128. port->MODER = moder;
  129. __DMB();
  130. }
  131. static void PlsrHwReleaseAbPairFast(uint8_t axis)
  132. {
  133. uint8_t pairAxis = (uint8_t)(axis + 1U);
  134. GPIO_TypeDef *port = PlsrHwAxisMap[axis].gpioPort;
  135. uint32_t firstShift = (uint32_t)PlsrHwPulsePinIndex[axis] * 2UL;
  136. uint32_t secondShift =
  137. (uint32_t)PlsrHwPulsePinIndex[pairAxis] * 2UL;
  138. uint32_t moder = port->MODER;
  139. moder &= ~((3UL << firstShift) | (3UL << secondShift));
  140. moder |= (2UL << firstShift) | (2UL << secondShift);
  141. port->MODER = moder;
  142. __DMB();
  143. }
  144. #endif
  145. /* host 测试:模拟定时器寄存器。 */
  146. #ifdef PLSR_HOST_TEST
  147. typedef struct
  148. {
  149. uint32_t cr1;
  150. uint32_t dier;
  151. uint32_t sr;
  152. uint32_t psc;
  153. uint32_t arr;
  154. uint32_t ccr1;
  155. uint32_t cnt;
  156. uint32_t ccmr1;
  157. uint32_t ccer;
  158. uint8_t dirLevel;
  159. } PLSR_HW_TIMER_REGS;
  160. static PLSR_HW_TIMER_REGS PlsrHwTimers[PLSR_HW_AXIS_COUNT];
  161. #endif
  162. typedef struct
  163. {
  164. PLSR_HW_STATE state;
  165. PLSR_OUTPUT_MODE outputMode;
  166. uint32_t currentFrequencyHz;
  167. int64_t targetPulses;
  168. int64_t emittedPulses;
  169. uint16_t directionDelayRemainingMs;
  170. uint8_t directionPoint;
  171. uint8_t configuredDirectionPoint;
  172. uint8_t directionPositive;
  173. uint8_t directionNegativeLogic;
  174. uint8_t directionTerminalOn;
  175. uint8_t directionOutputPending;
  176. uint8_t abQuarter;
  177. uint8_t abCountAxis;
  178. uint8_t abStartupPriming;
  179. uint16_t abActiveBasePsc;
  180. uint16_t abActivePairPsc;
  181. uint16_t abActiveArr;
  182. uint16_t abPendingBasePsc;
  183. uint16_t abPendingPairPsc;
  184. uint16_t abPendingArr;
  185. uint8_t abFrequencyPending;
  186. uint8_t abStopArmed;
  187. uint8_t abFastGated;
  188. uint8_t abPausePending;
  189. uint8_t abPauseGated;
  190. uint8_t abCompletionDeferred;
  191. uint8_t counterSourceAxis;
  192. uint32_t abCounterBoundaryCnt;
  193. uint8_t cwActiveAxis;
  194. uint8_t cwStopPending;
  195. uint8_t pulseBoundaryStopPending;
  196. uint8_t pulseTargetStopArmed;
  197. uint8_t counterIndex;
  198. uint8_t hardwareCounterActive;
  199. uint8_t hardwareCounterConfigured;
  200. uint64_t counterBlockPulses;
  201. uint64_t counterPublishedPulses;
  202. } PLSR_HW_AXIS_STATE;
  203. static PLSR_HW_AXIS_STATE PlsrHwAxes[PLSR_HW_AXIS_COUNT];
  204. static uint8_t PlsrHwDirectionBatchActive;
  205. static uint8_t PlsrHwCounterOwners[PLSR_HW_COUNTER_COUNT];
  206. static volatile uint32_t PlsrHwMaxOutputIsrCycles;
  207. static volatile uint32_t PlsrHwMaxCounterIsrCycles;
  208. static volatile uint32_t PlsrHwMaxControlIsrCycles;
  209. static volatile uint32_t PlsrHwMaxAbGateCycles;
  210. static uint8_t PlsrHwValidationOutputMask;
  211. #ifndef PLSR_HOST_TEST
  212. static volatile uint8_t PlsrHwAbGateMeasurePending;
  213. #endif
  214. static volatile uint64_t PlsrHwTotalIsrCycles;
  215. static volatile uint32_t PlsrHwIsrBusyStarted;
  216. static volatile uint8_t PlsrHwIsrNesting;
  217. #ifndef PLSR_HOST_TEST
  218. static TIM_TypeDef * const PlsrHwCounters[PLSR_HW_COUNTER_COUNT] =
  219. {
  220. TIM9, TIM12
  221. };
  222. #endif
  223. static void PlsrHwCounterBegin(uint8_t axis);
  224. static void PlsrHwCounterSuspend(uint8_t axis);
  225. static void PlsrHwFinishDeferredAbWork(uint8_t axis);
  226. static uint8_t PlsrHwGateArmedAbOutputs(uint8_t preferredAxis);
  227. static void PlsrHwTimerSetCc1PolarityInverted(uint8_t axis,
  228. uint32_t value);
  229. #ifdef PLSR_HOST_TEST
  230. static uint8_t PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
  231. static uint32_t PlsrHwTestAbFullGateCount;
  232. #endif
  233. #ifndef PLSR_HOST_TEST
  234. static uint32_t PlsrHwCycleBegin(void)
  235. {
  236. uint32_t started = DWT->CYCCNT;
  237. if (PlsrHwIsrNesting == 0U)
  238. {
  239. PlsrHwIsrBusyStarted = started;
  240. }
  241. PlsrHwIsrNesting++;
  242. return started;
  243. }
  244. static void PlsrHwRecordMaxCycles(volatile uint32_t *maximum,
  245. uint32_t started)
  246. {
  247. uint32_t finished = DWT->CYCCNT;
  248. uint32_t elapsed = finished - started;
  249. if (elapsed > *maximum)
  250. {
  251. *maximum = elapsed;
  252. }
  253. if (PlsrHwIsrNesting > 0U)
  254. {
  255. PlsrHwIsrNesting--;
  256. if (PlsrHwIsrNesting == 0U)
  257. {
  258. uint32_t busyStarted = PlsrHwIsrBusyStarted;
  259. uint64_t totalCycles = PlsrHwTotalIsrCycles;
  260. /* Count a nested TIM6/high-speed interrupt window once. */
  261. totalCycles += finished - busyStarted;
  262. PlsrHwTotalIsrCycles = totalCycles;
  263. }
  264. }
  265. }
  266. #endif
  267. /* 调试快照:当前上板自测只记录 Q0 的 160 ms,避免四轴
  268. * PlsrHwTick 互相混入,同时控制临时 RAM 占用。reason=0 表示段启动,
  269. * reason=3 表示 1 ms HAL tick,reason=4 表示 AB 在 00 边界换频重定相。 */
  270. #if !defined(PLSR_HOST_TEST) && (PLSR_ENABLE_HW_TRACE != 0U)
  271. typedef struct
  272. {
  273. uint8_t reason; /* 0=PwmBegin(UG后) 3=PlsrHwTick(每1ms) */
  274. uint32_t psc;
  275. uint32_t arr;
  276. uint32_t ccr;
  277. uint32_t cnt;
  278. uint32_t frequencyHz;
  279. int64_t emittedPulses;
  280. } PLSR_HW_DBG_SNAP;
  281. static PLSR_HW_DBG_SNAP PlsrHwDbgSnap[PLSR_HW_DBG_SNAPSHOT_COUNT];
  282. static volatile uint16_t PlsrHwDbgCount;
  283. static void PlsrHwDbgCapture(uint8_t axis, uint8_t reason)
  284. {
  285. if (axis != 0U)
  286. {
  287. return;
  288. }
  289. if (reason == 0U)
  290. {
  291. PlsrHwDbgCount = 0U;
  292. }
  293. if (PlsrHwDbgCount < PLSR_HW_DBG_SNAPSHOT_COUNT)
  294. {
  295. PLSR_HW_DBG_SNAP *snap = &PlsrHwDbgSnap[PlsrHwDbgCount++];
  296. snap->reason = reason;
  297. snap->psc = PlsrHwAxisMap[axis].timer->PSC;
  298. snap->arr = PlsrHwAxisMap[axis].timer->ARR;
  299. snap->ccr = PlsrHwAxisMap[axis].timer->CCR1;
  300. snap->cnt = PlsrHwAxisMap[axis].timer->CNT;
  301. snap->frequencyHz = PlsrHwAxes[axis].currentFrequencyHz;
  302. snap->emittedPulses = PlsrHwAxes[axis].emittedPulses;
  303. }
  304. }
  305. #else
  306. #define PlsrHwDbgCapture(axis, reason) ((void)0)
  307. #endif
  308. /* ---- 定时器寄存器访问抽象(host 模拟 / 生产真实) ---- */
  309. static void PlsrHwTimerSetArr(uint8_t axis, uint32_t value)
  310. {
  311. #ifdef PLSR_HOST_TEST
  312. PlsrHwTimers[axis].arr = value;
  313. #else
  314. PlsrHwAxisMap[axis].timer->ARR = value;
  315. #endif
  316. }
  317. static void PlsrHwTimerSetPsc(uint8_t axis, uint32_t value)
  318. {
  319. #ifdef PLSR_HOST_TEST
  320. PlsrHwTimers[axis].psc = value;
  321. #else
  322. PlsrHwAxisMap[axis].timer->PSC = value;
  323. #endif
  324. }
  325. static void PlsrHwTimerSetCcr(uint8_t axis, uint32_t value)
  326. {
  327. #ifdef PLSR_HOST_TEST
  328. PlsrHwTimers[axis].ccr1 = value;
  329. #else
  330. PlsrHwAxisMap[axis].timer->CCR1 = value;
  331. #endif
  332. }
  333. static uint32_t PlsrHwTimerGetCcr(uint8_t axis)
  334. {
  335. #ifdef PLSR_HOST_TEST
  336. return PlsrHwTimers[axis].ccr1;
  337. #else
  338. return PlsrHwAxisMap[axis].timer->CCR1;
  339. #endif
  340. }
  341. static void PlsrHwTimerSetCnt(uint8_t axis, uint32_t value)
  342. {
  343. #ifdef PLSR_HOST_TEST
  344. PlsrHwTimers[axis].cnt = value;
  345. /* F407 实测语义:CNT 写到活动 CCR1 比较值会置 CC1IF。 */
  346. if (value == PlsrHwTimers[axis].ccr1)
  347. {
  348. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_CC1_BIT;
  349. }
  350. #else
  351. PlsrHwAxisMap[axis].timer->CNT = value;
  352. #endif
  353. }
  354. static void PlsrHwTimerSetCen(uint8_t axis, uint32_t value)
  355. {
  356. #ifdef PLSR_HOST_TEST
  357. PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0001UL) | value;
  358. #else
  359. if (value != 0UL)
  360. {
  361. PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_CEN;
  362. }
  363. else
  364. {
  365. PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_CEN;
  366. }
  367. #endif
  368. }
  369. static void PlsrHwStartAbTimersTightly(uint8_t axis, uint8_t pairAxis)
  370. {
  371. #ifdef PLSR_HOST_TEST
  372. PlsrHwTimerSetCen(axis, 1UL);
  373. PlsrHwTimerSetCen(pairAxis, 1UL);
  374. #else
  375. TIM_TypeDef *baseTimer = PlsrHwAxisMap[axis].timer;
  376. TIM_TypeDef *pairTimer = PlsrHwAxisMap[pairAxis].timer;
  377. uint32_t baseCr1 = baseTimer->CR1 | TIM_CR1_CEN;
  378. uint32_t pairCr1 = pairTimer->CR1 | TIM_CR1_CEN;
  379. /* Keep the two volatile stores adjacent. Calling the generic CEN helper
  380. * twice in the low-optimization validation build delayed the second
  381. * timer by about 0.33us. */
  382. baseTimer->CR1 = baseCr1;
  383. pairTimer->CR1 = pairCr1;
  384. #endif
  385. }
  386. static void PlsrHwTimerSetCc1e(uint8_t axis, uint32_t value)
  387. {
  388. #ifdef PLSR_HOST_TEST
  389. PlsrHwTimers[axis].ccer = (PlsrHwTimers[axis].ccer & ~0x0001UL) | value;
  390. #else
  391. if (value != 0UL)
  392. {
  393. PlsrHwAxisMap[axis].timer->CCER |= TIM_CCER_CC1E;
  394. }
  395. else
  396. {
  397. PlsrHwAxisMap[axis].timer->CCER &= ~TIM_CCER_CC1E;
  398. }
  399. #endif
  400. }
  401. /* 通道 1 输出模式 = PWM 模式 1(OC1M=110)+ CCR 预装载(OC1PE)。
  402. * 上电复位后 CCMR1=0(冻结),通道输出恒定电平、无方波,必须显式配置。 */
  403. static void PlsrHwTimerSetPwmMode1(uint8_t axis)
  404. {
  405. #ifdef PLSR_HOST_TEST
  406. PlsrHwTimers[axis].ccmr1 = 0x0068UL;
  407. #else
  408. PlsrHwAxisMap[axis].timer->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
  409. | TIM_CCMR1_OC1PE;
  410. #endif
  411. }
  412. /* AB 启动和重定相时先把 OC1REF 钳到低电平,再切到 frozen 保持 00。
  413. * 两路 CNT 就位后从 frozen 切到 PWM1,硬件会按当前 CNT/CCR 重新计算输出,
  414. * 避免 UG 后残留的 OC1REF 高电平经 CC1E 暴露为窄脉冲。 */
  415. static void PlsrHwTimerSetForcedInactive(uint8_t axis)
  416. {
  417. #ifdef PLSR_HOST_TEST
  418. PlsrHwTimers[axis].ccmr1 = 0x0048UL;
  419. #else
  420. PlsrHwAxisMap[axis].timer->CCMR1 = TIM_CCMR1_OC1M_2
  421. | TIM_CCMR1_OC1PE;
  422. #endif
  423. }
  424. static void PlsrHwTimerSetUie(uint8_t axis, uint32_t value)
  425. {
  426. #ifdef PLSR_HOST_TEST
  427. PlsrHwTimers[axis].dier = (PlsrHwTimers[axis].dier & ~0x0001UL) | value;
  428. #else
  429. if (value != 0UL)
  430. {
  431. PlsrHwAxisMap[axis].timer->DIER |= TIM_DIER_UIE;
  432. }
  433. else
  434. {
  435. PlsrHwAxisMap[axis].timer->DIER &= ~TIM_DIER_UIE;
  436. }
  437. #endif
  438. }
  439. static void PlsrHwTimerSetCc1ie(uint8_t axis, uint32_t value)
  440. {
  441. #ifdef PLSR_HOST_TEST
  442. PlsrHwTimers[axis].dier =
  443. (PlsrHwTimers[axis].dier & ~PLSR_HW_TIMER_CC1_BIT)
  444. | ((value != 0UL) ? PLSR_HW_TIMER_CC1_BIT : 0UL);
  445. if ((value != 0UL)
  446. && ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_CC1_BIT) != 0UL))
  447. {
  448. PlsrHwOnTimerUpdate(axis);
  449. }
  450. #else
  451. if (value != 0UL)
  452. {
  453. PlsrHwAxisMap[axis].timer->DIER |= TIM_DIER_CC1IE;
  454. }
  455. else
  456. {
  457. PlsrHwAxisMap[axis].timer->DIER &= ~TIM_DIER_CC1IE;
  458. }
  459. #endif
  460. }
  461. static void PlsrHwTimerClearUif(uint8_t axis)
  462. {
  463. #ifdef PLSR_HOST_TEST
  464. PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_UPDATE_BIT;
  465. #else
  466. PlsrHwAxisMap[axis].timer->SR &= ~TIM_SR_UIF;
  467. #endif
  468. }
  469. static uint8_t PlsrHwTimerHasUif(uint8_t axis)
  470. {
  471. #ifdef PLSR_HOST_TEST
  472. return ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_UPDATE_BIT) != 0UL)
  473. ? 1U
  474. : 0U;
  475. #else
  476. return ((PlsrHwAxisMap[axis].timer->SR & TIM_SR_UIF) != 0UL) ? 1U : 0U;
  477. #endif
  478. }
  479. static void PlsrHwTimerClearCc1if(uint8_t axis)
  480. {
  481. #ifdef PLSR_HOST_TEST
  482. PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_CC1_BIT;
  483. #else
  484. PlsrHwAxisMap[axis].timer->SR &= ~TIM_SR_CC1IF;
  485. #endif
  486. }
  487. static uint8_t PlsrHwTimerHasCc1if(uint8_t axis)
  488. {
  489. #ifdef PLSR_HOST_TEST
  490. return ((PlsrHwTimers[axis].sr & PLSR_HW_TIMER_CC1_BIT) != 0UL)
  491. ? 1U
  492. : 0U;
  493. #else
  494. return ((PlsrHwAxisMap[axis].timer->SR & TIM_SR_CC1IF) != 0UL) ? 1U : 0U;
  495. #endif
  496. }
  497. /* ---- DIR 输出 ----
  498. * XDM 为晶体管(NPN 漏型)输出:ON(导通)= 引脚低电平。
  499. * 正逻辑:正向=ON;负逻辑:正向=OFF。逻辑运动方向始终单独保存,
  500. * 不能因电气极性反转而改变位置符号、AB相序或SM方向标志。 */
  501. static void PlsrHwApplyDirLevel(uint8_t axis)
  502. {
  503. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  504. if (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
  505. {
  506. return;
  507. }
  508. #ifdef PLSR_HOST_TEST
  509. PlsrHwTimers[axis].dirLevel = state->directionTerminalOn;
  510. state->configuredDirectionPoint = state->directionPoint;
  511. #else
  512. if (state->directionPoint < PLSR_HW_OUTPUT_POINT_COUNT)
  513. {
  514. const PLSR_HW_OUTPUT_PIN *pin =
  515. &PlsrHwOutputPins[state->directionPoint];
  516. GPIO_InitTypeDef gpio;
  517. if (pin->port != NULL)
  518. {
  519. /* DIR 点按需配置为推挽输出(上电默认高阻=截止,安全)。 */
  520. pin->port->BSRR = (state->directionTerminalOn != 0U)
  521. ? ((uint32_t)pin->pin << 16U)
  522. : (uint32_t)pin->pin;
  523. if (state->configuredDirectionPoint != state->directionPoint)
  524. {
  525. gpio.Pin = pin->pin;
  526. gpio.Mode = GPIO_MODE_OUTPUT_PP;
  527. gpio.Pull = GPIO_NOPULL;
  528. gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  529. HAL_GPIO_Init(pin->port, &gpio);
  530. state->configuredDirectionPoint = state->directionPoint;
  531. }
  532. /* 漏型输出:ON(导通)= 低电平。 */
  533. }
  534. }
  535. #endif
  536. }
  537. /* ---- PWM 启停 ----
  538. * ARR/CCR 使用预装载(ARPE/OC1PE):运行中调频写入延迟到更新事件生效,
  539. * 避免 ARR 变小瞬间 CNT 超调提前回绕(每段加速会多出 ~ln(f1/f0) 个假脉冲)。
  540. * 首次启动用 EGR.UG 把预装载值加载到影子寄存器,杜绝首个周期用复位值。 */
  541. static void PlsrHwSetDirLevel(uint8_t axis,
  542. uint8_t positive,
  543. uint8_t negativeLogic)
  544. {
  545. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  546. state->directionPositive = (positive != 0U) ? 1U : 0U;
  547. state->directionNegativeLogic =
  548. (negativeLogic != 0U) ? 1U : 0U;
  549. state->directionTerminalOn =
  550. (uint8_t)(state->directionPositive
  551. ^ state->directionNegativeLogic);
  552. if (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
  553. {
  554. return;
  555. }
  556. if ((PlsrHwDirectionBatchActive != 0U)
  557. && (state->configuredDirectionPoint == state->directionPoint))
  558. {
  559. state->directionOutputPending = 1U;
  560. return;
  561. }
  562. PlsrHwApplyDirLevel(axis);
  563. }
  564. void PlsrHwBeginDirectionBatch(void)
  565. {
  566. PlsrHwDirectionBatchActive = 1U;
  567. }
  568. void PlsrHwEndDirectionBatch(void)
  569. {
  570. uint8_t axis;
  571. #ifndef PLSR_HOST_TEST
  572. uint32_t interruptState = __get_PRIMASK();
  573. __disable_irq();
  574. __DMB();
  575. #endif
  576. PlsrHwDirectionBatchActive = 0U;
  577. for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++)
  578. {
  579. if (PlsrHwAxes[axis].directionOutputPending != 0U)
  580. {
  581. PlsrHwAxes[axis].directionOutputPending = 0U;
  582. PlsrHwApplyDirLevel(axis);
  583. }
  584. }
  585. #ifndef PLSR_HOST_TEST
  586. __DMB();
  587. if (interruptState == 0UL)
  588. {
  589. __enable_irq();
  590. }
  591. #endif
  592. }
  593. static void PlsrHwTimerSetArpe(uint8_t axis, uint32_t value)
  594. {
  595. #ifdef PLSR_HOST_TEST
  596. PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0080UL)
  597. | ((value != 0UL) ? 0x0080UL : 0UL);
  598. #else
  599. if (value != 0UL)
  600. {
  601. PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_ARPE;
  602. }
  603. else
  604. {
  605. PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_ARPE;
  606. }
  607. #endif
  608. }
  609. /* 生成更新事件:立即加载 ARR/CCR/PSC 影子寄存器(启动时用)。 */
  610. static void PlsrHwTimerSetUg(uint8_t axis)
  611. {
  612. #ifdef PLSR_HOST_TEST
  613. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT;
  614. #else
  615. PlsrHwAxisMap[axis].timer->EGR = TIM_EGR_UG;
  616. #endif
  617. }
  618. /* 配置 PWM 定时器(预装载写入;启动/调频共用,不触碰使能位)。 */
  619. static void PlsrHwConfigurePwm(uint8_t axis, uint32_t frequencyHz)
  620. {
  621. uint16_t psc;
  622. uint16_t arr;
  623. #ifndef PLSR_HOST_TEST
  624. uint32_t interruptState;
  625. #endif
  626. if (PlsrCalculateTimerDivider(PlsrHwAxisMap[axis].timerClockHz,
  627. frequencyHz,
  628. &psc,
  629. &arr) != PLSR_RESULT_OK)
  630. {
  631. return;
  632. }
  633. #ifndef PLSR_HOST_TEST
  634. interruptState = __get_PRIMASK();
  635. __disable_irq();
  636. __DMB();
  637. #endif
  638. PlsrHwTimerSetPsc(axis, psc);
  639. PlsrHwTimerSetArr(axis, arr);
  640. PlsrHwTimerSetCcr(axis, (uint32_t)arr / 2UL); /* 50% 占空比 */
  641. PlsrHwTimerSetPwmMode1(axis);
  642. PlsrHwTimerSetArpe(axis, 1UL);
  643. #ifndef PLSR_HOST_TEST
  644. __DMB();
  645. if (interruptState == 0UL)
  646. {
  647. __enable_irq();
  648. }
  649. #endif
  650. }
  651. /* 首次启动输出:加载影子寄存器后使能更新中断、通道与计数。 */
  652. static void PlsrHwPwmBegin(uint8_t axis)
  653. {
  654. /* Stop the slave before changing the source OCREF phase. This is also
  655. * required when a paused hardware-counted segment is resumed. */
  656. PlsrHwCounterSuspend(axis);
  657. PlsrHwTimerSetUg(axis);
  658. if (PlsrHwAxes[axis].hardwareCounterActive != 0U)
  659. {
  660. /* PWM1 is inactive when CNT >= CCR1. Arm the ITR slave from that
  661. * known-low OCREF phase, then expose the first complete terminal high
  662. * half-cycle through CC1E. Starting at CNT=0 leaves OCREF high while
  663. * SMS is enabled; TIM9/TIM12 count that internal startup level as one
  664. * event even though no complete terminal pulse has occurred. */
  665. PlsrHwTimerSetCnt(axis, PlsrHwTimerGetCcr(axis));
  666. }
  667. /* UG 只用于加载影子寄存器,不是物理脉冲,不得计数。 */
  668. PlsrHwTimerClearUif(axis);
  669. PlsrHwTimerClearCc1if(axis);
  670. /* The slave trigger was selected while OCREF was forced low. Enable its
  671. * external-clock mode only after the source PWM and startup UG are stable. */
  672. PlsrHwCounterBegin(axis);
  673. PlsrHwDbgCapture(axis, 0U);
  674. PlsrHwTimerSetCc1ie(axis, 0UL);
  675. /* PULSE/DIR retains the previously validated non-inverted polarity. */
  676. PlsrHwTimerSetCc1PolarityInverted(axis, 0UL);
  677. /* TIM9/TIM12 count OC events in hardware. Only the two fallback axes
  678. * retain a per-period output-timer interrupt. */
  679. PlsrHwTimerSetUie(axis,
  680. (PlsrHwAxes[axis].hardwareCounterActive != 0U)
  681. ? 0UL
  682. : 1UL);
  683. PlsrHwTimerSetCc1e(axis, 1UL);
  684. #ifndef PLSR_HOST_TEST
  685. /* A previous stop may leave GPIO owning the pin at terminal-low. The
  686. * timer is fully configured and still stopped here; hand it back to AF
  687. * before CEN so the first transition is a deliberate full pulse. */
  688. PlsrHwReleasePulsePin(axis);
  689. #endif
  690. PlsrHwTimerSetCen(axis, 1UL);
  691. }
  692. static void PlsrHwStopPwmTimer(uint8_t axis)
  693. {
  694. /* Freeze the ITR slave before changing OCREF/CC1E so a stop or pause
  695. * transition cannot be mistaken for a physical pulse boundary. */
  696. PlsrHwCounterSuspend(axis);
  697. PlsrHwTimerSetCc1e(axis, 0UL);
  698. PlsrHwTimerSetUie(axis, 0UL);
  699. PlsrHwTimerSetCc1ie(axis, 0UL);
  700. PlsrHwTimerSetCen(axis, 0UL);
  701. PlsrHwTimerClearUif(axis);
  702. PlsrHwTimerClearCc1if(axis);
  703. }
  704. static uint8_t PlsrHwIsAbBaseAxis(uint8_t axis)
  705. {
  706. return ((axis == 0U) || (axis == 2U)) ? 1U : 0U;
  707. }
  708. static uint8_t PlsrHwGetPairedAxis(uint8_t axis)
  709. {
  710. return (uint8_t)(axis + 1U);
  711. }
  712. static void PlsrHwStopPulseDirOutput(uint8_t axis)
  713. {
  714. #ifndef PLSR_HOST_TEST
  715. uint32_t interruptState = __get_PRIMASK();
  716. __disable_irq();
  717. __DMB();
  718. /* At a natural update boundary the terminal is already low. GPIO-high
  719. * represents that same level through the board's inverting sink stage,
  720. * so taking ownership before CC1E is cleared cannot create a short pulse. */
  721. PlsrHwHoldPulsePinLow(axis);
  722. #endif
  723. PlsrHwStopPwmTimer(axis);
  724. #ifndef PLSR_HOST_TEST
  725. __DMB();
  726. if (interruptState == 0UL)
  727. {
  728. __enable_irq();
  729. }
  730. #endif
  731. }
  732. static void PlsrHwArmAbBoundaryInterrupts(uint8_t axis)
  733. {
  734. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  735. /* Either phase may be the first falling edge after an asynchronous
  736. * request. Observe both and accept only the event for which both PWM
  737. * phases are already low. */
  738. PlsrHwTimerClearCc1if(axis);
  739. PlsrHwTimerClearCc1if(pairAxis);
  740. PlsrHwTimerSetCc1ie(axis, 1UL);
  741. PlsrHwTimerSetCc1ie(pairAxis, 1UL);
  742. }
  743. static void PlsrHwRestoreAbCycleInterrupt(uint8_t axis)
  744. {
  745. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  746. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  747. PlsrHwTimerSetCc1ie(axis, 0UL);
  748. PlsrHwTimerSetCc1ie(pairAxis, 0UL);
  749. if ((state->hardwareCounterActive == 0U)
  750. || (state->abStopArmed != 0U))
  751. {
  752. PlsrHwTimerClearCc1if(state->abCountAxis);
  753. PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
  754. }
  755. }
  756. static uint8_t PlsrHwIsAbPhysicalZeroBoundary(uint8_t axis)
  757. {
  758. #ifdef PLSR_HOST_TEST
  759. return (PlsrHwAxes[axis].abQuarter == 0U) ? 1U : 0U;
  760. #else
  761. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  762. TIM_TypeDef *baseTimer = PlsrHwAxisMap[axis].timer;
  763. TIM_TypeDef *pairTimer = PlsrHwAxisMap[pairAxis].timer;
  764. uint32_t baseCnt = baseTimer->CNT;
  765. uint32_t baseCcr = baseTimer->CCR1;
  766. uint32_t pairCnt = pairTimer->CNT;
  767. uint32_t pairCcr = pairTimer->CCR1;
  768. /* With the validated AB double inversion, physical Q is high exactly
  769. * while PWM1 CNT<CCR. At the later of the two falling-edge compares both
  770. * counters are in their low half-cycle. */
  771. return ((baseCnt >= baseCcr) && (pairCnt >= pairCcr))
  772. ? 1U
  773. : 0U;
  774. #endif
  775. }
  776. /* 为 168MHz/84MHz 配对定时器选择相同 ARR,并让前者的 PSC 分频
  777. * 始终是后者的 2 倍。两路获得完全相同的计数时钟与周期,避免
  778. * 独立取整造成 AB 相位随运行时间漂移。 */
  779. static uint8_t PlsrHwCalculateAbDividers(uint8_t axis,
  780. uint32_t frequencyHz,
  781. uint16_t *basePsc,
  782. uint16_t *pairPsc,
  783. uint16_t *arr)
  784. {
  785. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  786. uint64_t baseClock = PlsrHwAxisMap[axis].timerClockHz;
  787. uint64_t pairClock = PlsrHwAxisMap[pairAxis].timerClockHz;
  788. uint64_t ratio;
  789. uint64_t pairDivider;
  790. uint64_t baseDivider;
  791. uint64_t periodTicks;
  792. if ((frequencyHz == 0UL) || (basePsc == NULL) || (pairPsc == NULL)
  793. || (arr == NULL) || (pairClock == 0UL)
  794. || ((baseClock % pairClock) != 0UL))
  795. {
  796. return 0U;
  797. }
  798. ratio = baseClock / pairClock;
  799. if (ratio == 0UL)
  800. {
  801. return 0U;
  802. }
  803. pairDivider = (pairClock
  804. + (uint64_t)frequencyHz * UINT64_C(65536) - 1UL)
  805. / ((uint64_t)frequencyHz * UINT64_C(65536));
  806. if (pairDivider == 0UL)
  807. {
  808. pairDivider = 1UL;
  809. }
  810. baseDivider = pairDivider * ratio;
  811. if ((pairDivider > UINT64_C(65536))
  812. || (baseDivider > UINT64_C(65536)))
  813. {
  814. return 0U;
  815. }
  816. periodTicks = (pairClock
  817. + ((uint64_t)frequencyHz * pairDivider) / 2UL)
  818. / ((uint64_t)frequencyHz * pairDivider);
  819. if ((periodTicks < 4UL) || (periodTicks > UINT64_C(65536)))
  820. {
  821. return 0U;
  822. }
  823. *basePsc = (uint16_t)(baseDivider - 1UL);
  824. *pairPsc = (uint16_t)(pairDivider - 1UL);
  825. *arr = (uint16_t)(periodTicks - 1UL);
  826. return 1U;
  827. }
  828. static void PlsrHwLoadAbPwm(uint8_t axis,
  829. uint16_t basePsc,
  830. uint16_t pairPsc,
  831. uint16_t arr)
  832. {
  833. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  834. uint32_t compare;
  835. compare = ((uint32_t)arr + 1UL) / 2UL;
  836. PlsrHwTimerSetPsc(axis, basePsc);
  837. PlsrHwTimerSetPsc(pairAxis, pairPsc);
  838. PlsrHwTimerSetArr(axis, arr);
  839. PlsrHwTimerSetArr(pairAxis, arr);
  840. PlsrHwTimerSetCcr(axis, compare);
  841. PlsrHwTimerSetCcr(pairAxis, compare);
  842. PlsrHwTimerSetPwmMode1(axis);
  843. PlsrHwTimerSetPwmMode1(pairAxis);
  844. PlsrHwTimerSetArpe(axis, 1UL);
  845. PlsrHwTimerSetArpe(pairAxis, 1UL);
  846. PlsrHwAxes[axis].abActiveBasePsc = basePsc;
  847. PlsrHwAxes[axis].abActivePairPsc = pairPsc;
  848. PlsrHwAxes[axis].abActiveArr = arr;
  849. }
  850. static uint8_t PlsrHwConfigureAbPwm(uint8_t axis, uint32_t frequencyHz)
  851. {
  852. uint16_t basePsc;
  853. uint16_t pairPsc;
  854. uint16_t arr;
  855. if (PlsrHwCalculateAbDividers(axis,
  856. frequencyHz,
  857. &basePsc,
  858. &pairPsc,
  859. &arr) == 0U)
  860. {
  861. return 0U;
  862. }
  863. PlsrHwLoadAbPwm(axis, basePsc, pairPsc, arr);
  864. return 1U;
  865. }
  866. /* 运行中的 AB 调频不能直接写两路 ARR 预装载:两路定时器相差 1/4 周期,
  867. * 各自的 update 时刻也相差 1/4 周期,会短暂使用不同周期并永久积累相位误差。
  868. * 任务上下文只计算并发布最新参数,真正装载由 00 周期边界中断完成。 */
  869. static uint8_t PlsrHwQueueAbFrequency(uint8_t axis, uint32_t frequencyHz)
  870. {
  871. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  872. uint16_t basePsc;
  873. uint16_t pairPsc;
  874. uint16_t arr;
  875. #ifndef PLSR_HOST_TEST
  876. uint32_t interruptState;
  877. #endif
  878. if (PlsrHwCalculateAbDividers(axis,
  879. frequencyHz,
  880. &basePsc,
  881. &pairPsc,
  882. &arr) == 0U)
  883. {
  884. return 0U;
  885. }
  886. #ifndef PLSR_HOST_TEST
  887. interruptState = __get_PRIMASK();
  888. __disable_irq();
  889. __DMB();
  890. #endif
  891. if ((basePsc == state->abActiveBasePsc)
  892. && (pairPsc == state->abActivePairPsc)
  893. && (arr == state->abActiveArr))
  894. {
  895. /* 量化后的分频参数未变化时取消旧请求,避免匀速段每 1ms 重定相。 */
  896. state->abFrequencyPending = 0U;
  897. if ((state->abStopArmed == 0U)
  898. && (state->abPausePending == 0U))
  899. {
  900. PlsrHwRestoreAbCycleInterrupt(axis);
  901. }
  902. }
  903. else
  904. {
  905. state->abPendingBasePsc = basePsc;
  906. state->abPendingPairPsc = pairPsc;
  907. state->abPendingArr = arr;
  908. state->abFrequencyPending = 1U;
  909. /* Observe both falling edges. The first can be the 11->single-low
  910. * boundary; only the second is a proven physical 00. */
  911. PlsrHwArmAbBoundaryInterrupts(axis);
  912. }
  913. #ifndef PLSR_HOST_TEST
  914. __DMB();
  915. if (interruptState == 0UL)
  916. {
  917. __enable_irq();
  918. }
  919. #endif
  920. return 1U;
  921. }
  922. static void PlsrHwBeginAbOutput(uint8_t axis, uint8_t debugReason)
  923. {
  924. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  925. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  926. uint8_t leadAxis = (state->directionPositive != 0U) ? axis : pairAxis;
  927. uint8_t lagAxis = (state->directionPositive != 0U) ? pairAxis : axis;
  928. uint32_t periodTicks;
  929. uint32_t leadStart;
  930. uint32_t lagStart;
  931. #ifndef PLSR_HOST_TEST
  932. uint32_t interruptState;
  933. #else
  934. (void)debugReason;
  935. #endif
  936. #ifdef PLSR_HOST_TEST
  937. periodTicks = PlsrHwTimers[axis].arr + 1UL;
  938. #else
  939. periodTicks = PlsrHwAxisMap[axis].timer->ARR + 1UL;
  940. #endif
  941. leadStart = (periodTicks * 3UL) / 4UL + 1UL;
  942. if (leadStart >= periodTicks)
  943. {
  944. leadStart = periodTicks - 1UL;
  945. }
  946. /* Both counters must be strictly beyond CCR while GPIO is handed back to
  947. * AF. CNT==CCR can leave the compare/OCREF state implementation-defined
  948. * at the mux boundary and previously exposed one simultaneous A/B edge. */
  949. lagStart = periodTicks / 2UL + 1UL;
  950. if (lagStart >= periodTicks)
  951. {
  952. lagStart = periodTicks - 1UL;
  953. }
  954. state->abCountAxis = lagAxis;
  955. state->counterSourceAxis = (axis == 0U) ? axis : pairAxis;
  956. state->abCounterBoundaryCnt =
  957. (state->counterSourceAxis == leadAxis)
  958. ? leadStart - 1UL
  959. : periodTicks / 2UL;
  960. state->abQuarter = 0U;
  961. state->abStartupPriming = 0U;
  962. state->abFastGated = 0U;
  963. #ifndef PLSR_HOST_TEST
  964. interruptState = __get_PRIMASK();
  965. __disable_irq();
  966. __DMB();
  967. PlsrHwCounterSuspend(axis);
  968. PlsrHwHoldPulsePinLow(axis);
  969. PlsrHwHoldPulsePinLow(pairAxis);
  970. #endif
  971. PlsrHwTimerSetCen(axis, 0UL);
  972. PlsrHwTimerSetCen(pairAxis, 0UL);
  973. PlsrHwTimerSetCc1e(axis, 0UL);
  974. PlsrHwTimerSetCc1e(pairAxis, 0UL);
  975. PlsrHwTimerSetUie(axis, 0UL);
  976. PlsrHwTimerSetUie(pairAxis, 0UL);
  977. PlsrHwTimerSetCc1ie(axis, 0UL);
  978. PlsrHwTimerSetCc1ie(pairAxis, 0UL);
  979. PlsrHwTimerSetForcedInactive(axis);
  980. PlsrHwTimerSetForcedInactive(pairAxis);
  981. PlsrHwTimerSetUg(axis);
  982. PlsrHwTimerSetUg(pairAxis);
  983. PlsrHwTimerClearUif(axis);
  984. PlsrHwTimerClearUif(pairAxis);
  985. PlsrHwTimerClearCc1if(axis);
  986. PlsrHwTimerClearCc1if(pairAxis);
  987. PlsrHwTimerSetCnt(leadAxis, leadStart);
  988. PlsrHwTimerSetCnt(lagAxis, lagStart);
  989. /* The board's open-collector stage inverts the MCU waveform. Invert both
  990. * timer channels as well so forced-inactive/CNT>CCR and the lag compare
  991. * boundary are physical terminal 00 rather than 11. Complementing both
  992. * phases preserves the established quadrature direction. */
  993. PlsrHwTimerSetCc1PolarityInverted(axis, 1UL);
  994. PlsrHwTimerSetCc1PolarityInverted(pairAxis, 1UL);
  995. /* Enable the forced-inactive channels while GPIO still owns the pins.
  996. * AF handoff and the later PWM1 selection therefore preserve the same 00
  997. * electrical level at every mux point. */
  998. PlsrHwTimerSetCc1e(axis, 1UL);
  999. PlsrHwTimerSetCc1e(pairAxis, 1UL);
  1000. #ifndef PLSR_HOST_TEST
  1001. __DMB();
  1002. /* CC1E is enabled, but forced-inactive drives the same idle level as
  1003. * the GPIO hold. Hand the pins to AF now, before either timer can run. */
  1004. PlsrHwReleasePulsePin(axis);
  1005. PlsrHwReleasePulsePin(pairAxis);
  1006. #endif
  1007. #ifdef PLSR_HOST_TEST
  1008. PlsrHwTimerSetPwmMode1(axis);
  1009. PlsrHwTimerSetPwmMode1(pairAxis);
  1010. PlsrHwTimerClearCc1if(axis);
  1011. PlsrHwTimerClearCc1if(pairAxis);
  1012. PlsrHwCounterBegin(axis);
  1013. PlsrHwStartAbTimersTightly(axis, pairAxis);
  1014. PlsrHwTimerClearCc1if(axis);
  1015. PlsrHwTimerClearCc1if(pairAxis);
  1016. PlsrHwTimerSetCc1ie(
  1017. lagAxis,
  1018. ((state->hardwareCounterActive != 0U)
  1019. && (state->abStopArmed == 0U))
  1020. ? 0UL
  1021. : 1UL);
  1022. #else
  1023. PlsrHwTimerSetPwmMode1(axis);
  1024. PlsrHwTimerSetPwmMode1(pairAxis);
  1025. PlsrHwTimerClearCc1if(axis);
  1026. PlsrHwTimerClearCc1if(pairAxis);
  1027. PlsrHwCounterBegin(axis);
  1028. PlsrHwStartAbTimersTightly(axis, pairAxis);
  1029. PlsrHwTimerClearCc1if(axis);
  1030. PlsrHwTimerClearCc1if(pairAxis);
  1031. PlsrHwTimerSetCc1ie(
  1032. lagAxis,
  1033. ((state->hardwareCounterActive != 0U)
  1034. && (state->abStopArmed == 0U))
  1035. ? 0UL
  1036. : 1UL);
  1037. __DMB();
  1038. if (interruptState == 0UL)
  1039. {
  1040. __enable_irq();
  1041. }
  1042. #endif
  1043. PlsrHwDbgCapture(axis, debugReason);
  1044. }
  1045. /* Apply an already calculated AB divider at a verified 00 boundary. Direct
  1046. * target-register writes keep the complete rephase inside the 10us output ISR
  1047. * budget while GPIO owns physical 00 during every CCR/ARR transition. */
  1048. static void PlsrHwApplyAbFrequencyAtBoundary(uint8_t axis,
  1049. uint16_t basePsc,
  1050. uint16_t pairPsc,
  1051. uint16_t arr)
  1052. {
  1053. #ifdef PLSR_HOST_TEST
  1054. PlsrHwLoadAbPwm(axis, basePsc, pairPsc, arr);
  1055. PlsrHwBeginAbOutput(axis, 4U);
  1056. #else
  1057. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1058. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  1059. uint8_t leadAxis = (state->directionPositive != 0U) ? axis : pairAxis;
  1060. uint8_t lagAxis = (state->directionPositive != 0U) ? pairAxis : axis;
  1061. TIM_TypeDef *baseTimer = PlsrHwAxisMap[axis].timer;
  1062. TIM_TypeDef *pairTimer = PlsrHwAxisMap[pairAxis].timer;
  1063. TIM_TypeDef *leadTimer = PlsrHwAxisMap[leadAxis].timer;
  1064. TIM_TypeDef *lagTimer = PlsrHwAxisMap[lagAxis].timer;
  1065. uint32_t periodTicks = (uint32_t)arr + 1UL;
  1066. uint32_t compare = periodTicks / 2UL;
  1067. uint32_t leadStart = (periodTicks * 3UL) / 4UL + 1UL;
  1068. uint32_t lagStart = periodTicks / 2UL + 1UL;
  1069. uint32_t baseCr1;
  1070. uint32_t pairCr1;
  1071. if (leadStart >= periodTicks)
  1072. {
  1073. leadStart = periodTicks - 1UL;
  1074. }
  1075. if (lagStart >= periodTicks)
  1076. {
  1077. lagStart = periodTicks - 1UL;
  1078. }
  1079. baseTimer->CR1 &= ~TIM_CR1_CEN;
  1080. pairTimer->CR1 &= ~TIM_CR1_CEN;
  1081. PlsrHwCounterSuspend(axis);
  1082. PlsrHwHoldAbPairLowFast(axis);
  1083. baseTimer->CCER &= ~TIM_CCER_CC1E;
  1084. pairTimer->CCER &= ~TIM_CCER_CC1E;
  1085. baseTimer->DIER &= ~(TIM_DIER_UIE | TIM_DIER_CC1IE);
  1086. pairTimer->DIER &= ~(TIM_DIER_UIE | TIM_DIER_CC1IE);
  1087. baseTimer->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE;
  1088. pairTimer->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE;
  1089. baseTimer->PSC = basePsc;
  1090. pairTimer->PSC = pairPsc;
  1091. baseTimer->ARR = arr;
  1092. pairTimer->ARR = arr;
  1093. baseTimer->CCR1 = compare;
  1094. pairTimer->CCR1 = compare;
  1095. baseTimer->CR1 |= TIM_CR1_ARPE;
  1096. pairTimer->CR1 |= TIM_CR1_ARPE;
  1097. baseTimer->EGR = TIM_EGR_UG;
  1098. pairTimer->EGR = TIM_EGR_UG;
  1099. baseTimer->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
  1100. pairTimer->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
  1101. leadTimer->CNT = leadStart;
  1102. lagTimer->CNT = lagStart;
  1103. baseTimer->CCER =
  1104. (baseTimer->CCER & ~(TIM_CCER_CC1P | TIM_CCER_CC1E))
  1105. | TIM_CCER_CC1P | TIM_CCER_CC1E;
  1106. pairTimer->CCER =
  1107. (pairTimer->CCER & ~(TIM_CCER_CC1P | TIM_CCER_CC1E))
  1108. | TIM_CCER_CC1P | TIM_CCER_CC1E;
  1109. PlsrHwReleaseAbPairFast(axis);
  1110. baseTimer->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
  1111. | TIM_CCMR1_OC1PE;
  1112. pairTimer->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
  1113. | TIM_CCMR1_OC1PE;
  1114. state->abActiveBasePsc = basePsc;
  1115. state->abActivePairPsc = pairPsc;
  1116. state->abActiveArr = arr;
  1117. state->abCountAxis = lagAxis;
  1118. state->counterSourceAxis = (axis == 0U) ? axis : pairAxis;
  1119. state->abCounterBoundaryCnt =
  1120. (state->counterSourceAxis == leadAxis)
  1121. ? leadStart - 1UL
  1122. : periodTicks / 2UL;
  1123. state->abQuarter = 0U;
  1124. state->abStartupPriming = 0U;
  1125. state->abFastGated = 0U;
  1126. PlsrHwCounterBegin(axis);
  1127. baseCr1 = baseTimer->CR1 | TIM_CR1_CEN;
  1128. pairCr1 = pairTimer->CR1 | TIM_CR1_CEN;
  1129. baseTimer->CR1 = baseCr1;
  1130. pairTimer->CR1 = pairCr1;
  1131. baseTimer->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
  1132. pairTimer->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
  1133. if ((state->hardwareCounterActive == 0U)
  1134. || (state->abStopArmed != 0U))
  1135. {
  1136. lagTimer->DIER |= TIM_DIER_CC1IE;
  1137. }
  1138. __DMB();
  1139. #endif
  1140. }
  1141. static void PlsrHwConfigureCwCcwPwm(uint8_t axis, uint32_t frequencyHz)
  1142. {
  1143. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1144. uint8_t activeAxis = state->cwActiveAxis;
  1145. uint16_t psc;
  1146. uint16_t arr;
  1147. #ifndef PLSR_HOST_TEST
  1148. uint32_t interruptState;
  1149. #endif
  1150. if (PlsrCalculateTimerDivider(PlsrHwAxisMap[activeAxis].timerClockHz,
  1151. frequencyHz,
  1152. &psc,
  1153. &arr) != PLSR_RESULT_OK)
  1154. {
  1155. return;
  1156. }
  1157. #ifndef PLSR_HOST_TEST
  1158. interruptState = __get_PRIMASK();
  1159. __disable_irq();
  1160. __DMB();
  1161. #endif
  1162. /* The compare ISR may arm final-pulse shutdown while TIM6 is calculating
  1163. * a new divider. Recheck under the same short critical section as the
  1164. * preload writes so the tail period can no longer be changed afterwards. */
  1165. if (state->cwStopPending == 0U)
  1166. {
  1167. PlsrHwTimerSetPsc(activeAxis, psc);
  1168. PlsrHwTimerSetArr(activeAxis, arr);
  1169. PlsrHwTimerSetCcr(activeAxis, (uint32_t)arr / 2UL);
  1170. PlsrHwTimerSetPwmMode1(activeAxis);
  1171. PlsrHwTimerSetArpe(activeAxis, 1UL);
  1172. }
  1173. #ifndef PLSR_HOST_TEST
  1174. __DMB();
  1175. if (interruptState == 0UL)
  1176. {
  1177. __enable_irq();
  1178. }
  1179. #endif
  1180. }
  1181. static void PlsrHwBeginCwCcwOutput(uint8_t axis)
  1182. {
  1183. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1184. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  1185. uint8_t activeAxis = state->cwActiveAxis;
  1186. #ifndef PLSR_HOST_TEST
  1187. uint32_t interruptState = __get_PRIMASK();
  1188. __disable_irq();
  1189. __DMB();
  1190. #endif
  1191. /* Keep both pins in timer AF. On this output chain, switching a channel
  1192. * to GPIO-low is observable as an asserted Q edge. CC1E=0 is the tested
  1193. * inactive level and avoids the extra start/end edge. */
  1194. PlsrHwStopPwmTimer(axis);
  1195. PlsrHwStopPwmTimer(pairAxis);
  1196. /* CW/CCW retains the previously validated non-inverted polarity. */
  1197. PlsrHwTimerSetCc1PolarityInverted(axis, 0UL);
  1198. PlsrHwTimerSetCc1PolarityInverted(pairAxis, 0UL);
  1199. state->cwStopPending = 0U;
  1200. PlsrHwTimerSetUg(activeAxis);
  1201. PlsrHwTimerClearUif(activeAxis);
  1202. PlsrHwTimerClearCc1if(activeAxis);
  1203. PlsrHwTimerSetCnt(activeAxis, 0UL);
  1204. PlsrHwTimerSetUie(activeAxis, 0UL);
  1205. PlsrHwTimerSetCc1ie(activeAxis, 1UL);
  1206. PlsrHwTimerSetCc1e(activeAxis, 1UL);
  1207. PlsrHwTimerSetCen(activeAxis, 1UL);
  1208. #ifndef PLSR_HOST_TEST
  1209. __DMB();
  1210. if (interruptState == 0UL)
  1211. {
  1212. __enable_irq();
  1213. }
  1214. #endif
  1215. }
  1216. static void PlsrHwStopCwCcwOutput(uint8_t axis)
  1217. {
  1218. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  1219. #ifndef PLSR_HOST_TEST
  1220. uint32_t interruptState = __get_PRIMASK();
  1221. __disable_irq();
  1222. __DMB();
  1223. #endif
  1224. /* Disable both compare outputs while retaining AF mode; do not force
  1225. * either pin through GPIO during the direction handover. */
  1226. PlsrHwStopPwmTimer(axis);
  1227. PlsrHwStopPwmTimer(pairAxis);
  1228. PlsrHwAxes[axis].cwStopPending = 0U;
  1229. #ifndef PLSR_HOST_TEST
  1230. __DMB();
  1231. if (interruptState == 0UL)
  1232. {
  1233. __enable_irq();
  1234. }
  1235. #endif
  1236. }
  1237. static void PlsrHwConfigureActiveOutput(uint8_t axis,
  1238. PLSR_OUTPUT_MODE outputMode,
  1239. uint32_t frequencyHz)
  1240. {
  1241. if (outputMode == PLSR_OUTPUT_AB)
  1242. {
  1243. (void)PlsrHwConfigureAbPwm(axis, frequencyHz);
  1244. }
  1245. else if (outputMode == PLSR_OUTPUT_CW_CCW)
  1246. {
  1247. PlsrHwConfigureCwCcwPwm(axis, frequencyHz);
  1248. }
  1249. else
  1250. {
  1251. PlsrHwConfigurePwm(axis, frequencyHz);
  1252. }
  1253. }
  1254. static void PlsrHwBeginActiveOutput(uint8_t axis,
  1255. PLSR_OUTPUT_MODE outputMode)
  1256. {
  1257. if (outputMode == PLSR_OUTPUT_AB)
  1258. {
  1259. PlsrHwBeginAbOutput(axis, 0U);
  1260. }
  1261. else if (outputMode == PLSR_OUTPUT_CW_CCW)
  1262. {
  1263. PlsrHwBeginCwCcwOutput(axis);
  1264. }
  1265. else
  1266. {
  1267. PlsrHwPwmBegin(axis);
  1268. }
  1269. }
  1270. static void PlsrHwStopActiveOutput(uint8_t axis,
  1271. PLSR_OUTPUT_MODE outputMode)
  1272. {
  1273. if ((outputMode == PLSR_OUTPUT_AB) && (PlsrHwIsAbBaseAxis(axis) != 0U))
  1274. {
  1275. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  1276. #ifndef PLSR_HOST_TEST
  1277. uint32_t interruptState = __get_PRIMASK();
  1278. __disable_irq();
  1279. __DMB();
  1280. /* DONE/STOP 后继续由 GPIO 保持 00,禁止已关闭 timer 泄漏残余边沿。 */
  1281. PlsrHwHoldPulsePinLow(axis);
  1282. PlsrHwHoldPulsePinLow(pairAxis);
  1283. #endif
  1284. PlsrHwStopPwmTimer(axis);
  1285. PlsrHwStopPwmTimer(pairAxis);
  1286. PlsrHwAxes[axis].abStartupPriming = 0U;
  1287. #ifndef PLSR_HOST_TEST
  1288. __DMB();
  1289. if (interruptState == 0UL)
  1290. {
  1291. __enable_irq();
  1292. }
  1293. #endif
  1294. }
  1295. else if ((outputMode == PLSR_OUTPUT_CW_CCW)
  1296. && (PlsrHwIsAbBaseAxis(axis) != 0U))
  1297. {
  1298. PlsrHwStopCwCcwOutput(axis);
  1299. }
  1300. else
  1301. {
  1302. PlsrHwStopPulseDirOutput(axis);
  1303. }
  1304. }
  1305. static uint8_t PlsrHwCounterIndexForAxis(uint8_t axis)
  1306. {
  1307. return (uint8_t)(axis & 1U);
  1308. }
  1309. static void PlsrHwTimerSetCc1PolarityInverted(uint8_t axis, uint32_t value)
  1310. {
  1311. #ifdef PLSR_HOST_TEST
  1312. PlsrHwTimers[axis].ccer =
  1313. (PlsrHwTimers[axis].ccer & ~PLSR_HW_TIMER_CC1P_BIT)
  1314. | ((value != 0UL) ? PLSR_HW_TIMER_CC1P_BIT : 0UL);
  1315. #else
  1316. if (value != 0UL)
  1317. {
  1318. PlsrHwAxisMap[axis].timer->CCER |= TIM_CCER_CC1P;
  1319. }
  1320. else
  1321. {
  1322. PlsrHwAxisMap[axis].timer->CCER &= ~TIM_CCER_CC1P;
  1323. }
  1324. #endif
  1325. }
  1326. static uint8_t PlsrHwCounterTryAcquire(uint8_t axis,
  1327. PLSR_OUTPUT_MODE outputMode)
  1328. {
  1329. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1330. uint8_t counterIndex;
  1331. uint8_t acquired = 0U;
  1332. #ifndef PLSR_HOST_TEST
  1333. uint32_t interruptState;
  1334. #endif
  1335. state->counterIndex = PLSR_HW_COUNTER_NONE;
  1336. state->hardwareCounterActive = 0U;
  1337. state->hardwareCounterConfigured = 0U;
  1338. state->counterBlockPulses = 0UL;
  1339. state->counterPublishedPulses = 0UL;
  1340. if ((outputMode != PLSR_OUTPUT_PULSE_DIR)
  1341. && (outputMode != PLSR_OUTPUT_AB))
  1342. {
  1343. return 0U;
  1344. }
  1345. if (outputMode == PLSR_OUTPUT_AB)
  1346. {
  1347. /* One counter per fixed AB pair: Q0/Q1 -> TIM9, Q2/Q3 -> TIM12.
  1348. * Very short jobs retain the existing per-cycle ISR because a
  1349. * target-1 guard compare cannot be armed at raw count zero. */
  1350. if ((PlsrHwIsAbBaseAxis(axis) == 0U) || (state->targetPulses < 2))
  1351. {
  1352. return 0U;
  1353. }
  1354. counterIndex = (uint8_t)(axis >> 1U);
  1355. }
  1356. else
  1357. {
  1358. counterIndex = PlsrHwCounterIndexForAxis(axis);
  1359. }
  1360. #ifndef PLSR_HOST_TEST
  1361. interruptState = __get_PRIMASK();
  1362. __disable_irq();
  1363. __DMB();
  1364. #endif
  1365. if (PlsrHwCounterOwners[counterIndex] == PLSR_HW_COUNTER_NONE)
  1366. {
  1367. PlsrHwCounterOwners[counterIndex] = axis;
  1368. state->counterIndex = counterIndex;
  1369. state->hardwareCounterActive = 1U;
  1370. acquired = 1U;
  1371. }
  1372. #ifndef PLSR_HOST_TEST
  1373. __DMB();
  1374. if (interruptState == 0UL)
  1375. {
  1376. __enable_irq();
  1377. }
  1378. #endif
  1379. return acquired;
  1380. }
  1381. static void PlsrHwCounterRelease(uint8_t axis)
  1382. {
  1383. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1384. #ifndef PLSR_HOST_TEST
  1385. uint32_t interruptState = __get_PRIMASK();
  1386. __disable_irq();
  1387. __DMB();
  1388. #endif
  1389. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1390. {
  1391. #ifndef PLSR_HOST_TEST
  1392. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1393. counter->CR1 = 0UL;
  1394. counter->DIER = 0UL;
  1395. counter->SMCR = 0UL;
  1396. counter->SR = 0UL;
  1397. #endif
  1398. if (PlsrHwCounterOwners[state->counterIndex] == axis)
  1399. {
  1400. PlsrHwCounterOwners[state->counterIndex] =
  1401. PLSR_HW_COUNTER_NONE;
  1402. }
  1403. }
  1404. state->counterIndex = PLSR_HW_COUNTER_NONE;
  1405. state->hardwareCounterActive = 0U;
  1406. state->hardwareCounterConfigured = 0U;
  1407. state->counterBlockPulses = 0UL;
  1408. state->counterPublishedPulses = 0UL;
  1409. #ifndef PLSR_HOST_TEST
  1410. __DMB();
  1411. if (interruptState == 0UL)
  1412. {
  1413. __enable_irq();
  1414. }
  1415. #endif
  1416. }
  1417. static void PlsrHwCounterConfigure(uint8_t axis)
  1418. {
  1419. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1420. state->counterBlockPulses = 0UL;
  1421. state->counterPublishedPulses = 0UL;
  1422. #ifndef PLSR_HOST_TEST
  1423. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1424. {
  1425. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1426. uint32_t triggerSelection = ((axis & 2U) == 0U)
  1427. ? TIM_SMCR_TS_1
  1428. : (TIM_SMCR_TS_1 | TIM_SMCR_TS_0);
  1429. uint64_t comparePulses = (uint64_t)state->targetPulses;
  1430. /* PULSE/DIR owns one source timer and can force it inactive here. AB
  1431. * owns a pair; its atomic startup routine establishes both OCREF lows
  1432. * immediately before CounterBegin instead. */
  1433. if (state->outputMode == PLSR_OUTPUT_PULSE_DIR)
  1434. {
  1435. PlsrHwTimerSetCen(axis, 0UL);
  1436. PlsrHwTimerSetCc1e(axis, 0UL);
  1437. PlsrHwTimerSetForcedInactive(axis);
  1438. PlsrHwTimerSetUg(axis);
  1439. PlsrHwTimerClearUif(axis);
  1440. PlsrHwTimerClearCc1if(axis);
  1441. if (comparePulses > 1UL)
  1442. {
  1443. /* Wake one pulse before the target. The output timer can
  1444. * then stop on the target pulse's compare/falling edge rather
  1445. * than truncating the high level in this counter ISR. */
  1446. comparePulses--;
  1447. }
  1448. }
  1449. else
  1450. {
  1451. /* Wake the lag-CC1 one complete cycle before the target boundary.
  1452. * It verifies raw>=target at 00, avoiding ISR-latency overshoot. */
  1453. comparePulses--;
  1454. }
  1455. counter->CR1 = 0UL;
  1456. counter->DIER = 0UL;
  1457. counter->SMCR = 0UL;
  1458. counter->PSC = 0UL;
  1459. counter->ARR = 0xFFFFUL;
  1460. counter->CCR1 = (uint32_t)(comparePulses & UINT64_C(0xFFFF));
  1461. counter->CNT = 0UL;
  1462. counter->EGR = TIM_EGR_UG;
  1463. counter->SR = 0UL;
  1464. counter->SMCR = triggerSelection;
  1465. counter->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE;
  1466. }
  1467. #endif
  1468. }
  1469. static void PlsrHwCounterBegin(uint8_t axis)
  1470. {
  1471. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1472. if (state->hardwareCounterActive == 0U)
  1473. {
  1474. return;
  1475. }
  1476. if (state->hardwareCounterConfigured == 0U)
  1477. {
  1478. return;
  1479. }
  1480. #ifndef PLSR_HOST_TEST
  1481. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1482. {
  1483. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1484. counter->SMCR |= TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0;
  1485. counter->CR1 |= TIM_CR1_CEN;
  1486. }
  1487. #endif
  1488. }
  1489. static void PlsrHwArmPulseTargetTail(uint8_t axis)
  1490. {
  1491. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1492. if (state->pulseTargetStopArmed != 0U)
  1493. {
  1494. return;
  1495. }
  1496. state->pulseTargetStopArmed = 1U;
  1497. /* ARR/CCR are both preloaded and become active only at the target update.
  1498. * The in-flight target pulse therefore retains its old validated width;
  1499. * afterwards CNT<CCR remains terminal-low for the entire long period.
  1500. * Extending ARR alone is insufficient because the old short CCR would
  1501. * start one more physical pulse before a delayed completion ISR runs. */
  1502. PlsrHwTimerSetArr(axis, 0xFFFFUL);
  1503. PlsrHwTimerSetCcr(axis, 0xFFFFUL);
  1504. PlsrHwTimerClearUif(axis);
  1505. PlsrHwTimerSetUie(axis, 1UL);
  1506. }
  1507. static void PlsrHwCounterSuspend(uint8_t axis)
  1508. {
  1509. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1510. if ((state->hardwareCounterActive == 0U)
  1511. || (state->hardwareCounterConfigured == 0U))
  1512. {
  1513. return;
  1514. }
  1515. #ifndef PLSR_HOST_TEST
  1516. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1517. {
  1518. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1519. counter->CR1 &= ~TIM_CR1_CEN;
  1520. counter->SMCR &= ~(TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0);
  1521. }
  1522. #endif
  1523. }
  1524. static void PlsrHwCounterRebase(uint8_t axis, uint64_t pulses)
  1525. {
  1526. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1527. if ((state->hardwareCounterActive == 0U)
  1528. || (state->hardwareCounterConfigured == 0U))
  1529. {
  1530. return;
  1531. }
  1532. #ifdef PLSR_HOST_TEST
  1533. state->counterBlockPulses = pulses;
  1534. #else
  1535. state->counterBlockPulses =
  1536. pulses & ~(PLSR_HW_COUNTER_BLOCK_PULSES - UINT64_C(1));
  1537. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1538. {
  1539. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1540. counter->CNT = (uint16_t)pulses;
  1541. counter->SR = 0UL;
  1542. }
  1543. #endif
  1544. state->counterPublishedPulses = pulses;
  1545. }
  1546. static uint64_t PlsrHwCounterRawSnapshot(uint8_t axis)
  1547. {
  1548. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1549. uint64_t pulses = state->counterBlockPulses;
  1550. #ifndef PLSR_HOST_TEST
  1551. if (state->counterIndex < PLSR_HW_COUNTER_COUNT)
  1552. {
  1553. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  1554. pulses += (uint16_t)counter->CNT;
  1555. /* Cover the short window after wrap and before the block ISR. */
  1556. if ((counter->SR & TIM_SR_UIF) != 0UL)
  1557. {
  1558. pulses += PLSR_HW_COUNTER_BLOCK_PULSES;
  1559. }
  1560. }
  1561. #else
  1562. pulses = (state->outputMode == PLSR_OUTPUT_AB)
  1563. ? state->counterBlockPulses
  1564. : (uint64_t)state->emittedPulses;
  1565. #endif
  1566. return pulses;
  1567. }
  1568. static uint64_t PlsrHwCounterSnapshot(uint8_t axis)
  1569. {
  1570. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  1571. uint64_t pulses = PlsrHwCounterRawSnapshot(axis);
  1572. #ifndef PLSR_HOST_TEST
  1573. if ((state->outputMode == PLSR_OUTPUT_AB) && (pulses > 0UL)
  1574. && (state->counterSourceAxis < PLSR_HW_AXIS_COUNT))
  1575. {
  1576. uint64_t verifiedPulses;
  1577. uint32_t sourceCnt;
  1578. uint8_t attempt;
  1579. /* The ITR source rises inside an AB cycle, before the following 00
  1580. * boundary. A stable raw/CNT/raw snapshot identifies that interval
  1581. * and publishes only complete four-state cycles. */
  1582. for (attempt = 0U; attempt < 2U; attempt++)
  1583. {
  1584. pulses = PlsrHwCounterRawSnapshot(axis);
  1585. sourceCnt = PlsrHwAxisMap[state->counterSourceAxis].timer->CNT;
  1586. verifiedPulses = PlsrHwCounterRawSnapshot(axis);
  1587. if (pulses == verifiedPulses)
  1588. {
  1589. if ((sourceCnt < state->abCounterBoundaryCnt)
  1590. && (pulses > 0UL))
  1591. {
  1592. pulses--;
  1593. }
  1594. break;
  1595. }
  1596. pulses = verifiedPulses;
  1597. }
  1598. }
  1599. #else
  1600. if ((state->outputMode == PLSR_OUTPUT_AB) && (pulses > 0UL))
  1601. {
  1602. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  1603. uint8_t leadAxis = (state->directionPositive != 0U)
  1604. ? axis
  1605. : pairAxis;
  1606. uint8_t sourceQuarter = (state->counterSourceAxis == leadAxis)
  1607. ? 1U
  1608. : 2U;
  1609. if ((state->abQuarter >= sourceQuarter)
  1610. && (state->abQuarter != 0U))
  1611. {
  1612. pulses--;
  1613. }
  1614. }
  1615. #endif
  1616. if (pulses > (uint64_t)state->targetPulses)
  1617. {
  1618. pulses = (uint64_t)state->targetPulses;
  1619. }
  1620. /* The source timer and its TIM9/TIM12 ITR slave are separate hardware
  1621. * domains. Immediately after the source edge, source CNT can already be
  1622. * inside the next AB cycle while the slave raw count still has its old
  1623. * value. Phase correction would then transiently report N-1 after N was
  1624. * already published, and the core correctly treats that regression as a
  1625. * counter fault. Complete physical AB cycles never go backwards, so keep
  1626. * the last verified value as a monotonic floor. */
  1627. if (pulses < state->counterPublishedPulses)
  1628. {
  1629. pulses = state->counterPublishedPulses;
  1630. }
  1631. else
  1632. {
  1633. state->counterPublishedPulses = pulses;
  1634. }
  1635. return pulses;
  1636. }
  1637. /* AB terminal and pause IRQs only freeze the two phase timers at a verified
  1638. * 00 boundary. GPIO handoff, counter release/rebase and event publication
  1639. * are deliberately deferred to PlsrHwTick so an equal-priority second AB
  1640. * boundary can be serviced before its next quarter-period transition. */
  1641. static void PlsrHwFinishDeferredAbWork(uint8_t axis)
  1642. {
  1643. PLSR_HW_AXIS_STATE *state;
  1644. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  1645. {
  1646. return;
  1647. }
  1648. state = &PlsrHwAxes[axis];
  1649. if (state->abPauseGated != 0U)
  1650. {
  1651. uint64_t completedPulses =
  1652. (state->hardwareCounterActive != 0U)
  1653. ? PlsrHwCounterSnapshot(axis)
  1654. : (uint64_t)state->emittedPulses;
  1655. state->emittedPulses = (int64_t)completedPulses;
  1656. PlsrHwStopActiveOutput(axis, state->outputMode);
  1657. if (state->hardwareCounterActive != 0U)
  1658. {
  1659. PlsrHwCounterRebase(axis, completedPulses);
  1660. }
  1661. state->abQuarter = 0U;
  1662. state->abFrequencyPending = 0U;
  1663. state->abPauseGated = 0U;
  1664. state->abStopArmed =
  1665. (completedPulses
  1666. >= (uint64_t)(state->targetPulses - 1))
  1667. ? 1U
  1668. : 0U;
  1669. }
  1670. if (state->abCompletionDeferred != 0U)
  1671. {
  1672. state->emittedPulses = state->targetPulses;
  1673. PlsrHwStopActiveOutput(axis, state->outputMode);
  1674. PlsrHwCounterRelease(axis);
  1675. state->abQuarter = 0U;
  1676. state->abFrequencyPending = 0U;
  1677. state->abStopArmed = 0U;
  1678. state->abFastGated = 0U;
  1679. state->abCompletionDeferred = 0U;
  1680. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  1681. }
  1682. }
  1683. uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber)
  1684. {
  1685. /* 与资源层一致的合法输出点掩码(Q0~Q7、Q10~Q17、Q20)。 */
  1686. const uint32_t validOutputMask = 0x0013FCFFUL;
  1687. if (pointNumber >= PLSR_HW_OUTPUT_POINT_COUNT)
  1688. {
  1689. return 0U;
  1690. }
  1691. if ((validOutputMask & (1UL << pointNumber)) == 0UL)
  1692. {
  1693. return 0U;
  1694. }
  1695. #ifndef PLSR_HOST_TEST
  1696. if (PlsrHwOutputPins[pointNumber].port == NULL)
  1697. {
  1698. return 0U;
  1699. }
  1700. #endif
  1701. return 1U;
  1702. }
  1703. PLSR_RESULT PlsrHwSetValidationOutput(uint8_t pointNumber,
  1704. uint8_t terminalOn)
  1705. {
  1706. uint8_t axis;
  1707. if ((pointNumber < 1U) || (pointNumber > 4U))
  1708. {
  1709. return PLSR_RESULT_INVALID_RESOURCE;
  1710. }
  1711. for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++)
  1712. {
  1713. if ((PlsrHwAxes[axis].directionPoint == pointNumber)
  1714. && (PlsrHwAxes[axis].state != PLSR_HW_STATE_IDLE)
  1715. && (PlsrHwAxes[axis].state != PLSR_HW_STATE_DONE))
  1716. {
  1717. return PLSR_RESULT_RESOURCE_CONFLICT;
  1718. }
  1719. if ((pointNumber < PLSR_HW_AXIS_COUNT)
  1720. && (PlsrHwAxes[axis].state != PLSR_HW_STATE_IDLE)
  1721. && (PlsrHwAxes[axis].state != PLSR_HW_STATE_DONE)
  1722. && ((axis == pointNumber)
  1723. || (((PlsrHwAxes[axis].outputMode == PLSR_OUTPUT_AB)
  1724. || (PlsrHwAxes[axis].outputMode
  1725. == PLSR_OUTPUT_CW_CCW))
  1726. && ((axis & 0xFEU) == (pointNumber & 0xFEU)))))
  1727. {
  1728. return PLSR_RESULT_RESOURCE_CONFLICT;
  1729. }
  1730. }
  1731. #ifdef PLSR_HOST_TEST
  1732. if (terminalOn != 0U)
  1733. {
  1734. PlsrHwValidationOutputMask |=
  1735. (uint8_t)(1U << (pointNumber - 1U));
  1736. }
  1737. else
  1738. {
  1739. PlsrHwValidationOutputMask &=
  1740. (uint8_t)(~(uint8_t)(1U << (pointNumber - 1U)));
  1741. }
  1742. #else
  1743. {
  1744. const PLSR_HW_OUTPUT_PIN *pin = &PlsrHwOutputPins[pointNumber];
  1745. GPIO_InitTypeDef gpio;
  1746. uint8_t mask = (uint8_t)(1U << (pointNumber - 1U));
  1747. /* Board output ON is MCU-low. Establish OFF before changing MODER
  1748. * so enabling the validation fixture cannot create a terminal pulse. */
  1749. if ((PlsrHwValidationOutputMask & mask) == 0U)
  1750. {
  1751. pin->port->BSRR = (uint32_t)pin->pin;
  1752. gpio.Pin = pin->pin;
  1753. gpio.Mode = GPIO_MODE_OUTPUT_PP;
  1754. gpio.Pull = GPIO_NOPULL;
  1755. gpio.Speed = GPIO_SPEED_FREQ_LOW;
  1756. gpio.Alternate = 0U;
  1757. HAL_GPIO_Init(pin->port, &gpio);
  1758. }
  1759. pin->port->BSRR = (terminalOn != 0U)
  1760. ? ((uint32_t)pin->pin << 16U)
  1761. : (uint32_t)pin->pin;
  1762. if (terminalOn != 0U)
  1763. {
  1764. PlsrHwValidationOutputMask |= mask;
  1765. }
  1766. else
  1767. {
  1768. PlsrHwValidationOutputMask &= (uint8_t)(~mask);
  1769. }
  1770. }
  1771. #endif
  1772. return PLSR_RESULT_OK;
  1773. }
  1774. void PlsrHwValidationOutputsOff(void)
  1775. {
  1776. uint8_t point;
  1777. for (point = 1U; point <= 4U; point++)
  1778. {
  1779. (void)PlsrHwSetValidationOutput(point, 0U);
  1780. }
  1781. }
  1782. PLSR_RESULT PlsrHwInit(void)
  1783. {
  1784. uint8_t axis;
  1785. uint8_t counterIndex;
  1786. (void)memset(PlsrHwAxes, 0, sizeof(PlsrHwAxes));
  1787. PlsrHwDirectionBatchActive = 0U;
  1788. PlsrHwMaxOutputIsrCycles = 0UL;
  1789. PlsrHwMaxCounterIsrCycles = 0UL;
  1790. PlsrHwMaxControlIsrCycles = 0UL;
  1791. PlsrHwMaxAbGateCycles = 0UL;
  1792. PlsrHwValidationOutputMask = 0U;
  1793. #ifndef PLSR_HOST_TEST
  1794. PlsrHwAbGateMeasurePending = 0U;
  1795. #endif
  1796. PlsrHwTotalIsrCycles = 0UL;
  1797. PlsrHwIsrBusyStarted = 0UL;
  1798. PlsrHwIsrNesting = 0U;
  1799. #ifdef PLSR_HOST_TEST
  1800. PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
  1801. PlsrHwTestAbFullGateCount = 0UL;
  1802. #endif
  1803. for (counterIndex = 0U;
  1804. counterIndex < PLSR_HW_COUNTER_COUNT;
  1805. counterIndex++)
  1806. {
  1807. PlsrHwCounterOwners[counterIndex] = PLSR_HW_COUNTER_NONE;
  1808. }
  1809. for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++)
  1810. {
  1811. PlsrHwAxes[axis].state = PLSR_HW_STATE_IDLE;
  1812. PlsrHwAxes[axis].directionPoint = PLSR_HW_DIR_POINT_NONE;
  1813. PlsrHwAxes[axis].counterIndex = PLSR_HW_COUNTER_NONE;
  1814. PlsrHwAxes[axis].configuredDirectionPoint =
  1815. PLSR_HW_DIR_POINT_NONE;
  1816. #ifdef PLSR_HOST_TEST
  1817. (void)memset(&PlsrHwTimers[axis], 0, sizeof(PlsrHwTimers[axis]));
  1818. #else
  1819. PlsrHwTimerSetCc1e(axis, 0UL);
  1820. PlsrHwTimerSetCc1PolarityInverted(axis, 0UL);
  1821. PlsrHwTimerSetUie(axis, 0UL);
  1822. PlsrHwTimerSetCc1ie(axis, 0UL);
  1823. PlsrHwTimerSetCen(axis, 0UL);
  1824. #endif
  1825. }
  1826. #ifndef PLSR_HOST_TEST
  1827. {
  1828. GPIO_InitTypeDef gpio;
  1829. uint32_t tim6ClockHz;
  1830. uint16_t tim6Psc;
  1831. uint16_t tim6Arr;
  1832. CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  1833. DWT->CYCCNT = 0UL;
  1834. DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
  1835. /* 1. 输出点 GPIO 时钟(DIR 点按需配置时使用)。 */
  1836. __HAL_RCC_GPIOF_CLK_ENABLE();
  1837. __HAL_RCC_GPIOI_CLK_ENABLE();
  1838. __HAL_RCC_GPIOE_CLK_ENABLE();
  1839. __HAL_RCC_GPIOG_CLK_ENABLE();
  1840. __HAL_RCC_GPIOH_CLK_ENABLE();
  1841. __HAL_RCC_GPIOB_CLK_ENABLE();
  1842. /* 2. 上电安全:输出点保持复位默认高阻(漏型输出 = 截止 = OFF)。
  1843. * 不驱动任何 Y 点,DIR 点仅在 PlsrHwSetDirLevel 时按需配置。 */
  1844. /* 3. 定时器时钟。 */
  1845. __HAL_RCC_TIM10_CLK_ENABLE();
  1846. __HAL_RCC_TIM11_CLK_ENABLE();
  1847. __HAL_RCC_TIM13_CLK_ENABLE();
  1848. __HAL_RCC_TIM14_CLK_ENABLE();
  1849. __HAL_RCC_TIM6_CLK_ENABLE();
  1850. __HAL_RCC_TIM9_CLK_ENABLE();
  1851. __HAL_RCC_TIM12_CLK_ENABLE();
  1852. /* Independent 10kHz control clock for S2 refreshCode=2. */
  1853. tim6ClockHz = HAL_RCC_GetPCLK1Freq();
  1854. if ((RCC->CFGR & RCC_CFGR_PPRE1) != RCC_CFGR_PPRE1_DIV1)
  1855. {
  1856. tim6ClockHz *= 2UL;
  1857. }
  1858. if (PlsrCalculateTimerDivider(tim6ClockHz,
  1859. 10000UL,
  1860. &tim6Psc,
  1861. &tim6Arr) != PLSR_RESULT_OK)
  1862. {
  1863. return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
  1864. }
  1865. TIM6->CR1 = 0UL;
  1866. TIM6->DIER = 0UL;
  1867. TIM6->PSC = tim6Psc;
  1868. TIM6->ARR = tim6Arr;
  1869. TIM6->EGR = TIM_EGR_UG;
  1870. TIM6->SR = 0UL;
  1871. TIM6->DIER = TIM_DIER_UIE;
  1872. HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 2U, 0U);
  1873. HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
  1874. TIM6->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
  1875. /* 4. 脉冲点切定时器复用(PF6/7=AF3、PF8/9=AF9)。
  1876. * 定时器通道尚未使能(CC1E=0),输出级断开,无毛刺。 */
  1877. gpio.Mode = GPIO_MODE_AF_PP;
  1878. gpio.Pull = GPIO_NOPULL;
  1879. gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  1880. gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  1881. gpio.Alternate = 3U;
  1882. HAL_GPIO_Init(GPIOF, &gpio);
  1883. gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9;
  1884. gpio.Alternate = 9U;
  1885. HAL_GPIO_Init(GPIOF, &gpio);
  1886. /* 5. 更新中断 NVIC:高速计数/尾脉冲层(P3b 统一规划优先级表)。 */
  1887. HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 1U, 0U);
  1888. HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
  1889. HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 1U, 0U);
  1890. HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
  1891. HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM11_IRQn, 1U, 0U);
  1892. HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM11_IRQn);
  1893. HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, 1U, 0U);
  1894. HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
  1895. HAL_NVIC_SetPriority(TIM1_BRK_TIM9_IRQn, 1U, 0U);
  1896. HAL_NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
  1897. HAL_NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 1U, 0U);
  1898. HAL_NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
  1899. }
  1900. #endif
  1901. return PLSR_RESULT_OK;
  1902. }
  1903. PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params)
  1904. {
  1905. PLSR_HW_AXIS_STATE *state;
  1906. uint8_t directionChanged = 0U;
  1907. if ((axis >= PLSR_HW_AXIS_COUNT) || (params == NULL))
  1908. {
  1909. return PLSR_RESULT_INVALID_ARGUMENT;
  1910. }
  1911. if (params->targetPulses <= 0)
  1912. {
  1913. return PLSR_RESULT_INVALID_ARGUMENT;
  1914. }
  1915. if ((uint32_t)params->outputMode > (uint32_t)PLSR_OUTPUT_CW_CCW)
  1916. {
  1917. return PLSR_RESULT_INVALID_ARGUMENT;
  1918. }
  1919. if (((params->outputMode == PLSR_OUTPUT_AB)
  1920. || (params->outputMode == PLSR_OUTPUT_CW_CCW))
  1921. && (PlsrHwIsAbBaseAxis(axis) == 0U))
  1922. {
  1923. return PLSR_RESULT_INVALID_AXIS;
  1924. }
  1925. state = &PlsrHwAxes[axis];
  1926. if ((state->state == PLSR_HW_STATE_RUNNING)
  1927. || (state->abCompletionDeferred != 0U)
  1928. || (state->abPauseGated != 0U))
  1929. {
  1930. return PLSR_RESULT_BUSY;
  1931. }
  1932. /* A caller may replace a prepared-but-not-started segment. Return its
  1933. * counter lease first, otherwise the paired axis would fall back forever. */
  1934. if (state->hardwareCounterActive != 0U)
  1935. {
  1936. PlsrHwCounterRelease(axis);
  1937. }
  1938. /* 方向延时只在方向发生变化时生效(首次启动/换向/换方向点):
  1939. * 段间同向衔接不再等待 10ms,直接进入 PWM 待启动。 */
  1940. if (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
  1941. {
  1942. directionChanged =
  1943. (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
  1944. || (state->directionPoint != params->directionPoint)
  1945. || (state->directionPositive != params->directionPositive)
  1946. || (state->directionNegativeLogic
  1947. != params->directionNegativeLogic);
  1948. }
  1949. state->outputMode = params->outputMode;
  1950. state->targetPulses = params->targetPulses;
  1951. state->emittedPulses = 0;
  1952. state->currentFrequencyHz = params->frequencyHz;
  1953. state->directionPoint =
  1954. (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
  1955. ? params->directionPoint
  1956. : PLSR_HW_DIR_POINT_NONE;
  1957. state->directionDelayRemainingMs =
  1958. ((params->outputMode == PLSR_OUTPUT_PULSE_DIR)
  1959. && (directionChanged != 0U))
  1960. ? params->directionDelayMs
  1961. : 0U;
  1962. state->abQuarter = 0U;
  1963. state->abFrequencyPending = 0U;
  1964. state->abStopArmed = 0U;
  1965. state->abFastGated = 0U;
  1966. state->abPausePending = 0U;
  1967. state->abPauseGated = 0U;
  1968. state->abCompletionDeferred = 0U;
  1969. state->pulseBoundaryStopPending = 0U;
  1970. state->pulseTargetStopArmed = 0U;
  1971. if (params->outputMode == PLSR_OUTPUT_PULSE_DIR)
  1972. {
  1973. PlsrHwSetDirLevel(axis,
  1974. params->directionPositive,
  1975. params->directionNegativeLogic);
  1976. }
  1977. else
  1978. {
  1979. state->directionPositive =
  1980. (params->directionPositive != 0U) ? 1U : 0U;
  1981. state->directionNegativeLogic = 0U;
  1982. }
  1983. if (params->outputMode == PLSR_OUTPUT_CW_CCW)
  1984. {
  1985. state->cwActiveAxis = (state->directionPositive != 0U)
  1986. ? axis
  1987. : PlsrHwGetPairedAxis(axis);
  1988. }
  1989. (void)PlsrHwCounterTryAcquire(axis, params->outputMode);
  1990. if (state->hardwareCounterActive != 0U)
  1991. {
  1992. PlsrHwCounterConfigure(axis);
  1993. state->hardwareCounterConfigured = 1U;
  1994. }
  1995. state->state = (state->directionDelayRemainingMs > 0U)
  1996. ? PLSR_HW_STATE_DIR_SETTLING
  1997. : PLSR_HW_STATE_PWM_PENDING;
  1998. return PLSR_RESULT_OK;
  1999. }
  2000. PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz)
  2001. {
  2002. PLSR_HW_AXIS_STATE *state;
  2003. if (axis >= PLSR_HW_AXIS_COUNT)
  2004. {
  2005. return PLSR_RESULT_INVALID_ARGUMENT;
  2006. }
  2007. state = &PlsrHwAxes[axis];
  2008. if ((state->pulseBoundaryStopPending != 0U)
  2009. || (state->pulseTargetStopArmed != 0U))
  2010. {
  2011. /* ACT/EXT already froze the requested waveform. Do not allow the
  2012. * 100us profile ISR to move ARR/CCR before the natural tail edge. */
  2013. return PLSR_RESULT_OK;
  2014. }
  2015. if ((state->outputMode == PLSR_OUTPUT_CW_CCW)
  2016. && (state->cwStopPending != 0U))
  2017. {
  2018. /* Preserve the final physical high width until its natural update
  2019. * boundary; no later profile write may move that boundary. */
  2020. return PLSR_RESULT_OK;
  2021. }
  2022. if ((state->state == PLSR_HW_STATE_RUNNING)
  2023. && (frequencyHz == state->currentFrequencyHz))
  2024. {
  2025. /* Cruise ticks commonly request the same frequency for every axis.
  2026. * Recomputing PSC/ARR performs two 64-bit divisions and rewrites the
  2027. * same preload registers without changing the waveform. */
  2028. return PLSR_RESULT_OK;
  2029. }
  2030. state->currentFrequencyHz = frequencyHz;
  2031. if (state->state == PLSR_HW_STATE_RUNNING)
  2032. {
  2033. if (frequencyHz > 0UL)
  2034. {
  2035. if (state->outputMode == PLSR_OUTPUT_AB)
  2036. {
  2037. if (PlsrHwQueueAbFrequency(axis, frequencyHz) == 0U)
  2038. {
  2039. return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
  2040. }
  2041. }
  2042. else
  2043. {
  2044. /* PULSE/DIR 仍由单定时器在自身 update 边界加载预装值。 */
  2045. PlsrHwConfigureActiveOutput(axis,
  2046. state->outputMode,
  2047. frequencyHz);
  2048. }
  2049. }
  2050. else
  2051. {
  2052. if (state->outputMode == PLSR_OUTPUT_AB)
  2053. {
  2054. #ifndef PLSR_HOST_TEST
  2055. uint32_t interruptState = __get_PRIMASK();
  2056. __disable_irq();
  2057. __DMB();
  2058. #endif
  2059. /* PAUSE is a controlled AB stop. Keep both timers running
  2060. * until the lag compare reaches the next real 00 boundary;
  2061. * forcing GPIO low here would discard and later re-emit an
  2062. * already-started cycle, adding one terminal edge. */
  2063. state->abFrequencyPending = 0U;
  2064. state->abPausePending = 1U;
  2065. PlsrHwArmAbBoundaryInterrupts(axis);
  2066. #ifndef PLSR_HOST_TEST
  2067. __DMB();
  2068. if (interruptState == 0UL)
  2069. {
  2070. __enable_irq();
  2071. }
  2072. #endif
  2073. }
  2074. else
  2075. {
  2076. PlsrHwStopActiveOutput(axis, state->outputMode);
  2077. }
  2078. }
  2079. }
  2080. else if ((state->state == PLSR_HW_STATE_PWM_PENDING)
  2081. && (frequencyHz > 0UL))
  2082. {
  2083. PlsrHwConfigureActiveOutput(axis, state->outputMode, frequencyHz);
  2084. /* 必须先发布 RUNNING,避免启用 timer IRQ 后观察到 PWM_PENDING。 */
  2085. state->state = PLSR_HW_STATE_RUNNING;
  2086. PlsrHwBeginActiveOutput(axis, state->outputMode);
  2087. if ((state->outputMode == PLSR_OUTPUT_PULSE_DIR)
  2088. && (state->targetPulses == 1))
  2089. {
  2090. PlsrHwArmPulseTargetTail(axis);
  2091. }
  2092. }
  2093. return PLSR_RESULT_OK;
  2094. }
  2095. PLSR_RESULT PlsrHwResumePulse(uint8_t axis)
  2096. {
  2097. PLSR_HW_AXIS_STATE *state;
  2098. if (axis >= PLSR_HW_AXIS_COUNT)
  2099. {
  2100. return PLSR_RESULT_INVALID_ARGUMENT;
  2101. }
  2102. state = &PlsrHwAxes[axis];
  2103. if ((state->state != PLSR_HW_STATE_RUNNING)
  2104. || (state->currentFrequencyHz != 0UL)
  2105. || (state->abPausePending != 0U)
  2106. || (state->abPauseGated != 0U)
  2107. || (PlsrHwGetEmittedPulses(axis) >= state->targetPulses))
  2108. {
  2109. return PLSR_RESULT_INVALID_STATE;
  2110. }
  2111. /* The pause boundary worker stopped the physical timers at 00 and kept
  2112. * the completed-cycle count. PWM_PENDING makes the next non-zero control
  2113. * tick use the clean-start path without resetting that count. */
  2114. state->state = PLSR_HW_STATE_PWM_PENDING;
  2115. return PLSR_RESULT_OK;
  2116. }
  2117. PLSR_RESULT PlsrHwStopPulseAtBoundary(uint8_t axis)
  2118. {
  2119. PLSR_HW_AXIS_STATE *state;
  2120. if (axis >= PLSR_HW_AXIS_COUNT)
  2121. {
  2122. return PLSR_RESULT_INVALID_ARGUMENT;
  2123. }
  2124. state = &PlsrHwAxes[axis];
  2125. if ((state->state != PLSR_HW_STATE_RUNNING)
  2126. || (state->outputMode != PLSR_OUTPUT_PULSE_DIR))
  2127. {
  2128. return PLSR_RESULT_INVALID_STATE;
  2129. }
  2130. if (state->pulseBoundaryStopPending != 0U)
  2131. {
  2132. return PLSR_RESULT_OK;
  2133. }
  2134. #ifndef PLSR_HOST_TEST
  2135. {
  2136. uint32_t interruptState = __get_PRIMASK();
  2137. __disable_irq();
  2138. __DMB();
  2139. #endif
  2140. state->pulseBoundaryStopPending = 1U;
  2141. /* An update is the completed terminal-pulse boundary. Hardware-counted
  2142. * axes normally keep UIE disabled, so explicitly arm one update here. */
  2143. PlsrHwTimerClearUif(axis);
  2144. PlsrHwTimerSetUie(axis, 1UL);
  2145. #ifndef PLSR_HOST_TEST
  2146. __DMB();
  2147. if (interruptState == 0UL)
  2148. {
  2149. __enable_irq();
  2150. }
  2151. }
  2152. #endif
  2153. return PLSR_RESULT_OK;
  2154. }
  2155. PLSR_RESULT PlsrHwStopPulse(uint8_t axis)
  2156. {
  2157. PLSR_HW_AXIS_STATE *state;
  2158. if (axis >= PLSR_HW_AXIS_COUNT)
  2159. {
  2160. return PLSR_RESULT_INVALID_ARGUMENT;
  2161. }
  2162. state = &PlsrHwAxes[axis];
  2163. if (state->state != PLSR_HW_STATE_IDLE)
  2164. {
  2165. if (state->hardwareCounterActive != 0U)
  2166. {
  2167. state->emittedPulses =
  2168. (int64_t)PlsrHwCounterSnapshot(axis);
  2169. }
  2170. PlsrHwStopActiveOutput(axis, state->outputMode);
  2171. PlsrHwCounterRelease(axis);
  2172. state->abQuarter = 0U;
  2173. state->abFrequencyPending = 0U;
  2174. state->abStopArmed = 0U;
  2175. state->abFastGated = 0U;
  2176. state->abPausePending = 0U;
  2177. state->abPauseGated = 0U;
  2178. state->abCompletionDeferred = 0U;
  2179. state->pulseBoundaryStopPending = 0U;
  2180. state->pulseTargetStopArmed = 0U;
  2181. state->state = PLSR_HW_STATE_IDLE;
  2182. }
  2183. return PLSR_RESULT_OK;
  2184. }
  2185. uint8_t PlsrHwIsPulseActive(uint8_t axis)
  2186. {
  2187. if (axis >= PLSR_HW_AXIS_COUNT)
  2188. {
  2189. return 0U;
  2190. }
  2191. return (PlsrHwAxes[axis].state == PLSR_HW_STATE_RUNNING) ? 1U : 0U;
  2192. }
  2193. PLSR_HW_STATE PlsrHwGetState(uint8_t axis)
  2194. {
  2195. if (axis >= PLSR_HW_AXIS_COUNT)
  2196. {
  2197. return PLSR_HW_STATE_IDLE;
  2198. }
  2199. return PlsrHwAxes[axis].state;
  2200. }
  2201. uint32_t PlsrHwGetTimerClockHz(uint8_t axis)
  2202. {
  2203. if (axis >= PLSR_HW_AXIS_COUNT)
  2204. {
  2205. return 0UL;
  2206. }
  2207. return PlsrHwAxisMap[axis].timerClockHz;
  2208. }
  2209. uint32_t PlsrHwGetCurrentFrequencyHz(uint8_t axis)
  2210. {
  2211. if ((axis >= PLSR_HW_AXIS_COUNT)
  2212. || (PlsrHwAxes[axis].state != PLSR_HW_STATE_RUNNING))
  2213. {
  2214. return 0UL;
  2215. }
  2216. return PlsrHwAxes[axis].currentFrequencyHz;
  2217. }
  2218. /* 硬件已发出的脉冲数(profile 虚拟计数校准用,中断内递增)。 */
  2219. int64_t PlsrHwGetEmittedPulses(uint8_t axis)
  2220. {
  2221. int64_t emittedPulses;
  2222. if (axis >= PLSR_HW_AXIS_COUNT)
  2223. {
  2224. return 0;
  2225. }
  2226. #ifdef PLSR_HOST_TEST
  2227. emittedPulses = (PlsrHwAxes[axis].hardwareCounterActive != 0U)
  2228. ? (int64_t)PlsrHwCounterSnapshot(axis)
  2229. : PlsrHwAxes[axis].emittedPulses;
  2230. #else
  2231. {
  2232. uint32_t interruptState = __get_PRIMASK();
  2233. __disable_irq();
  2234. __DMB();
  2235. emittedPulses = (PlsrHwAxes[axis].hardwareCounterActive != 0U)
  2236. ? (int64_t)PlsrHwCounterSnapshot(axis)
  2237. : PlsrHwAxes[axis].emittedPulses;
  2238. __DMB();
  2239. if (interruptState == 0UL)
  2240. {
  2241. __enable_irq();
  2242. }
  2243. }
  2244. #endif
  2245. return emittedPulses;
  2246. }
  2247. uint8_t PlsrHwIsAbStartupPriming(uint8_t axis)
  2248. {
  2249. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  2250. {
  2251. return 0U;
  2252. }
  2253. return PlsrHwAxes[axis].abStartupPriming;
  2254. }
  2255. uint8_t PlsrHwUsesHardwareCounter(uint8_t axis)
  2256. {
  2257. if (axis >= PLSR_HW_AXIS_COUNT)
  2258. {
  2259. return 0U;
  2260. }
  2261. return PlsrHwAxes[axis].hardwareCounterActive;
  2262. }
  2263. uint32_t PlsrHwGetMaxOutputIsrCycles(void)
  2264. {
  2265. return PlsrHwMaxOutputIsrCycles;
  2266. }
  2267. uint32_t PlsrHwGetMaxCounterIsrCycles(void)
  2268. {
  2269. return PlsrHwMaxCounterIsrCycles;
  2270. }
  2271. uint32_t PlsrHwGetMaxControlIsrCycles(void)
  2272. {
  2273. return PlsrHwMaxControlIsrCycles;
  2274. }
  2275. uint32_t PlsrHwGetMaxAbGateCycles(void)
  2276. {
  2277. return PlsrHwMaxAbGateCycles;
  2278. }
  2279. void PlsrHwGetCycleSnapshot(uint32_t *cycleCount,
  2280. uint64_t *plsrIsrCycles)
  2281. {
  2282. #ifdef PLSR_HOST_TEST
  2283. if (cycleCount != NULL)
  2284. {
  2285. *cycleCount = 0UL;
  2286. }
  2287. if (plsrIsrCycles != NULL)
  2288. {
  2289. *plsrIsrCycles = 0UL;
  2290. }
  2291. #else
  2292. uint32_t interruptState = __get_PRIMASK();
  2293. __disable_irq();
  2294. __DMB();
  2295. if (cycleCount != NULL)
  2296. {
  2297. *cycleCount = DWT->CYCCNT;
  2298. }
  2299. if (plsrIsrCycles != NULL)
  2300. {
  2301. *plsrIsrCycles = PlsrHwTotalIsrCycles;
  2302. }
  2303. __DMB();
  2304. if (interruptState == 0UL)
  2305. {
  2306. __enable_irq();
  2307. }
  2308. #endif
  2309. }
  2310. void PlsrHwTick(uint8_t axis)
  2311. {
  2312. PLSR_HW_AXIS_STATE *state;
  2313. if (axis >= PLSR_HW_AXIS_COUNT)
  2314. {
  2315. return;
  2316. }
  2317. state = &PlsrHwAxes[axis];
  2318. if ((state->abPauseGated != 0U)
  2319. || (state->abCompletionDeferred != 0U))
  2320. {
  2321. PlsrHwFinishDeferredAbWork(axis);
  2322. }
  2323. /* 调试:每 tick 记录定时器实况(CNT 演化定位第一周期压缩)。 */
  2324. PlsrHwDbgCapture(axis, 3U);
  2325. switch (state->state)
  2326. {
  2327. case PLSR_HW_STATE_DIR_SETTLING:
  2328. if (state->directionDelayRemainingMs > 0U)
  2329. {
  2330. state->directionDelayRemainingMs--;
  2331. }
  2332. if (state->directionDelayRemainingMs == 0U)
  2333. {
  2334. state->state = PLSR_HW_STATE_PWM_PENDING;
  2335. }
  2336. break;
  2337. case PLSR_HW_STATE_PWM_PENDING:
  2338. if (state->currentFrequencyHz > 0UL)
  2339. {
  2340. PlsrHwConfigureActiveOutput(axis,
  2341. state->outputMode,
  2342. state->currentFrequencyHz);
  2343. state->state = PLSR_HW_STATE_RUNNING;
  2344. PlsrHwBeginActiveOutput(axis, state->outputMode);
  2345. }
  2346. break;
  2347. default:
  2348. break;
  2349. }
  2350. }
  2351. static void PlsrHwFastGateAbPair(uint8_t axis)
  2352. {
  2353. uint8_t pairAxis = PlsrHwGetPairedAxis(axis);
  2354. #ifdef PLSR_HOST_TEST
  2355. PlsrHwTimerSetCen(axis, 0UL);
  2356. PlsrHwTimerSetCen(pairAxis, 0UL);
  2357. PlsrHwCounterSuspend(axis);
  2358. #else
  2359. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  2360. /* This is the sub-2.5us terminal boundary. Use the same register writes
  2361. * as the generic helpers without their low-optimization call overhead. */
  2362. PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_CEN;
  2363. PlsrHwAxisMap[pairAxis].timer->CR1 &= ~TIM_CR1_CEN;
  2364. if ((state->hardwareCounterActive != 0U)
  2365. && (state->hardwareCounterConfigured != 0U)
  2366. && (state->counterIndex < PLSR_HW_COUNTER_COUNT))
  2367. {
  2368. TIM_TypeDef *counter = PlsrHwCounters[state->counterIndex];
  2369. counter->CR1 &= ~TIM_CR1_CEN;
  2370. counter->SMCR &= ~(TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1
  2371. | TIM_SMCR_SMS_0);
  2372. }
  2373. #endif
  2374. }
  2375. static void PlsrHwDeferAbCompletion(uint8_t axis)
  2376. {
  2377. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  2378. state->emittedPulses = state->targetPulses;
  2379. state->abQuarter = 0U;
  2380. state->abFrequencyPending = 0U;
  2381. state->abStopArmed = 0U;
  2382. state->abPausePending = 0U;
  2383. state->abPauseGated = 0U;
  2384. state->abCompletionDeferred = 1U;
  2385. state->state = PLSR_HW_STATE_DONE;
  2386. }
  2387. static void PlsrHwRecordFullAbGateTime(void)
  2388. {
  2389. #ifndef PLSR_HOST_TEST
  2390. /* The wrapper records the endpoint after the common ISR accounting, just
  2391. * before exception return. Measuring here would omit that equal-priority
  2392. * blocking tail and could understate the 2.5us near-simultaneous window. */
  2393. PlsrHwAbGateMeasurePending = 1U;
  2394. #else
  2395. PlsrHwTestAbFullGateCount++;
  2396. #endif
  2397. }
  2398. #ifndef PLSR_HOST_TEST
  2399. static void PlsrHwFinishAbGateMeasurement(uint32_t started,
  2400. uint8_t interruptAxis)
  2401. {
  2402. uint8_t finalScanRequired;
  2403. uint8_t preferredAxis = (uint8_t)(interruptAxis & 0xFEU);
  2404. (void)started;
  2405. /* Re-scan as the final ISR operation. A second equal-priority AB pair may
  2406. * reach its 00 boundary after PlsrHwOnTimerUpdate() performed its early
  2407. * scans. Gating it here leaves only exception-return/tail-chain overhead
  2408. * before its pending IRQ runs, rather than the bookkeeping tail of the
  2409. * first pair. D1468 measures this safety-critical final scan. */
  2410. finalScanRequired = PlsrHwAbGateMeasurePending;
  2411. if ((((PlsrHwAxes[0U].state == PLSR_HW_STATE_RUNNING)
  2412. && (PlsrHwAxes[0U].outputMode == PLSR_OUTPUT_AB)
  2413. && (PlsrHwAxes[0U].abStopArmed != 0U)))
  2414. || (((PlsrHwAxes[2U].state == PLSR_HW_STATE_RUNNING)
  2415. && (PlsrHwAxes[2U].outputMode == PLSR_OUTPUT_AB)
  2416. && (PlsrHwAxes[2U].abStopArmed != 0U))))
  2417. {
  2418. finalScanRequired = 1U;
  2419. }
  2420. if (finalScanRequired == 0U)
  2421. {
  2422. return;
  2423. }
  2424. if (PlsrHwGateArmedAbOutputs(preferredAxis) != 0U)
  2425. {
  2426. PlsrHwAbGateMeasurePending = 1U;
  2427. }
  2428. if (PlsrHwAbGateMeasurePending != 0U)
  2429. {
  2430. /* Actual gate latency is recorded at the register write that freezes
  2431. * the pair. Do not extend it with checks performed after the output
  2432. * is already physically safe. */
  2433. PlsrHwAbGateMeasurePending = 0U;
  2434. }
  2435. }
  2436. #endif
  2437. static uint8_t PlsrHwGateArmedAbOutputs(uint8_t preferredAxis)
  2438. {
  2439. uint8_t gated = 0U;
  2440. uint8_t index;
  2441. #ifndef PLSR_HOST_TEST
  2442. uint32_t started = DWT->CYCCNT;
  2443. #endif
  2444. /* Both AB pairs use equal-priority IRQs. Scan and gate every pair before
  2445. * doing any event publication so two simultaneous 100kHz completions
  2446. * cannot make the second pair run an extra quarter while its IRQ waits. */
  2447. if ((preferredAxis != 0U) && (preferredAxis != 2U))
  2448. {
  2449. preferredAxis = 0U;
  2450. }
  2451. for (index = 0U; index < 2U; index++)
  2452. {
  2453. uint8_t axis = (index == 0U)
  2454. ? preferredAxis
  2455. : (uint8_t)(preferredAxis ^ 2U);
  2456. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  2457. if ((state->state == PLSR_HW_STATE_RUNNING)
  2458. && (state->outputMode == PLSR_OUTPUT_AB)
  2459. && (state->abStopArmed != 0U)
  2460. && (state->abFastGated == 0U)
  2461. && (PlsrHwTimerHasCc1if(state->abCountAxis) != 0U)
  2462. && (PlsrHwCounterRawSnapshot(axis)
  2463. >= (uint64_t)state->targetPulses))
  2464. {
  2465. /* This entry is the verified 00 boundary. Stop both counters
  2466. * first, but keep CC1E driving the frozen 00 until the slower path
  2467. * hands the pins to GPIO. */
  2468. PlsrHwFastGateAbPair(axis);
  2469. state->abFastGated = 1U;
  2470. gated = 1U;
  2471. #ifndef PLSR_HOST_TEST
  2472. {
  2473. uint32_t elapsed = DWT->CYCCNT - started;
  2474. if (elapsed > PlsrHwMaxAbGateCycles)
  2475. {
  2476. PlsrHwMaxAbGateCycles = elapsed;
  2477. }
  2478. }
  2479. #endif
  2480. }
  2481. }
  2482. return gated;
  2483. }
  2484. /* 输出定时器中断入口:PULSE/DIR 在 update 计数;AB 在落后相
  2485. * CC1 下降沿(四状态回到 00)计一个完整正交周期。 */
  2486. static uint8_t PlsrHwPulseDirFastPathAllowed(uint8_t axis)
  2487. {
  2488. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  2489. if ((state->state != PLSR_HW_STATE_RUNNING)
  2490. || (state->outputMode != PLSR_OUTPUT_PULSE_DIR))
  2491. {
  2492. return 0U;
  2493. }
  2494. /* An AB target boundary may arrive while another output IRQ is active.
  2495. * Preserve the cross-pair scan in that short safety-critical window. */
  2496. if (
  2497. #ifndef PLSR_HOST_TEST
  2498. (PlsrHwAbGateMeasurePending != 0U) ||
  2499. #endif
  2500. ((PlsrHwAxes[0U].state == PLSR_HW_STATE_RUNNING)
  2501. && (PlsrHwAxes[0U].outputMode == PLSR_OUTPUT_AB)
  2502. && (PlsrHwAxes[0U].abStopArmed != 0U))
  2503. || ((PlsrHwAxes[2U].state == PLSR_HW_STATE_RUNNING)
  2504. && (PlsrHwAxes[2U].outputMode == PLSR_OUTPUT_AB)
  2505. && (PlsrHwAxes[2U].abStopArmed != 0U)))
  2506. {
  2507. return 0U;
  2508. }
  2509. return 1U;
  2510. }
  2511. static void PlsrHwAccountPulseDirUpdate(uint8_t axis)
  2512. {
  2513. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis];
  2514. if ((state->state != PLSR_HW_STATE_RUNNING)
  2515. || (state->outputMode != PLSR_OUTPUT_PULSE_DIR))
  2516. {
  2517. return;
  2518. }
  2519. if (state->pulseBoundaryStopPending != 0U)
  2520. {
  2521. #ifndef PLSR_HOST_TEST
  2522. if (state->hardwareCounterActive != 0U)
  2523. {
  2524. state->emittedPulses = (int64_t)PlsrHwCounterSnapshot(axis);
  2525. }
  2526. else
  2527. #endif
  2528. {
  2529. state->emittedPulses++;
  2530. }
  2531. state->pulseBoundaryStopPending = 0U;
  2532. state->pulseTargetStopArmed = 0U;
  2533. PlsrHwStopActiveOutput(axis, state->outputMode);
  2534. state->state = PLSR_HW_STATE_DONE;
  2535. PlsrHwCounterRelease(axis);
  2536. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2537. return;
  2538. }
  2539. #ifndef PLSR_HOST_TEST
  2540. if (state->hardwareCounterActive != 0U)
  2541. {
  2542. uint64_t pulses = PlsrHwCounterSnapshot(axis);
  2543. if ((state->pulseTargetStopArmed != 0U)
  2544. && (pulses >= (uint64_t)state->targetPulses))
  2545. {
  2546. state->emittedPulses = state->targetPulses;
  2547. state->pulseTargetStopArmed = 0U;
  2548. PlsrHwStopActiveOutput(axis, state->outputMode);
  2549. state->state = PLSR_HW_STATE_DONE;
  2550. PlsrHwCounterRelease(axis);
  2551. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2552. }
  2553. return;
  2554. }
  2555. #endif
  2556. state->emittedPulses++;
  2557. if (state->emittedPulses >= state->targetPulses)
  2558. {
  2559. state->emittedPulses = state->targetPulses;
  2560. state->pulseTargetStopArmed = 0U;
  2561. PlsrHwStopActiveOutput(axis, state->outputMode);
  2562. state->state = PLSR_HW_STATE_DONE;
  2563. PlsrHwCounterRelease(axis);
  2564. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2565. }
  2566. else if ((state->targetPulses > 1)
  2567. && (state->emittedPulses >= (state->targetPulses - 1)))
  2568. {
  2569. PlsrHwArmPulseTargetTail(axis);
  2570. }
  2571. }
  2572. static void PlsrHwHandlePulseDirUpdate(uint8_t axis)
  2573. {
  2574. if (PlsrHwTimerHasCc1if(axis) != 0U)
  2575. {
  2576. /* CC1IE is disabled for PULSE/DIR, but the compare flag itself still
  2577. * latches. Consume it so it cannot survive a later mode change. */
  2578. PlsrHwTimerClearCc1if(axis);
  2579. }
  2580. if (PlsrHwTimerHasUif(axis) == 0U)
  2581. {
  2582. return;
  2583. }
  2584. PlsrHwTimerClearUif(axis);
  2585. PlsrHwAccountPulseDirUpdate(axis);
  2586. }
  2587. static void PlsrHwHandlePulseDirFastGroup(uint8_t interruptAxis)
  2588. {
  2589. #ifdef PLSR_HOST_TEST
  2590. uint8_t peerAxis;
  2591. PlsrHwHandlePulseDirUpdate(interruptAxis);
  2592. if (interruptAxis < 2U)
  2593. {
  2594. return;
  2595. }
  2596. peerAxis = (uint8_t)(interruptAxis ^ 1U);
  2597. /* Host models the same single snapshot used by the target fast path. */
  2598. if ((PlsrHwPulseDirFastPathAllowed(peerAxis) != 0U)
  2599. && (PlsrHwTimerHasUif(peerAxis) != 0U))
  2600. {
  2601. PlsrHwHandlePulseDirUpdate(peerAxis);
  2602. }
  2603. #else
  2604. if (interruptAxis < 2U)
  2605. {
  2606. PlsrHwHandlePulseDirUpdate(interruptAxis);
  2607. return;
  2608. }
  2609. {
  2610. uint32_t tim11Flags = TIM11->SR;
  2611. uint32_t tim14Flags = TIM14->SR;
  2612. /* Clear exactly the snapshotted update/compare flags in one APB write
  2613. * per timer. Writing 1 to all other timer flags preserves an event
  2614. * that arrives after the snapshot. */
  2615. if ((tim11Flags & (TIM_SR_UIF | TIM_SR_CC1IF)) != 0UL)
  2616. {
  2617. TIM11->SR = ~(tim11Flags & (TIM_SR_UIF | TIM_SR_CC1IF));
  2618. }
  2619. if ((tim14Flags & (TIM_SR_UIF | TIM_SR_CC1IF)) != 0UL)
  2620. {
  2621. TIM14->SR = ~(tim14Flags & (TIM_SR_UIF | TIM_SR_CC1IF));
  2622. }
  2623. if (((tim11Flags & TIM_SR_UIF) != 0UL)
  2624. && (PlsrHwPulseDirFastPathAllowed(2U) != 0U))
  2625. {
  2626. PlsrHwAccountPulseDirUpdate(2U);
  2627. }
  2628. if (((tim14Flags & TIM_SR_UIF) != 0UL)
  2629. && (PlsrHwPulseDirFastPathAllowed(3U) != 0U))
  2630. {
  2631. PlsrHwAccountPulseDirUpdate(3U);
  2632. }
  2633. }
  2634. #endif
  2635. }
  2636. void PlsrHwOnTimerUpdate(uint8_t axis)
  2637. {
  2638. PLSR_HW_AXIS_STATE *state;
  2639. uint8_t ownerAxis;
  2640. uint8_t hasCc1;
  2641. uint8_t abGated;
  2642. if (axis >= PLSR_HW_AXIS_COUNT)
  2643. {
  2644. return;
  2645. }
  2646. if (PlsrHwPulseDirFastPathAllowed(axis) != 0U)
  2647. {
  2648. PlsrHwHandlePulseDirFastGroup(axis);
  2649. return;
  2650. }
  2651. ownerAxis = (uint8_t)(axis & 0xFEU);
  2652. abGated = PlsrHwGateArmedAbOutputs(ownerAxis);
  2653. #ifdef PLSR_HOST_TEST
  2654. /* Model the second equal-priority lag flag arriving after the first scan
  2655. * but before any completion bookkeeping. */
  2656. if (PlsrHwTestLateAbFlagAxis < PLSR_HW_AXIS_COUNT)
  2657. {
  2658. PlsrHwTimers[PlsrHwTestLateAbFlagAxis].sr |=
  2659. PLSR_HW_TIMER_CC1_BIT;
  2660. PlsrHwTestLateAbFlagAxis = PLSR_HW_COUNTER_NONE;
  2661. abGated = 1U;
  2662. }
  2663. #endif
  2664. /* Completion cleanup is deferred. This second scan closes the injected
  2665. * arrival window; a still-later flag gets CPU back before its next jump. */
  2666. if (abGated != 0U)
  2667. {
  2668. (void)PlsrHwGateArmedAbOutputs(ownerAxis);
  2669. }
  2670. /* CC1IF 无论当前状态如何都必须先清除;否则启动窗口中的杂散
  2671. * compare 标志会让共享 IRQ 持续重入,主线程无法完成 CEN 配置。 */
  2672. hasCc1 = PlsrHwTimerHasCc1if(axis);
  2673. if (hasCc1 != 0U)
  2674. {
  2675. PlsrHwTimerClearCc1if(axis);
  2676. }
  2677. state = &PlsrHwAxes[ownerAxis];
  2678. if ((state->state == PLSR_HW_STATE_RUNNING)
  2679. && (state->outputMode == PLSR_OUTPUT_AB))
  2680. {
  2681. if (PlsrHwTimerHasUif(axis) != 0U)
  2682. {
  2683. PlsrHwTimerClearUif(axis);
  2684. }
  2685. if (hasCc1 == 0U)
  2686. {
  2687. return;
  2688. }
  2689. if ((state->abPausePending != 0U)
  2690. || (state->abFrequencyPending != 0U))
  2691. {
  2692. if (PlsrHwIsAbPhysicalZeroBoundary(ownerAxis) == 0U)
  2693. {
  2694. /* This was the first phase falling from 11. Leave both
  2695. * one-shot interrupts armed for the later physical 00 edge. */
  2696. return;
  2697. }
  2698. }
  2699. else if (axis != state->abCountAxis)
  2700. {
  2701. return;
  2702. }
  2703. if (state->abFastGated != 0U)
  2704. {
  2705. PlsrHwDeferAbCompletion(ownerAxis);
  2706. PlsrHwRecordFullAbGateTime();
  2707. return;
  2708. }
  2709. if (state->abPausePending != 0U)
  2710. {
  2711. uint8_t targetReached;
  2712. targetReached =
  2713. (state->hardwareCounterActive != 0U)
  2714. ? ((PlsrHwCounterRawSnapshot(ownerAxis)
  2715. >= (uint64_t)state->targetPulses)
  2716. ? 1U
  2717. : 0U)
  2718. : (((uint64_t)state->emittedPulses + 1UL
  2719. >= (uint64_t)state->targetPulses)
  2720. ? 1U
  2721. : 0U);
  2722. PlsrHwFastGateAbPair(ownerAxis);
  2723. #ifndef PLSR_HOST_TEST
  2724. /* Own the pins at physical 00 immediately. Waiting for the 1ms
  2725. * deferred cleanup previously exposed a frozen active phase. */
  2726. PlsrHwHoldAbPairLowFast(ownerAxis);
  2727. #endif
  2728. if (targetReached != 0U)
  2729. {
  2730. PlsrHwDeferAbCompletion(ownerAxis);
  2731. }
  2732. else
  2733. {
  2734. if (state->hardwareCounterActive == 0U)
  2735. {
  2736. state->emittedPulses++;
  2737. }
  2738. state->abQuarter = 0U;
  2739. state->abFrequencyPending = 0U;
  2740. state->abPausePending = 0U;
  2741. state->abPauseGated = 1U;
  2742. }
  2743. PlsrHwRecordFullAbGateTime();
  2744. return;
  2745. }
  2746. if (state->hardwareCounterActive != 0U)
  2747. {
  2748. /* One-shot 00 interrupt for a queued frequency change or for the
  2749. * target guard. Counting itself remains entirely in TIM9/12. */
  2750. if (state->abFrequencyPending != 0U)
  2751. {
  2752. uint16_t basePsc = state->abPendingBasePsc;
  2753. uint16_t pairPsc = state->abPendingPairPsc;
  2754. uint16_t arr = state->abPendingArr;
  2755. state->abFrequencyPending = 0U;
  2756. PlsrHwApplyAbFrequencyAtBoundary(ownerAxis,
  2757. basePsc,
  2758. pairPsc,
  2759. arr);
  2760. }
  2761. else if (state->abStopArmed == 0U)
  2762. {
  2763. PlsrHwTimerSetCc1ie(state->abCountAxis, 0UL);
  2764. }
  2765. return;
  2766. }
  2767. state->emittedPulses++;
  2768. if (state->emittedPulses >= state->targetPulses)
  2769. {
  2770. PlsrHwFastGateAbPair(ownerAxis);
  2771. PlsrHwDeferAbCompletion(ownerAxis);
  2772. PlsrHwRecordFullAbGateTime();
  2773. }
  2774. else if (state->abFrequencyPending != 0U)
  2775. {
  2776. uint16_t basePsc = state->abPendingBasePsc;
  2777. uint16_t pairPsc = state->abPendingPairPsc;
  2778. uint16_t arr = state->abPendingArr;
  2779. state->abFrequencyPending = 0U;
  2780. PlsrHwApplyAbFrequencyAtBoundary(ownerAxis,
  2781. basePsc,
  2782. pairPsc,
  2783. arr);
  2784. }
  2785. return;
  2786. }
  2787. if ((state->state == PLSR_HW_STATE_RUNNING)
  2788. && (state->outputMode == PLSR_OUTPUT_CW_CCW))
  2789. {
  2790. uint8_t hasUif = PlsrHwTimerHasUif(axis);
  2791. if (hasUif != 0U)
  2792. {
  2793. PlsrHwTimerClearUif(axis);
  2794. }
  2795. if (axis != state->cwActiveAxis)
  2796. {
  2797. return;
  2798. }
  2799. if (hasCc1 != 0U)
  2800. {
  2801. state->emittedPulses++;
  2802. if (state->emittedPulses >= state->targetPulses)
  2803. {
  2804. /* The board output is inverted relative to OC1REF: CC1 is
  2805. * the physical rising edge. Arm the tail here, then stop on
  2806. * the following update (physical falling edge). */
  2807. state->cwStopPending = 1U;
  2808. PlsrHwTimerSetCc1ie(axis, 0UL);
  2809. PlsrHwTimerClearUif(axis);
  2810. PlsrHwTimerSetUie(axis, 1UL);
  2811. }
  2812. return;
  2813. }
  2814. if ((hasUif != 0U) && (state->cwStopPending != 0U))
  2815. {
  2816. PlsrHwStopActiveOutput(ownerAxis, state->outputMode);
  2817. state->state = PLSR_HW_STATE_DONE;
  2818. (void)PlsrPostEvent(ownerAxis, PLSR_EVENT_SEGMENT_COMPLETE);
  2819. }
  2820. return;
  2821. }
  2822. state = &PlsrHwAxes[axis];
  2823. if ((hasCc1 != 0U)
  2824. && !((state->state == PLSR_HW_STATE_RUNNING)
  2825. && (state->outputMode == PLSR_OUTPUT_PULSE_DIR)))
  2826. {
  2827. /* 非运行态或其他模式的 CC1 仅作为杂散标志消费。PULSE/DIR
  2828. * 的 CC1IE 关闭,但半周期比较仍会置 CC1IF;更新 IRQ 必须
  2829. * 在同一次入口继续消费 UIF,不能留下 UIF 再触发第二次 ISR。 */
  2830. return;
  2831. }
  2832. if (PlsrHwTimerHasUif(axis) == 0U)
  2833. {
  2834. return;
  2835. }
  2836. PlsrHwTimerClearUif(axis);
  2837. if (state->state != PLSR_HW_STATE_RUNNING)
  2838. {
  2839. return;
  2840. }
  2841. if (state->outputMode != PLSR_OUTPUT_PULSE_DIR)
  2842. {
  2843. return;
  2844. }
  2845. if (state->pulseBoundaryStopPending != 0U)
  2846. {
  2847. #ifndef PLSR_HOST_TEST
  2848. if (state->hardwareCounterActive != 0U)
  2849. {
  2850. state->emittedPulses = (int64_t)PlsrHwCounterSnapshot(axis);
  2851. }
  2852. else
  2853. #endif
  2854. {
  2855. state->emittedPulses++;
  2856. }
  2857. state->pulseBoundaryStopPending = 0U;
  2858. state->pulseTargetStopArmed = 0U;
  2859. PlsrHwStopActiveOutput(axis, state->outputMode);
  2860. state->state = PLSR_HW_STATE_DONE;
  2861. PlsrHwCounterRelease(axis);
  2862. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2863. return;
  2864. }
  2865. #ifndef PLSR_HOST_TEST
  2866. if (state->hardwareCounterActive != 0U)
  2867. {
  2868. uint64_t pulses = PlsrHwCounterSnapshot(axis);
  2869. if ((state->pulseTargetStopArmed != 0U)
  2870. && (pulses >= (uint64_t)state->targetPulses))
  2871. {
  2872. state->emittedPulses = state->targetPulses;
  2873. state->pulseTargetStopArmed = 0U;
  2874. PlsrHwStopActiveOutput(axis, state->outputMode);
  2875. state->state = PLSR_HW_STATE_DONE;
  2876. PlsrHwCounterRelease(axis);
  2877. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2878. }
  2879. return;
  2880. }
  2881. #endif
  2882. state->emittedPulses++;
  2883. if (state->emittedPulses >= state->targetPulses)
  2884. {
  2885. state->emittedPulses = state->targetPulses;
  2886. state->pulseTargetStopArmed = 0U;
  2887. PlsrHwStopActiveOutput(axis, state->outputMode);
  2888. state->state = PLSR_HW_STATE_DONE;
  2889. PlsrHwCounterRelease(axis);
  2890. (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
  2891. }
  2892. else if ((state->targetPulses > 1)
  2893. && (state->emittedPulses >= (state->targetPulses - 1)))
  2894. {
  2895. PlsrHwArmPulseTargetTail(axis);
  2896. }
  2897. }
  2898. #ifdef PLSR_HOST_TEST
  2899. uint32_t PlsrHwTestGetArr(uint8_t axis)
  2900. {
  2901. return PlsrHwTimers[axis].arr;
  2902. }
  2903. uint32_t PlsrHwTestGetCcr(uint8_t axis)
  2904. {
  2905. return PlsrHwTimers[axis].ccr1;
  2906. }
  2907. uint32_t PlsrHwTestGetCnt(uint8_t axis)
  2908. {
  2909. return PlsrHwTimers[axis].cnt;
  2910. }
  2911. uint32_t PlsrHwTestGetCcmr1(uint8_t axis)
  2912. {
  2913. return PlsrHwTimers[axis].ccmr1;
  2914. }
  2915. uint32_t PlsrHwTestGetCr1(uint8_t axis)
  2916. {
  2917. return PlsrHwTimers[axis].cr1;
  2918. }
  2919. uint32_t PlsrHwTestGetPsc(uint8_t axis)
  2920. {
  2921. return PlsrHwTimers[axis].psc;
  2922. }
  2923. uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis)
  2924. {
  2925. return ((PlsrHwTimers[axis].ccer & PLSR_HW_TIMER_CHANNEL1_BIT) != 0UL)
  2926. ? 1U
  2927. : 0U;
  2928. }
  2929. uint8_t PlsrHwTestGetCc1PolarityInverted(uint8_t axis)
  2930. {
  2931. return ((PlsrHwTimers[axis].ccer & PLSR_HW_TIMER_CC1P_BIT) != 0UL)
  2932. ? 1U
  2933. : 0U;
  2934. }
  2935. uint8_t PlsrHwTestGetDirLevel(uint8_t axis)
  2936. {
  2937. return PlsrHwTimers[axis].dirLevel;
  2938. }
  2939. uint8_t PlsrHwTestGetAbPhaseA(uint8_t axis)
  2940. {
  2941. const PLSR_HW_AXIS_STATE *state;
  2942. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  2943. {
  2944. return 0U;
  2945. }
  2946. state = &PlsrHwAxes[axis];
  2947. if (state->directionPositive != 0U)
  2948. {
  2949. return ((state->abQuarter == 1U) || (state->abQuarter == 2U))
  2950. ? 1U
  2951. : 0U;
  2952. }
  2953. return ((state->abQuarter == 2U) || (state->abQuarter == 3U))
  2954. ? 1U
  2955. : 0U;
  2956. }
  2957. uint8_t PlsrHwTestGetAbPhaseB(uint8_t axis)
  2958. {
  2959. const PLSR_HW_AXIS_STATE *state;
  2960. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  2961. {
  2962. return 0U;
  2963. }
  2964. state = &PlsrHwAxes[axis];
  2965. if (state->directionPositive != 0U)
  2966. {
  2967. return ((state->abQuarter == 2U) || (state->abQuarter == 3U))
  2968. ? 1U
  2969. : 0U;
  2970. }
  2971. return ((state->abQuarter == 1U) || (state->abQuarter == 2U))
  2972. ? 1U
  2973. : 0U;
  2974. }
  2975. uint8_t PlsrHwTestGetAbQuarter(uint8_t axis)
  2976. {
  2977. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  2978. {
  2979. return 0U;
  2980. }
  2981. return PlsrHwAxes[axis].abQuarter;
  2982. }
  2983. void PlsrHwTestSetAbQuarterWithoutCounter(uint8_t axis, uint8_t quarter)
  2984. {
  2985. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  2986. {
  2987. return;
  2988. }
  2989. PlsrHwAxes[axis].abQuarter =
  2990. (uint8_t)(quarter % PLSR_HW_AB_QUARTER_COUNT);
  2991. }
  2992. uint32_t PlsrHwTestGetAbFullGateCount(void)
  2993. {
  2994. return PlsrHwTestAbFullGateCount;
  2995. }
  2996. void PlsrHwTestAdvanceAbQuarter(uint8_t axis)
  2997. {
  2998. PLSR_HW_AXIS_STATE *state;
  2999. uint8_t countAxis;
  3000. uint8_t leadAxis;
  3001. uint8_t sourceQuarter;
  3002. if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
  3003. {
  3004. return;
  3005. }
  3006. state = &PlsrHwAxes[axis];
  3007. if ((state->state != PLSR_HW_STATE_RUNNING)
  3008. || (state->outputMode != PLSR_OUTPUT_AB))
  3009. {
  3010. return;
  3011. }
  3012. countAxis = state->abCountAxis;
  3013. leadAxis = (state->directionPositive != 0U)
  3014. ? axis
  3015. : PlsrHwGetPairedAxis(axis);
  3016. sourceQuarter = (state->counterSourceAxis == leadAxis) ? 1U : 2U;
  3017. state->abQuarter = (uint8_t)((state->abQuarter + 1U)
  3018. % PLSR_HW_AB_QUARTER_COUNT);
  3019. if ((state->hardwareCounterActive != 0U)
  3020. && (state->abQuarter == sourceQuarter))
  3021. {
  3022. state->counterBlockPulses++;
  3023. if ((state->abStopArmed == 0U)
  3024. && (state->counterBlockPulses
  3025. >= (uint64_t)(state->targetPulses - 1)))
  3026. {
  3027. /* Host model of the target-1 TIM9/TIM12 compare. */
  3028. state->abStopArmed = 1U;
  3029. PlsrHwTimerClearCc1if(countAxis);
  3030. PlsrHwTimerSetCc1ie(countAxis, 1UL);
  3031. }
  3032. }
  3033. if ((state->abQuarter == 3U)
  3034. && ((PlsrHwTimers[leadAxis].dier & PLSR_HW_TIMER_CC1_BIT) != 0UL))
  3035. {
  3036. /* First falling edge after an asynchronous boundary request: AB is
  3037. * not 00 yet, so production must observe it without applying work. */
  3038. PlsrHwTimers[leadAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
  3039. PlsrHwOnTimerUpdate(leadAxis);
  3040. }
  3041. if (state->abQuarter == 0U)
  3042. {
  3043. /* 模拟目标板落后相 CC1 下降沿中断,复用生产计数路径。 */
  3044. /* Model the lag CC IRQ only while it is enabled. This detects a
  3045. * regression that accidentally restores one interrupt per AB cycle. */
  3046. if ((PlsrHwTimers[countAxis].dier & PLSR_HW_TIMER_CC1_BIT) != 0UL)
  3047. {
  3048. PlsrHwTimers[countAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
  3049. PlsrHwOnTimerUpdate(countAxis);
  3050. }
  3051. }
  3052. }
  3053. void PlsrHwTestSignalDualAbFinalBoundary(uint8_t firstAxis)
  3054. {
  3055. static const uint8_t baseAxes[2] = {0U, 2U};
  3056. uint8_t index;
  3057. uint8_t firstCountAxis;
  3058. if ((firstAxis != 0U) && (firstAxis != 2U))
  3059. {
  3060. return;
  3061. }
  3062. for (index = 0U; index < 2U; index++)
  3063. {
  3064. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
  3065. if ((state->state != PLSR_HW_STATE_RUNNING)
  3066. || (state->outputMode != PLSR_OUTPUT_AB)
  3067. || (state->hardwareCounterActive == 0U))
  3068. {
  3069. return;
  3070. }
  3071. }
  3072. for (index = 0U; index < 2U; index++)
  3073. {
  3074. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
  3075. state->counterBlockPulses = (uint64_t)state->targetPulses;
  3076. state->abQuarter = 0U;
  3077. state->abStopArmed = 1U;
  3078. PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
  3079. PlsrHwTimers[state->abCountAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
  3080. }
  3081. firstCountAxis = PlsrHwAxes[firstAxis].abCountAxis;
  3082. PlsrHwOnTimerUpdate(firstCountAxis);
  3083. }
  3084. void PlsrHwTestSignalDualAbStaggeredFinalBoundary(uint8_t firstAxis)
  3085. {
  3086. static const uint8_t baseAxes[2] = {0U, 2U};
  3087. uint8_t firstCountAxis;
  3088. uint8_t secondAxis;
  3089. uint8_t secondCountAxis;
  3090. uint8_t index;
  3091. if ((firstAxis != 0U) && (firstAxis != 2U))
  3092. {
  3093. return;
  3094. }
  3095. secondAxis = (firstAxis == 0U) ? 2U : 0U;
  3096. for (index = 0U; index < 2U; index++)
  3097. {
  3098. PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[baseAxes[index]];
  3099. if ((state->state != PLSR_HW_STATE_RUNNING)
  3100. || (state->outputMode != PLSR_OUTPUT_AB)
  3101. || (state->hardwareCounterActive == 0U))
  3102. {
  3103. return;
  3104. }
  3105. state->counterBlockPulses = (uint64_t)state->targetPulses;
  3106. state->abQuarter = 0U;
  3107. state->abStopArmed = 1U;
  3108. PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
  3109. }
  3110. firstCountAxis = PlsrHwAxes[firstAxis].abCountAxis;
  3111. secondCountAxis = PlsrHwAxes[secondAxis].abCountAxis;
  3112. PlsrHwTimers[firstCountAxis].sr |= PLSR_HW_TIMER_CC1_BIT;
  3113. PlsrHwTestLateAbFlagAxis = secondCountAxis;
  3114. PlsrHwOnTimerUpdate(firstCountAxis);
  3115. }
  3116. void PlsrHwTestTriggerUpdate(uint8_t axis)
  3117. {
  3118. if (axis < PLSR_HW_AXIS_COUNT)
  3119. {
  3120. PlsrHwTimers[axis].cnt = 0UL;
  3121. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT;
  3122. }
  3123. PlsrHwOnTimerUpdate(axis);
  3124. }
  3125. void PlsrHwTestSetUpdatePending(uint8_t axis)
  3126. {
  3127. if (axis < PLSR_HW_AXIS_COUNT)
  3128. {
  3129. PlsrHwTimers[axis].cnt = 0UL;
  3130. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT;
  3131. }
  3132. }
  3133. void PlsrHwTestTriggerUpdateAndCompare(uint8_t axis)
  3134. {
  3135. if (axis < PLSR_HW_AXIS_COUNT)
  3136. {
  3137. PlsrHwTimers[axis].cnt = PlsrHwTimers[axis].ccr1;
  3138. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_UPDATE_BIT
  3139. | PLSR_HW_TIMER_CC1_BIT;
  3140. }
  3141. PlsrHwOnTimerUpdate(axis);
  3142. }
  3143. void PlsrHwTestTriggerCompare(uint8_t axis)
  3144. {
  3145. if (axis < PLSR_HW_AXIS_COUNT)
  3146. {
  3147. PlsrHwTimers[axis].cnt = PlsrHwTimers[axis].ccr1;
  3148. PlsrHwTimers[axis].sr |= PLSR_HW_TIMER_CC1_BIT;
  3149. }
  3150. PlsrHwOnTimerUpdate(axis);
  3151. }
  3152. #endif
  3153. #ifndef PLSR_HOST_TEST
  3154. static void PlsrHwOnCounterInterrupt(uint8_t counterIndex)
  3155. {
  3156. TIM_TypeDef *counter;
  3157. PLSR_HW_AXIS_STATE *state;
  3158. uint32_t flags;
  3159. uint64_t pulses;
  3160. uint8_t axis;
  3161. if (counterIndex >= PLSR_HW_COUNTER_COUNT)
  3162. {
  3163. return;
  3164. }
  3165. counter = PlsrHwCounters[counterIndex];
  3166. flags = counter->SR & (TIM_SR_UIF | TIM_SR_CC1IF);
  3167. counter->SR = ~(TIM_SR_UIF | TIM_SR_CC1IF);
  3168. axis = PlsrHwCounterOwners[counterIndex];
  3169. if ((flags == 0UL) || (axis >= PLSR_HW_AXIS_COUNT))
  3170. {
  3171. return;
  3172. }
  3173. state = &PlsrHwAxes[axis];
  3174. if ((state->hardwareCounterActive == 0U)
  3175. || (state->counterIndex != counterIndex)
  3176. || (state->state != PLSR_HW_STATE_RUNNING))
  3177. {
  3178. return;
  3179. }
  3180. if ((flags & TIM_SR_UIF) != 0UL)
  3181. {
  3182. state->counterBlockPulses += PLSR_HW_COUNTER_BLOCK_PULSES;
  3183. }
  3184. pulses = state->counterBlockPulses + (uint16_t)counter->CNT;
  3185. if (state->outputMode == PLSR_OUTPUT_AB)
  3186. {
  3187. uint64_t guard = (uint64_t)state->targetPulses - 1UL;
  3188. if (((flags & TIM_SR_CC1IF) != 0UL) && (pulses >= guard))
  3189. {
  3190. /* Wake the lag-CC1 one cycle early. It remains enabled until a
  3191. * 00 boundary observes raw>=target and fast-gates both phases. */
  3192. state->abStopArmed = 1U;
  3193. PlsrHwTimerClearCc1if(state->abCountAxis);
  3194. PlsrHwTimerSetCc1ie(state->abCountAxis, 1UL);
  3195. }
  3196. return;
  3197. }
  3198. if (((flags & TIM_SR_CC1IF) != 0UL)
  3199. && (pulses >= ((uint64_t)state->targetPulses - 1UL)))
  3200. {
  3201. /* Do not gate on the counter source edge: it starts the terminal high
  3202. * level. Pre-arm the output compare so the target high completes. */
  3203. PlsrHwArmPulseTargetTail(axis);
  3204. }
  3205. }
  3206. void TIM1_UP_TIM10_IRQHandler(void)
  3207. {
  3208. uint32_t started = PlsrHwCycleBegin();
  3209. PlsrHwOnTimerUpdate(0U);
  3210. PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
  3211. PlsrHwFinishAbGateMeasurement(started, 0U);
  3212. }
  3213. void TIM8_UP_TIM13_IRQHandler(void)
  3214. {
  3215. uint32_t started = PlsrHwCycleBegin();
  3216. PlsrHwOnTimerUpdate(1U);
  3217. PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
  3218. PlsrHwFinishAbGateMeasurement(started, 1U);
  3219. }
  3220. void TIM1_TRG_COM_TIM11_IRQHandler(void)
  3221. {
  3222. uint32_t started = PlsrHwCycleBegin();
  3223. PlsrHwOnTimerUpdate(2U);
  3224. PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
  3225. PlsrHwFinishAbGateMeasurement(started, 2U);
  3226. }
  3227. void TIM8_TRG_COM_TIM14_IRQHandler(void)
  3228. {
  3229. uint32_t started = PlsrHwCycleBegin();
  3230. PlsrHwOnTimerUpdate(3U);
  3231. PlsrHwRecordMaxCycles(&PlsrHwMaxOutputIsrCycles, started);
  3232. PlsrHwFinishAbGateMeasurement(started, 3U);
  3233. }
  3234. void TIM1_BRK_TIM9_IRQHandler(void)
  3235. {
  3236. uint32_t started = PlsrHwCycleBegin();
  3237. PlsrHwOnCounterInterrupt(0U);
  3238. PlsrHwRecordMaxCycles(&PlsrHwMaxCounterIsrCycles, started);
  3239. }
  3240. void TIM8_BRK_TIM12_IRQHandler(void)
  3241. {
  3242. uint32_t started = PlsrHwCycleBegin();
  3243. PlsrHwOnCounterInterrupt(1U);
  3244. PlsrHwRecordMaxCycles(&PlsrHwMaxCounterIsrCycles, started);
  3245. }
  3246. void TIM6_DAC_IRQHandler(void)
  3247. {
  3248. uint32_t started = PlsrHwCycleBegin();
  3249. if ((TIM6->SR & TIM_SR_UIF) != 0UL)
  3250. {
  3251. TIM6->SR &= ~TIM_SR_UIF;
  3252. PlsrControlTick100us();
  3253. }
  3254. PlsrHwRecordMaxCycles(&PlsrHwMaxControlIsrCycles, started);
  3255. }
  3256. #endif