Sfoglia il codice sorgente

仅支持Y0输出脉冲和Y3输出方向,Y1,Y2均不支持,不支持等待条件

master
hanyongwei 1 mese fa
parent
commit
9d9e3aeca6
26 ha cambiato i file con 3879 aggiunte e 1789 eliminazioni
  1. +3
    -1
      .gitignore
  2. +35
    -55
      app/main.c
  3. +1
    -0
      drivers/Include/stm32f4xx_it.h
  4. +2
    -50
      drivers/Source/gpio.c
  5. +12
    -5
      drivers/Source/stm32f4xx_it.c
  6. +3
    -5
      drivers/Source/tim.c
  7. +1881
    -1521
      iar/plsr.dep
  8. +71
    -0
      iar/plsr.ewp
  9. +4
    -1
      modbus/modbus_fc.c
  10. +25
    -83
      modbus/modbus_port.c
  11. +2
    -14
      modbus/modbus_port.h
  12. +5
    -54
      modbus/modbus_rtu.c
  13. +166
    -0
      plsr/accel_curve/plsr_accel_curve.c
  14. +47
    -0
      plsr/accel_curve/plsr_accel_curve.h
  15. +66
    -0
      plsr/command/plsr_command.c
  16. +39
    -0
      plsr/command/plsr_command.h
  17. +363
    -0
      plsr/param/plsr_param.c
  18. +140
    -0
      plsr/param/plsr_param.h
  19. +65
    -0
      plsr/path_plan/plsr_path_plan.c
  20. +37
    -0
      plsr/path_plan/plsr_path_plan.h
  21. +117
    -0
      plsr/plsr.c
  22. +41
    -0
      plsr/plsr.h
  23. +253
    -0
      plsr/pulse_driver/plsr_pulse_driver.c
  24. +39
    -0
      plsr/pulse_driver/plsr_pulse_driver.h
  25. +424
    -0
      plsr/run_control/plsr_run_control.c
  26. +38
    -0
      plsr/run_control/plsr_run_control.h

+ 3
- 1
.gitignore Vedi File

@@ -15,5 +15,7 @@ iar/modbus/Exe/
iar/modbus/List/ iar/modbus/List/
iar/modbus/Obj/ iar/modbus/Obj/
iar/Backup of modbus.ewp iar/Backup of modbus.ewp
host/
iar/settings/modbus.reggroups iar/settings/modbus.reggroups
build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/
plsr/test/

+ 35
- 55
app/main.c Vedi File

@@ -3,57 +3,38 @@
* @file main.c * @file main.c
* @brief 初始化系统时钟配置、初始化GPIO、初始化串口、启动任务 * @brief 初始化系统时钟配置、初始化GPIO、初始化串口、启动任务
* *
* @details
* 1. HAL/OS/GPIO/USART1/TIM2 初始化
* 2. 创建任务并启动任务
*
* @note modbus任务入口在modbus_rtu.h/modbus_rtu.c
* @note LED: PF6 接收指示灯,PF7 CRC错误等异常指示灯
* @note PF8 发送指示灯, PF9 连接断开指示灯
* @note Modbus / PLSR / 参数块管理 / 指令接口
* @note PLSR: Y0(PF6)=TIM10 脉冲,Y3(PF9)=方向
* *
****************************************************************************** ******************************************************************************
*/ */



#include "main.h" #include "main.h"
#include "usart.h" #include "usart.h"
#include "gpio.h" #include "gpio.h"
#include "ucos_ii.h" #include "ucos_ii.h"
#include "modbus_rtu.h" #include "modbus_rtu.h"
#include "tim.h" #include "tim.h"
#include "plsr.h"
#include "plsr_param.h"


/**
* @brief Modbus 任务栈
* @note 需覆盖帧拷贝、功能码处理与阻塞发送时的调用深度
*/
#define MODBUS_TASK_STACK_SIZE 512 #define MODBUS_TASK_STACK_SIZE 512

/**
* @brief Modbus 任务优先级
* @note 数字越大优先级越低
*/
#define MODBUS_TASK_PRIO 3 #define MODBUS_TASK_PRIO 3


/**
* @brief Modbus 从站任务栈空间
* @note 栈顶地址:&ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1]
*/
OS_STK ModbusTaskStk[MODBUS_TASK_STACK_SIZE];
#define PLSR_TASK_STACK_SIZE 512
#define PLSR_TASK_PRIO 4


#define PARAM_BLOCK_STACK_SIZE 256
#define PARAM_BLOCK_TASK_PRIO 5

OS_STK ModbusTaskStk[MODBUS_TASK_STACK_SIZE];
OS_STK PlsrTaskStk[PLSR_TASK_STACK_SIZE];
OS_STK ParamBlockTaskStk[PARAM_BLOCK_STACK_SIZE];


void SystemClock_Config(void); void SystemClock_Config(void);


/**
* @brief 应用程序入口
*
* @details 完成本地外设与操作系统初始化后调用 OSStart(),此后由调度器接管;
* 函数末尾的 while(1) 在正常路径下不会执行到。
*
* @retval int 形式上的返回值
*/
int main(void) int main(void)
{ {

HAL_Init(); HAL_Init();
HAL_NVIC_SetPriority(SysTick_IRQn, 14, 0); HAL_NVIC_SetPriority(SysTick_IRQn, 14, 0);
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0); HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
@@ -65,10 +46,11 @@ int main(void)
MX_USART1_UART_Init(); MX_USART1_UART_Init();


MX_TIM2_Init(); MX_TIM2_Init();
PlsrInit();


OSInit(); OSInit();
PlsrParamBlockInit(); /* 依赖 OS,须在 OSInit 之后 */


/* OS_TASK_OPT_SAVE_FP: 硬件浮点开启时由 OS 保存/恢复 S0-S31 */
OSTaskCreateExt(ModbusSlaveTask, OSTaskCreateExt(ModbusSlaveTask,
NULL, NULL,
&ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1], &ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1],
@@ -79,37 +61,41 @@ int main(void)
NULL, NULL,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);


OSStart();
OSTaskCreateExt(PlsrTask,
NULL,
&PlsrTaskStk[PLSR_TASK_STACK_SIZE - 1],
PLSR_TASK_PRIO,
PLSR_TASK_PRIO,
&PlsrTaskStk[0],
PLSR_TASK_STACK_SIZE,
NULL,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);

OSTaskCreateExt(PlsrParamBlockTask,
NULL,
&ParamBlockTaskStk[PARAM_BLOCK_STACK_SIZE - 1],
PARAM_BLOCK_TASK_PRIO,
PARAM_BLOCK_TASK_PRIO,
&ParamBlockTaskStk[0],
PARAM_BLOCK_STACK_SIZE,
NULL,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);


OSStart();


while (1) while (1)
{ {
} }
} }


/**
* @brief 系统时钟配置
*
* @details HSE + PLL:SYSCLK = 168 MHz;
* AHB = 168 MHz,APB1 = 42 MHz,APB2 = 84 MHz。
*
* @retval None
*/
void SystemClock_Config(void) void SystemClock_Config(void)
{ {
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE(); __HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);


/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
@@ -123,8 +109,6 @@ void SystemClock_Config(void)
Error_Handler(); Error_Handler();
} }


/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
@@ -138,10 +122,6 @@ void SystemClock_Config(void)
} }
} }


/**
* @brief HAL / 应用错误处理
* @retval None
*/
void Error_Handler(void) void Error_Handler(void)
{ {
__disable_irq(); __disable_irq();
@@ -152,4 +132,4 @@ void Error_Handler(void)


#ifdef USE_FULL_ASSERT #ifdef USE_FULL_ASSERT


#endif
#endif

+ 1
- 0
drivers/Include/stm32f4xx_it.h Vedi File

@@ -56,6 +56,7 @@ void DebugMon_Handler(void);
void PendSV_Handler(void); void PendSV_Handler(void);
void SysTick_Handler(void); void SysTick_Handler(void);
void TIM2_IRQHandler(void); void TIM2_IRQHandler(void);
void TIM1_UP_TIM10_IRQHandler(void);
void USART1_IRQHandler(void); void USART1_IRQHandler(void);
/* USER CODE BEGIN EFP */ /* USER CODE BEGIN EFP */




+ 2
- 50
drivers/Source/gpio.c Vedi File

@@ -2,64 +2,16 @@
/** /**
****************************************************************************** ******************************************************************************
* @file gpio.c * @file gpio.c
* @brief This file provides code for the configuration
* of all used GPIO pins.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
* @brief GPIO 时钟与基础初始化(Y0/Y1 由 PLSR 硬件层配置)
****************************************************************************** ******************************************************************************
*/ */
/* USER CODE END Header */ /* USER CODE END Header */


/* Includes ------------------------------------------------------------------*/
#include "gpio.h" #include "gpio.h"


/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void) void MX_GPIO_Init(void)
{ {

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

/*Configure GPIO pin Output Level (PF6/7/8/9:低电平点亮,初始化先熄灭) */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
/*Configure GPIO pins : PF6 PF7 PF8 PF9 */
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

} }

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

+ 12
- 5
drivers/Source/stm32f4xx_it.c Vedi File

@@ -59,6 +59,7 @@
/* External variables --------------------------------------------------------*/ /* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart1;
extern TIM_HandleTypeDef htim2; extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim10;


/* USER CODE BEGIN EV */ /* USER CODE BEGIN EV */


@@ -211,17 +212,23 @@ void SysTick_Handler(void)


/** /**
* @brief This function handles TIM2 global interrupt. * @brief This function handles TIM2 global interrupt.
* @note TIM2 用于 Modbus RTU 帧空闲定帧(约 T3.5),须调用 HAL 分发回调
* @note TIM2 用于 Modbus RTU 帧空闲定帧(约 T3.5)
*/ */
void TIM2_IRQHandler(void) void TIM2_IRQHandler(void)
{ {
/* USER CODE BEGIN TIM2_IRQn 0 */
OSIntEnter(); OSIntEnter();
/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2); HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
OSIntExit(); OSIntExit();
/* USER CODE END TIM2_IRQn 1 */
}

/**
* @brief TIM1 Update / TIM10 全局中断(PLSR 脉冲计数)
*/
void TIM1_UP_TIM10_IRQHandler(void)
{
OSIntEnter();
HAL_TIM_IRQHandler(&htim10);
OSIntExit();
} }


/** /**


+ 3
- 5
drivers/Source/tim.c Vedi File

@@ -16,8 +16,7 @@ void MX_TIM2_Init(void)
htim2.Instance = TIM2; htim2.Instance = TIM2;
htim2.Init.Prescaler = 167; htim2.Init.Prescaler = 167;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
/* 3.5T@9600 8E1 ≈ 4010us → 2005 ticks → ARR=2004 */
htim2.Init.Period = 2004; /*(2004 + 1)* 2us = 4.01ms*/
htim2.Init.Period = 2004;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
@@ -25,7 +24,6 @@ void MX_TIM2_Init(void)
Error_Handler(); Error_Handler();
} }


// NVIC中断优先级
HAL_NVIC_SetPriority(TIM2_IRQn,6,0);
HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn); HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}

+ 1881
- 1521
iar/plsr.dep
File diff soppresso perché troppo grande
Vedi File


+ 71
- 0
iar/plsr.ewp Vedi File

