feat: 添加小车控制指令解析和校验和计算工具

This commit is contained in:
2026-04-03 21:20:30 +08:00
parent 1cc8327f13
commit e382b06b03
4 changed files with 198 additions and 3 deletions

View File

@@ -8,6 +8,7 @@
*/ */
#include "protocol.h" #include "protocol.h"
#include "bsp_motor.h"
#include "bsp_uart.h" #include "bsp_uart.h"
#include "checksum.h" #include "checksum.h"
#include "cmsis_os.h" #include "cmsis_os.h"
@@ -19,6 +20,129 @@
/* 定义日志 TAG */ /* 定义日志 TAG */
#define Protocol_TAG "Protocol" #define Protocol_TAG "Protocol"
/* 小车控制状态相关定义 */
#define CAR_PWM_MAX 3599U // PWM最大值对应100%
static uint8_t car_running = 0; // 小车运行状态1=运行0=停止)
static uint8_t car_speed_percent = 0; // 当前整体速度百分比0~100
static uint16_t car_target_station = 0; // 目标站点编号
/**
* @brief 百分比转PWM值
* @param percent 速度百分比0~100
* @return int16_t PWM占空比
*/
static int16_t CarCtrl_PercentToPwm(uint8_t percent)
{
if (percent > 100U) {
percent = 100U;
}
return (int16_t)((CAR_PWM_MAX * percent) / 100U);
}
/**
* @brief 停止所有电机
*/
static void CarCtrl_StopAll(void)
{
motor_stop(MOTOR_1);
motor_stop(MOTOR_2);
motor_stop(MOTOR_3);
motor_stop(MOTOR_4);
}
/**
* @brief 按当前 car_speed_percent 设置所有电机速度
*/
static void CarCtrl_ApplySpeed(void)
{
int16_t pwm = CarCtrl_PercentToPwm(car_speed_percent);
motor_set_speed(MOTOR_1, pwm);
motor_set_speed(MOTOR_2, pwm);
motor_set_speed(MOTOR_3, pwm);
motor_set_speed(MOTOR_4, pwm);
}
/**
* @brief 解析并执行协议命令
* @param cmd_payload 队列传入的命令字符串(如 "ST:RUN"、"SP:080"、"GS:005"
*/
static void CarCtrl_HandleCommand(const char *cmd_payload)
{
const char *arg = NULL;
if (cmd_payload == NULL || cmd_payload[0] == '\0') {
return;
}
// 查找冒号分隔符,分割命令类型和参数
arg = strchr(cmd_payload, ':');
if (arg == NULL) {
elog_w(Protocol_TAG, "未知控制指令: %s", cmd_payload);
return;
}
// 启动/停止命令(仅作为自动循迹任务的启动/暂停信号,不直接控制电机)
if (strncmp(cmd_payload, "ST", 2) == 0) {
if (strcmp(arg + 1, "RUN") == 0) {
car_running = 1;
elog_i(Protocol_TAG, "小车自动循迹启动, speed=%u%%, station=%u", car_speed_percent,
car_target_station);
} else if (strcmp(arg + 1, "STOP") == 0) {
car_running = 0;
elog_i(Protocol_TAG, "小车自动循迹暂停");
} else {
elog_w(Protocol_TAG, "未知启动/停止命令: %s", cmd_payload);
}
return;
}
// 设置整体速度命令
if (strncmp(cmd_payload, "SP", 2) == 0) {
unsigned int speed = 0;
// 解析速度参数百分比0~100
if (sscanf(arg + 1, "%u", &speed) == 1) {
if (speed > 100U) {
speed = 100U;
}
car_speed_percent = (uint8_t)speed;
elog_i(Protocol_TAG, "设置整体速度: %u%%", car_speed_percent);
// 如果当前处于运行状态,立即生效
if (car_running != 0U) {
CarCtrl_ApplySpeed();
}
} else {
elog_w(Protocol_TAG, "速度参数解析失败: %s", cmd_payload);
}
return;
}
// 设置目标站点命令
if (strncmp(cmd_payload, "GS", 2) == 0) {
unsigned int station = 0;
// 解析站点编号0~999
if (sscanf(arg + 1, "%u", &station) == 1) {
if (station > 999U) {
station = 999U;
}
car_target_station = (uint16_t)station;
elog_i(Protocol_TAG, "设置目标站点: %03u", car_target_station);
} else {
elog_w(Protocol_TAG, "站点参数解析失败: %s", cmd_payload);
}
return;
}
// 未知命令类型
elog_w(Protocol_TAG, "未支持的控制命令: %s", cmd_payload);
}
/* 引用在 freertos.c 中定义的消息队列句柄 */ /* 引用在 freertos.c 中定义的消息队列句柄 */
extern osMessageQueueId_t CmdQueueHandle; extern osMessageQueueId_t CmdQueueHandle;
/** /**
@@ -110,7 +234,7 @@ void Protocol_HandleMessage(uint8_t *data, uint16_t len) {
void CarCtrl_Task(void *argument) { void CarCtrl_Task(void *argument) {
/* USER CODE BEGIN CarCtrl_Task */ /* USER CODE BEGIN CarCtrl_Task */
char cmd_payload[16]; char cmd_payload[16] = {0};
/* Infinite loop */ /* Infinite loop */
for (;;) { for (;;) {
/* 从消息队列中获取数据,阻塞等待 */ /* 从消息队列中获取数据,阻塞等待 */
@@ -118,7 +242,7 @@ void CarCtrl_Task(void *argument) {
osOK) { osOK) {
elog_i(CarCtrlTask_TAG, "CarCtrl: Received command (ASCII: %s) from queue", elog_i(CarCtrlTask_TAG, "CarCtrl: Received command (ASCII: %s) from queue",
cmd_payload); cmd_payload);
/* 可以在这里添加根据 cmd_payload 控制小车的逻辑 */ CarCtrl_HandleCommand(cmd_payload);
} }
// osDelay(1); // osMessageQueueGet 已经是阻塞的,不需要额外的 osDelay // osDelay(1); // osMessageQueueGet 已经是阻塞的,不需要额外的 osDelay
} }

