This commit is contained in:
2026-07-16 15:24:40 +08:00
parent 22ae87f4b4
commit feed450554
32 changed files with 6634 additions and 95 deletions

View File

@ -0,0 +1,248 @@
/*
* 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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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();
* }
* }
*/