@@ -355,6 +355,14 @@
<state>$PROJ_DIR$\..\drivers\Include</state> <state>$PROJ_DIR$\..\drivers\Include</state>
<state>$PROJ_DIR$\..\drivers\Source</state> <state>$PROJ_DIR$\..\drivers\Source</state>
<state>$PROJ_DIR$\..\modbus</state> <state>$PROJ_DIR$\..\modbus</state>
<state>$PROJ_DIR$\..\plsr</state>
<state>$PROJ_DIR$\..\plsr\param</state>
<state>$PROJ_DIR$\..\plsr\pulse_driver</state>
<state>$PROJ_DIR$\..\plsr\path_plan</state>
<state>$PROJ_DIR$\..\plsr\accel_curve</state>
<state>$PROJ_DIR$\..\plsr\run_control</state>
<state>$PROJ_DIR$\..\plsr\command</state>
<state>$PROJ_DIR$\..\plsr\test</state>
<state>$PROJ_DIR$\..\ucos\uC-CPU</state> <state>$PROJ_DIR$\..\ucos\uC-CPU</state>
<state>$PROJ_DIR$\..\ucos\uC-LIB\Ports</state> <state>$PROJ_DIR$\..\ucos\uC-LIB\Ports</state>
<state>$PROJ_DIR$\..\ucos\uC-LIB</state> <state>$PROJ_DIR$\..\ucos\uC-LIB</state>
@@ -2859,6 +2867,69 @@
<name>$PROJ_DIR$\..\modbus\modbus_rtu.h</name> <name>$PROJ_DIR$\..\modbus\modbus_rtu.h</name>
</file> </file>
</group> </group>
<group>
<name>plsr</name>
<file>
<name>$PROJ_DIR$\..\plsr\plsr.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\plsr.h</name>
</file>
<group>
<name>param</name>
<file>
<name>$PROJ_DIR$\..\plsr\param\plsr_param.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\param\plsr_param.h</name>
</file>
</group>
<group>
<name>pulse_driver</name>
<file>
<name>$PROJ_DIR$\..\plsr\pulse_driver\plsr_pulse_driver.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\pulse_driver\plsr_pulse_driver.h</name>
</file>
</group>
<group>
<name>path_plan</name>
<file>
<name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.h</name>
</file>
</group>
<group>
<name>accel_curve</name>
<file>
<name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.h</name>
</file>
</group>
<group>
<name>run_control</name>
<file>
<name>$PROJ_DIR$\..\plsr\run_control\plsr_run_control.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\run_control\plsr_run_control.h</name>
</file>
</group>
<group>
<name>command</name>
<file>
<name>$PROJ_DIR$\..\plsr\command\plsr_command.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\plsr\command\plsr_command.h</name>
</file>
</group>
</group>
<group> <group>
<name>ucos</name> <name>ucos</name>
<group> <group>


+ 4
- 1
modbus/modbus_fc.c Vedi File

@@ -7,6 +7,7 @@
#include "modbus_common.h" #include "modbus_common.h"
#include "modbus_data.h" #include "modbus_data.h"
#include "modbus_port.h" #include "modbus_port.h"
#include "plsr_param.h"
#include <string.h> #include <string.h>


/*应答帧组帧缓冲 */ /*应答帧组帧缓冲 */
@@ -318,14 +319,16 @@ static void WriteMultiRegs(const uint8_t *frame, uint16_t len)
ReadU16Be(&frame[7U + (i * 2U)])); ReadU16Be(&frame[7U + (i * 2U)]));
} }


/* 先回 FC10 应答,再做分帧计数(避免应答被后续逻辑拖住) */
g_modbus_tx_buf[0] = frame[0]; g_modbus_tx_buf[0] = frame[0];
g_modbus_tx_buf[1] = WRITE_MULTI_REGS; g_modbus_tx_buf[1] = WRITE_MULTI_REGS;
g_modbus_tx_buf[2] = frame[2]; g_modbus_tx_buf[2] = frame[2];
g_modbus_tx_buf[3] = frame[3]; g_modbus_tx_buf[3] = frame[3];
g_modbus_tx_buf[4] = frame[4]; g_modbus_tx_buf[4] = frame[4];
g_modbus_tx_buf[5] = frame[5]; g_modbus_tx_buf[5] = frame[5];

AppendCrcAndSend(g_modbus_tx_buf, 6U); AppendCrcAndSend(g_modbus_tx_buf, 6U);

PlsrParamBlockOnHoldWrite(startAddr, quantity);
} }


/*============================================================================*/ /*============================================================================*/


+ 25
- 83
modbus/modbus_port.c Vedi File

@@ -1,52 +1,35 @@
/** /**
* @file modbus_port.c * @file modbus_port.c
* @author lve * @author lve
* @brief 链路端口实现(收字节、空闲定帧、发送、指示灯
* @version 0.1.1
* @brief 链路端口实现(收字节、空闲定帧、发送)
* @version 0.1.2
* @date 2026/7/31 * @date 2026/7/31
* @note 从站任务取帧处理后,通过ModbusSendData函数阻塞回发
* @note 从站任务取帧处理后,通过 SendData 阻塞回发
* @copyright Copyright (c) 2026 * @copyright Copyright (c) 2026
*/ */


#include "modbus_port.h" #include "modbus_port.h"
#include "modbus_common.h" #include "modbus_common.h"
#include "gpio.h" #include "gpio.h"
#include "plsr.h"


/* 接收缓冲区数组 */
uint8_t g_modbus_rx_buf[MODBUS_RX_BUF_LEN] = {0}; uint8_t g_modbus_rx_buf[MODBUS_RX_BUF_LEN] = {0};

/* 接收字节计数 */
volatile uint16_t g_modbus_rx_len = 0U; volatile uint16_t g_modbus_rx_len = 0U;

/* 声明信号量指针 */
OS_EVENT *g_modbus_rx_sem = (OS_EVENT *)0; OS_EVENT *g_modbus_rx_sem = (OS_EVENT *)0;


/* 接收单字节 */
static uint8_t g_rxByte; static uint8_t g_rxByte;

/* 完整帧待处理标志 */
static volatile uint8_t g_modbus_frame_pending = 0U; static volatile uint8_t g_modbus_frame_pending = 0U;

/* TIM2 帧空闲定时器是否已启动 */
static volatile uint8_t g_modbus_tim_armed = 0U; static volatile uint8_t g_modbus_tim_armed = 0U;


static void StopFrameTimer(void); static void StopFrameTimer(void);
static void FlushUartRx(void); static void FlushUartRx(void);
static void RestartReceive(void); static void RestartReceive(void);



/**
* @brief PF7 闪烁约 50 ms(CRC / 帧错 / 协议异常)
* @note 仅允许在任务上下文调用(内部使用 OSTimeDly)
*/
void LedErrorBlink(void) void LedErrorBlink(void)
{ {
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_RESET);
OSTimeDly(5U); /* OS_TICKS_PER_SEC=100 时约 50 ms */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_SET);
/* 指示灯已取消,保留空函数以兼容调用点 */
} }


/* 停止帧空闲定时器 TIM2 */
static void StopFrameTimer(void) static void StopFrameTimer(void)
{ {
(void)HAL_TIM_Base_Stop_IT(&htim2); (void)HAL_TIM_Base_Stop_IT(&htim2);
@@ -55,29 +38,22 @@ static void StopFrameTimer(void)
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
} }


/* 清除 UART 错误标志
* ORE 溢出错误
* NE 噪声错误
* FE 帧错误
*/
static void FlushUartRx(void) static void FlushUartRx(void)
{ {
__HAL_UART_CLEAR_OREFLAG(&huart1);//清除溢出标志
__HAL_UART_CLEAR_NEFLAG(&huart1);//清除噪声标志
__HAL_UART_CLEAR_FEFLAG(&huart1);//清除帧标志
__HAL_UART_CLEAR_OREFLAG(&huart1);
__HAL_UART_CLEAR_NEFLAG(&huart1);
__HAL_UART_CLEAR_FEFLAG(&huart1);
while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET) while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{ {
(void)huart1.Instance->DR; (void)huart1.Instance->DR;
} }
} }


/* 清除帧挂起标志 */
void PortClearFramePending(void) void PortClearFramePending(void)
{ {
g_modbus_frame_pending = 0U; g_modbus_frame_pending = 0U;
} }


/* 重新启动 USART1 单字节中断接收 */
static void RestartReceive(void) static void RestartReceive(void)
{ {
FlushUartRx(); FlushUartRx();
@@ -85,42 +61,29 @@ static void RestartReceive(void)
(void)HAL_UART_Receive_IT(&huart1, &g_rxByte, 1U); (void)HAL_UART_Receive_IT(&huart1, &g_rxByte, 1U);
} }


/* 初始化链路端口 */
void ModbusPortInit(void) void ModbusPortInit(void)
{ {
g_modbus_rx_len = 0U; g_modbus_rx_len = 0U;
g_modbus_frame_pending = 0U; g_modbus_frame_pending = 0U;
g_modbus_tim_armed = 0U; g_modbus_tim_armed = 0U;

g_modbus_rx_sem = OSSemCreate(0U); g_modbus_rx_sem = OSSemCreate(0U);

/* PF6/PF7/PF8/PF9:低电平点亮,上电先全部熄灭 */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6 |
GPIO_PIN_7 |
GPIO_PIN_8 |
GPIO_PIN_9, GPIO_PIN_SET);

RestartReceive(); RestartReceive();
} }


/* 串口接收完成回调:缓存一字节并重启动帧空闲定时器 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{ {
if (huart->Instance == USART1) if (huart->Instance == USART1)
{ {
uint32_t gapTicks; uint32_t gapTicks;
/* 点亮接收指示灯 */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_RESET);

if (g_modbus_frame_pending == 0U) if (g_modbus_frame_pending == 0U)
{
/* 增加帧内字节之间1.5T判断 */
{
/* 1.5T 字符间隙:判定上一帧已结束,丢弃半包后重新组帧 */
if (g_modbus_tim_armed != 0U) if (g_modbus_tim_armed != 0U)
{ {
gapTicks = __HAL_TIM_GET_COUNTER(&htim2); gapTicks = __HAL_TIM_GET_COUNTER(&htim2);
if (gapTicks >= (uint32_t)MODBUS_T15_TICKS) if (gapTicks >= (uint32_t)MODBUS_T15_TICKS)
{ {
/* 超过 1.5T 且未到 3.5T:旧帧作废,新字节放到缓冲最前 */
g_modbus_rx_len = 0U; g_modbus_rx_len = 0U;
} }
} }
@@ -136,14 +99,13 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{ {
g_modbus_tim_armed = 1U; g_modbus_tim_armed = 1U;
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
(void)HAL_TIM_Base_Start_IT(&htim2);//开启定时器溢出中断
(void)HAL_TIM_Base_Start_IT(&htim2);
} }


(void)HAL_UART_Receive_IT(&huart1, &g_rxByte, 1U); (void)HAL_UART_Receive_IT(&huart1, &g_rxByte, 1U);
} }
} }


/* USART 错误回调:恢复接收,避免永久停收 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{ {
if (huart->Instance == USART1) if (huart->Instance == USART1)
@@ -152,19 +114,12 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
} }
} }


/**
* @brief TIM2 周期回调:帧间空闲到达,通知从站任务
* @param[in] htim HAL 定时器句柄
*
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{ {
if (htim->Instance == TIM2) if (htim->Instance == TIM2)
{ {
(void)HAL_TIM_Base_Stop_IT(&htim2); (void)HAL_TIM_Base_Stop_IT(&htim2);
g_modbus_tim_armed = 0U; g_modbus_tim_armed = 0U;
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_SET);


if ((g_modbus_rx_sem != (OS_EVENT *)0) && if ((g_modbus_rx_sem != (OS_EVENT *)0) &&
(g_modbus_rx_len > 0U) && (g_modbus_rx_len > 0U) &&
@@ -174,58 +129,48 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
(void)OSSemPost(g_modbus_rx_sem); (void)OSSemPost(g_modbus_rx_sem);
} }
} }
else if (htim->Instance == TIM10)
{
/* 每个 PWM 周期溢出 = 1 个脉冲 */
PlsrOnPulseIsr();
}
} }


