Files
Handcontrol_LYT_Version_Win…/stm32f103_drv8870_servo_main.c
2026-07-16 15:24:40 +08:00

249 lines
6.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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();
* }
* }
*/