feat: 添加协议处理和校验和计算功能,更新 UART 相关代码
This commit is contained in:
@@ -51,6 +51,8 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/easylogger/src/elog.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/easylogger/src/elog_utils.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/easylogger/port/elog_port.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/checksum.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/protocol.c
|
||||
)
|
||||
|
||||
# Add include paths
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "bsp_uart.h"
|
||||
#include "checksum.h"
|
||||
#include "elog.h"
|
||||
#include "protocol.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief 使用 UART1 和 DMA 发送数据到 ESP12F 模块(用于 TCP 透传)
|
||||
@@ -7,28 +11,27 @@
|
||||
* @return HAL_StatusTypeDef: HAL_OK 表示成功,其他值表示失败
|
||||
*/
|
||||
HAL_StatusTypeDef ESP12F_TCP_SendMessage(const char *data) {
|
||||
if (data == NULL) {
|
||||
return HAL_ERROR; // 参数无效
|
||||
}
|
||||
if (data == NULL) {
|
||||
return HAL_ERROR; // 参数无效
|
||||
}
|
||||
|
||||
// 计算数据大小
|
||||
uint16_t size = strlen(data);
|
||||
if (size == 0) {
|
||||
return HAL_ERROR; // 数据为空
|
||||
}
|
||||
// 计算数据大小
|
||||
uint16_t size = strlen(data);
|
||||
if (size == 0) {
|
||||
return HAL_ERROR; // 数据为空
|
||||
}
|
||||
|
||||
// 启动 DMA 传输
|
||||
return HAL_UART_Transmit_DMA(&huart1, (uint8_t *)data, size);
|
||||
// 启动 DMA 传输
|
||||
return HAL_UART_Transmit_DMA(&huart1, (uint8_t *)data, size);
|
||||
}
|
||||
|
||||
|
||||
/* External DMA handle */
|
||||
extern DMA_HandleTypeDef hdma_usart1_rx;
|
||||
|
||||
/* DMA 接收缓冲区及状态变量 */
|
||||
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
|
||||
volatile uint16_t uart1_rx_len = 0; /* 记录最近一次接收到的数据包长度 */
|
||||
volatile uint8_t uart1_rx_flag = 0; /* 接收完成标志位:1表示有一帧新数据 */
|
||||
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
|
||||
volatile uint16_t uart1_rx_len = 0; /* 记录最近一次接收到的数据包长度 */
|
||||
volatile uint8_t uart1_rx_flag = 0; /* 接收完成标志位:1表示有一帧新数据 */
|
||||
|
||||
/**
|
||||
* @brief 初始化 UART1 并启动 DMA 接收
|
||||
@@ -38,14 +41,13 @@ volatile uint8_t uart1_rx_flag = 0; /* 接收完成标志位:1表示
|
||||
* @return 无
|
||||
* @note 需要在调用此函数前确保 UART1 和 DMA 已经正确初始化
|
||||
*/
|
||||
void BSP_UART1_Init(void)
|
||||
{
|
||||
void BSP_UART1_Init(void) {
|
||||
/* 停止并重置 DMA 状态 */
|
||||
HAL_UART_DMAStop(&huart1);
|
||||
HAL_UART_DMAStop(&huart1);
|
||||
uart1_rx_len = 0;
|
||||
uart1_rx_flag = 0;
|
||||
memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));
|
||||
|
||||
|
||||
/* 启动 DMA 接收,将接收到的数据存储到 uart1_rx_buf 缓冲区 */
|
||||
HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART1_RX_BUF_SIZE);
|
||||
|
||||
@@ -66,7 +68,7 @@ void UART_IDLE_Callback(UART_HandleTypeDef *huart) {
|
||||
|
||||
/* 清除空闲中断标志位(HAL库宏) */
|
||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
||||
|
||||
|
||||
/* 停止 DMA 以便安全读取计数器并更新状态 */
|
||||
HAL_UART_DMAStop(huart);
|
||||
|
||||
@@ -75,13 +77,13 @@ void UART_IDLE_Callback(UART_HandleTypeDef *huart) {
|
||||
|
||||
if (recv_len > 0 && recv_len < UART1_RX_BUF_SIZE) {
|
||||
uart1_rx_len = recv_len;
|
||||
uart1_rx_buf[recv_len] = '\0'; /* 添加字符串结束符,方便后续字符串处理 */
|
||||
uart1_rx_flag = 1; /* 置位标志,通知应用层新消息到达 */
|
||||
|
||||
/* 仅供调试:在中断中打印接收到的数据(注意:printf 可能会影响实时性) */
|
||||
elog_raw("UART1 Received: %s\r\n", (char *)uart1_rx_buf);
|
||||
uart1_rx_buf[recv_len] = '\0'; /* 添加字符串结束符,方便后续字符串处理 */
|
||||
uart1_rx_flag = 1; /* 置位标志,通知应用层新消息到达 */
|
||||
|
||||
// 调用协议处理函数,通过消息队列发送接收到的数据
|
||||
Protocol_HandleMessage(uart1_rx_buf, recv_len);
|
||||
}
|
||||
|
||||
/* 重新启动新一轮的 DMA 接收 */
|
||||
HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART1_RX_BUF_SIZE);
|
||||
}
|
||||
}
|
||||
24
Core/Bsp/checksum.c
Normal file
24
Core/Bsp/checksum.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 计算校验和
|
||||
* @param buf: 数据缓冲区指针 (即你的 uart1_rx_buf)
|
||||
* @param start_pos: 校验数据的起始下标 (通常跳过帧头 "LOGI:")
|
||||
* @param length: 需要校验的数据长度 (不包含校验位本身和帧尾)
|
||||
* @return uint8_t: 计算得出的校验和
|
||||
*
|
||||
* 算法说明: 将所有字节相加,取低8位 (相当于 % 256)
|
||||
*/
|
||||
uint8_t Calculate_CheckSum(uint8_t *buf, uint16_t start_pos, uint16_t length)
|
||||
{
|
||||
uint32_t sum = 0; // 使用32位防止累加溢出,虽然uint8累加也不会溢出单片机寄存器
|
||||
uint16_t i;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
sum += buf[start_pos + i];
|
||||
}
|
||||
|
||||
// 返回低8位,相当于 sum % 256
|
||||
return (uint8_t)(sum & 0xFF);
|
||||
}
|
||||
17
Core/Bsp/checksum.h
Normal file
17
Core/Bsp/checksum.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef CHECKSUM_H
|
||||
#define CHECKSUM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 计算校验和
|
||||
* @param buf: 数据缓冲区指针 (即你的 uart1_rx_buf)
|
||||
* @param start_pos: 校验数据的起始下标 (通常跳过帧头 "LOGI:")
|
||||
* @param length: 需要校验的数据长度 (不包含校验位本身和帧尾)
|
||||
* @return uint8_t: 计算得出的校验和
|
||||
*
|
||||
* 算法说明: 将所有字节相加,取低8位 (相当于 % 256)
|
||||
*/
|
||||
uint8_t Calculate_CheckSum(uint8_t *buf, uint16_t start_pos, uint16_t length);
|
||||
|
||||
#endif // CHECKSUM_H
|
||||
126
Core/Bsp/protocol.c
Normal file
126
Core/Bsp/protocol.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @file protocol.c
|
||||
* @brief 协议处理函数实现
|
||||
* @details 该文件包含了协议解析和处理的相关函数,主要用于处理从 ESP12F
|
||||
* 模块接收到的数据。
|
||||
* @author Beihong Wang
|
||||
* @date 2026-04-01
|
||||
*/
|
||||
|
||||
#include "protocol.h"
|
||||
#include "bsp_uart.h"
|
||||
#include "checksum.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "elog.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* 定义日志 TAG */
|
||||
#define Protocol_TAG "Protocol"
|
||||
|
||||
/* 引用在 freertos.c 中定义的消息队列句柄 */
|
||||
extern osMessageQueueId_t CmdQueueHandle;
|
||||
/**
|
||||
* @brief 协议处理函数
|
||||
* @details 严格按照协议文档:校验范围 = 帧头 + 命令 + 数据
|
||||
* 即从下标 0 开始,一直加到最后一个冒号之前
|
||||
*/
|
||||
void Protocol_HandleMessage(uint8_t *data, uint16_t len) {
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
// 1. 基础检查:长度必须足够,且必须以 '#' 结尾
|
||||
if (len < 10 || data[len - 1] != '#') {
|
||||
elog_w(Protocol_TAG, "协议错误:长度不足或帧尾错误 (len: %d)", len);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 寻找校验位前的分隔符
|
||||
// 协议格式:LOGI:CMD:DATA:CS#
|
||||
// 我们需要找到最后一个冒号 ':' 的位置,它前面是数据,后面是校验位
|
||||
int last_colon_pos = -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (data[i] == ':') {
|
||||
last_colon_pos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到冒号,说明格式错误
|
||||
if (last_colon_pos == -1 || last_colon_pos < 5) {
|
||||
elog_w(Protocol_TAG, "协议错误:找不到分隔符 ':' 或位置非法");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 提取接收到的校验位 (从 ASCII 转为 Hex 数值)
|
||||
// 校验位紧跟在 last_colon_pos 之后,长度为 2 字节
|
||||
char recv_cs_hex_str[3] = {0};
|
||||
// 防止越界
|
||||
if (last_colon_pos + 3 >= len) {
|
||||
elog_w(Protocol_TAG, "协议错误:校验位数据越界");
|
||||
return;
|
||||
}
|
||||
|
||||
recv_cs_hex_str[0] = data[last_colon_pos + 1];
|
||||
recv_cs_hex_str[1] = data[last_colon_pos + 2];
|
||||
|
||||
unsigned int received_checksum = 0;
|
||||
sscanf(recv_cs_hex_str, "%02X", &received_checksum);
|
||||
|
||||
// 4. 计算本地校验和
|
||||
// 【核心修改点】
|
||||
// 严格按照协议文档:从下标 0 开始,长度为 last_colon_pos
|
||||
// 也就是计算 "LOGI:SP:080" 的累加和
|
||||
uint8_t calculated_checksum =
|
||||
Calculate_CheckSum(data, 0, (uint16_t)last_colon_pos);
|
||||
|
||||
// 5. 对比校验和
|
||||
if (calculated_checksum == (uint8_t)received_checksum) {
|
||||
elog_i(Protocol_TAG, "✅ 校验通过!执行指令: %s", (char *)data);
|
||||
|
||||
/* 提取有效载荷发送到消息队列 */
|
||||
char cmd_payload[16] = {0};
|
||||
// 将 "LOGI:" 之后到最后一个冒号之前的内容作为指令
|
||||
uint16_t payload_len = last_colon_pos - 5;
|
||||
uint16_t copy_len = (payload_len > 15) ? 15 : payload_len;
|
||||
if (copy_len > 0) {
|
||||
memcpy(cmd_payload, &data[5], copy_len);
|
||||
}
|
||||
|
||||
osStatus_t status = osMessageQueuePut(CmdQueueHandle, cmd_payload, 0, 0);
|
||||
if (status != osOK) {
|
||||
elog_e(Protocol_TAG, "Protocol: Queue put failed: %d", status);
|
||||
}
|
||||
} else {
|
||||
elog_w(Protocol_TAG, "❌ 校验失败!计算值: 0x%02X, 接收值: 0x%02X",
|
||||
calculated_checksum, (uint8_t)received_checksum);
|
||||
|
||||
// 辅助调试:打印实际参与计算的数据段
|
||||
char debug_buf[32] = {0};
|
||||
if (last_colon_pos < 32) {
|
||||
memcpy(debug_buf, data, last_colon_pos);
|
||||
elog_i(Protocol_TAG, " -> 单片机正在计算这段数据的校验和: [%s]",
|
||||
debug_buf);
|
||||
elog_i(Protocol_TAG, " -> 请检查上位机是否也是按照此范围计算累加和");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CarCtrlTask_TAG "CarCtrlTask"
|
||||
|
||||
void CarCtrl_Task(void *argument) {
|
||||
/* USER CODE BEGIN CarCtrl_Task */
|
||||
char cmd_payload[16];
|
||||
/* Infinite loop */
|
||||
for (;;) {
|
||||
/* 从消息队列中获取数据,阻塞等待 */
|
||||
if (osMessageQueueGet(CmdQueueHandle, cmd_payload, NULL, osWaitForever) ==
|
||||
osOK) {
|
||||
elog_i(CarCtrlTask_TAG, "CarCtrl: Received command (ASCII: %s) from queue",
|
||||
cmd_payload);
|
||||
/* 可以在这里添加根据 cmd_payload 控制小车的逻辑 */
|
||||
}
|
||||
// osDelay(1); // osMessageQueueGet 已经是阻塞的,不需要额外的 osDelay
|
||||
}
|
||||
/* USER CODE END CarCtrl_Task */
|
||||
}
|
||||
16
Core/Bsp/protocol.h
Normal file
16
Core/Bsp/protocol.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef PROTOCOL_H
|
||||
#define PROTOCOL_H
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**
|
||||
* @brief 协议处理函数,解析接收到的数据并通过消息队列发送命令
|
||||
* @param data: 接收到的原始数据指针
|
||||
* @param len: 数据长度
|
||||
*/
|
||||
void Protocol_HandleMessage(uint8_t *data, uint16_t len);
|
||||
|
||||
|
||||
void CarCtrl_Task(void *argument);
|
||||
|
||||
#endif /* PROTOCOL_H */
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "bsp_beep.h"
|
||||
#include "usart.h"
|
||||
#include "elog.h"
|
||||
#include "protocol.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
@@ -57,6 +58,18 @@ const osThreadAttr_t initTask_attributes = {
|
||||
.stack_size = 128 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
/* Definitions for CarCtrlTask */
|
||||
osThreadId_t CarCtrlTaskHandle;
|
||||
const osThreadAttr_t CarCtrlTask_attributes = {
|
||||
.name = "CarCtrlTask",
|
||||
.stack_size = 256 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
/* Definitions for CmdQueue */
|
||||
osMessageQueueId_t CmdQueueHandle;
|
||||
const osMessageQueueAttr_t CmdQueue_attributes = {
|
||||
.name = "CmdQueue"
|
||||
};
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
@@ -73,6 +86,7 @@ PUTCHAR_PROTOTYPE {
|
||||
/* USER CODE END FunctionPrototypes */
|
||||
|
||||
void StartDefaultTask(void *argument);
|
||||
void CarCtrl_Task(void *argument);
|
||||
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
@@ -103,6 +117,10 @@ void MX_FREERTOS_Init(void) {
|
||||
/* start timers, add new ones, ... */
|
||||
/* USER CODE END RTOS_TIMERS */
|
||||
|
||||
/* Create the queue(s) */
|
||||
/* creation of CmdQueue */
|
||||
CmdQueueHandle = osMessageQueueNew (16, 16, &CmdQueue_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_QUEUES */
|
||||
/* add queues, ... */
|
||||
/* USER CODE END RTOS_QUEUES */
|
||||
@@ -111,6 +129,9 @@ void MX_FREERTOS_Init(void) {
|
||||
/* creation of initTask */
|
||||
initTaskHandle = osThreadNew(StartDefaultTask, NULL, &initTask_attributes);
|
||||
|
||||
/* creation of CarCtrlTask */
|
||||
CarCtrlTaskHandle = osThreadNew(CarCtrl_Task, NULL, &CarCtrlTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* add threads, ... */
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
@@ -134,24 +155,35 @@ void StartDefaultTask(void *argument)
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
const char *message = "Hello, ESP12F! This is a test message.";
|
||||
HAL_StatusTypeDef status = ESP12F_TCP_SendMessage(message);
|
||||
if (status == HAL_OK) {
|
||||
HAL_GPIO_WritePin(RUN_LED_GPIO_Port, RUN_LED_Pin, GPIO_PIN_SET);
|
||||
BEEP_On();
|
||||
osDelay(50);
|
||||
BEEP_Off();
|
||||
osDelay(20);
|
||||
|
||||
} else {
|
||||
HAL_GPIO_WritePin(RUN_LED_GPIO_Port, RUN_LED_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);
|
||||
|
||||
osDelay(1000);
|
||||
|
||||
}
|
||||
/* USER CODE END StartDefaultTask */
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_CarCtrl_Task */
|
||||
/**
|
||||
* @brief Function implementing the CarCtrlTask thread.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_CarCtrl_Task */
|
||||
__weak void CarCtrl_Task(void *argument)
|
||||
{
|
||||
/* USER CODE BEGIN CarCtrl_Task */
|
||||
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
|
||||
osDelay(1); // osMessageQueueGet 已经是阻塞的,不需要额外的 osDelay
|
||||
}
|
||||
/* USER CODE END CarCtrl_Task */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
|
||||
130
README.md
130
README.md
@@ -177,3 +177,133 @@ void UART_IDLE_Callback(UART_HandleTypeDef *huart) {
|
||||
const char *message = "Hello, ESP12F! This is a test message.";
|
||||
HAL_StatusTypeDef status = ESP12F_TCP_SendMessage(message);
|
||||
```
|
||||
|
||||
|
||||
这些是上位机发送的指令:
|
||||
按照这些来写代码
|
||||
|
||||
这是一个基于 TCP/IP 局域网通信的物流小车控制指令协议设计方案。
|
||||
|
||||
为了方便调试和开发,本协议采用 **ASCII 文本格式**(类似于 Modbus ASCII 或简单的串口透传格式),而不是二进制格式。这样你可以直接使用网络调试助手(如 NetAssist)手动发送字符串来测试小车,而无需编写专门的上位机软件。
|
||||
|
||||
---
|
||||
|
||||
### 📡 通信基础参数
|
||||
|
||||
- **通信方式**:TCP Client (上位机) 连接 TCP Server (单片机/小车)
|
||||
- **数据格式**:ASCII 字符串
|
||||
- **换行符**:建议使用 `\r\n` (回车+换行) 作为每条指令的结束标志,以便单片机解析。
|
||||
- **字节序**:N/A (文本协议不涉及大小端问题)
|
||||
|
||||
---
|
||||
|
||||
### 📦 指令帧结构
|
||||
|
||||
每条指令由以下几个部分组成,字段之间用英文冒号 `:` 分隔:
|
||||
|
||||
`[帧头][命令字][数据内容][校验和][帧尾]`
|
||||
|
||||
- **帧头**:固定为 `LOGI` (代表 Logistics),用于快速识别有效数据包。
|
||||
- **命令字**:2位字符,代表具体操作(如 `GS` 代表去站点)。
|
||||
- **数据内容**:具体的参数,长度可变。
|
||||
- **校验和**:2位十六进制数,用于验证数据完整性(防止丢包或乱码)。
|
||||
- **帧尾**:固定为 `#`。
|
||||
|
||||
---
|
||||
|
||||
### 📝 具体控制指令定义
|
||||
|
||||
以下是针对你提出的四个需求(去站点、停止、启动、速度)的具体指令格式。
|
||||
|
||||
#### 1. 去往指定站点
|
||||
- **功能**:指示小车移动到编号为 N 的站点。
|
||||
- **指令格式**:`LOGI:GS:NNN:CS#`
|
||||
- **参数说明**:
|
||||
- `GS`: 命令字 (Go to Station)。
|
||||
- `NNN`: 站点编号,3位数字,不足补0。例如:1号站写为 `001`,12号站写为 `012`。
|
||||
- `CS`: 校验和。
|
||||
- **示例**:
|
||||
- 去往 **5号站点**:`LOGI:GS:005:15#` (假设校验和计算结果为15)
|
||||
|
||||
#### 2. 启动运行
|
||||
- **功能**:让处于停止或待机状态的小车开始执行任务或继续运行。
|
||||
- **指令格式**:`LOGI:ST:RUN:CS#`
|
||||
- **参数说明**:
|
||||
- `ST`: 命令字 (Start/Status)。
|
||||
- `RUN`: 固定参数,表示启动。
|
||||
- **示例**:
|
||||
- 启动小车:`LOGI:ST:RUN:2A#`
|
||||
|
||||
#### 3. 紧急停止/暂停
|
||||
- **功能**:立即停止小车的运动,通常用于急停或到达站点后的确认暂停。
|
||||
- **指令格式**:`LOGI:ST:STOP:CS#`
|
||||
- **参数说明**:
|
||||
- `STOP`: 固定参数,表示停止。
|
||||
- **示例**:
|
||||
- 停止小车:`LOGI:ST:STOP:32#`
|
||||
|
||||
#### 4. 设置运行速度
|
||||
- **功能**:动态调整小车的行驶速度。
|
||||
- **指令格式**:`LOGI:SP:VVV:CS#`
|
||||
- **参数说明**:
|
||||
- `SP`: 命令字 (Set Speed)。
|
||||
- `VVV`: 速度值,3位数字 (000-100),代表百分比或具体PWM占空比等级。
|
||||
- `000`: 停止
|
||||
- `050`: 50% 速度
|
||||
- `100`: 全速
|
||||
- **示例**:
|
||||
- 设置速度为 **80%**:`LOGI:SP:080:04#`
|
||||
|
||||
---
|
||||
|
||||
### 🧮 校验和算法
|
||||
|
||||
为了防止无线信号干扰导致指令错误,我们需要一个简单的校验和。
|
||||
|
||||
- **算法**:将 **帧头** 到 **数据内容** 结束的所有字符的 ASCII 码值相加,然后对 256 取余,最后转换为 2位十六进制字符串。
|
||||
- **公式**:`Sum = (Byte1 + Byte2 + ... + ByteN) % 256`
|
||||
|
||||
**举例计算 (去往 1 号站点):**
|
||||
1. 原始字符串:`LOGI:GS:001`
|
||||
2. ASCII 码值相加:
|
||||
- 'L'(76) + 'O'(79) + 'G'(71) + 'I'(73) + ':'(58) + 'G'(71) + 'S'(83) + ':'(58) + '0'(48) + '0'(48) + '1'(49)
|
||||
- 总和 = 614
|
||||
3. 取余:`614 % 256 = 102`
|
||||
4. 转十六进制:`102` -> `66`
|
||||
5. 最终发送指令:`LOGI:GS:001:66#`
|
||||
|
||||
---
|
||||
|
||||
### 💬 小车回复机制 (可选但推荐)
|
||||
|
||||
单片机执行指令后,应向上位机返回执行结果,以便上位机显示状态。
|
||||
|
||||
**回复格式**:`[命令字]:[状态码]:[描述]#`
|
||||
|
||||
- **状态码定义**:
|
||||
- `OK`: 指令接收正确并执行。
|
||||
- `ERR`: 指令格式错误或校验失败。
|
||||
- `BUSY`: 小车正在忙,无法执行新指令。
|
||||
|
||||
**示例回复**:
|
||||
- 成功去往站点:`GS:OK:Arrived#`
|
||||
- 速度设置成功:`SP:OK:SpeedSet#`
|
||||
- 校验错误:`CMD:ERR:CheckSum#`
|
||||
|
||||
---
|
||||
|
||||
### 📌 总结清单
|
||||
|
||||
你可以直接将下表发给单片机开发人员:
|
||||
|
||||
| 功能 | 指令模板 | 示例 (假设校验和为 XX) | 说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **去站点** | `LOGI:GS:NNN:XX#` | `LOGI:GS:003:XX#` | NNN为3位站点号 |
|
||||
| **启动** | `LOGI:ST:RUN:XX#` | `LOGI:ST:RUN:XX#` | 开始运动 |
|
||||
| **停止** | `LOGI:ST:STOP:XX#` | `LOGI:ST:STOP:XX#` | 立即停止 |
|
||||
| **设速度** | `LOGI:SP:VVV:XX#` | `LOGI:SP:050:XX#` | VVV为0-100 |
|
||||
|
||||
|
||||
我创建了 处理解析指令的任务和传递消息的消息队列。
|
||||

|
||||
|
||||
|
||||
@@ -40,8 +40,9 @@ Dma.USART2_TX.2.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.USART2_TX.2.Priority=DMA_PRIORITY_LOW
|
||||
Dma.USART2_TX.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
FREERTOS.FootprintOK=true
|
||||
FREERTOS.IPParameters=Tasks01,FootprintOK
|
||||
FREERTOS.Tasks01=initTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL
|
||||
FREERTOS.IPParameters=Tasks01,FootprintOK,Queues01
|
||||
FREERTOS.Queues01=CmdQueue,16,16,1,Dynamic,NULL,NULL
|
||||
FREERTOS.Tasks01=initTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;CarCtrlTask,24,256,CarCtrl_Task,As weak,NULL,Dynamic,NULL,NULL
|
||||
File.Version=6
|
||||
GPIO.groupedBy=Group By Peripherals
|
||||
KeepUserPlacement=false
|
||||
|
||||
BIN
image-6.png
Normal file
BIN
image-6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
@@ -1 +1 @@
|
||||
0ИеИе
|
||||
0hhh
|
||||
Reference in New Issue
Block a user