/**
* @brief 计算 Modbus RTU CRC16
* @param[in] data 待校验数据,不含 CRC
* @param[in] len 长度字节
* @retval CRC16;低字节在前
*/
uint16_t ModbusCrc16(uint8_t *data, uint16_t len) uint16_t ModbusCrc16(uint8_t *data, uint16_t len)
{ {
uint16_t crc = 0xFFFFU; /* crc寄存器初始化 */
uint16_t crc = 0xFFFFU;
uint16_t i; uint16_t i;
uint16_t j; uint16_t j;


for (i = 0U; i < len; i++) for (i = 0U; i < len; i++)
{ {
crc ^= data[i]; /* crc 异或当前字节 */
crc ^= data[i];
for (j = 0U; j < 8U; j++) for (j = 0U; j < 8U; j++)
{ {
if ((crc & 0x0001U) != 0U) /*取出最低位*/
if ((crc & 0x0001U) != 0U)
{ {
crc = (uint16_t)((crc >> 1) ^ 0xA001U);/* LSB不为0,异或多项式A001 */
crc = (uint16_t)((crc >> 1) ^ 0xA001U);
} }
else else
{ {
crc >>= 1; /* LSB为0,直接右移1位 */
crc >>= 1;
} }
} }
} }

return crc; return crc;
} }


/**
* @brief 阻塞发送一帧完整 ADU
* @param[in] buf 待发送缓冲,含 CRC
* @param[in] len 长度字节
*/
void SendData(uint8_t *buf, uint16_t len) void SendData(uint8_t *buf, uint16_t len)
{ {
/* 存放中断屏蔽位快照 */
OS_CPU_SR cpu_sr;
OS_CPU_SR cpu_sr;
volatile uint32_t turnaround; volatile uint32_t turnaround;


HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_RESET);

(void)HAL_UART_AbortReceive_IT(&huart1); (void)HAL_UART_AbortReceive_IT(&huart1);
StopFrameTimer(); StopFrameTimer();


OS_ENTER_CRITICAL();/* 进入临界区 */
OS_ENTER_CRITICAL();
g_modbus_rx_len = 0U; g_modbus_rx_len = 0U;
OS_EXIT_CRITICAL();/* 退出临界区 */
OS_EXIT_CRITICAL();


(void)HAL_UART_Transmit(&huart1, buf, len, 200U); (void)HAL_UART_Transmit(&huart1, buf, len, 200U);


@@ -233,13 +178,10 @@ void SendData(uint8_t *buf, uint16_t len)
{ {
} }


/* 换向短期延时 */
for (turnaround = 0U; turnaround < 8000U; turnaround++) for (turnaround = 0U; turnaround < 8000U; turnaround++)
{ {
} }


FlushUartRx(); FlushUartRx();
RestartReceive(); RestartReceive();

HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_SET);
} }

+ 2
- 14
modbus/modbus_port.h Vedi File

@@ -1,6 +1,6 @@
/** /**
* @file modbus_port.h * @file modbus_port.h
* @brief Modbus RTU 链路端口:UART/TIM 定帧/指示灯/CRC/发送
* @brief Modbus RTU 链路端口:UART/TIM 定帧/CRC/发送
*/ */


#ifndef MODBUS_PORT_H #ifndef MODBUS_PORT_H
@@ -8,22 +8,10 @@


#include <stdint.h> #include <stdint.h>


/**
* @brief 清除帧挂起标志
* @details 此函数在modbus_port.c中定义,在modbus_rtu.h中使用
*/
void PortClearFramePending(void); void PortClearFramePending(void);

/**
* @brief 初始化链路端口
* @details 复位接收状态,创建帧信号量,熄灭指示灯,启动 USART1 中断接收。
*/
void ModbusPortInit(void); void ModbusPortInit(void);


/**
* @brief PF7闪烁约50 ms(CRC/帧错/协议异常)
* @note 仅在任务上下文调用。
*/
/* 保留空实现,兼容旧调用点(已取消指示灯) */
void LedErrorBlink(void); void LedErrorBlink(void);


#endif #endif

+ 5
- 54
modbus/modbus_rtu.c Vedi File

@@ -2,7 +2,7 @@
* @file modbus_rtu.c * @file modbus_rtu.c
* @author lve * @author lve
* @brief Modbus RTU 从站主流程:初始化与从站任务 * @brief Modbus RTU 从站主流程:初始化与从站任务
* @version 0.1.1
* @version 0.1.2
* @date 2026/7/31 * @date 2026/7/31
* @copyright Copyright (c) 2026 * @copyright Copyright (c) 2026
*/ */
@@ -13,21 +13,10 @@
#include "modbus_fc.h" #include "modbus_fc.h"
#include <string.h> #include <string.h>


/* 存储接收帧的副本 */
static uint8_t g_modbus_proc_buf[MODBUS_RX_BUF_LEN]; static uint8_t g_modbus_proc_buf[MODBUS_RX_BUF_LEN];


/*============================================================================*/
/* 从站任务主循环 */
/*============================================================================*/

/** /**
* @brief 从站任务:阻塞等待完整帧,校验后分发处理 * @brief 从站任务:阻塞等待完整帧,校验后分发处理
* @details 最短合法 ADU 为4字节。站号为本机或广播0时处理;
* 广播可执行写请求但不发送应答。
* 若超过设定时间未收到任何已定帧的请求,
* 点亮 PF9;收到下一帧(含错帧)后熄灭 PF9。
* 错帧由PF7闪烁指示。
* @param[in] pArg 任务参数(未使用)
*/ */
void ModbusSlaveTask(void *pArg) void ModbusSlaveTask(void *pArg)
{ {
@@ -36,53 +25,21 @@ void ModbusSlaveTask(void *pArg)
uint16_t crcRecv; uint16_t crcRecv;
uint16_t crcCalc; uint16_t crcCalc;
uint8_t slaveAddr; uint8_t slaveAddr;
INT32U lastOkTick;
INT32U now;
INT32U elapsed;
INT16U waitTicks;
OS_CPU_SR cpu_sr; OS_CPU_SR cpu_sr;


(void)pArg; (void)pArg;


ModbusDataInit(); /* 数据初始化 */
ModbusPortInit(); /* 端口初始化 */
lastOkTick = OSTimeGet();
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
ModbusDataInit();
ModbusPortInit();


while (1) while (1)
{ {
now = OSTimeGet();
elapsed = now - lastOkTick;
if (elapsed >= (INT32U)MODBUS_MASTER_OFFLINE_TICKS)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
waitTicks = (INT16U)MODBUS_MASTER_OFFLINE_TICKS;
}
else
{
waitTicks = (INT16U)((INT32U)MODBUS_MASTER_OFFLINE_TICKS - elapsed);
if (waitTicks == 0U)
{
waitTicks = 1U;
}
}

OSSemPend(g_modbus_rx_sem, waitTicks, &err);
if (err == OS_ERR_TIMEOUT)
{
if ((OSTimeGet() - lastOkTick) >= (INT32U)MODBUS_MASTER_OFFLINE_TICKS)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
}
continue;
}
OSSemPend(g_modbus_rx_sem, 0U, &err);
if (err != OS_ERR_NONE) if (err != OS_ERR_NONE)
{ {
continue; continue;
} }


/* 临界区:拷贝一帧并释放接收缓冲供下一帧使用 */
OS_ENTER_CRITICAL(); OS_ENTER_CRITICAL();
frameLen = g_modbus_rx_len; frameLen = g_modbus_rx_len;
if (frameLen > MODBUS_RX_BUF_LEN) if (frameLen > MODBUS_RX_BUF_LEN)
@@ -91,20 +48,15 @@ void ModbusSlaveTask(void *pArg)
} }
memcpy(g_modbus_proc_buf, g_modbus_rx_buf, frameLen); memcpy(g_modbus_proc_buf, g_modbus_rx_buf, frameLen);
g_modbus_rx_len = 0U; g_modbus_rx_len = 0U;
PortClearFramePending(); /* 清除帧挂起标志 */
PortClearFramePending();
OS_EXIT_CRITICAL(); OS_EXIT_CRITICAL();


lastOkTick = OSTimeGet();
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
/* 最短ADU:地址+功能码+CRC(2) = 4字节 */
if (frameLen < 4U) if (frameLen < 4U)
{ {
LedErrorBlink(); LedErrorBlink();
continue; continue;
} }


/* CRC:低字节在前、高字节在后 */
crcRecv = ((uint16_t)g_modbus_proc_buf[frameLen - 1U] << 8) | crcRecv = ((uint16_t)g_modbus_proc_buf[frameLen - 1U] << 8) |
g_modbus_proc_buf[frameLen - 2U]; g_modbus_proc_buf[frameLen - 2U];
crcCalc = ModbusCrc16(g_modbus_proc_buf, (uint16_t)(frameLen - 2U)); crcCalc = ModbusCrc16(g_modbus_proc_buf, (uint16_t)(frameLen - 2U));
@@ -120,7 +72,6 @@ void ModbusSlaveTask(void *pArg)
continue; continue;
} }


/* 地址 0 为广播:可执行写请求,但不回帧 */
ReplyContext(slaveAddr, (slaveAddr != 0U) ? 1U : 0U); ReplyContext(slaveAddr, (slaveAddr != 0U) ? 1U : 0U);
ProcessRequest(g_modbus_proc_buf, frameLen); ProcessRequest(g_modbus_proc_buf, frameLen);
} }


+ 166
- 0
plsr/accel_curve/plsr_accel_curve.c Vedi File

@@ -0,0 +1,166 @@
/**
* @file plsr_accel_curve.c
* @brief 加减速曲线实现:千分比整形 + 梯形脉冲分配 + 按已发脉冲求频
*/
#include "plsr_accel_curve.h"

uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz)
{
if (freq_hz < 1U)
{
freq_hz = 1U;
}
if (freq_hz > 100000U)
{
freq_hz = 100000U;
}
return freq_hz;
}

/**
* @brief 把进度 num/den 映射为 0~1000 的千分比,再按曲线整形
* @return 0~1000,1000 表示该相结束
*/
static uint32_t PlsrAccelCurveShape(uint32_t num, uint32_t den, PlsrAccelMode_e mode)
{
uint32_t x;
uint32_t y;
uint32_t d;

if ((den == 0U) || (num >= den))
{
return 1000U;
}
x = (num * 1000UL) / den;

if (mode == PLSR_ACCEL_S)
{
/* smoothstep:3x^2 - 2x^3(x 为千分比) */
y = (3UL * x * x) / 1000UL;
y -= (2UL * x * x * x) / (1000UL * 1000UL);
}
else if (mode == PLSR_ACCEL_SINE)
{
/* (1-cos(πt))/2 的整数近似 */
if (x <= 500UL)
{
y = (2UL * x * x) / 1000UL;
}
else
{
d = 1000UL - x;
y = 1000UL - (2UL * d * d) / 1000UL;
}
}
else
{
y = x; /* 直线 */
}

if (y > 1000UL)
{
y = 1000UL;
}
return y;
}