BIN
物流小车/app.exe Normal file

Binary file not shown.

72
物流小车/checksum.py Normal file
View File

@@ -0,0 +1,72 @@
"""
物流小车控制指令 - 校验和算法工具(交互式)
用法:
python checksum.py # 启动交互模式,手动输入指令计算校验和
"""
import sys
import io
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
def calc_checksum(data: str) -> str:
"""计算校验和ASCII码求和 % 256 → 2位十六进制"""
total = sum(ord(c) for c in data)
return format(total % 256, '02X')
def print_ascii_detail(data: str):
"""打印每个字符的 ASCII 码值"""
print(f"\n 原始字符串: {data}")
print(f" ASCII 码分解:")
total = 0
parts = []
for c in data:
val = ord(c)
total += val
parts.append(f"'{c}'({val})")
print(" " + " + ".join(parts))
print(f" 总和 = {total}")
print(f" 取余 = {total} % 256 = {total % 256}")
print(f" 十六进制 = {calc_checksum(data)}")
def main():
print("\n" + "=" * 55)
print(" 物流小车指令校验和工具(交互式)")
print(" 输入原始字符串,自动计算校验和并生成完整指令")
print(" 输入 q 退出")
print("=" * 55)
while True:
print(f"\n{'' * 55}")
raw = input("\n 请输入指令内容(如 LOGI:GS:001: ").strip()
if raw.lower() in ('q', 'quit', 'exit'):
print("\n 再见!\n")
break
if not raw:
continue
# 如果用户输入的不是以 LOGI: 开头,自动拼接
if not raw.upper().startswith('LOGI:'):
print(f"\n [提示] 检测到未包含帧头,已自动添加 LOGI:")
raw = 'LOGI:' + raw
cs = calc_checksum(raw)
full_cmd = f"{raw}:{cs}#"
# 显示计算过程
print_ascii_detail(raw)
print(f"\n >>> 完整指令: {full_cmd}")
if __name__ == '__main__':
main()

View File

@@ -1 +0,0 @@
0hhh