Skip to content

Repository files navigation

VOFA 移植说明

本文说明如何把本目录下的 VOFA 串口组件移植到其他 STM32 HAL 工程中。

功能概览

当前组件提供:

  • 通过 UART DMA 环形缓冲发送数据
  • 通过 UART ReceiveToIdle DMA 接收数据
  • \r\n 组装一帧下行命令
  • 支持 VOFA+ JustFloat 格式发送 6 个 float
  • 支持简单的 key=value 命令解析,例如 VOFA 发送控件格式 kp=%.3f,帧尾选择 \n

文件组成

需要移植的文件:

vofa/
├── vofa.c
└── vofa.h

vofa.c 加入工程编译,把 vofa.h 所在目录加入头文件搜索路径。

VOFA+ 配置导入

仓库根目录提供了两个 VOFA+ 配置文件:

vofa+.tabviews.json
vofa+.cmds.json

使用 VOFA+ 上位机时,建议先导入这两个配置文件:

  1. 导入 vofa+.tabviews.json,用于恢复波形/标签页视图配置。
  2. 导入 vofa+.cmds.json,用于恢复下发命令配置。

导入后可以直接使用预设的窗口波形显示和 kp=%.3f 等命令发送格式。

CubeMX 外设配置

以 STM32 HAL 工程为例,需要先在 CubeMX 中配置好 UART 和 DMA:

  1. 开启一个 UART,例如 USART1。
  2. 配置 UART 波特率,例如 115200 或更高。
  3. 开启 UART TX DMA。
  4. 开启 UART RX DMA。
  5. 开启 UART 全局中断。
  6. 生成代码后确认工程中已初始化 MX_DMA_Init()MX_USARTx_UART_Init()

初始化顺序建议如下:

MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
Vofa_Init(&huart1);

如果使用其他串口,例如 huart2,只需要改成:

Vofa_Init(&huart2);

组件内部的 HAL 回调会通过 Vofa_Init() 传入的 UART 句柄判断目标串口,不再依赖固定的 huart1

平台对接宏

平台相关内容集中在 vofa.h 顶部:

#include "stm32f4xx_hal.h"

#define VOFA_ENTER_CRITICAL()               __disable_irq()
#define VOFA_EXIT_CRITICAL()                __enable_irq()
#define VOFA_UART_DMA_SEND(huart, buf, len) HAL_UART_Transmit_DMA((UART_HandleTypeDef *)(huart), (uint8_t *)(buf), (len))

移植到其他 STM32 系列时,通常只需要把 HAL 头文件改成对应芯片系列,例如:

#include "stm32h7xx_hal.h"

如果不是 STM32 HAL 平台,需要适配这 3 个宏:

  • VOFA_ENTER_CRITICAL():进入临界区,防止环形缓冲状态被中断打断
  • VOFA_EXIT_CRITICAL():退出临界区
  • VOFA_UART_DMA_SEND(huart, buf, len):启动一次 UART DMA 发送

缓冲区大小

默认缓冲区在 vofa.c 中定义:

uint8_t tx_ring[512];
uint8_t rx_frame[128];
uint8_t dma_rx[128];

含义如下:

  • tx_ring:发送环形缓冲区,发送数据量大时可以适当增大
  • rx_frame:接收命令帧缓冲区,必须能容纳一整条命令
  • dma_rx:UART RX DMA 临时缓冲区

如果 VOFA 发送频率高、波特率低,建议增大 tx_ring,避免 Vofa_Write() 因缓冲区满而丢弃部分数据。

在主程序中使用

main.c 中包含头文件:

#include "vofa.h"

初始化外设后调用:

Vofa_Init(&huart1);

定义好你要接收的变量

static void VofaCmdHandler(const char *str)
{
    float value;

    if (Vofa_GetKeyFloat(str, "kp=", &value))
    {
        // 使用 value
    }
}

在主循环中轮询接收命令并发送数据:

while (1)
{
    Vofa_ReceivePoll(VofaCmdHandler);
    Vofa_SendFloat(v1, v2, v3, v4, v5, v6);
    HAL_Delay(10);
}

发送数据

JustFloat 发送

Vofa_SendFloat() 固定发送 6 个 float,格式为 VOFA+ JustFloat