void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan,
uint32_t total_pulses,
uint32_t f0,
uint32_t f1,
uint32_t f2,
uint32_t accel_ms,
uint32_t decel_ms,
PlsrAccelMode_e mode)
{
uint32_t acc_n;
uint32_t dec_n;

if (plan == (PlsrAccelPlan_t *)0)
{
return;
}

plan->spd_start = f0;
plan->spd_target = f1;
plan->spd_end = f2;
plan->mode = (mode > PLSR_ACCEL_SINE) ? PLSR_ACCEL_LINEAR : mode;

if (accel_ms == 0U)
{
acc_n = 0U;
}
else
{
acc_n = ((f0 + f1) / 2UL) * accel_ms / 1000UL;
}
if (decel_ms == 0U)
{
dec_n = 0U;
}
else
{
dec_n = ((f1 + f2) / 2UL) * decel_ms / 1000UL;
}

if ((acc_n + dec_n) >= total_pulses)
{
acc_n = total_pulses / 2UL;
dec_n = total_pulses - acc_n;
plan->const_n = 0U;
}
else
{
plan->const_n = total_pulses - acc_n - dec_n;
}
plan->acc_n = acc_n;
plan->dec_n = dec_n;
}

uint32_t PlsrAccelCurveFreqAt(const PlsrAccelPlan_t *plan, uint32_t done_pulses)
{
uint32_t done = done_pulses;
uint32_t ratio;
uint32_t f;

if (plan == (PlsrAccelPlan_t *)0)
{
return 1U;
}

if (done < plan->acc_n)
{
ratio = PlsrAccelCurveShape(done, plan->acc_n, plan->mode);
if (plan->spd_target >= plan->spd_start)
{
f = plan->spd_start + ((plan->spd_target - plan->spd_start) * ratio) / 1000UL;
}
else
{
f = plan->spd_start - ((plan->spd_start - plan->spd_target) * ratio) / 1000UL;
}
return PlsrAccelCurveClampFreq(f);
}

done -= plan->acc_n;
if (done < plan->const_n)
{
return PlsrAccelCurveClampFreq(plan->spd_target);
}

done -= plan->const_n;
if (plan->dec_n == 0U)
{
return PlsrAccelCurveClampFreq(plan->spd_end);
}
ratio = PlsrAccelCurveShape(done, plan->dec_n, plan->mode);
if (plan->spd_target >= plan->spd_end)
{
f = plan->spd_target - ((plan->spd_target - plan->spd_end) * ratio) / 1000UL;
}
else
{
f = plan->spd_target + ((plan->spd_end - plan->spd_target) * ratio) / 1000UL;
}
return PlsrAccelCurveClampFreq(f);
}

+ 47
- 0
plsr/accel_curve/plsr_accel_curve.h Vedi File

@@ -0,0 +1,47 @@
/**
* @file plsr_accel_curve.h
* @brief 加减速曲线:直线 / S / 正弦;按脉冲数规划梯形并求瞬时频率
*
* 输入:总脉冲、起速 f0、目标 f1、止速 f2、加减速时间、曲线类型
* 输出:acc/const/dec 脉冲分配;按已发脉冲查当前 Hz
*/
#ifndef PLSR_ACCEL_CURVE_H
#define PLSR_ACCEL_CURVE_H

#include <stdint.h>
#include "plsr_param.h"

/** 一段运动的频率规划结果(由 Plan 填充,FreqAt 查询) */
typedef struct {
uint32_t acc_n;
uint32_t const_n;
uint32_t dec_n;
uint32_t spd_start;
uint32_t spd_target;
uint32_t spd_end;
PlsrAccelMode_e mode;
} PlsrAccelPlan_t;

/** 限幅到 [1, 100000] Hz */
uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz);

/**
* @brief 规划梯形:用平均速度×时间估算加/减速脉冲数,剩余为匀速
* @note 若 acc+dec 超过总脉冲,则对半分加速/减速、无匀速段
*/
void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan,
uint32_t total_pulses,
uint32_t f0,
uint32_t f1,
uint32_t f2,
uint32_t accel_ms,
uint32_t decel_ms,
PlsrAccelMode_e mode);

/**
* @brief 按本段已发出脉冲数插值当前频率
* @param done_pulses 本段内已发个数(从 0 计)
*/
uint32_t PlsrAccelCurveFreqAt(const PlsrAccelPlan_t *plan, uint32_t done_pulses);

#endif

+ 66
- 0
plsr/command/plsr_command.c Vedi File

@@ -0,0 +1,66 @@
/**
* @file plsr_command.c
* @brief 指令接口:Modbus 命令/线圈 → 参数块 + 运行控制;状态回写
*/
#include "plsr_command.h"
#include "plsr_param.h"
#include "plsr_run_control.h"
#include "plsr.h"
#include "modbus_rtu.h"

/** 写 32 位值到相邻两个保持寄存器(低字在前) */
static void PlsrCommandWriteDWord(uint16_t addr, uint32_t value)
{
WriteHoldReg(addr, (uint16_t)(value & 0xFFFFU));
WriteHoldReg((uint16_t)(addr + 1U), (uint16_t)((value >> 16) & 0xFFFFU));
}

void PlsrCommandInit(void)
{
WriteHoldReg(PLSR_CMD_REG_COMMAND, PLSR_CMD_IDLE);
WriteHoldReg(PLSR_CMD_REG_ERR, 0U);
/* TODO:重复启动防护、GOON、fault_buf */
}

void PlsrCommandPoll(void)
{
uint16_t cmd = ReadHoldReg(PLSR_CMD_REG_COMMAND);

/* --- 线圈边沿消费(写 1 触发后清 0) --- */
if (ReadCoil(PLSR_CMD_COIL_CLR_ACC) != 0U)
{
WriteCoil(PLSR_CMD_COIL_CLR_ACC, 0U);
PlsrClearAccPulse();
}
if (ReadCoil(PLSR_CMD_COIL_STOP) != 0U)
{
WriteCoil(PLSR_CMD_COIL_STOP, 0U);
PlsrStop();
}

/* --- 命令寄存器(写后清 IDLE) --- */
if (cmd == PLSR_CMD_STOP)
{
WriteHoldReg(PLSR_CMD_REG_COMMAND, PLSR_CMD_IDLE);
PlsrStop();
}
else if ((cmd == PLSR_CMD_START) || (cmd == PLSR_CMD_IMPORT))
{
WriteHoldReg(PLSR_CMD_REG_COMMAND, PLSR_CMD_IDLE);
/* 启动前刷新参数块;IMPORT 只刷新不启动 */
(void)PlsrParamBlockApplyFromHold();
if (cmd == PLSR_CMD_START)
{
/* TODO:忙时再次 START 应写故障码,而非静默 */
PlsrStop();
(void)PlsrStart(PlsrParamGetStartSeg());
}
}

/* --- 状态镜像,供上位机读 --- */
WriteHoldReg(PLSR_CMD_REG_BUSY, PlsrIsBusy());
WriteHoldReg(PLSR_CMD_REG_CUR_SEG, PlsrRunControlGetCurSeg());
PlsrCommandWriteDWord(PLSR_CMD_REG_CUR_FREQ_L, PlsrRunControlGetCurFreq());
PlsrCommandWriteDWord(PLSR_CMD_REG_ACC_PULSE_L, (uint32_t)PlsrGetAccPulse());
WriteHoldReg(PLSR_CMD_REG_ERR, 0U);
}

+ 39
- 0
plsr/command/plsr_command.h Vedi File

@@ -0,0 +1,39 @@
/**
* @file plsr_command.h
* @brief 指令接口模块
*
* 职责(与方案书 3.1 对齐;未实现项后续补充):
* 1. 接收启动 / STOP 急停等外部触发(当前经 Modbus 命令寄存器与线圈)
* 2. (待实现)重复启动防护、GOON 恢复、fault_buf
* 3. 指令分发:启动时先走参数块管理再启运行;停机直接停运动
* 4. 状态上报:忙闲、当前段、频率、累计脉冲写回保持寄存器
*/
#ifndef PLSR_COMMAND_H
#define PLSR_COMMAND_H

#include <stdint.h>

/* 指令与状态保持寄存器(地址与原协议兼容) */
#define PLSR_CMD_REG_COMMAND 0x10F0U /* 写命令 */
#define PLSR_CMD_REG_BUSY 0x10F1U /* 运动忙 */
#define PLSR_CMD_REG_CUR_SEG 0x10F2U /* 当前段号 1-based */
#define PLSR_CMD_REG_CUR_FREQ_L 0x10F3U
#define PLSR_CMD_REG_CUR_FREQ_H 0x10F4U
#define PLSR_CMD_REG_ACC_PULSE_L 0x10F5U
#define PLSR_CMD_REG_ACC_PULSE_H 0x10F6U
#define PLSR_CMD_REG_ERR 0x10F7U /* 故障码(待完善) */

#define PLSR_CMD_IDLE 0U
#define PLSR_CMD_START 1U
#define PLSR_CMD_STOP 2U
#define PLSR_CMD_IMPORT 3U /* 仅从寄存器刷新参数块,不启动 */

#define PLSR_CMD_COIL_STOP 0x2001U /* 急停线圈 */
#define PLSR_CMD_COIL_CLR_ACC 0x2003U /* 清累计脉冲线圈 */

void PlsrCommandInit(void);

/* 由 PlsrTask 周期调用:收指令、分发、回写状态 */
void PlsrCommandPoll(void);

#endif

+ 363
- 0
plsr/param/plsr_param.c Vedi File

@@ -0,0 +1,363 @@
/**
* @file plsr_param.c
* @brief 参数块管理模块实现(运行映像 + 分帧导入/导出)
*/
#include "plsr_param.h"
#include "modbus_rtu.h"
#include "ucos_ii.h"
#include <string.h>

static PlsrCfg_t s_cfg;
static PlsrSeg_t s_seg[PLSR_SEG_MAX];

static OS_EVENT *s_param_sem;
static volatile uint16_t s_expect_frames;
static volatile uint16_t s_recv_frames;
static volatile uint8_t s_xfer_active;

/*============================================================================*/
/* 运行映像 */
/*============================================================================*/

void PlsrParamInitDefault(void)
{
uint16_t i;

memset(&s_cfg, 0, sizeof(s_cfg));
memset(s_seg, 0, sizeof(s_seg));

/* 与上位机默认约定一致;真正运行参数由 ApplyFromHold 覆盖 */
s_cfg.pulse_y = 0U;
s_cfg.dir_y = 3U;
s_cfg.wait_x_sel = 0U;
s_cfg.ext_x_sel = 0U;
s_cfg.send_mode = PLSR_SEND_COMPLETE;
s_cfg.dir_delay_ms = 10U;
s_cfg.dir_logic = PLSR_DIR_LOGIC_POS;
s_cfg.accel_mode = PLSR_ACCEL_LINEAR;
s_cfg.run_mode = PLSR_POS_RELATIVE;
s_cfg.seg_count = 0U;
s_cfg.start_seg = 1U;
s_cfg.default_speed = 1000UL;
s_cfg.start_speed = 0UL;
s_cfg.end_speed = 0UL;
s_cfg.accel_ms = 100U;
s_cfg.decel_ms = 100U;

for (i = 0U; i < PLSR_SEG_MAX; i++)
{
s_seg[i].freq_hz = 0;
s_seg[i].pulse_cnt = 0;
s_seg[i].wait_type = PLSR_WAIT_TIME;
s_seg[i].wait_ms = 0U;
s_seg[i].act_ms = 0U;
s_seg[i].jump_seg = 0U;
}
}

PlsrCfg_t *PlsrParamGetCfg(void)
{
return &s_cfg;
}

PlsrSeg_t *PlsrParamGetSeg(uint16_t seg_0based)
{
if (seg_0based >= PLSR_SEG_MAX)
{
seg_0based = 0U;
}
return &s_seg[seg_0based];
}

uint16_t PlsrParamGetSegCount(void)
{
if (s_cfg.seg_count < 1U)
{
return 0U;
}
if (s_cfg.seg_count > PLSR_SEG_MAX)
{
return (uint16_t)PLSR_SEG_MAX;
}
return s_cfg.seg_count;
}

