| @@ -15,5 +15,7 @@ iar/modbus/Exe/ | |||
| iar/modbus/List/ | |||
| iar/modbus/Obj/ | |||
| iar/Backup of modbus.ewp | |||
| host/ | |||
| iar/settings/modbus.reggroups | |||
| build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/ | |||
| plsr/test/ | |||
| @@ -3,57 +3,38 @@ | |||
| * @file main.c | |||
| * @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 "usart.h" | |||
| #include "gpio.h" | |||
| #include "ucos_ii.h" | |||
| #include "modbus_rtu.h" | |||
| #include "tim.h" | |||
| #include "plsr.h" | |||
| #include "plsr_param.h" | |||
| /** | |||
| * @brief Modbus 任务栈 | |||
| * @note 需覆盖帧拷贝、功能码处理与阻塞发送时的调用深度 | |||
| */ | |||
| #define MODBUS_TASK_STACK_SIZE 512 | |||
| /** | |||
| * @brief Modbus 任务优先级 | |||
| * @note 数字越大优先级越低 | |||
| */ | |||
| #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); | |||
| /** | |||
| * @brief 应用程序入口 | |||
| * | |||
| * @details 完成本地外设与操作系统初始化后调用 OSStart(),此后由调度器接管; | |||
| * 函数末尾的 while(1) 在正常路径下不会执行到。 | |||
| * | |||
| * @retval int 形式上的返回值 | |||
| */ | |||
| int main(void) | |||
| { | |||
| HAL_Init(); | |||
| HAL_NVIC_SetPriority(SysTick_IRQn, 14, 0); | |||
| HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0); | |||
| @@ -65,10 +46,11 @@ int main(void) | |||
| MX_USART1_UART_Init(); | |||
| MX_TIM2_Init(); | |||
| PlsrInit(); | |||
| OSInit(); | |||
| PlsrParamBlockInit(); /* 依赖 OS,须在 OSInit 之后 */ | |||
| /* OS_TASK_OPT_SAVE_FP: 硬件浮点开启时由 OS 保存/恢复 S0-S31 */ | |||
| OSTaskCreateExt(ModbusSlaveTask, | |||
| NULL, | |||
| &ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1], | |||
| @@ -79,37 +61,41 @@ int main(void) | |||
| NULL, | |||
| 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) | |||
| { | |||
| } | |||
| } | |||
| /** | |||
| * @brief 系统时钟配置 | |||
| * | |||
| * @details HSE + PLL:SYSCLK = 168 MHz; | |||
| * AHB = 168 MHz,APB1 = 42 MHz,APB2 = 84 MHz。 | |||
| * | |||
| * @retval None | |||
| */ | |||
| void SystemClock_Config(void) | |||
| { | |||
| RCC_OscInitTypeDef RCC_OscInitStruct = {0}; | |||
| RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; | |||
| /** Configure the main internal regulator output voltage | |||
| */ | |||
| __HAL_RCC_PWR_CLK_ENABLE(); | |||
| __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.HSEState = RCC_HSE_ON; | |||
| RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; | |||
| @@ -123,8 +109,6 @@ void SystemClock_Config(void) | |||
| Error_Handler(); | |||
| } | |||
| /** Initializes the CPU, AHB and APB buses clocks | |||
| */ | |||
| RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK | |||
| |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; | |||
| RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; | |||
| @@ -138,10 +122,6 @@ void SystemClock_Config(void) | |||
| } | |||
| } | |||
| /** | |||
| * @brief HAL / 应用错误处理 | |||
| * @retval None | |||
| */ | |||
| void Error_Handler(void) | |||
| { | |||
| __disable_irq(); | |||
| @@ -152,4 +132,4 @@ void Error_Handler(void) | |||
| #ifdef USE_FULL_ASSERT | |||
| #endif | |||
| #endif | |||
| @@ -56,6 +56,7 @@ void DebugMon_Handler(void); | |||
| void PendSV_Handler(void); | |||
| void SysTick_Handler(void); | |||
| void TIM2_IRQHandler(void); | |||
| void TIM1_UP_TIM10_IRQHandler(void); | |||
| void USART1_IRQHandler(void); | |||
| /* USER CODE BEGIN EFP */ | |||
| @@ -2,64 +2,16 @@ | |||
| /** | |||
| ****************************************************************************** | |||
| * @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 */ | |||
| /* Includes ------------------------------------------------------------------*/ | |||
| #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) | |||
| { | |||
| GPIO_InitTypeDef GPIO_InitStruct = {0}; | |||
| /* GPIO Ports Clock Enable */ | |||
| __HAL_RCC_GPIOA_CLK_ENABLE(); | |||
| __HAL_RCC_GPIOF_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 */ | |||
| @@ -59,6 +59,7 @@ | |||
| /* External variables --------------------------------------------------------*/ | |||
| extern UART_HandleTypeDef huart1; | |||
| extern TIM_HandleTypeDef htim2; | |||
| extern TIM_HandleTypeDef htim10; | |||
| /* USER CODE BEGIN EV */ | |||
| @@ -211,17 +212,23 @@ void SysTick_Handler(void) | |||
| /** | |||
| * @brief This function handles TIM2 global interrupt. | |||
| * @note TIM2 用于 Modbus RTU 帧空闲定帧(约 T3.5),须调用 HAL 分发回调 | |||
| * @note TIM2 用于 Modbus RTU 帧空闲定帧(约 T3.5) | |||
| */ | |||
| void TIM2_IRQHandler(void) | |||
| { | |||
| /* USER CODE BEGIN TIM2_IRQn 0 */ | |||
| OSIntEnter(); | |||
| /* USER CODE END TIM2_IRQn 0 */ | |||
| HAL_TIM_IRQHandler(&htim2); | |||
| /* USER CODE BEGIN TIM2_IRQn 1 */ | |||
| OSIntExit(); | |||
| /* USER CODE END TIM2_IRQn 1 */ | |||
| } | |||
| /** | |||
| * @brief TIM1 Update / TIM10 全局中断(PLSR 脉冲计数) | |||
| */ | |||
| void TIM1_UP_TIM10_IRQHandler(void) | |||
| { | |||
| OSIntEnter(); | |||
| HAL_TIM_IRQHandler(&htim10); | |||
| OSIntExit(); | |||
| } | |||
| /** | |||
| @@ -16,8 +16,7 @@ void MX_TIM2_Init(void) | |||
| htim2.Instance = TIM2; | |||
| htim2.Init.Prescaler = 167; | |||
| 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.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; | |||
| if (HAL_TIM_Base_Init(&htim2) != HAL_OK) | |||
| @@ -25,7 +24,6 @@ void MX_TIM2_Init(void) | |||
| Error_Handler(); | |||
| } | |||
| // NVIC中断优先级 | |||
| HAL_NVIC_SetPriority(TIM2_IRQn,6,0); | |||
| HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0); | |||
| HAL_NVIC_EnableIRQ(TIM2_IRQn); | |||
| } | |||
| } | |||
| @@ -355,6 +355,14 @@ | |||
| <state>$PROJ_DIR$\..\drivers\Include</state> | |||
| <state>$PROJ_DIR$\..\drivers\Source</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-LIB\Ports</state> | |||
| <state>$PROJ_DIR$\..\ucos\uC-LIB</state> | |||
| @@ -2859,6 +2867,69 @@ | |||
| <name>$PROJ_DIR$\..\modbus\modbus_rtu.h</name> | |||
| </file> | |||
| </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> | |||
| <name>ucos</name> | |||
| <group> | |||
| @@ -7,6 +7,7 @@ | |||
| #include "modbus_common.h" | |||
| #include "modbus_data.h" | |||
| #include "modbus_port.h" | |||
| #include "plsr_param.h" | |||
| #include <string.h> | |||
| /*应答帧组帧缓冲 */ | |||
| @@ -318,14 +319,16 @@ static void WriteMultiRegs(const uint8_t *frame, uint16_t len) | |||
| ReadU16Be(&frame[7U + (i * 2U)])); | |||
| } | |||
| /* 先回 FC10 应答,再做分帧计数(避免应答被后续逻辑拖住) */ | |||
| g_modbus_tx_buf[0] = frame[0]; | |||
| g_modbus_tx_buf[1] = WRITE_MULTI_REGS; | |||
| g_modbus_tx_buf[2] = frame[2]; | |||
| g_modbus_tx_buf[3] = frame[3]; | |||
| g_modbus_tx_buf[4] = frame[4]; | |||
| g_modbus_tx_buf[5] = frame[5]; | |||
| AppendCrcAndSend(g_modbus_tx_buf, 6U); | |||
| PlsrParamBlockOnHoldWrite(startAddr, quantity); | |||
| } | |||
| /*============================================================================*/ | |||
| @@ -1,52 +1,35 @@ | |||
| /** | |||
| * @file modbus_port.c | |||
| * @author lve | |||
| * @brief 链路端口实现(收字节、空闲定帧、发送、指示灯) | |||
| * @version 0.1.1 | |||
| * @brief 链路端口实现(收字节、空闲定帧、发送) | |||
| * @version 0.1.2 | |||
| * @date 2026/7/31 | |||
| * @note 从站任务取帧处理后,通过ModbusSendData函数阻塞回发 | |||
| * @note 从站任务取帧处理后,通过 SendData 阻塞回发 | |||
| * @copyright Copyright (c) 2026 | |||
| */ | |||
| #include "modbus_port.h" | |||
| #include "modbus_common.h" | |||
| #include "gpio.h" | |||
| #include "plsr.h" | |||
| /* 接收缓冲区数组 */ | |||
| uint8_t g_modbus_rx_buf[MODBUS_RX_BUF_LEN] = {0}; | |||
| /* 接收字节计数 */ | |||
| volatile uint16_t g_modbus_rx_len = 0U; | |||
| /* 声明信号量指针 */ | |||
| OS_EVENT *g_modbus_rx_sem = (OS_EVENT *)0; | |||
| /* 接收单字节 */ | |||
| static uint8_t g_rxByte; | |||
| /* 完整帧待处理标志 */ | |||
| static volatile uint8_t g_modbus_frame_pending = 0U; | |||
| /* TIM2 帧空闲定时器是否已启动 */ | |||
| static volatile uint8_t g_modbus_tim_armed = 0U; | |||
| static void StopFrameTimer(void); | |||
| static void FlushUartRx(void); | |||
| static void RestartReceive(void); | |||
| /** | |||
| * @brief PF7 闪烁约 50 ms(CRC / 帧错 / 协议异常) | |||
| * @note 仅允许在任务上下文调用(内部使用 OSTimeDly) | |||
| */ | |||
| 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) | |||
| { | |||
| (void)HAL_TIM_Base_Stop_IT(&htim2); | |||
| @@ -55,29 +38,22 @@ static void StopFrameTimer(void) | |||
| __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); | |||
| } | |||
| /* 清除 UART 错误标志 | |||
| * ORE 溢出错误 | |||
| * NE 噪声错误 | |||
| * FE 帧错误 | |||
| */ | |||
| 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) | |||
| { | |||
| (void)huart1.Instance->DR; | |||
| } | |||
| } | |||
| /* 清除帧挂起标志 */ | |||
| void PortClearFramePending(void) | |||
| { | |||
| g_modbus_frame_pending = 0U; | |||
| } | |||
| /* 重新启动 USART1 单字节中断接收 */ | |||
| static void RestartReceive(void) | |||
| { | |||
| FlushUartRx(); | |||
| @@ -85,42 +61,29 @@ static void RestartReceive(void) | |||
| (void)HAL_UART_Receive_IT(&huart1, &g_rxByte, 1U); | |||
| } | |||
| /* 初始化链路端口 */ | |||
| void ModbusPortInit(void) | |||
| { | |||
| g_modbus_rx_len = 0U; | |||
| g_modbus_frame_pending = 0U; | |||
| g_modbus_tim_armed = 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(); | |||
| } | |||
| /* 串口接收完成回调:缓存一字节并重启动帧空闲定时器 */ | |||
| void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) | |||
| { | |||
| if (huart->Instance == USART1) | |||
| { | |||
| uint32_t gapTicks; | |||
| /* 点亮接收指示灯 */ | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_RESET); | |||
| if (g_modbus_frame_pending == 0U) | |||
| { | |||
| /* 增加帧内字节之间1.5T判断 */ | |||
| { | |||
| /* 1.5T 字符间隙:判定上一帧已结束,丢弃半包后重新组帧 */ | |||
| if (g_modbus_tim_armed != 0U) | |||
| { | |||
| gapTicks = __HAL_TIM_GET_COUNTER(&htim2); | |||
| if (gapTicks >= (uint32_t)MODBUS_T15_TICKS) | |||
| { | |||
| /* 超过 1.5T 且未到 3.5T:旧帧作废,新字节放到缓冲最前 */ | |||
| g_modbus_rx_len = 0U; | |||
| } | |||
| } | |||
| @@ -136,14 +99,13 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) | |||
| { | |||
| g_modbus_tim_armed = 1U; | |||
| __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); | |||
| } | |||
| } | |||
| /* USART 错误回调:恢复接收,避免永久停收 */ | |||
| void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) | |||
| { | |||
| 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) | |||
| { | |||
| if (htim->Instance == TIM2) | |||
| { | |||
| (void)HAL_TIM_Base_Stop_IT(&htim2); | |||
| g_modbus_tim_armed = 0U; | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_SET); | |||
| if ((g_modbus_rx_sem != (OS_EVENT *)0) && | |||
| (g_modbus_rx_len > 0U) && | |||
| @@ -174,58 +129,48 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) | |||
| (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 crc = 0xFFFFU; /* crc寄存器初始化 */ | |||
| uint16_t crc = 0xFFFFU; | |||
| uint16_t i; | |||
| uint16_t j; | |||
| for (i = 0U; i < len; i++) | |||
| { | |||
| crc ^= data[i]; /* crc 异或当前字节 */ | |||
| crc ^= data[i]; | |||
| 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 | |||
| { | |||
| crc >>= 1; /* LSB为0,直接右移1位 */ | |||
| crc >>= 1; | |||
| } | |||
| } | |||
| } | |||
| return crc; | |||
| } | |||
| /** | |||
| * @brief 阻塞发送一帧完整 ADU | |||
| * @param[in] buf 待发送缓冲,含 CRC | |||
| * @param[in] len 长度字节 | |||
| */ | |||
| void SendData(uint8_t *buf, uint16_t len) | |||
| { | |||
| /* 存放中断屏蔽位快照 */ | |||
| OS_CPU_SR cpu_sr; | |||
| OS_CPU_SR cpu_sr; | |||
| volatile uint32_t turnaround; | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_RESET); | |||
| (void)HAL_UART_AbortReceive_IT(&huart1); | |||
| StopFrameTimer(); | |||
| OS_ENTER_CRITICAL();/* 进入临界区 */ | |||
| OS_ENTER_CRITICAL(); | |||
| g_modbus_rx_len = 0U; | |||
| OS_EXIT_CRITICAL();/* 退出临界区 */ | |||
| OS_EXIT_CRITICAL(); | |||
| (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++) | |||
| { | |||
| } | |||
| FlushUartRx(); | |||
| RestartReceive(); | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_SET); | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /** | |||
| * @file modbus_port.h | |||
| * @brief Modbus RTU 链路端口:UART/TIM 定帧/指示灯/CRC/发送 | |||
| * @brief Modbus RTU 链路端口:UART/TIM 定帧/CRC/发送 | |||
| */ | |||
| #ifndef MODBUS_PORT_H | |||
| @@ -8,22 +8,10 @@ | |||
| #include <stdint.h> | |||
| /** | |||
| * @brief 清除帧挂起标志 | |||
| * @details 此函数在modbus_port.c中定义,在modbus_rtu.h中使用 | |||
| */ | |||
| void PortClearFramePending(void); | |||
| /** | |||
| * @brief 初始化链路端口 | |||
| * @details 复位接收状态,创建帧信号量,熄灭指示灯,启动 USART1 中断接收。 | |||
| */ | |||
| void ModbusPortInit(void); | |||
| /** | |||
| * @brief PF7闪烁约50 ms(CRC/帧错/协议异常) | |||
| * @note 仅在任务上下文调用。 | |||
| */ | |||
| /* 保留空实现,兼容旧调用点(已取消指示灯) */ | |||
| void LedErrorBlink(void); | |||
| #endif | |||
| @@ -2,7 +2,7 @@ | |||
| * @file modbus_rtu.c | |||
| * @author lve | |||
| * @brief Modbus RTU 从站主流程:初始化与从站任务 | |||
| * @version 0.1.1 | |||
| * @version 0.1.2 | |||
| * @date 2026/7/31 | |||
| * @copyright Copyright (c) 2026 | |||
| */ | |||
| @@ -13,21 +13,10 @@ | |||
| #include "modbus_fc.h" | |||
| #include <string.h> | |||
| /* 存储接收帧的副本 */ | |||
| static uint8_t g_modbus_proc_buf[MODBUS_RX_BUF_LEN]; | |||
| /*============================================================================*/ | |||
| /* 从站任务主循环 */ | |||
| /*============================================================================*/ | |||
| /** | |||
| * @brief 从站任务:阻塞等待完整帧,校验后分发处理 | |||
| * @details 最短合法 ADU 为4字节。站号为本机或广播0时处理; | |||
| * 广播可执行写请求但不发送应答。 | |||
| * 若超过设定时间未收到任何已定帧的请求, | |||
| * 点亮 PF9;收到下一帧(含错帧)后熄灭 PF9。 | |||
| * 错帧由PF7闪烁指示。 | |||
| * @param[in] pArg 任务参数(未使用) | |||
| */ | |||
| void ModbusSlaveTask(void *pArg) | |||
| { | |||
| @@ -36,53 +25,21 @@ void ModbusSlaveTask(void *pArg) | |||
| uint16_t crcRecv; | |||
| uint16_t crcCalc; | |||
| uint8_t slaveAddr; | |||
| INT32U lastOkTick; | |||
| INT32U now; | |||
| INT32U elapsed; | |||
| INT16U waitTicks; | |||
| OS_CPU_SR cpu_sr; | |||
| (void)pArg; | |||
| ModbusDataInit(); /* 数据初始化 */ | |||
| ModbusPortInit(); /* 端口初始化 */ | |||
| lastOkTick = OSTimeGet(); | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET); | |||
| ModbusDataInit(); | |||
| ModbusPortInit(); | |||
| 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) | |||
| { | |||
| continue; | |||
| } | |||
| /* 临界区:拷贝一帧并释放接收缓冲供下一帧使用 */ | |||
| OS_ENTER_CRITICAL(); | |||
| frameLen = g_modbus_rx_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); | |||
| g_modbus_rx_len = 0U; | |||
| PortClearFramePending(); /* 清除帧挂起标志 */ | |||
| PortClearFramePending(); | |||
| OS_EXIT_CRITICAL(); | |||
| lastOkTick = OSTimeGet(); | |||
| HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET); | |||
| /* 最短ADU:地址+功能码+CRC(2) = 4字节 */ | |||
| if (frameLen < 4U) | |||
| { | |||
| LedErrorBlink(); | |||
| continue; | |||
| } | |||
| /* CRC:低字节在前、高字节在后 */ | |||
| crcRecv = ((uint16_t)g_modbus_proc_buf[frameLen - 1U] << 8) | | |||
| g_modbus_proc_buf[frameLen - 2U]; | |||
| crcCalc = ModbusCrc16(g_modbus_proc_buf, (uint16_t)(frameLen - 2U)); | |||
| @@ -120,7 +72,6 @@ void ModbusSlaveTask(void *pArg) | |||
| continue; | |||
| } | |||
| /* 地址 0 为广播:可执行写请求,但不回帧 */ | |||
| ReplyContext(slaveAddr, (slaveAddr != 0U) ? 1U : 0U); | |||
| ProcessRequest(g_modbus_proc_buf, frameLen); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||