先看效果👇

STM32 实时输出 3 路波形,VOFA+ 自动刷新:

做 STM32 开发调试时,不想再 printf、看串口助手、看数据表了吗? VOFA+ 能把数据变成波形,实时显示,不用写 PC 程序,5 分钟上手。

下载安装 VOFA+

官网链接:https://www.vofa.plus/

STM32 外设配置(只讲 UART)

uart_tx的Mode要为 Normal

注意,uart的全局中断需要打开,不然会导致串口只发生一次后卡住

VOFA+ 数据格式

VOFA+ 支持三种协议:

协议 特点
RawData 只看字节流,不解析
firewater 类 CSV,可 printf 发送,但解析耗时较大
justfloat(推荐) 小端 float + 帧尾,最省带宽,最适合高频波形绘图

本文使用 justfloat:小端 float 数组 + 0x7F800000(+Inf)帧尾

 

STM32 代码(阻塞 + DMA 两套实现)

/**
 * @file    vofa_uart.c
 * @brief   VOFA+ 串口发送实现(阻塞版 + DMA 版)
 * @details 发送格式固定为:
 *          [N 个 32 位 float(小端)] + [尾标记 +Inf:0x7F800000]
 */
#include "vofa_uart.h"
#include <string.h>
#include <stdint.h>

/**
 * @brief  单帧允许发送的最大 float 个数
 *   缓冲大小 = VOFA_TX_MAX_FLOATS * 4 字节 + 4 字节帧尾
 */
#define VOFA_TX_MAX_FLOATS (64u)

/*==================================================================================================
 * 私有常量与缓冲区
 *================================================================================================*/

/**
 * @brief VOFA+ 帧尾标记(IEEE754 单精度 +Inf)
 *
 * 小端发送字节序:00 00 80 7F
 * VOFA+ 通过该特殊值识别一帧数据的结束位置。
 */
static const uint32_t s_vofa_tail = 0x7F800000u;

/* 单个 float 占用字节数(32 位) */
#define VOFA_TAIL_SIZE (4u)
#define VOFA_FLOAT_SIZE (4u)

/* 最多支持发送的浮点字节数:VOFA_TX_MAX_FLOATS 个 float */
#define VOFA_MAX_DATA_BYTES (VOFA_TX_MAX_FLOATS * VOFA_FLOAT_SIZE)

/**
 * @brief 发送打包缓冲区
 *
 * 用于存放:N 个 float 数据 + 4 字节帧尾。
 * 阻塞发送与 DMA 发送共用该缓冲区,因此:
 * - 不适合在多线程/中断重入环境下同时调用两个接口
 * - 若需重入,请改为每通道/每实例独立缓冲区
 */
static uint8_t s_vofa_tx_buf[VOFA_MAX_DATA_BYTES + VOFA_TAIL_SIZE];

/**
 * @brief  阻塞方式发送:一次打包 N 个 float + 帧尾
 *
 * @param  huart  串口句柄(例如 &huart3)
 * @param  data   指向连续 float 数组的指针
 * @param  n      需要发送的浮点个数(超过 VOFA_TX_MAX_FLOATS 会被截断)
 *
 * @note   内部使用 HAL_UART_Transmit,超时时间固定为 100 ms。
 *         不检查返回值,只做简单 (void) 丢弃,可根据需要自行扩展错误处理。
 */
void VOFA_SendFloats_Blocking(UART_HandleTypeDef *huart,
                              const float *data,
                              uint16_t n)
{
    /* 限制最多发送 VOFA_TX_MAX_FLOATS 个点,防止越界 */
    if (n > VOFA_TX_MAX_FLOATS)
        n = VOFA_TX_MAX_FLOATS;

    /* 计算需要拷贝的浮点数据字节数 */
    uint16_t bytes_data = (uint16_t)(n * VOFA_FLOAT_SIZE);

    /* 拷贝 N 个 float 到发送缓冲(内存连续即可) */
    memcpy(s_vofa_tx_buf, data, bytes_data);

    /* 在数据后紧跟写入 4 字节帧尾标记 */
    *(uint32_t *)&s_vofa_tx_buf[bytes_data] = s_vofa_tail;

    /* 一次性阻塞发送:N 个 float + 帧尾 */
    (void)HAL_UART_Transmit(huart,
                            s_vofa_tx_buf,
                            (uint16_t)(bytes_data + VOFA_TAIL_SIZE),
                            100u);
}

/**
 * @brief  使用 DMA 一次性发送:N 个 float + 帧尾
 *
 * @param  huart  串口句柄(需提前配置好对应 UART + DMA)
 * @param  data   指向连续 float 数组的指针
 * @param  n      需要发送的浮点个数(超过 VOFA_TX_MAX_FLOATS 会被截断)
 *
 * @note   - 若 UART 正在忙(包括上一次 DMA 发送未完成),本函数直接返回,不做等待。
 *         - 内部使用全局缓冲区打包,适合「周期性波形上报」这类简单场景。
 *         - HAL 错误码(包括 HAL_BUSY)在此接口中不向上传递,如需精细处理可自行修改。
 */
void VOFA_SendFloats_DMA(UART_HandleTypeDef *huart,
                         const float *data,
                         uint16_t n)
{
    /* 简单忙检测:避免在 DMA 传输未完成时重入打包缓冲区 */
    if (huart->gState != HAL_UART_STATE_READY)
        return;

    /* 限制最多发送 VOFA_TX_MAX_FLOATS 个点,防止越界 */
    if (n > VOFA_TX_MAX_FLOATS)
        n = VOFA_TX_MAX_FLOATS;

    /* 计算需要拷贝的浮点数据字节数 */
    uint16_t bytes_data = (uint16_t)(n * VOFA_FLOAT_SIZE);

    /* 拷贝 N 个 float 到发送缓冲 */
    memcpy(s_vofa_tx_buf, data, bytes_data);

    /* 在数据后紧跟写入 4 字节帧尾标记 */
    *(uint32_t *)&s_vofa_tx_buf[bytes_data] = s_vofa_tail;

    /* 启动一次 DMA 发送:N 个 float + 帧尾 */
    (void)HAL_UART_Transmit_DMA(huart,
                                s_vofa_tx_buf,
                                (uint16_t)(bytes_data + VOFA_TAIL_SIZE));
}
/**
 * @file    vofa_uart.h
 * @brief   VOFA+ 串口发送接口(极简版)
 *
 * @details 发送数据格式:
 *          [N 个 float(32 位,小端)] + [+Inf 帧尾 0x7F800000]
 *          VOFA+ 端配置为「小端 float + +Inf 结尾」即可直接画波形。
 */
#ifndef VOFA_UART_H
#define VOFA_UART_H

#include "main.h"

/**
 * @brief  阻塞发送:打包 N 个 float + 帧尾后一次性发送
 *
 * @param  huart  串口句柄(如 &huart3)
 * @param  data   连续 float 数组首地址
 * @param  n      浮点个数(> VOFA_TX_MAX_FLOATS 将被截断)
 *
 * @note   适合在主循环或低频调试场景使用,简单可靠但会占用 CPU 等待发送完成。
 */
void VOFA_SendFloats_Blocking(UART_HandleTypeDef *huart,
                              const float *data,
                              uint16_t n);

/**
 * @brief  使用 DMA 发送:打包 N 个 float + 帧尾后启动一次 DMA
 *
 * @param  huart  串口句柄(如 &huart3,需预先使能 UART + DMA)
 * @param  data   连续 float 数组首地址
 * @param  n      浮点个数(> VOFA_TX_MAX_FLOATS 将被截断)
 *
 * @note   - 内部仅做最小化忙检测,不负责 DMA 回调与错误处理。
 *         - 适合在定时中断或调度器中周期性调用,减轻 CPU 发送负担。
 */
void VOFA_SendFloats_DMA(UART_HandleTypeDef *huart,
                         const float *data,
                         uint16_t n);

#endif /* VOFA_UART_H */

使用示例(3 路波形)

示例每秒发送 1000 个点,输出:

  • 正弦波(-1 ~ +1)

  • 三角波(上移到 1 ~ 3

  • 方波(上移到 4 ~ 6

让 3 路波形互不重叠,看起来更清晰。

/* 波形频率(点频率) */
#define SAMPLE_FREQ 1000.0f // 每秒采样 1000 次
#define WAVE_FREQ 5.0f      // 波形频率 5Hz
/* 2π */
#define TWO_PI 6.28318530718f
float t = 0.0f; // 时间(秒)
const float dt = 1.0f / SAMPLE_FREQ;
float buf[3]; // 要发送给 VOFA 的 3 个波形

while (1)
{
    /* ========== 1. 正弦波(-1 ~ +1)========== */
    float sine = sinf(TWO_PI * WAVE_FREQ * t);

    /* ========== 2. 三角波(1 ~ 3)========== */
    float tri_raw = 2.0f * fabsf(2.0f * (t * WAVE_FREQ - floorf(t * WAVE_FREQ + 0.5f))) - 1.0f;
    float tri = tri_raw + 2.0f;

    /* ========== 3. 方波(4 ~ 6)========== */
    float sq_raw = (sinf(TWO_PI * WAVE_FREQ * t) >= 0.0f) ? 1.0f : -1.0f;
    float sq = sq_raw + 5.0f; // 中心从 0 平移到 5 → 范围 4 ~ 6

    /* 写入发送缓冲 */
    buf[0] = sine;
    buf[1] = tri;
    buf[2] = sq;

    /* ========= 发送给 VOFA+ ========= */
    VOFA_SendFloats_DMA(&huart1, buf, 3);
    // VOFA_SendFloats_Blocking(&huart1, buf, 3); // 阻塞方式发送(可选)

    /* 更新时间 */
    t += dt;

    /* 控制发送速率(1ms) */
    HAL_Delay(1);
}

完整工程下载

STM32F407VGT6例程:

https://www.notion.so/ahhh111/5-VOFA-STM32-2aaa84b2a47b801a818ed33de694c264?source=copy_link

打开链接后点击压缩包下载

总结

只需要:

  1. 配 UART

  2. 按 justfloat 格式发 float

  3. 尾部加一个 0x7F800000

STM32 → VOFA+ 的实时绘图就能跑起来。

无打印、无解析、无 GUI 程序,仅靠 UART 即可实现高刷示波效果

想看更多嵌入式干货、工具技巧,也欢迎关注:公众号「单片机那点事」

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