uint16_t PlsrParamGetStartSeg(void)
{
uint16_t n = PlsrParamGetSegCount();

if (n < 1U)
{
return 1U;
}
if (s_cfg.start_seg < 1U)
{
return 1U;
}
/* TODO:起始段非法时输出故障码 */
if (s_cfg.start_seg > n)
{
return n;
}
return s_cfg.start_seg;
}

/*============================================================================*/
/* 分帧导入 / 导出 */
/*============================================================================*/

/** 读相邻两寄存器拼成无符号 32 位(低字在前) */
static uint32_t PlsrParamReadDWord(uint16_t addr)
{
uint16_t low = ReadHoldReg(addr);
uint16_t high = ReadHoldReg((uint16_t)(addr + 1U));
return ((uint32_t)high << 16) | (uint32_t)low;
}

static void PlsrParamWriteDWord(uint16_t addr, uint32_t value)
{
WriteHoldReg(addr, (uint16_t)(value & 0xFFFFU));
WriteHoldReg((uint16_t)(addr + 1U), (uint16_t)((value >> 16) & 0xFFFFU));
}

static int32_t PlsrParamReadSignedDWord(uint16_t addr)
{
return (int32_t)PlsrParamReadDWord(addr);
}

static uint8_t PlsrParamIsSegFrameWrite(uint16_t start_addr, uint16_t quantity)
{
uint16_t i;
uint16_t base;

if (quantity < 8U)
{
return 0U;
}
for (i = 0U; i < PLSR_SEG_MAX; i++)
{
base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE);
if (start_addr == base)
{
return 1U;
}
}
return 0U;
}

static void PlsrParamSetXferStatus(uint16_t st, uint16_t fail_frame)
{
WriteHoldReg(PLSR_PARAM_XFER_STATUS_REG, st);
WriteHoldReg(PLSR_PARAM_XFER_FAIL_FRAME_REG, fail_frame);
WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames);
}

uint8_t PlsrParamBlockApplyFromHold(void)
{
uint16_t i;
uint16_t n;
uint16_t base;
uint16_t m;
PlsrSeg_t *seg;

/*
* 从保持寄存器组装运行映像。
* TODO:段数/频率/脉冲/端子冲突/跳转非法 → 故障码(当前仅裁剪)
*/
s_cfg.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y);
if (s_cfg.pulse_y > 2U)
{
s_cfg.pulse_y = 0U;
}
s_cfg.dir_y = ReadHoldReg(PLSR_REG_DIR_Y);
if (s_cfg.dir_y != 3U)
{
s_cfg.dir_y = 3U;
WriteHoldReg(PLSR_REG_DIR_Y, 3U);
}

s_cfg.wait_x_sel = ReadHoldReg(PLSR_REG_WAIT_X) ? 1U : 0U;
s_cfg.ext_x_sel = ReadHoldReg(PLSR_REG_EXT_X) ? 1U : 0U;
s_cfg.send_mode = (PlsrSendMode_e)(ReadHoldReg(PLSR_REG_SEND_MODE) ? 1U : 0U);
s_cfg.dir_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY);
s_cfg.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ?
PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS;

m = ReadHoldReg(PLSR_REG_ACCEL_MODE);
if (m > (uint16_t)PLSR_ACCEL_SINE)
{
m = (uint16_t)PLSR_ACCEL_LINEAR;
}
s_cfg.accel_mode = (PlsrAccelMode_e)m;
s_cfg.run_mode = (ReadHoldReg(PLSR_REG_RUN_MODE) != 0U) ?
PLSR_POS_ABSOLUTE : PLSR_POS_RELATIVE;

n = ReadHoldReg(PLSR_REG_SEG_COUNT);
if (n > PLSR_SEG_MAX)
{
n = (uint16_t)PLSR_SEG_MAX;
}
s_cfg.seg_count = n;

s_cfg.start_seg = ReadHoldReg(PLSR_REG_START_SEG);
if (s_cfg.start_seg < 1U)
{
s_cfg.start_seg = 1U;
}
if ((n >= 1U) && (s_cfg.start_seg > n))
{
s_cfg.start_seg = n;
}

s_cfg.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L);
s_cfg.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L);
s_cfg.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L);
s_cfg.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS);
s_cfg.decel_ms = ReadHoldReg(PLSR_REG_DECEL_MS);

for (i = 0U; i < n; i++)
{
seg = &s_seg[i];
base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE);
seg->freq_hz = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L));
seg->pulse_cnt = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L));
{
uint16_t w = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT));
if (w > 4U)
{
w = 0U;
}
seg->wait_type = (PlsrWaitType_e)w;
}
seg->wait_ms = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS));
seg->act_ms = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS));
seg->jump_seg = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP));
}

return 1U;
}

void PlsrParamBlockExportToHold(void)
{
uint16_t i;
uint16_t base;
PlsrSeg_t *seg;

WriteHoldReg(PLSR_REG_PULSE_Y, s_cfg.pulse_y);
WriteHoldReg(PLSR_REG_DIR_Y, s_cfg.dir_y);
WriteHoldReg(PLSR_REG_WAIT_X, s_cfg.wait_x_sel);
WriteHoldReg(PLSR_REG_EXT_X, s_cfg.ext_x_sel);
WriteHoldReg(PLSR_REG_SEND_MODE, (uint16_t)s_cfg.send_mode);
WriteHoldReg(PLSR_REG_DIR_DELAY, s_cfg.dir_delay_ms);
WriteHoldReg(PLSR_REG_DIR_LOGIC, (uint16_t)s_cfg.dir_logic);
WriteHoldReg(PLSR_REG_ACCEL_MODE, (uint16_t)s_cfg.accel_mode);
WriteHoldReg(PLSR_REG_RUN_MODE, (uint16_t)s_cfg.run_mode);
WriteHoldReg(PLSR_REG_SEG_COUNT, s_cfg.seg_count);
WriteHoldReg(PLSR_REG_START_SEG, s_cfg.start_seg);
PlsrParamWriteDWord(PLSR_REG_DEFAULT_SPD_L, s_cfg.default_speed);
PlsrParamWriteDWord(PLSR_REG_START_SPD_L, s_cfg.start_speed);
WriteHoldReg(0x100FU, 0U);
PlsrParamWriteDWord(PLSR_REG_END_SPD_L, s_cfg.end_speed);
WriteHoldReg(PLSR_REG_ACCEL_MS, s_cfg.accel_ms);
WriteHoldReg(PLSR_REG_DECEL_MS, s_cfg.decel_ms);

for (i = 0U; i < PLSR_SEG_MAX; i++)
{
seg = &s_seg[i];
base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L), (uint32_t)seg->freq_hz);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L), (uint32_t)seg->pulse_cnt);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT), (uint16_t)seg->wait_type);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS), seg->wait_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS), seg->act_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP), seg->jump_seg);
}
}

void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity)
{
if (quantity == 0U)
{
return;
}

/*
* 公共帧:起始 0x1000 且覆盖到 0x1013 → 开启传输,期望帧=1+段数。
* 段帧:传输中且写到某段基址 → 帧计数+1。
* 收齐后 post 信号量,由 ParamBlockTask 执行 Apply。
*/
if ((start_addr == PLSR_REG_PULSE_Y) &&
(quantity >= (uint16_t)(PLSR_REG_DECEL_MS - PLSR_REG_PULSE_Y + 1U)))
{
uint16_t seg_n = ReadHoldReg(PLSR_REG_SEG_COUNT);
if (seg_n > PLSR_SEG_MAX)
{
seg_n = (uint16_t)PLSR_SEG_MAX;
}
s_expect_frames = (uint16_t)(1U + seg_n);
s_recv_frames = 1U;
s_xfer_active = 1U;
PlsrParamSetXferStatus(PLSR_PARAM_XFER_BUSY, 0U);
}
else if ((s_xfer_active != 0U) &&
(PlsrParamIsSegFrameWrite(start_addr, quantity) != 0U))
{
s_recv_frames++;
WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames);
}
else
{
return;
}

if ((s_xfer_active != 0U) && (s_recv_frames >= s_expect_frames))
{
s_xfer_active = 0U;
if (s_param_sem != (OS_EVENT *)0)
{
(void)OSSemPost(s_param_sem);
}
else if (PlsrParamBlockApplyFromHold() != 0U)
{
PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U);
}
else
{
PlsrParamSetXferStatus(PLSR_PARAM_XFER_FAIL, s_recv_frames);
}
}
}

void PlsrParamBlockInit(void)
{
s_param_sem = OSSemCreate(0U);
s_expect_frames = 0U;
s_recv_frames = 0U;
s_xfer_active = 0U;
PlsrParamSetXferStatus(PLSR_PARAM_XFER_IDLE, 0U);
}

void PlsrParamBlockTask(void *pArg)
{
INT8U err;

(void)pArg;

for (;;)
{
(void)OSSemPend(s_param_sem, 0U, &err);
if (err != OS_ERR_NONE)
{
continue;
}

if (PlsrParamBlockApplyFromHold() != 0U)
{
PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U);
}
else
{
PlsrParamSetXferStatus(PLSR_PARAM_XFER_FAIL, s_recv_frames);
}
}
}

+ 140
- 0
plsr/param/plsr_param.h Vedi File

@@ -0,0 +1,140 @@
/**
* @file plsr_param.h
* @brief 参数块管理模块:寄存器地址、运行结构体、分帧导入/导出
*
* 协议概要:
* - 公共帧:0x1000~0x1013(20 字),0x1009=段数 N,预期总帧=1+N
* - 段帧:0x1100+(i)*0x10,每段 8 字
* - 传输状态:0x0FFC/0x0FFD/0x0FFE
*/
#ifndef PLSR_PARAM_H
#define PLSR_PARAM_H

#include <stdint.h>

#define PLSR_SEG_MAX 10U

/* ---------- 公共参数保持寄存器 ---------- */
#define PLSR_REG_PULSE_Y 0x1000U /* 脉冲端子 0=Y0..2=Y2 */
#define PLSR_REG_DIR_Y 0x1001U /* 方向端子,当前仅 3=Y3 */
#define PLSR_REG_WAIT_X 0x1002U /* WAIT 输入选择 */
#define PLSR_REG_EXT_X 0x1003U /* EXT 输入选择 */
#define PLSR_REG_SEND_MODE 0x1004U /* 0完成 1后续 */
#define PLSR_REG_DIR_DELAY 0x1005U /* 方向建立延时 ms */
#define PLSR_REG_DIR_LOGIC 0x1006U /* 0正逻辑 1负逻辑 */
#define PLSR_REG_ACCEL_MODE 0x1007U /* 0直线 1-S 2正弦 */
#define PLSR_REG_RUN_MODE 0x1008U /* 0相对 1绝对 */
#define PLSR_REG_SEG_COUNT 0x1009U /* 脉冲总段数 N */
#define PLSR_REG_START_SEG 0x100AU /* 起始执行段 1-based */
#define PLSR_REG_DEFAULT_SPD_L 0x100BU /* 默认速度 DW 低 */
#define PLSR_REG_DEFAULT_SPD_H 0x100CU
#define PLSR_REG_START_SPD_L 0x100DU /* 起速 DW */
#define PLSR_REG_START_SPD_H 0x100EU
/* 0x100F 保留,写帧填 0 */
#define PLSR_REG_END_SPD_L 0x1010U /* 止速 DW */
#define PLSR_REG_END_SPD_H 0x1011U
#define PLSR_REG_ACCEL_MS 0x1012U
#define PLSR_REG_DECEL_MS 0x1013U