Vofa_SendFloat(ch1, ch2, ch3, ch4, ch5, ch6);

VOFA+ 上位机中选择 JustFloat 协议,即可查看波形。

原始字节发送

如果需要发送自定义协议,可以直接使用:

const uint8_t data[] = {0x01, 0x02, 0x03};
Vofa_Write(data, sizeof(data));

Vofa_Write() 会把数据写入发送环形缓冲区,并自动启动 DMA 发送。

接收命令

组件按 \r\n 判断一帧结束。VOFA+ 下发命令时可以使用发送控件,数据格式填写:

kp=%.3f

帧尾填写:

\n

发送时 VOFA 会把输入数值格式化成类似 kp=1.200\n 的文本帧,单片机会解析 kp= 后面的浮点数。

其他参数可以按同样方式添加发送控件,例如:

ki=%.3f
kd=%.3f
target=%.3f

主循环中调用:

Vofa_ReceivePoll(VofaCmdHandler);

命令解析辅助函数:

float f;
int i;

if (Vofa_GetKeyFloat(str, "kp=", &f))
{
    // 解析到浮点值
}

if (Vofa_GetKeyInt(str, "mode=", &i))
{
    // 解析到整数值
}

HAL 回调要求

vofa.c 已实现两个 HAL 回调:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);

如果你的工程中其他文件已经实现了同名回调,不能重复定义。需要把 VOFA 相关逻辑合并到已有回调中:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    // 保留已有逻辑

    if (huart == (UART_HandleTypeDef *)g_vofa.huart)
    {
        VOFA_ENTER_CRITICAL();
        uint16_t sent = g_vofa.tx_dma_len;
        g_vofa.tx_tail = (g_vofa.tx_tail + sent) % g_vofa.tx_ring_size;
        g_vofa.tx_dma_busy = false;
        VOFA_EXIT_CRITICAL();
        Vofa_TxKick();
    }
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    // 保留已有逻辑

    if (huart == (UART_HandleTypeDef *)g_vofa.huart)
    {
        for (uint16_t i = 0; i < Size; i++)
        {
            VofaSerial_PushRxByte(&g_vofa, dma_rx[i]);
        }
        HAL_UARTEx_ReceiveToIdle_DMA(huart, dma_rx, sizeof(dma_rx));
    }
}

常见问题

编译提示找不到 stm32f4xx_hal.h

说明目标工程不是 STM32F4,或 HAL 头文件路径不同。修改 vofa.h 顶部的 HAL 头文件,例如:

#include "stm32g4xx_hal.h"

编译提示回调重复定义

说明工程中已经有 HAL_UART_TxCpltCallback()HAL_UARTEx_RxEventCallback()。删除其中一份定义,或把 VOFA 逻辑合并到现有回调中。

VOFA 收不到数据

检查:

  • 是否调用了 Vofa_Init(&huartx)
  • 是否开启 UART RX DMA
  • 是否开启 UART 全局中断
  • 是否在回调中重新调用了 HAL_UARTEx_ReceiveToIdle_DMA()
  • 上位机串口号、波特率、数据位、停止位是否一致

波形卡顿或丢数据

检查:

  • 波特率是否太低
  • Vofa_SendFloat() 调用频率是否太高
  • tx_ring 是否需要增大
  • Vofa_Write() 返回值是否小于待发送长度

最小移植清单

  1. 复制 vofa.cvofa.h
  2. vofa.c 加入编译。
  3. vofa.h 所在目录加入 include 路径。
  4. 配置 UART TX DMA、RX DMA 和 UART 中断。
  5. 根据芯片系列修改 vofa.h 中的 HAL 头文件。
  6. 在外设初始化后调用 Vofa_Init(&huartx)
  7. 在主循环中调用 Vofa_ReceivePoll() 和需要的发送函数。
  8. 如已有 HAL UART 回调,合并 VOFA 回调逻辑。
  9. 在 VOFA+ 上位机中导入根目录的 vofa+.tabviews.jsonvofa+.cmds.json

反馈与 Issue

如果移植过程中遇到问题,或发现文档/代码有需要改进的地方,欢迎提交 Issue 交流。

About

给vofa的双向通信代码(Justfloat)便于调参

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages