Vofa上位机发送float数据(justfloat协议)

justfloat | VOFA-Plus上位机

0.小端浮点数

本协议是小端浮点数组形式的字节流协议,纯十六进制浮点传输,节省带宽。此协议非常适合用在通道数量多、发送频率高的时候。

0.0 问题:

  • 发什么?
    小端浮点数

  • 什么是小端浮点数?
    小端和大端差别在于:字节存储顺序

    (以32位float 0x12345678为例)

    端序类型 内存地址增长方向(低→高) 实际存储顺序
    大端序 12 34 56 78 高位字节在前(人类直观顺序)
    小端序 78 56 34 12 低位字节在前(反向存储)
bf 10 59 3f	//单精度浮点数发送
			//低位字节在前先发

0.1单精度浮点数float(补充,不究原理可以不看)

0.1.1 IEEE-754

IEEE-754(IEEE二进制浮点数算术标准)标准规定了

EEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。

0.1.2 单精度浮点数表示方式

IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU浮点运算器所采用。

image

$$N=(-1)^s \times (1 +{ \frac{M}{2^{23}} }) \times 2^{E-127}$$

M 2 23 为小数部分 \frac{M}{2^{23}}为小数部分 223M为小数部分

例如:

$$(0.0625_{10} = (0.0001)_{2} = (-1)^0 \times (1 + \frac{0}{2^{23}}) \times 2^{123-127}$$

$$S = 0 \\M = 0 \\ E = 123$$

image

image

由此,我们明显可以看出,浮点数的表示除了满足2的整数倍之类的情况总是存在误差的。

1. DEMO 程序说明

核心:发送的单精度浮点数转变为4个字节Byte数据发送,每一帧数据后需接包尾 00 00 80 7f

1.1 mid_debug_uart.h

  1. 用户数据
//需要添加多少数据,就往enum里加
typedef enum
{
	D1=0,
	D2,
	DATA_COUNT_MAX
}Data_Index;
  1. 数据包结构体
typedef struct
{
    float fdata[DATA_COUNT_MAX];
    uint8_t tail[4];
}Vofa_Packet;

extern Vofa_Packet Vofa_packet;	

1.2 mid_debug_uart.c

定义发送的数据

Vofa_Packet Vofa_packet;	
  1. 包尾初始化
/**
 * @brief:	初始化包尾
 * @note:   Must to use Vofa 
 */
void Vofa_Init(Vofa_Packet *frame)
{
    frame->tail[0] = 0x00;
    frame->tail[1] = 0x00;
    frame->tail[2] = 0x80;
    frame->tail[3] = 0x7f;
}
  1. 发送浮点数
/**
 * @brief:Vofa_SendFloat
 * @note:
 */
 void Vofa_SendFloat(Vofa_Packet *packet)
 {
    uint8_t byte[4];
    uint8_t i;
    uint32_t temp;
    for(i=0;i<DATA_COUNT_MAX;i++)
    {
        temp = *((uint32_t*)&packet->fdata[i]);  // Reinterpret float as Uint32
        byte[0] = (uint8_t)(temp);
        byte[1] = (uint8_t)(temp >> 8);
        byte[2] = (uint8_t)(temp >> 16);
        byte[3] = (uint8_t)(temp >> 24);
		debug_uart_send_array(byte,4);
    }     
    debug_uart_send_array(packet->tail,4);
 }
  1. 发送字节函数(底层)Byte
/**
 * @brief 输入字节(阻塞式发送) hex
 */
void debug_uart_send_byte(uint8_t byte)
{
	while( DL_UART_isBusy(UART_DEBUG_INST) == true );
	DL_UART_Main_transmitData(UART_DEBUG_INST, byte);
}

void debug_uart_send_array(uint8_t *Array,uint8_t length)
{
	for(int i=0;i<length;i++)
	{
		debug_uart_send_byte(Array[i]);
	}
}

2.测试

2.1 main.c测试

在main.c函数线初始化数据包

	Vofa_Init(&Vofa_packet);

    while (1)
	{
		t+=tb;
		if(t>=2*_PI)
		{
			t-=2*_PI;
		}
		Vofa_packet.fdata[D1] = _sin(t);
		Vofa_packet.fdata[D2] = _cos(t);
		Vofa_SendFloat(&Vofa_packet);
		delay_ms(3);
    }

在vof上位机中查看波形:

image

2.2 mid_utils.c

其中_PI、_sin和_cos在我的mid_utils.c和mid_utils.h中,使用查表法来提升正弦运算速度@author:彭志辉

/**
 * @brief:  辅助性函数或工具方法,用于解决常见问题和任务
 * @author: PengZhihui
 * @date:   2025-08-28
 */

#include "mid_utils.h"

/** 
 * @brief: 200点正弦波数据,范围0~10000 sin*10000
 * @note:   int array instead of float array
 *          2x storage save (int 2Byte float 4 Byte )
 *          200 points per cycle, 5 cycles per second when timer 1ms
 *          4x200 points per 360 deg
*/

const int sine_array[200] = {0, 79, 158, 237, 316, 395, 473, 552, 631, 710, 789, 867, 946, 1024, 1103, 1181,
                            1260, 1338, 1416, 1494, 1572, 1650, 1728, 1806, 1883, 1961, 2038, 2115, 2192,
                            2269, 2346, 2423, 2499, 2575, 2652, 2728, 2804, 2879, 2955, 3030, 3105, 3180,
                            3255, 3329, 3404, 3478, 3552, 3625, 3699, 3772, 3845, 3918, 3990, 4063, 4135,
                            4206, 4278, 4349, 4420, 4491, 4561, 4631, 4701, 4770, 4840, 4909, 4977, 5046,
                            5113, 5181, 5249, 5316, 5382, 5449, 5515, 5580, 5646, 5711, 5775, 5839, 5903,
                            5967, 6030, 6093, 6155, 6217, 6279, 6340, 6401, 6461, 6521, 6581, 6640, 6699,
                            6758, 6815, 6873, 6930, 6987, 7043, 7099, 7154, 7209, 7264, 7318, 7371, 7424,
                            7477, 7529, 7581, 7632, 7683, 7733, 7783, 7832, 7881, 7930, 7977, 8025, 8072,
                            8118, 8164, 8209, 8254, 8298, 8342, 8385, 8428, 8470, 8512, 8553, 8594, 8634,
                            8673, 8712, 8751, 8789, 8826, 8863, 8899, 8935, 8970, 9005, 9039, 9072, 9105,
                            9138, 9169, 9201, 9231, 9261, 9291, 9320, 9348, 9376, 9403, 9429, 9455, 9481,
                            9506, 9530, 9554, 9577, 9599, 9621, 9642, 9663, 9683, 9702, 9721, 9739, 9757,
                            9774, 9790, 9806, 9821, 9836, 9850, 9863, 9876, 9888, 9899, 9910, 9920, 9930,
                            9939, 9947, 9955, 9962, 9969, 9975, 9980, 9985, 9989, 9992, 9995, 9997, 9999,
                            10000, 10000};

/**
 *@brief:  sine calculation by using fixed size array
 *@param a:  angle in between 0 and 2PI
 *@note:   ~40us (float array)
            ~50us (int array)
            precision +-0.005
 *@note:    126.6873 = 199 / (π/2)
            a:(0,π/2) -> (0,199)
 */
float _sin(float a)
{
    if (a < _PI_2)
    {
        return 0.0001f * sine_array[_round(126.6873f * a)];      // int array optimized
    } 
    else if (a < _PI)
    {
        return 0.0001f * sine_array[398 - _round(126.6873f * a)];     // int array optimized
    } 
    else if (a < _3PI_2)
    {
        return -0.0001f * sine_array[-398 + _round(126.6873f * a)];      // int array optimized
    } 
    else
    {
        return -0.0001f * sine_array[796 - _round(126.6873f * a)];      // int array optimized
    }
}

float _cos(float a)
{
    float a_sin = a + _PI_2;
    a_sin = a_sin > _2PI ? a_sin - _2PI : a_sin;
    return _sin(a_sin);
}

/**
 * @brief:square root approximation function using
 * @note:计算平方根的近似值
 * https://reprap.org/forum/read.php?147,219210
 * https://en.wikipedia.org/wiki/Fast_inverse_square_root
 */
float _sqrtApprox(float number)
{//low in fat
    long i;
    float y;
    // float x;
    // const float f = 1.5F; // better precision

    // x = number * 0.5F;
    y = number;
    i = *(long*) &y;
    i = 0x5f375a86 - (i >> 1);
    y = *(float*) &i;
    // y = y * ( f - ( x * y * y ) ); // better precision
    return number * y;
}

/**
 * @brief:  计算快速平方根倒数(fast inverse square root)
 * @note:   
 */
float FastInvSqrt(float x)
{
    float halfx = 0.5f * x;
    float y = x;
    long i = *(long *)&y;
    i = 0x5f3759df - (i >> 1);
    y = *(float *)&i;
    y = y * (1.5f - (halfx * y * y));
    return y;
}



2.3 mid_utlis.h


#ifndef __MID_UTILS_H__
#define __MID_UTILS_H__


// sign function
#define _sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
#define _round(x) ((x)>=0?(long)((x)+0.5f):(long)((x)-0.5f))
#define _constrain(amt, low, high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))



// utility defines
#define _2_SQRT3 1.15470053838f
#define _SQRT3 1.73205080757f
#define _1_SQRT3 0.57735026919f
#define _SQRT3_2 0.86602540378f
#define _SQRT2 1.41421356237f
#define _120_D2R 2.09439510239f

#define _PI 3.14159265359f
#define _PI_2 1.57079632679f
#define _PI_3 1.0471975512f
#define _2PI 6.28318530718f
#define _3PI_2 4.71238898038f
#define _PI_6 0.52359877559f

float _sin(float a);
float _cos(float a);

#endif

3.总结和不足

若是纠结发送的数据,可以开启串口DMA降低对系统主频的占用

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