/* ---------- 段参数:第 i 段基址 = 0x1100 + i*0x10 ---------- */
#define PLSR_REG_SEG1_BASE 0x1100U
#define PLSR_SEG_STRIDE 0x0010U

#define PLSR_SEG_OFF_FREQ_L 0U /* 频率 SDW */
#define PLSR_SEG_OFF_FREQ_H 1U
#define PLSR_SEG_OFF_PULSE_L 2U /* 脉冲数 SDW,负=反转 */
#define PLSR_SEG_OFF_PULSE_H 3U
#define PLSR_SEG_OFF_WAIT 4U /* 等待类型 */
#define PLSR_SEG_OFF_WAIT_MS 5U
#define PLSR_SEG_OFF_ACT_MS 6U
#define PLSR_SEG_OFF_JUMP 7U /* 0=顺序下一段;1~N=跳转段号 */

/* 分帧传输状态(上位机可轮询) */
#define PLSR_PARAM_XFER_RECV_REG 0x0FFCU
#define PLSR_PARAM_XFER_FAIL_FRAME_REG 0x0FFDU
#define PLSR_PARAM_XFER_STATUS_REG 0x0FFEU

#define PLSR_PARAM_XFER_IDLE 0U
#define PLSR_PARAM_XFER_BUSY 1U
#define PLSR_PARAM_XFER_OK 2U
#define PLSR_PARAM_XFER_FAIL 3U

typedef enum {
PLSR_DIR_LOGIC_POS = 0, /* 正转→方向高,反转→低 */
PLSR_DIR_LOGIC_NEG = 1 /* 相反 */
} PlsrDirLogic_e;

typedef enum {
PLSR_SEND_COMPLETE = 0, /* 本段发完再规划下一段加减速 */
PLSR_SEND_FOLLOW = 1 /* 本段末速对准下一段频率,同向可不停表 */
} PlsrSendMode_e;

typedef enum {
PLSR_ACCEL_LINEAR = 0,
PLSR_ACCEL_S = 1,
PLSR_ACCEL_SINE = 2
} PlsrAccelMode_e;

typedef enum {
PLSR_POS_RELATIVE = 0, /* 脉冲数=相对位移 */
PLSR_POS_ABSOLUTE = 1 /* 脉冲数=绝对坐标 */
} PlsrPosMode_e;

typedef enum {
PLSR_WAIT_TIME = 0, /* WAIT 时间 */
PLSR_WAIT_SIGNAL = 1, /* WAIT 信号(待实现) */
PLSR_WAIT_ACT = 2, /* ACT 时间 */
PLSR_WAIT_EXT = 3, /* EXT 信号(待实现) */
PLSR_WAIT_EXT_OR_DONE = 4 /* EXT 或发完(当前等同立即继续) */
} PlsrWaitType_e;

/** 公共运行参数(运动侧只读此映像,不直接啃寄存器) */
typedef struct {
uint16_t pulse_y; /* 0=Y0,1=Y1,2=Y2 */
uint16_t dir_y; /* 3=Y3 */
uint16_t wait_x_sel;
uint16_t ext_x_sel;
PlsrSendMode_e send_mode;
uint16_t dir_delay_ms;
PlsrDirLogic_e dir_logic;
PlsrAccelMode_e accel_mode;
PlsrPosMode_e run_mode;
uint16_t seg_count;
uint16_t start_seg; /* 1-based */
uint32_t default_speed;
uint32_t start_speed;
uint32_t end_speed;
uint16_t accel_ms;
uint16_t decel_ms;
} PlsrCfg_t;

/** 单段运行参数 */
typedef struct {
int32_t freq_hz;
int32_t pulse_cnt; /* 可负 */
PlsrWaitType_e wait_type;
uint16_t wait_ms;
uint16_t act_ms;
uint16_t jump_seg; /* 0=顺序;1~N=跳转 */
} PlsrSeg_t;

/* ---------- 运行映像访问 ---------- */
void PlsrParamInitDefault(void);
PlsrCfg_t *PlsrParamGetCfg(void);
PlsrSeg_t *PlsrParamGetSeg(uint16_t seg_0based);
uint16_t PlsrParamGetSegCount(void);
uint16_t PlsrParamGetStartSeg(void);

/* ---------- 分帧导入 / 导出 / 管理任务 ---------- */
/** OSInit 之后调用,创建分帧应用信号量 */
void PlsrParamBlockInit(void);
/** 等信号量后 hold→结构体 */
void PlsrParamBlockTask(void *pArg);
/** FC10 应答后由 Modbus 调用:分帧计数,收齐则 post 信号量 */
void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity);
/** 寄存器→运行结构体;成功返回 1(完整校验/故障码待实现) */
uint8_t PlsrParamBlockApplyFromHold(void);
/** 运行结构体→寄存器(上电默认、上位机读回) */
void PlsrParamBlockExportToHold(void);

#endif

+ 65
- 0
plsr/path_plan/plsr_path_plan.c Vedi File

@@ -0,0 +1,65 @@
/**
* @file plsr_path_plan.c
* @brief 多段路径规划实现(只做决策,不碰 TIM/GPIO)
*/
#include "plsr_path_plan.h"
#include "plsr_param.h"

int16_t PlsrPathPlanNextSeg(uint16_t cur_seg_0based)
{
PlsrSeg_t *seg = PlsrParamGetSeg(cur_seg_0based);
uint16_t n = PlsrParamGetSegCount();
uint16_t jump = seg->jump_seg;

/* jump=0:顺序下一;越界则结束 */
if (jump == 0U)
{
if ((cur_seg_0based + 1U) >= n)
{
return -1;
}
return (int16_t)(cur_seg_0based + 1U);
}
/* jump=1..N:跳到指定段;非法则结束 */
if ((jump >= 1U) && (jump <= n))
{
return (int16_t)(jump - 1U);
}
return -1;
}

uint8_t PlsrPathPlanIsForward(uint16_t seg_0based, int32_t acc_pulse)
{
PlsrSeg_t *seg = PlsrParamGetSeg(seg_0based);
PlsrCfg_t *cfg = PlsrParamGetCfg();
int32_t move;

if (cfg->run_mode == PLSR_POS_ABSOLUTE)
{
move = seg->pulse_cnt - acc_pulse;
}
else
{
move = seg->pulse_cnt;
}
return (move >= 0) ? 1U : 0U;
}

uint16_t PlsrPathPlanGetWaitMs(uint16_t seg_0based)
{
PlsrSeg_t *seg = PlsrParamGetSeg(seg_0based);

if ((seg->wait_type == PLSR_WAIT_TIME) || (seg->wait_type == PLSR_WAIT_ACT))
{
return (seg->wait_type == PLSR_WAIT_ACT) ? seg->act_ms : seg->wait_ms;
}

/* WAIT_SIGNAL / EXT / EXT_OR_DONE:暂未接输入,按立即继续 */
(void)seg;
return 0U;
}

int16_t PlsrPathPlanResolveAfterSeg(uint16_t cur_seg_0based)
{
return PlsrPathPlanNextSeg(cur_seg_0based);
}

+ 37
- 0
plsr/path_plan/plsr_path_plan.h Vedi File

@@ -0,0 +1,37 @@
/**
* @file plsr_path_plan.h
* @brief 多段路径规划:段序、跳转、段间等待、相对/绝对方向判定
*
* 不直接驱动硬件;由运行控制查询后执行。
* 5 类等待中目前仅 TIME/ACT 产生延时,其余返回 0(立即继续)。
*/
#ifndef PLSR_PATH_PLAN_H
#define PLSR_PATH_PLAN_H

#include <stdint.h>

/**
* @brief 解析下一段索引
* @param cur_seg_0based 当前段 0-based
* @return 下一段 0-based;无下一段或非法跳转返回 -1
* @note jump=0 顺序下一段;jump=1~N 跳到对应段
*/
int16_t PlsrPathPlanNextSeg(uint16_t cur_seg_0based);

/**
* @brief 判断该段运动方向
* @param acc_pulse 当前累计脉冲(绝对模式算位移用)
* @return 1=正向,0=反向
*/
uint8_t PlsrPathPlanIsForward(uint16_t seg_0based, int32_t acc_pulse);

/**
* @brief 段结束后需要等待的毫秒数
* @return >0 进入等待;0 立即解析下一段
*/
uint16_t PlsrPathPlanGetWaitMs(uint16_t seg_0based);

/** 段结束(或等待结束)后解析下一段,语义同 NextSeg */
int16_t PlsrPathPlanResolveAfterSeg(uint16_t cur_seg_0based);

#endif

+ 117
- 0
plsr/plsr.c Vedi File

@@ -0,0 +1,117 @@
/**
* @file plsr.c
* @brief PLSR 门面实现:转发到运行控制,并提供周期任务
*/
#include "plsr.h"
#include "plsr_pulse_driver.h"
#include "plsr_run_control.h"
#include "plsr_param.h"
#include "plsr_command.h"
#include "ucos_ii.h"

void PlsrInit(void)
{
PlsrParamInitDefault();
PlsrPulseDriverInit();
PlsrRunControlInit();
PlsrParamBlockExportToHold(); /* 上电把默认映像摊到保持寄存器 */
PlsrCommandInit();
}

uint8_t PlsrStart(uint16_t start_seg)
{
if (start_seg == 0U)
{
start_seg = PlsrParamGetStartSeg();
}
return PlsrRunControlStart(start_seg);
}

void PlsrStop(void)
{
PlsrRunControlStop();
}

uint8_t PlsrIsBusy(void)
{
return PlsrRunControlIsBusy();
}

int32_t PlsrGetAccPulse(void)
{
return PlsrRunControlGetAccPulse();
}

void PlsrClearAccPulse(void)
{
PlsrRunControlClearAccPulse();
}

void PlsrOnPulseIsr(void)
{
PlsrRunControlOnPulseIsr();
}

void PlsrTask(void *pArg)
{
static uint8_t s_booted = 0U;
#if (PLSR_LOOP_TEST_EN != 0)
static uint8_t s_was_busy = 0U;
static uint16_t s_idle_ticks = 0U;
#endif

(void)pArg;

for (;;)
{
/* 首次进入:等 Modbus 清寄存器后再导出默认,避免被冲掉 */
if (s_booted == 0U)
{
OSTimeDly(30U); /* ~300ms @ 100Hz tick */
PlsrParamBlockExportToHold();
PlsrCommandInit();
s_booted = 1U;
#if (PLSR_LOOP_TEST_EN != 0)
s_was_busy = 0U;
s_idle_ticks = 0U;
#endif
}

PlsrCommandPoll(); /* 收 START/STOP 等,回写忙闲状态 */
PlsrRunControlTickMs(); /* 方向延时、段间等待到期处理 */

#if (PLSR_LOOP_TEST_EN != 0)
/* 调试:空闲满 1s 自动从第 1 段再启 */
if (s_booted != 0U)
{
uint8_t busy = PlsrIsBusy();

if ((s_was_busy != 0U) && (busy == 0U))
{
s_idle_ticks = 0U;
}

if (busy == 0U)
{
s_idle_ticks++;
if (s_idle_ticks >= (uint16_t)OS_TICKS_PER_SEC)
{
s_idle_ticks = 0U;
if (PlsrParamGetSegCount() >= 1U)
{
(void)PlsrStart(1U);
}
}
}
else
{
s_idle_ticks = 0U;
}

s_was_busy = busy;
}
#endif

OSTimeDly(1U);
}
}

+ 41
- 0
plsr/plsr.h Vedi File

@@ -0,0 +1,41 @@
/**
* @file plsr.h
* @brief PLSR 对外门面:初始化、周期任务、启停与状态查询
*
* 内部模块:参数块 / 指令接口 / 路径规划 / 加减速曲线 / 运行控制 / 脉冲驱动
*/
#ifndef PLSR_H
#define PLSR_H

