/* * STM32F103C8T6 firmware core for the new PCB: * - PC <-> STM32: USART1, PA9 TX, PA10 RX, 115200 8N1 * - Linear actuator driver: DRV8870, IN1/IN2 * - Servo outputs: TIM2 CH4/CH3 on PB11/PB10, 50 Hz PWM * * Put this logic into a CubeMX/HAL project. Keep the MX_* init functions * generated by CubeMX, then add the user code below. */ #include "main.h" #include #include #include #include extern UART_HandleTypeDef huart1; extern TIM_HandleTypeDef htim2; /* PCB V1.0 measured netlist. */ #define LM_IN1_GPIO_Port GPIOB #define LM_IN1_Pin GPIO_PIN_12 #define LM_IN2_GPIO_Port GPIOB #define LM_IN2_Pin GPIO_PIN_13 #define SERVO1_TIM htim2 #define SERVO1_CHANNEL TIM_CHANNEL_4 /* PB11 on STM32F103 TIM2_CH4 */ #define SERVO2_TIM htim2 #define SERVO2_CHANNEL TIM_CHANNEL_3 /* PB10 on STM32F103 TIM2_CH3 */ #define UART_RX_BUF_SIZE 96 #define FAILSAFE_MS 1000U static uint8_t rx_byte; static char rx_line[UART_RX_BUF_SIZE]; static uint8_t rx_len = 0; static volatile bool line_ready = false; static uint32_t last_cmd_tick = 0; // ====== 新增:电机运行超时保护变量 ====== static uint32_t motor_start_tick = 0; static uint8_t motor_state = 0; // 0=停止, 1=张开, 2=闭合 static void reply(const char *text) { HAL_UART_Transmit(&huart1, (uint8_t *)text, strlen(text), 100); } static void motor_stop(void) { HAL_GPIO_WritePin(LM_IN1_GPIO_Port, LM_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LM_IN2_GPIO_Port, LM_IN2_Pin, GPIO_PIN_RESET); motor_state = 0; // 新增 motor_start_tick = 0; // 新增 } static void motor_open(void) { HAL_GPIO_WritePin(LM_IN1_GPIO_Port, LM_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LM_IN2_GPIO_Port, LM_IN2_Pin, GPIO_PIN_SET); } static void motor_close(void) { HAL_GPIO_WritePin(LM_IN2_GPIO_Port, LM_IN2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LM_IN1_GPIO_Port, LM_IN1_Pin, GPIO_PIN_SET); } static uint16_t servo_angle_to_us(int angle) { if (angle < 0) { angle = 0; } if (angle > 180) { angle = 180; } /* MG996R/common servo: 500 us to 2500 us at 50 Hz. */ return (uint16_t)(500 + (angle * 2000) / 180); } static void servo_set_angle(uint8_t id, int angle) { uint16_t pulse_us = servo_angle_to_us(angle); if (id == 1) { __HAL_TIM_SET_COMPARE(&SERVO1_TIM, SERVO1_CHANNEL, pulse_us); } else if (id == 2) { __HAL_TIM_SET_COMPARE(&SERVO2_TIM, SERVO2_CHANNEL, pulse_us); } } static void apply_config(uint8_t mode) { switch (mode) { case 0: servo_set_angle(1, 90); servo_set_angle(2, 90); break; case 1: servo_set_angle(1, 30); servo_set_angle(2, 150); break; case 2: servo_set_angle(1, 120); servo_set_angle(2, 60); break; default: break; } } static void handle_command(char *cmd) { last_cmd_tick = HAL_GetTick(); if (strcmp(cmd, "PING") == 0) { reply("OK:PONG\r\n"); return; } if (strcmp(cmd, "M:OPEN") == 0) { motor_open(); reply("OK:M:OPEN\r\n"); return; } if (strcmp(cmd, "M:CLOSE") == 0) { motor_close(); reply("OK:M:CLOSE\r\n"); return; } if (strcmp(cmd, "M:STOP") == 0) { motor_stop(); reply("OK:M:STOP\r\n"); return; } if (strncmp(cmd, "S1:", 3) == 0) { servo_set_angle(1, atoi(cmd + 3)); reply("OK:S1\r\n"); return; } if (strncmp(cmd, "S2:", 3) == 0) { servo_set_angle(2, atoi(cmd + 3)); reply("OK:S2\r\n"); return; } if (strncmp(cmd, "CFG:", 4) == 0) { apply_config((uint8_t)atoi(cmd + 4)); reply("OK:CFG\r\n"); return; } reply("ERR:UNKNOWN\r\n"); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance != USART1) { return; } if (rx_byte == '\n' || rx_byte == '\r') { if (rx_len > 0) { rx_line[rx_len] = '\0'; line_ready = true; rx_len = 0; } } else if (rx_len < UART_RX_BUF_SIZE - 1) { rx_line[rx_len++] = (char)rx_byte; } else { rx_len = 0; reply("ERR:LINE_TOO_LONG\r\n"); } HAL_UART_Receive_IT(&huart1, &rx_byte, 1); } /* * Call this after MX_GPIO_Init(), MX_USART1_UART_Init(), MX_TIM2_Init(). * CubeMX TIM2 recommendation: * channels: PB10 = TIM2_CH3, PB11 = TIM2_CH4 * enable TIM2 remap for PB10/PB11 if CubeMX does not do it automatically * clock = 72 MHz, prescaler = 71, counter period = 19999 * PWM pulse units are microseconds. */ void app_init(void) { motor_stop(); // ====== 强制使能 TIM2 部分重映射(PB10/PB11) ====== // 不依赖 CubeMX 宏定义,直接写寄存器 __HAL_RCC_AFIO_CLK_ENABLE(); // 开启 AFIO 时钟 AFIO->MAPR |= AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2; // 部分重映射 2 HAL_TIM_PWM_Start(&SERVO1_TIM, SERVO1_CHANNEL); HAL_TIM_PWM_Start(&SERVO2_TIM, SERVO2_CHANNEL); servo_set_angle(1, 90); servo_set_angle(2, 90); last_cmd_tick = HAL_GetTick(); HAL_UART_Receive_IT(&huart1, &rx_byte, 1); reply("READY:DRV8870_SERVO\r\n"); } void app_loop(void) { if (line_ready) { char cmd[UART_RX_BUF_SIZE]; __disable_irq(); strncpy(cmd, rx_line, sizeof(cmd)); cmd[sizeof(cmd) - 1] = '\0'; line_ready = false; __enable_irq(); handle_command(cmd); } // 空闲超时停止(原有) if ((HAL_GetTick() - last_cmd_tick) > FAILSAFE_MS) { motor_stop(); } // ====== 新增:电机运行超时保护(3秒) ====== if (motor_state != 0) { if ((HAL_GetTick() - motor_start_tick) > 3000U) { motor_stop(); reply("ERR:MOTOR_TIMEOUT\r\n"); } } } /* * In generated main.c, use: * * int main(void) * { * HAL_Init(); * SystemClock_Config(); * MX_GPIO_Init(); * MX_USART1_UART_Init(); * MX_TIM2_Init(); * app_init(); * while (1) { * app_loop(); * } * } */