#include <stdint.h>
#include "plsr_param.h"

/* 1=第1段跑完后停约1s再循环(仅调试用,正式产品保持 0) */
#ifndef PLSR_LOOP_TEST_EN
#define PLSR_LOOP_TEST_EN 0
#endif

/** 初始化参数映像、脉冲驱动、运行控制、指令寄存器 */
void PlsrInit(void);

/** uC/OS 任务:周期处理指令 + 运行控制节拍 */
void PlsrTask(void *pArg);

/**
* @brief 启动多段脉冲
* @param start_seg 起始段号 1-based;传 0 则用参数块中的起始段
* @return 1=已启动,0=忙或无有效段
*/
uint8_t PlsrStart(uint16_t start_seg);

/** 急停:关 PWM、清方向、退出运行 */
void PlsrStop(void);

uint8_t PlsrIsBusy(void); /**< 1=运动中 */
int32_t PlsrGetAccPulse(void); /**< 累计脉冲(绝对坐标用) */
void PlsrClearAccPulse(void);

/** TIM UPDATE 中断入口(每发出一个脉冲调用一次) */
void PlsrOnPulseIsr(void);

#endif

+ 253
- 0
plsr/pulse_driver/plsr_pulse_driver.c Vedi File

@@ -0,0 +1,253 @@
/**
* @file plsr_pulse_driver.c
* @brief 脉冲输出驱动模块实现(TIM10 PWM + 方向 GPIO)
* @note TIMxCLK=168MHz;运行中改频不写 EGR.UG(避免 UPDATE 中断风暴)
*
* 板级:Y0=PF6 TIM10_CH1;Y3=PF9 方向(cfg->dir_y=3)
* AB 正交模式待实现。
*/
#include "plsr_pulse_driver.h"
#include "plsr_param.h"
#include "main.h"

TIM_HandleTypeDef htim10;

#define PLSR_TIM_CLK_HZ 168000000UL

#define PLSR_Y0_PORT GPIOF
#define PLSR_Y0_PIN GPIO_PIN_6
#define PLSR_Y3_PORT GPIOF
#define PLSR_Y3_PIN GPIO_PIN_9

static uint32_t s_last_freq;

static void PlsrPulseDriverDirGpioInit(void)
{
GPIO_InitTypeDef gpio = {0};

__HAL_RCC_GPIOF_CLK_ENABLE();

gpio.Pin = PLSR_Y3_PIN;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(PLSR_Y3_PORT, &gpio);
HAL_GPIO_WritePin(PLSR_Y3_PORT, PLSR_Y3_PIN, GPIO_PIN_RESET);
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef gpio = {0};

if (htim->Instance != TIM10)
{
return;
}

__HAL_RCC_TIM10_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();

gpio.Pin = PLSR_Y0_PIN;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
gpio.Alternate = GPIO_AF3_TIM10;
HAL_GPIO_Init(PLSR_Y0_PORT, &gpio);

HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
}

static void PlsrPulseDriverTim10Init(void)
{
TIM_OC_InitTypeDef oc = {0};

htim10.Instance = TIM10;
htim10.Init.Prescaler = 167U;
htim10.Init.CounterMode = TIM_COUNTERMODE_UP;
htim10.Init.Period = 999U;
htim10.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim10.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim10) != HAL_OK)
{
Error_Handler();
}

oc.OCMode = TIM_OCMODE_PWM1;
oc.Pulse = 500U;
oc.OCPolarity = TIM_OCPOLARITY_HIGH;
oc.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim10, &oc, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
htim10.Instance->CCMR1 |= TIM_CCMR1_OC1PE;
}

void PlsrPulseDriverInit(void)
{
s_last_freq = 0U;
PlsrPulseDriverDirGpioInit();
PlsrPulseDriverTim10Init();
/* TODO:按全局参数切换脉冲+方向 / AB 正交;故障码上报自诊断 */
}

void PlsrPulseDriverSetDir(uint8_t forward)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
uint8_t level;
GPIO_TypeDef *port;
uint16_t pin;

if (cfg->dir_logic == PLSR_DIR_LOGIC_POS)
{
level = (forward != 0U) ? 1U : 0U;
}
else
{
level = (forward != 0U) ? 0U : 1U;
}

switch (cfg->dir_y)
{
case 3U:
default:
port = PLSR_Y3_PORT;
pin = PLSR_Y3_PIN;
break;
}

HAL_GPIO_WritePin(port, pin, level ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

void PlsrPulseDriverClearDir(void)
{
HAL_GPIO_WritePin(PLSR_Y3_PORT, PLSR_Y3_PIN, GPIO_PIN_RESET);
}

static void PlsrPulseDriverCalcPscArr(uint32_t freq_hz, uint32_t *psc, uint32_t *arr, uint32_t *ccr)
{
uint32_t ticks;
uint32_t p;
uint32_t a;
uint32_t c;

if (freq_hz < 1U)
{
freq_hz = 1U;
}
if (freq_hz > 100000U)
{
freq_hz = 100000U;
}

ticks = PLSR_TIM_CLK_HZ / freq_hz;
if (ticks < 2UL)
{
ticks = 2UL;
}

p = (ticks + 65535UL - 1UL) / 65535UL;
if (p == 0UL)
{
p = 1UL;
}
p -= 1UL;
if (p > 65535UL)
{
p = 65535UL;
}

a = ticks / (p + 1UL);
if (a < 2UL)
{
a = 2UL;
}
a -= 1UL;
if (a > 65535UL)
{
a = 65535UL;
}

c = (a + 1UL) / 2UL;
if (c == 0UL)
{
c = 1UL;
}

*psc = p;
*arr = a;
*ccr = c;
}

void PlsrPulseDriverSetFreq(uint32_t freq_hz)
{
uint32_t psc;
uint32_t arr;
uint32_t ccr;

if (freq_hz == s_last_freq)
{
return;
}

PlsrPulseDriverCalcPscArr(freq_hz, &psc, &arr, &ccr);
s_last_freq = freq_hz;

__HAL_TIM_SET_PRESCALER(&htim10, psc);
__HAL_TIM_SET_AUTORELOAD(&htim10, arr);
__HAL_TIM_SET_COMPARE(&htim10, TIM_CHANNEL_1, ccr);
}

static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_it)
{
uint32_t psc;
uint32_t arr;
uint32_t ccr;

PlsrPulseDriverCalcPscArr(freq_hz, &psc, &arr, &ccr);
s_last_freq = freq_hz;

/* 先停表再装载参数,并用 UG 同步影子寄存器;计脉冲时再开 UPDATE IT */
__HAL_TIM_DISABLE(&htim10);
__HAL_TIM_DISABLE_IT(&htim10, TIM_IT_UPDATE);

__HAL_TIM_SET_PRESCALER(&htim10, psc);
__HAL_TIM_SET_AUTORELOAD(&htim10, arr);
__HAL_TIM_SET_COMPARE(&htim10, TIM_CHANNEL_1, ccr);
__HAL_TIM_SET_COUNTER(&htim10, 0U);

htim10.Instance->EGR = TIM_EGR_UG;
__HAL_TIM_CLEAR_FLAG(&htim10, TIM_FLAG_UPDATE);
__HAL_TIM_CLEAR_IT(&htim10, TIM_IT_UPDATE);

if (HAL_TIM_PWM_Start(&htim10, TIM_CHANNEL_1) != HAL_OK)
{
TIM_CHANNEL_STATE_SET(&htim10, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY);
(void)HAL_TIM_PWM_Start(&htim10, TIM_CHANNEL_1);
}

if (enable_update_it != 0U)
{
__HAL_TIM_ENABLE_IT(&htim10, TIM_IT_UPDATE);
}
}

void PlsrPulseDriverStart(uint32_t freq_hz)
{
PlsrPulseDriverStartCommon(freq_hz, 1U);
}

void PlsrPulseDriverStartFreeRun(uint32_t freq_hz)
{
PlsrPulseDriverStartCommon(freq_hz, 0U);
}

void PlsrPulseDriverStop(void)
{
__HAL_TIM_DISABLE_IT(&htim10, TIM_IT_UPDATE);
(void)HAL_TIM_PWM_Stop(&htim10, TIM_CHANNEL_1);
__HAL_TIM_CLEAR_FLAG(&htim10, TIM_FLAG_UPDATE);
__HAL_TIM_CLEAR_IT(&htim10, TIM_IT_UPDATE);
s_last_freq = 0U;
}

+ 39
- 0
plsr/pulse_driver/plsr_pulse_driver.h Vedi File

@@ -0,0 +1,39 @@
/**
* @file plsr_pulse_driver.h
* @brief 脉冲输出驱动(方案书 5.1):TIM 脉冲 + 方向 GPIO
*
* 已实现:脉冲+方向(TIM10→Y0,方向→Y3)
* 待实现:AB 正交模式、硬件故障码→自诊断
*
* 调用方:运行控制(启停/调频/方向)
*/
#ifndef PLSR_PULSE_DRIVER_H
#define PLSR_PULSE_DRIVER_H

#include <stdint.h>
#include "stm32f4xx_hal.h"

/* TIM10 句柄,供中断服务里 HAL_TIM_IRQHandler 使用 */
extern TIM_HandleTypeDef htim10;

void PlsrPulseDriverInit(void);

/* forward=1 为正转意图;实际电平还受 dir_logic、dir_y 影响 */
void PlsrPulseDriverSetDir(uint8_t forward);

/* 方向脚拉低(停机/全部结束) */
void PlsrPulseDriverClearDir(void);

/* 按频率启动 PWM,并打开 UPDATE 中断以便计脉冲 */
void PlsrPulseDriverStart(uint32_t freq_hz);

/* 启动 PWM 但不计脉冲(预留) */
void PlsrPulseDriverStartFreeRun(uint32_t freq_hz);

/* 运行中改频:只改 PSC/ARR/CCR,禁止写 EGR.UG */
void PlsrPulseDriverSetFreq(uint32_t freq_hz);

/* 停止 PWM 并关 UPDATE 中断 */
void PlsrPulseDriverStop(void);

#endif

+ 424
- 0
plsr/run_control/plsr_run_control.c Vedi File

@@ -0,0 +1,424 @@
/**
* @file plsr_run_control.c
* @brief 运行控制:完成/后续模式状态机,调度路径规划与曲线,驱动脉冲输出
* @note 延时一律用 OSTimeGet()(本工程 OS_APP_HOOKS_EN=0,HAL_GetTick 不递增)
*/
#include "plsr_run_control.h"
#include "plsr_path_plan.h"
#include "plsr_accel_curve.h"
#include "plsr_param.h"
#include "plsr_pulse_driver.h"
#include "ucos_ii.h"

typedef enum {
RC_IDLE = 0, /* 空闲 */
RC_DIR_WAIT, /* 已置方向,等待 dir_delay 后再开 PWM */
RC_RUN, /* 正在发脉冲 */
RC_WAIT_COND /* 段间等待(时间类) */
} RcState_e;

static volatile RcState_e s_state;
static volatile uint8_t s_busy;
static volatile uint8_t s_forward; /* 1=正转意图 */
static volatile uint16_t s_cur_seg; /* 当前段 0-based */
static volatile uint32_t s_cur_freq;
static volatile int32_t s_done; /* 本段已发脉冲 */
static volatile int32_t s_target; /* 本段目标脉冲数(绝对值) */
static volatile int32_t s_acc_pulse; /* 全局累计脉冲 */

static INT32U s_dir_deadline;
static INT32U s_wait_deadline;

static PlsrAccelPlan_t s_accel_plan;
static uint32_t s_chain_freq; /* 后续模式:上一段结束频率,作下一段起速 */
static uint8_t s_follow_cont; /* 1=同向衔接,不停 PWM */

static INT32U PlsrRunControlMsToTicks(uint32_t ms)
{
INT32U t;

if (ms == 0U)
{
return 0U;
}
t = ((INT32U)ms * (INT32U)OS_TICKS_PER_SEC + 999UL) / 1000UL;
if (t < 1UL)
{
t = 1UL;
}
return t;
}

static uint8_t PlsrRunControlOsTimeReached(INT32U deadline)
{
return ((INT32S)(OSTimeGet() - deadline) >= 0) ? 1U : 0U;
}

static void PlsrRunControlFinishAll(void)
{
PlsrPulseDriverStop();
PlsrPulseDriverClearDir();
s_busy = 0U;
s_state = RC_IDLE;
s_cur_freq = 0U;
s_follow_cont = 0U;
s_chain_freq = 0U;
}

static void PlsrRunControlBeginSeg(uint16_t seg_0);
static void PlsrRunControlGotoNextOrFinish(uint16_t cur_seg);

/** 按路径规划进入下一段,或全部结束 */
static void PlsrRunControlGotoNextOrFinish(uint16_t cur_seg)
{
int16_t next0 = PlsrPathPlanResolveAfterSeg(cur_seg);

if (next0 < 0)
{
PlsrRunControlFinishAll();
}
else
{
PlsrRunControlBeginSeg((uint16_t)next0);
}
}

/** 本段脉冲发完:处理完成/后续衔接、段间等待、跳转 */
static void PlsrRunControlAfterSegDone(void)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
int16_t next0;
uint16_t wait_ms;
uint8_t follow;
uint8_t keep_pwm;

next0 = PlsrPathPlanNextSeg(s_cur_seg);
follow = (cfg->send_mode == PLSR_SEND_FOLLOW) ? 1U : 0U;
keep_pwm = 0U;

/*
* 完成模式:整段发完后停表,下一段再加减速。
* 后续模式:同向时不停表,衔接下一段频率。
*/
if ((follow != 0U) && (next0 >= 0) &&
(PlsrPathPlanIsForward((uint16_t)next0, s_acc_pulse) == s_forward))
{
s_chain_freq = s_accel_plan.spd_end;
s_follow_cont = 1U;
keep_pwm = 1U;
}
else
{
PlsrPulseDriverStop();
s_follow_cont = 0U;
s_chain_freq = 0U;
}

wait_ms = PlsrPathPlanGetWaitMs(s_cur_seg);
if (wait_ms > 0U)
{
if (keep_pwm != 0U)
{
PlsrPulseDriverStop();
s_follow_cont = 0U;
}
s_wait_deadline = OSTimeGet() + PlsrRunControlMsToTicks(wait_ms);
s_state = RC_WAIT_COND;
return;
}

PlsrRunControlGotoNextOrFinish(s_cur_seg);
}

/**
* 装载并启动一段:算位移/方向、规划曲线、置方向延时或同向改频。
*/
static void PlsrRunControlBeginSeg(uint16_t seg_0)
{
PlsrSeg_t *seg;
PlsrCfg_t *cfg;
int32_t cnt;
int32_t move;
uint32_t f0;
uint32_t f1;
uint32_t f2;
uint32_t total;
PlsrAccelMode_e mode;

if (seg_0 >= PlsrParamGetSegCount())
{
PlsrRunControlFinishAll();
return;
}

seg = PlsrParamGetSeg(seg_0);
cfg = PlsrParamGetCfg();
s_cur_seg = seg_0;

mode = cfg->accel_mode;
if (mode > PLSR_ACCEL_SINE)
{
mode = PLSR_ACCEL_LINEAR;
}

cnt = seg->pulse_cnt;

/* 相对:位移=脉冲数;绝对:位移=目标-累计,为 0 则本段跳过 */
if (cfg->run_mode == PLSR_POS_ABSOLUTE)
{
move = cnt - s_acc_pulse;
if (move == 0)
{
s_busy = 1U;
s_target = 0;
s_done = 0;
PlsrRunControlAfterSegDone();
return;
}
}
else
{
move = cnt;
}

if (move >= 0)
{
s_forward = 1U;
s_target = move;
}
else
{
s_forward = 0U;
s_target = -move;
}

f1 = (uint32_t)((seg->freq_hz > 0) ? seg->freq_hz : (int32_t)cfg->default_speed);
if (f1 == 0U)
{
f1 = cfg->default_speed;
}
f1 = PlsrAccelCurveClampFreq(f1);

/* 完成模式:起速→本段频→止速;后续模式:末速对准下一段,同向可链式衔接 */
if (cfg->send_mode == PLSR_SEND_FOLLOW)
{
int16_t next0 = PlsrPathPlanNextSeg(seg_0);

if (s_follow_cont != 0U)
{
f0 = PlsrAccelCurveClampFreq((s_chain_freq != 0U) ? s_chain_freq : f1);
}
else
{
f0 = cfg->start_speed;
if (f0 == 0U)
{
f0 = 1U;
}
f0 = PlsrAccelCurveClampFreq(f0);
}

if (next0 >= 0)
{
PlsrSeg_t *nseg = PlsrParamGetSeg((uint16_t)next0);
f2 = (uint32_t)((nseg->freq_hz > 0) ? nseg->freq_hz
: (int32_t)cfg->default_speed);
if (f2 == 0U)
{
f2 = cfg->default_speed;
}
f2 = PlsrAccelCurveClampFreq(f2);
}
else
{
f2 = cfg->end_speed;
if (f2 == 0U)
{
f2 = 1U;
}
f2 = PlsrAccelCurveClampFreq(f2);
}
}
else
{
s_follow_cont = 0U;
f0 = cfg->start_speed;
f2 = cfg->end_speed;
if (f0 == 0U)
{
f0 = 1U;
}
if (f2 == 0U)
{
f2 = 1U;
}
f0 = PlsrAccelCurveClampFreq(f0);
f2 = PlsrAccelCurveClampFreq(f2);
}

total = (uint32_t)s_target;
PlsrAccelCurvePlan(&s_accel_plan, total, f0, f1, f2,
cfg->accel_ms, cfg->decel_ms, mode);

s_done = 0;
s_busy = 1U;

if (total == 0U)
{
PlsrRunControlAfterSegDone();
return;
}

if (s_follow_cont != 0U)
{
/* 后续模式同向:PWM 仍在跑,只改频进入本段曲线 */
s_cur_freq = PlsrAccelCurveFreqAt(&s_accel_plan, (uint32_t)s_done);
s_state = RC_RUN;
PlsrPulseDriverSetFreq(s_cur_freq);
s_follow_cont = 0U;
}
else
{
/* 先置方向,等 dir_delay 后再 Start(见 TickMs) */
PlsrPulseDriverSetDir(s_forward);
s_dir_deadline = OSTimeGet() + PlsrRunControlMsToTicks(cfg->dir_delay_ms);
s_state = RC_DIR_WAIT;
}
}

void PlsrRunControlInit(void)
{
s_state = RC_IDLE;
s_busy = 0U;
s_forward = 1U;
s_cur_seg = 0U;
s_cur_freq = 0U;
s_done = 0;
s_target = 0;
s_acc_pulse = 0;
s_chain_freq = 0U;
s_follow_cont = 0U;
}

uint8_t PlsrRunControlStart(uint16_t start_seg_1based)
{
uint16_t n;

if (s_busy != 0U)
{
return 0U;
}

n = PlsrParamGetSegCount();
if (n < 1U)
{
return 0U;
}
if ((start_seg_1based < 1U) || (start_seg_1based > n))
{
start_seg_1based = PlsrParamGetStartSeg();
}

s_chain_freq = 0U;
s_follow_cont = 0U;
PlsrRunControlBeginSeg((uint16_t)(start_seg_1based - 1U));
return 1U;
}

void PlsrRunControlStop(void)
{
PlsrPulseDriverStop();
PlsrPulseDriverClearDir();
s_busy = 0U;
s_state = RC_IDLE;
s_cur_freq = 0U;
s_chain_freq = 0U;
s_follow_cont = 0U;
}

void PlsrRunControlTickMs(void)
{
/* 方向延时到:进入 RUN 并开脉冲 */
if (s_state == RC_DIR_WAIT)
{
if (PlsrRunControlOsTimeReached(s_dir_deadline) != 0U)
{
s_cur_freq = PlsrAccelCurveFreqAt(&s_accel_plan, (uint32_t)s_done);
s_state = RC_RUN;
PlsrPulseDriverStart(s_cur_freq);
}
return;
}

/* 段间等待到:跳转或结束 */
if (s_state == RC_WAIT_COND)
{
if (PlsrRunControlOsTimeReached(s_wait_deadline) != 0U)
{
PlsrRunControlGotoNextOrFinish(s_cur_seg);
}
}
}

void PlsrRunControlOnPulseIsr(void)
{
if (s_state != RC_RUN)
{
return;
}

s_done++;
/* 累计脉冲随正反方向增减,供绝对定位 */
if (s_forward != 0U)
{
s_acc_pulse++;
}
else
{
s_acc_pulse--;
}

if (s_done >= s_target)
{
PlsrRunControlAfterSegDone();
return;
}

{
uint32_t next = PlsrAccelCurveFreqAt(&s_accel_plan, (uint32_t)s_done);
if (next != s_cur_freq)
{
s_cur_freq = next;
PlsrPulseDriverSetFreq(s_cur_freq);
}
}
}

uint8_t PlsrRunControlIsBusy(void)
{
return s_busy;
}

int32_t PlsrRunControlGetAccPulse(void)
{
return s_acc_pulse;
}

void PlsrRunControlClearAccPulse(void)
{
s_acc_pulse = 0;
}

uint16_t PlsrRunControlGetCurSeg(void)
{
if (s_busy == 0U)
{
return 0U;
}
return (uint16_t)(s_cur_seg + 1U);
}

uint32_t PlsrRunControlGetCurFreq(void)
{
return s_cur_freq;
}

+ 38
- 0
plsr/run_control/plsr_run_control.h Vedi File

@@ -0,0 +1,38 @@
/**
* @file plsr_run_control.h
* @brief 运行控制模块
*
* 状态机:IDLE → DIR_WAIT(方向延时)→ RUN(发脉冲/调频)
* → WAIT_COND(段间等待)→ 下一段 / IDLE
*
* 依赖:path_plan(段序/等待)、accel_curve(频率曲线)、pulse_driver(硬件)
*/
#ifndef PLSR_RUN_CONTROL_H
#define PLSR_RUN_CONTROL_H

#include <stdint.h>

void PlsrRunControlInit(void);

/**
* @brief 从指定段开始运行
* @param start_seg_1based 起始段;非法则回退到参数块起始段
* @return 1=已进入运行,0=忙或段数为 0
*/
uint8_t PlsrRunControlStart(uint16_t start_seg_1based);

void PlsrRunControlStop(void);

/** 约 1ms 周期调用:处理方向延时到期、段间等待到期 */
void PlsrRunControlTickMs(void);

/** 每个脉冲中断:累计计数、按曲线改频、段完成切换 */
void PlsrRunControlOnPulseIsr(void);

uint8_t PlsrRunControlIsBusy(void);
int32_t PlsrRunControlGetAccPulse(void);
void PlsrRunControlClearAccPulse(void);
uint16_t PlsrRunControlGetCurSeg(void); /**< 忙时返回 1-based 当前段;闲=0 */
uint32_t PlsrRunControlGetCurFreq(void);

#endif

Caricamento…
Annulla
Salva