LOADING

加载过慢请开启缓存 浏览器默认开启

zero two

解决mpu6050打印z轴数据出现0.000

2026/3/2

原来的代码

/**
 * @brief 打印校准后的传感器数据(物理单位)
 */
#define INT_PART(x) ((int)(x))
#define FRAC_PART(x) (abs((int)(((x) - (int)(x)) * 100 + 0.5)) % 100)
void mpu6050_print_calibrated(void)
{
    mpu6050_raw_data_t raw_data;
    mpu6050_calibrated_data_t calibrated_data, corrected_data;

    if (mpu6050_read_raw(&raw_data) == ERRCODE_SUCC) {
        mpu6050_convert_to_physical(&raw_data, &calibrated_data);
        mpu6050_apply_calibration(&calibrated_data, &corrected_data);

        osal_printk("=== MPU6050 Data ===\r\n");
        osal_printk("Temperature: %d C\r\n", (int)corrected_data.temperature_c);
        osal_printk("Accel: X=%d.%03d g, Y=%d.%03d g, Z=%d.%03d g\r\n",
                   (int)corrected_data.accel_x_g, (int)(abs(corrected_data.accel_x_g * 1000 + 0.5) % 1000),
                   (int)corrected_data.accel_y_g, (int)(abs(corrected_data.accel_y_g * 1000 + 0.5) % 1000),
                   (int)corrected_data.accel_z_g, (int)(abs(corrected_data.accel_z_g * 1000 + 0.5) % 1000));
        osal_printk("Gyro: X=%d.%02d dps, Y=%d.%02d dps, Z=%d.%02d dps\r\n\r\n",
                    INT_PART(corrected_data.gyro_x_dps), FRAC_PART(corrected_data.gyro_x_dps),
                    INT_PART(corrected_data.gyro_y_dps), FRAC_PART(corrected_data.gyro_y_dps),
                    INT_PART(corrected_data.gyro_z_dps), FRAC_PART(corrected_data.gyro_z_dps));
        osal_printk("====================\r\n");
    }
}

修改后的代码

void mpu6050_print_calibrated(mpu6050_calibrated_data_t *corrected_data)
{
    // 将浮点数转换为整数(毫g单位和百分度每秒)
    int16_t accel_x_mg = (int16_t)(corrected_data->accel_x_g * 1000 + 0.5f);
    int16_t accel_y_mg = (int16_t)(corrected_data->accel_y_g * 1000 + 0.5f);
    int16_t accel_z_mg = (int16_t)(corrected_data->accel_z_g * 1000 + 0.5f);

    int16_t gyro_x_cdps = (int16_t)(corrected_data->gyro_x_dps * 100 + 0.5f); // 百分度每秒
    int16_t gyro_y_cdps = (int16_t)(corrected_data->gyro_y_dps * 100 + 0.5f);
    int16_t gyro_z_cdps = (int16_t)(corrected_data->gyro_z_dps * 100 + 0.5f);

    osal_printk("=== MPU6050 Data ===\r\n");
    osal_printk("Temperature: %d C\r\n", (int)corrected_data->temperature_c);

    // 打印加速度计(单位:g)
    osal_printk("Accel: X=%d.%03d g, Y=%d.%03d g, Z=%d.%03d g\r\n", accel_x_mg / 1000, abs(accel_x_mg) % 1000,
                accel_y_mg / 1000, abs(accel_y_mg) % 1000, accel_z_mg / 1000, abs(accel_z_mg) % 1000);

    // 打印陀螺仪(单位:dps)
    osal_printk("Gyro: X=%d.%02d dps, Y=%d.%02d dps, Z=%d.%02d dps\r\n", gyro_x_cdps / 100, abs(gyro_x_cdps) % 100,
                gyro_y_cdps / 100, abs(gyro_y_cdps) % 100, gyro_z_cdps / 100, abs(gyro_z_cdps) % 100);

    osal_printk("====================\r\n\r\n");
}

 
  貌似两个代码都是同一个AI生成的。。
 

AI总结:

您的原代码:

#define INT_PART(x) ((int)(x))
#define FRAC_PART(x) (abs((int)(((x) - (int)(x)) * 100 + 0.5)) % 100)

osal_printk("Accel: X=%d.%03d g, Y=%d.%03d g, Z=%d.%03d g\r\n",
           (int)corrected_data.accel_x_g, (int)(abs(corrected_data.accel_x_g * 1000 + 0.5) % 1000),
           (int)corrected_data.accel_y_g, (int)(abs(corrected_data.accel_y_g * 1000 + 0.5) % 1000),
           (int)corrected_data.accel_z_g, (int)(abs(corrected_data.accel_z_g * 1000 + 0.5) % 1000));

我的新代码:

int16_t accel_z_mg = (int16_t)(corrected_data->accel_z_g * 1000.0f + 0.5f);
osal_printk("Accel: X=%d.%03d g, Y=%d.%03d g, Z=%d.%03d g\r\n",
           accel_x_mg / 1000, abs(accel_x_mg) % 1000,
           accel_y_mg / 1000, abs(accel_y_mg) % 1000,
           accel_z_mg / 1000, abs(accel_z_mg) % 1000);

关键区别:

1. 运算顺序和精度

  • 您的代码:在格式化字符串中直接计算,每次打印都重新计算

  • 我的代码:先计算好整数结果,再打印

2. 数值稳定性

  • 您的代码corrected_data.accel_z_g * 1000 + 0.5 中的 corrected_data.accel_z_g 是浮点数,直接用在 abs()

  • 我的代码:先将浮点数转换为整数,后续全部用整数运算

3. 类型转换

  • 您的代码abs(corrected_data.accel_z_g * 1000 + 0.5) - abs() 是整数函数,但传入的是浮点数表达式

  • 我的代码abs(accel_z_mg) - accel_z_mg 已经是整数

4. 问题所在

corrected_data.accel_z_g = 0.1000 时:

您的代码执行过程

  1. corrected_data.accel_z_g * 1000 + 0.5 = 100.5(浮点数)

  2. 将这个浮点数传入 abs() - 但 abs() 期望整数参数

  3. 编译器可能隐式转换:(int)100.5 = 100

  4. abs(100) = 100

  5. 100 % 1000 = 100 ✓ 按理应该显示正确

但为什么显示0? 可能是编译器在处理浮点数到整数的隐式转换时出了问题,或者浮点精度导致 100.5 变成了 99.999,取整后变成99。

我的代码执行过程

  1. corrected_data.accel_z_g * 1000.0f + 0.5f = 100.5f

  2. 显式转换为 (int16_t)100.5f = 100(四舍五入正确)

  3. accel_z_mg = 100

  4. accel_z_mg / 1000 = 0

  5. abs(accel_z_mg) % 1000 = 100

  6. 打印 “0.100 g” ✓

总结

您的代码逻辑上是正确的,但可能因为:

  1. 隐式类型转换的问题

  2. 浮点数精度损失

  3. 编译器优化导致的问题

阅读全文

毕业设计 (3) - 完成 WS63 的 ES8311 驱动移植和 MPU6050,实现音频播放和运动数据接收

毕业设计 2026/2/28

踩坑:

 
模块的DI连接mcu的DO,DOUT连接模块的DI
又因为奇怪的原因浪费三四天时间

代码:

es8311音频播放代码

/*使用本案例前先把sio_porting.h和sio_porting.c中的FREQ_OF_NEED改成16*/
#include "i2s.h"
#include "watchdog.h"
#include "hal_sio.h"
#include "hal_dma.h"
#include "soc_osal.h"
#include "app_init.h"
#include "converted_audio_data.h"
#include "common_def.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "pinctrl.h"
#include "i2c.h"

#define I2S_DIV_NUMBER 16
#define I2S_CHANNEL_NUMBER 2
#define I2S_TX_INT_THRESHOLD 7
#define I2S_RX_INT_THRESHOLD 1
#define I2S_DMA_SRC_WIDTH 2
#define I2S_DMA_DEST_WIDTH 2
#define I2S_DMA_BURST_LENGTH 0
#define I2S_DMA_TRANS_STEP 2

#define I2S_TASK_PRIO 24
#define I2S_TASK_STACK_SIZE 0xc00

extern const int16_t my_converted_audio[];
#define MY_AUDIO_SAMPLES_LEN 56842
int16_t my_txdata[256] = {0};

#define I2S_SAMPLE_DELAY_MS 1000

/* ES8311 I2C配置 */
#define ES8311_I2C_BUS 1           /* I2C总线号 */
#define ES8311_I2C_ADDR 0x18       /* ES8311的7位I2C地址 */
#define ES8311_I2C_BAUDRATE 400000 /* I2C波特率 400kHz */

#define AUDIO_PLAY_BUF_SIZE 4096

#define ES8311_RESET_REG00 0x00 /* reset digital,csm,clock manager etc. */

#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr  */
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
#define ES8311_SDPIN_REG09 0x09       /* dac serial digital port */
#define ES8311_SDPOUT_REG0A 0x0A      /* adc serial digital port */
#define ES8311_SYSTEM_REG0B 0x0B      /* system */
#define ES8311_SYSTEM_REG0C 0x0C      /* system */
#define ES8311_SYSTEM_REG0D 0x0D      /* system, power up/down */
#define ES8311_SYSTEM_REG0E 0x0E      /* system, power up/down */
#define ES8311_SYSTEM_REG0F 0x0F      /* system, low power */
#define ES8311_SYSTEM_REG10 0x10      /* system */
#define ES8311_SYSTEM_REG11 0x11      /* system */
#define ES8311_SYSTEM_REG12 0x12      /* system, Enable DAC */
#define ES8311_SYSTEM_REG13 0x13      /* system */
#define ES8311_SYSTEM_REG14 0x14      /* system, select DMIC, select analog pga gain */
#define ES8311_ADC_REG15 0x15         /* ADC, adc ramp rate, dmic sense */
#define ES8311_ADC_REG16 0x16         /* ADC */
#define ES8311_ADC_REG17 0x17         /* ADC, volume */
#define ES8311_ADC_REG18 0x18         /* ADC, alc enable and winsize */
#define ES8311_ADC_REG19 0x19         /* ADC, alc maxlevel */
#define ES8311_ADC_REG1A 0x1A         /* ADC, alc automute */
#define ES8311_ADC_REG1B 0x1B         /* ADC, alc automute, adc hpf s1 */
#define ES8311_ADC_REG1C 0x1C         /* ADC, equalizer, hpf s2 */
#define ES8311_DAC_REG31 0x31         /* DAC, mute */
#define ES8311_DAC_REG32 0x32         /* DAC, volume */
#define ES8311_DAC_REG33 0x33         /* DAC, offset */
#define ES8311_DAC_REG34 0x34         /* DAC, drc enable, drc winsize */
#define ES8311_DAC_REG35 0x35         /* DAC, drc maxlevel, minilevel */
#define ES8311_DAC_REG37 0x37         /* DAC, ramprate */
#define ES8311_GPIO_REG44 0x44        /* GPIO, dac2adc for test */
#define ES8311_GP_REG45 0x45          /* GP CONTROL */
#define ES8311_CHD1_REGFD 0xFD        /* CHIP ID1 */
#define ES8311_CHD2_REGFE 0xFE        /* CHIP ID2 */
#define ES8311_CHVER_REGFF 0xFF       /* VERSION */

#define ES8311_MAX_REGISTER 0xFF

#define ES8311_PDN_DAC_BIT (1 << 1)

#define CODEC_DEVICE_ADDR 0x30 /* 0011 00x */

typedef enum {
    HI_CODEC_SAMPLE_RATE_8K = 8,
    HI_CODEC_SAMPLE_RATE_16K = 16,
    HI_CODEC_SAMPLE_RATE_32K = 32,
    HI_CODEC_SAMPLE_RATE_48K = 48,
} hi_codec_sample_rate;

typedef enum {
    HI_CODEC_RESOLUTION_16BIT = 16,
    HI_CODEC_RESOLUTION_24BIT = 24,
} hi_codec_resolution;

typedef struct {
    hi_codec_sample_rate sample_rate;
    hi_codec_resolution resolution;
} hi_codec_attribute;

/**
 * @brief ES8311写寄存器
 * @param reg_addr 寄存器地址
 * @param data 写入数据
 * @return ERRCODE_SUCC成功,其他失败
 */
static errcode_t es8311_write_reg(uint8_t reg_addr, uint8_t data)
{
    uint8_t tx_buf[2] = {reg_addr, data};
    i2c_data_t i2c_data = {.send_buf = tx_buf, .send_len = 2, .receive_buf = NULL, .receive_len = 0};

    return uapi_i2c_master_write(ES8311_I2C_BUS, ES8311_I2C_ADDR, &i2c_data);
}

/**
 * @brief ES8311读寄存器
 * @param reg_addr 寄存器地址
 * @param data 读取数据指针
 * @return ERRCODE_SUCC成功,其他失败
 */
static errcode_t __attribute__((unused)) es8311_read_reg(uint8_t reg_addr, uint8_t *data)
{
    errcode_t ret;
    uint8_t tx_buf[1] = {reg_addr};
    uint8_t rx_buf[1] = {0};
    i2c_data_t i2c_data;

    /* 先写寄存器地址 */
    i2c_data.send_buf = tx_buf;
    i2c_data.send_len = 1;
    i2c_data.receive_buf = NULL;
    i2c_data.receive_len = 0;
    ret = uapi_i2c_master_write(ES8311_I2C_BUS, ES8311_I2C_ADDR, &i2c_data);
    if (ret != ERRCODE_SUCC) {
        return ret;
    }

    /* 再读寄存器数据 */
    osal_msleep(1);
    i2c_data.send_buf = NULL;
    i2c_data.send_len = 0;
    i2c_data.receive_buf = rx_buf;
    i2c_data.receive_len = 1;
    ret = uapi_i2c_master_read(ES8311_I2C_BUS, ES8311_I2C_ADDR, &i2c_data);
    if (ret == ERRCODE_SUCC) {
        *data = rx_buf[0];
    }

    return ret;
}

__attribute__((unused)) static uint32_t codec_set_gain(void)
{
    uint32_t ret = ERRCODE_SUCC;
    ret |= es8311_write_reg(ES8311_SYSTEM_REG0E, 0x02);
    ret |= es8311_write_reg(ES8311_SYSTEM_REG0F, 0x44);
    ret |= es8311_write_reg(ES8311_ADC_REG15, 0x40);
    ret |= es8311_write_reg(ES8311_ADC_REG1B, 0x0A);
    ret |= es8311_write_reg(ES8311_ADC_REG1C, 0x6A);
    ret |= es8311_write_reg(ES8311_ADC_REG17, 0xBF);
    ret |= es8311_write_reg(ES8311_DAC_REG37, 0x48);
    ret |= es8311_write_reg(ES8311_DAC_REG32, 0x84);
    ret |= es8311_write_reg(ES8311_ADC_REG16, 0x22);
    ret |= es8311_write_reg(ES8311_ADC_REG17, 0xDF);
    ret |= es8311_write_reg(ES8311_ADC_REG18, 0x87);
    ret |= es8311_write_reg(ES8311_ADC_REG19, 0xFB);
    ret |= es8311_write_reg(ES8311_ADC_REG1A, 0x03);
    ret |= es8311_write_reg(ES8311_ADC_REG1B, 0xEA);

    return ret;
}

/**
 * @brief 初始化ES8311音频编解码器
 * @param codec_attr 编解码器属性配置
 * @return ERRCODE_SUCC成功,其他失败
 */
__attribute__((unused)) static errcode_t es8311_init(const hi_codec_attribute *codec_attr)
{
    uint32_t ret;

    osal_printk("========== ES8311 Init Start ==========\r\n");

    if (codec_attr == NULL) {
        osal_printk("codec_attr is NULL!\r\n");
        return ERRCODE_FAIL;
    }

    /* 配置采样率 */
    osal_printk("ES8311 sample_rate: %dK, resolution: %dbit\r\n", codec_attr->sample_rate, codec_attr->resolution);

    ret = es8311_write_reg(ES8311_GPIO_REG44, 0x08);
    osal_msleep(5);

    ret |= es8311_write_reg(ES8311_DAC_REG31, 0x40);
    ret |= es8311_write_reg(ES8311_RESET_REG00, 0x1F);

    ret |= es8311_write_reg(ES8311_GP_REG45, 0x00);
    ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, 0x30);
    ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x10);

    if (codec_attr->sample_rate == HI_CODEC_SAMPLE_RATE_8K) {
        ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0xA0);
    } else if (codec_attr->sample_rate == HI_CODEC_SAMPLE_RATE_16K) {
        ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x40);
    } else if (codec_attr->sample_rate == HI_CODEC_SAMPLE_RATE_32K) {
        ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x48);
    } else if (codec_attr->sample_rate == HI_CODEC_SAMPLE_RATE_48K) {
        ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x00);
    }

    if ((codec_attr->resolution == HI_CODEC_RESOLUTION_16BIT)) {
        ret |= es8311_write_reg(ES8311_SDPIN_REG09, 0x0C);
        ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, 0x0C);
    } else {
        ret |= es8311_write_reg(ES8311_SDPIN_REG09, 0x00);
        ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, 0x00);
    }

    return ERRCODE_SUCC;
}

static void i2s_dma_master_init(void)
{
    uapi_i2s_deinit(SIO_BUS_0);
    uapi_i2s_init(SIO_BUS_0, NULL);
    sio_porting_i2s_pinmux();
    uapi_pin_set_mode(7, PIN_MODE_4);
    i2s_config_t config = {
        .drive_mode = MASTER,
        .transfer_mode = STD_MODE,
        .data_width = SIXTEEN_BIT,
        .channels_num = TWO_CH,
        .timing = NONE_TIMING_MODE,
        .clk_edge = RISING_EDGE,
        .div_number = I2S_DIV_NUMBER,
        .number_of_channels = I2S_CHANNEL_NUMBER,
    };
    i2s_dma_attr_t attr = {
        .tx_dma_enable = 1,
        .tx_int_threshold = I2S_TX_INT_THRESHOLD,
        .rx_dma_enable = 0,
        .rx_int_threshold = I2S_RX_INT_THRESHOLD,
    };
    uapi_i2s_set_config(SIO_BUS_0, &config);
    uapi_i2s_dma_config(SIO_BUS_0, &attr);
}

static void *i2s_dma_master_task(const char *arg)
{
    /* 配置I2C引脚:GPIO15为SCL,GPIO16为SDA,设置为模式2(I2C功能) */
    uapi_pin_set_mode(15, PIN_MODE_2); /* I2C_SCL */
    uapi_pin_set_mode(16, PIN_MODE_2); /* I2C_SDA */

    /* 初始化I2C主机 */
    uapi_i2c_master_init(ES8311_I2C_BUS, ES8311_I2C_BAUDRATE, 0);

    /* 配置编解码器属性 */
    __attribute__((unused)) hi_codec_attribute codec_attr = {
        .sample_rate = HI_CODEC_SAMPLE_RATE_16K, .resolution = HI_CODEC_RESOLUTION_16BIT, /* 16位分辨率 */
    };

    /* 初始化ES8311 */
    // es8311_init(&codec_attr);

    unused(arg);
    int32_t ret = 128;
#if defined(CONFIG_SIO_USING_V151)
    ret = ERRCODE_SUCC;
#endif
    osal_printk("ret=%d\r\n", ret);
    uapi_dma_deinit();
    i2s_dma_master_init();

    /* DMA init. */
    uapi_dma_init();
    uapi_dma_open();
    i2s_dma_config_t dma_cfg = {
        .src_width = I2S_DMA_SRC_WIDTH,
        .dest_width = I2S_DMA_DEST_WIDTH,
        .burst_length = I2S_DMA_BURST_LENGTH,
        .priority = 0,
    };
    osal_printk("playing threading start.\r\n");

    int p = 0;
    while (1) {
        uapi_watchdog_kick();

        while (1) {
            for (int i = 0; i < 64; i++) {
                for (int j = 0; j < 4; j++) {
                    my_txdata[i * 4 + j] = my_converted_audio[p + i];
                }
            }

            int16_t length = 128;
            if (uapi_i2s_merge_write_by_dma(SIO_BUS_0, &my_txdata, length, &dma_cfg, (uintptr_t)NULL, true) != ret) {
                osal_printk("master uapi_i2s_merge_write_by_dma error.\r\n");
            }

            p += 64; // 更新 p 以指向下一个数据块
            if (p > MY_AUDIO_SAMPLES_LEN - 64) {
                osal_printk("master uapi_i2s_merge_write_by_dma end.\r\n");
                p = 0; // 重置 p 以循环播放音频数据
                break; // 跳出循环,等待下一次播放
            }
        }
    }
    return NULL;
}

static void i2s_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle =
        osal_kthread_create((osal_kthread_handler)i2s_dma_master_task, 0, "I2sDmaMasterTask", I2S_TASK_STACK_SIZE);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, I2S_TASK_PRIO);
    }
    osal_kthread_unlock();
}

/* Run the i2s_entry. */
app_run(i2s_entry);

 
 
 
下一步准备开始数据采集打标签

阅读全文

hi3861与智谱ai的realtime模型对话(成功返回文本)

2026/2/24

做了一点不想做了,看了一些毕设的评论突然很着急,为什么他们进度那么快啊,我们学校都没什么通知,只是在1月30号让我们申请选题。为什么有的三月已经准备完成了,为什么有的上学期完成了,有点急啊。

唉,昨天发现一个喜欢的up主注销账号了,明明14号的时候才关注我,那时候真的很开心。新年也结束了,悲伤的氛围。又要找工作。又要准备毕设。真的烦死了。

阅读全文

毕业设计 (2) - 部署 TFLM hello_world demo 到 WS63 (Hi3863) 平台

前置工作

在开始部署之前,需要准备以下环境:

  • WS63 (Hi3863) 开发环境

  • TensorFlow Lite Micro 源码

  • CMake 构建工具

CMakeLists 配置

set(TFLM_SRC_PATH "G:/HiSpark_SDK/fbb_ws63/tflite-micro")
set(TFLM_CORE_PATH "${TFLM_SRC_PATH}/tensorflow/lite")
set(FLATBUFFERS_DIR "G:/HiSpark_SDK/fbb_ws63/tflite-micro/tensorflow/lite/micro/tools/make/downloads/flatbuffers")
set(TFLM_COMPILER_PATH "${TFLM_SRC_PATH}/tensorflow/compiler")

set(RUY_PATHS
    "${TFLM_SRC_PATH}/tensorflow/lite/micro/tools/make/downloads/ruy"
)

set(GEMMLOWP_FIXEDPOINT_PATHS
    "${TFLM_SRC_PATH}/tensorflow/lite/micro/tools/make/downloads/gemmlowp"
    "${TFLM_SRC_PATH}/tmp/tflm-ws63/third_party/gemmlowp"
    "${TFLM_SRC_PATH}/third_party/gemmlowp"
)

set(FOUND_GEMMLOWP FALSE)
foreach(path ${GEMMLOWP_FIXEDPOINT_PATHS})
    if(EXISTS "${path}/fixedpoint/fixedpoint.h")
        set(GEMMLOWP_PATH ${path})
        set(FOUND_GEMMLOWP TRUE)
        message(STATUS "✓ Found gemmlowp at: ${path}")
        break()
    endif()
endforeach()

unset(PUBLIC_HEADER)

if(FOUND_GEMMLOWP)
    list(APPEND PUBLIC_HEADER
        ${GEMMLOWP_PATH}
    )
endif()

# 设置 PUBLIC_HEADER 变量以包含所有需要的头文件路径
list(APPEND PUBLIC_HEADER
    ${TFLM_SRC_PATH}
    ${TFLM_SRC_PATH}/tensorflow
    ${TFLM_SRC_PATH}/tensorflow/lite/kernels
    ${TFLM_CORE_PATH}
    ${TFLM_CORE_PATH}/micro
    ${TFLM_CORE_PATH}/micro/kernels
    ${TFLM_CORE_PATH}/micro/memory_planner
    ${TFLM_CORE_PATH}/micro/testing
    ${TFLM_CORE_PATH}/schema
    ${TFLM_CORE_PATH}/c
    ${TFLM_CORE_PATH}/core/api
    ${FLATBUFFERS_DIR}/include
    ${TFLM_CORE_PATH}/micro/kernels/cmsis_nn
    CACHE INTERNAL ""
)

list(APPEND PUBLIC_HEADER ${RUY_PATHS})

# 设置源文件
set(CORE_API_SOURCES
    "${TFLM_CORE_PATH}/core/api/flatbuffer_conversions.cc"
    "${TFLM_CORE_PATH}/micro/tflite_bridge/flatbuffer_conversions_bridge.cc"
    "${TFLM_CORE_PATH}/core/api/tensor_utils.cc"
)

set(SCHEMA_SOURCES
    "${TFLM_COMPILER_PATH}/mlir/lite/schema/schema_utils.cc"
    "${TFLM_CORE_PATH}/micro/flatbuffer_utils.cc"
)

set(C_API_SOURCES
    "${TFLM_CORE_PATH}/core/c/common.cc"
)

set(CORE_INTERPRETER_SOURCES
    "${TFLM_CORE_PATH}/micro/micro_allocator.cc"
    "${TFLM_CORE_PATH}/micro/micro_interpreter.cc"
    "${TFLM_CORE_PATH}/micro/micro_utils.cc"
    "${TFLM_CORE_PATH}/micro/micro_op_resolver.cc"
    "${TFLM_CORE_PATH}/micro/micro_context.cc"
    "${TFLM_CORE_PATH}/micro/micro_interpreter_context.cc"
    "${TFLM_CORE_PATH}/micro/micro_interpreter_graph.cc"
)

set(MEMORY_SOURCES
    "${TFLM_CORE_PATH}/micro/memory_planner/greedy_memory_planner.cc"
    "${TFLM_CORE_PATH}/micro/memory_planner/linear_memory_planner.cc"
    "${TFLM_CORE_PATH}/micro/arena_allocator/single_arena_buffer_allocator.cc"
    "${TFLM_CORE_PATH}/micro/arena_allocator/persistent_arena_buffer_allocator.cc"
    "${TFLM_CORE_PATH}/micro/arena_allocator/non_persistent_arena_buffer_allocator.cc"
    "${TFLM_CORE_PATH}/micro/arena_allocator/recording_single_arena_buffer_allocator.cc"
    "${TFLM_CORE_PATH}/micro/recording_micro_allocator.cc"
    "${TFLM_CORE_PATH}/micro/memory_helpers.cc"
    "${TFLM_CORE_PATH}/micro/micro_allocation_info.cc"
)

set(KERNEL_REGISTRY_SOURCES
    "${TFLM_CORE_PATH}/micro/kernels/kernel_runner.cc"
    "${TFLM_CORE_PATH}/micro/kernels/kernel_util.cc"
)

set(KERNEL_COMMON_SOURCES
    "${TFLM_CORE_PATH}/micro/kernels/fully_connected_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/softmax_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/conv_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/depthwise_conv_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/pooling_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/add_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/mul_common.cc"
)

set(KERNEL_IMPLEMENTATION_SOURCES
    "${TFLM_CORE_PATH}/micro/kernels/fully_connected.cc"
    "${TFLM_CORE_PATH}/micro/kernels/softmax.cc"
    "${TFLM_CORE_PATH}/micro/kernels/reshape.cc"
    "${TFLM_CORE_PATH}/micro/kernels/quantize.cc"
    "${TFLM_CORE_PATH}/micro/kernels/dequantize.cc"
    "${TFLM_CORE_PATH}/micro/kernels/dequantize_common.cc"
    "${TFLM_CORE_PATH}/micro/kernels/logistic.cc"
    "${TFLM_CORE_PATH}/micro/kernels/add.cc"
    "${TFLM_CORE_PATH}/micro/kernels/mul.cc"
    "${TFLM_CORE_PATH}/micro/kernels/concatenation.cc"
    "${TFLM_CORE_PATH}/micro/kernels/pooling.cc"
)

set(LOG_SOURCES
    "${TFLM_CORE_PATH}/micro/micro_log.cc"
    "${TFLM_CORE_PATH}/micro/tflite_bridge/micro_error_reporter.cc"
    "${TFLM_COMPILER_PATH}/mlir/lite/core/api/error_reporter.cc"
    "${TFLM_CORE_PATH}/micro/micro_time.cc"
)

set(TENSOR_UTILS_SOURCES
    "${TFLM_CORE_PATH}/kernels/internal/tensor_utils.cc"
    "${TFLM_CORE_PATH}/kernels/internal/quantization_util.cc"
    "${TFLM_CORE_PATH}/kernels/internal/common.cc"
    "${TFLM_CORE_PATH}/kernels/internal/portable_tensor_utils.cc"
    "${TFLM_CORE_PATH}/kernels/kernel_util.cc"
)

set(OTHER_SOURCES
    "${TFLM_CORE_PATH}/micro/micro_profiler.cc"
    "${TFLM_CORE_PATH}/micro/micro_resource_variable.cc"
    "${TFLM_CORE_PATH}/micro/test_helpers.cc"
)

# 合并所有源文件
set(ALL_TFLM_SOURCES
    ${CORE_API_SOURCES}
    ${SCHEMA_SOURCES}
    ${C_API_SOURCES}
    ${CORE_INTERPRETER_SOURCES}
    ${MEMORY_SOURCES}
    ${KERNEL_REGISTRY_SOURCES}
    ${KERNEL_COMMON_SOURCES}
    ${KERNEL_IMPLEMENTATION_SOURCES}
    ${LOG_SOURCES}
    ${TENSOR_UTILS_SOURCES}
    ${OTHER_SOURCES}
)

set(PUBLIC_HEADER "${PUBLIC_HEADER}" PARENT_SCOPE)

set(SOURCES "${SOURCES}"
    ${ALL_TFLM_SOURCES}
    "${CMAKE_CURRENT_SOURCE_DIR}/tflm_hello_world.cc"
    "${CMAKE_CURRENT_SOURCE_DIR}/hello_world_model.cc"
    "${CMAKE_CURRENT_SOURCE_DIR}/tflm_debug_log.cc"
    PARENT_SCOPE)

一、项目背景

在 WS63 嵌入式平台上验证 TensorFlow Lite Micro (TFLM) 的功能,确保 TFLM 库能够正确编译和运行。

二、文件结构

src/application/samples/tflm_hello_world/
├── CMakeLists.txt           # 构建配置,引用 TFLM 源码
├── tflm_hello_world.cc      # 主验证程序 ⭐
├── tflm_debug_log.cc        # 平台日志实现 ⭐
├── hello_world_model.cc     # TFLite 模型数据
├── test_cpp.cc              # C++ 编译测试
└── models/
    └── hello_world_model.h  # 模型头文件

三、核心设计决策

1. 日志系统设计(关键难点)

问题:TFLM 的 micro_log.cc 需要平台相关的 DebugLog 函数。

初次尝试(错误)

// ❌ 错误:使用可变参数
extern "C" void DebugLog(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    osal_printk("%s", buffer);
    va_end(args);
}

问题表现:输出乱码

  • Tensor arena size: 10534308 bytes(应该是 6000)

  • Status: 恖5(应该是 PASS/FAIL)

根因分析

// TFLM 的 micro_log.cc 调用方式
void VMicroPrintf(const char* format, va_list args) {
  DebugLog(format, args);  // ❌ 传递的是 va_list,不是 ...
}

最终方案(正确)

// ✅ 正确:接受 va_list 参数
extern "C" void DebugLog(const char* format, va_list args) {
    char buffer[512];
    vsnprintf(buffer, sizeof(buffer), format, args);
    osal_printk("%s", buffer);
}

// TFLM 还需要这个函数
extern "C" int DebugVsnprintf(char* buffer, size_t buf_size,
                              const char* format, va_list vlist) {
    return vsnprintf(buffer, buf_size, format, vlist);
}

2. 主程序设计

核心流程

  1. 加载模型 (GetModel)

  2. 创建 OpResolver (注册 FullyConnected)

  3. 创建 Interpreter (分配 TensorArena)

  4. 分配张量 (AllocateTensors)

  5. 获取输入/输出张量

  6. 执行推理测试 (Invoke)

  7. 验证结果

关键代码结构

// 使用 int8 量化模型
input->data.int8[0] = golden_inputs_int8[i];
interpreter.Invoke();
float y_pred = (output->data.int8[0] - zero_point) * scale;

3. 内存管理

静态内存分配(嵌入式环境要求):

constexpr int kTensorArenaSize = 6000;
static uint8_t tensor_arena[kTensorArenaSize];

避免使用 new/delete,防止动态内存分配问题。

四、遇到的问题与解决

问题 原因 解决方案
undefined reference to ‘DebugLog’ TFLM 需要平台日志函数 实现 DebugLog(const char*, va_list)
输出乱码、数值错误 函数签名不匹配(... vs va_list 修正为接受 va_list 参数
编译时未发现源文件 CMakeLists.txt 缺少文件 添加 tflm_debug_log.ccSOURCES

五、验证结果

测试 4 个输入值的 sin(x) 预测:

x (输入) sin(x) 期望值 预测值 误差 状态
0.77 0.6961 0.6964 0.00031 ✅ PASS
1.57 1.0000 0.9949 0.00508 ✅ PASS
2.30 0.7457 0.7296 0.01610 ✅ PASS
3.14 0.0016 -0.0083 0.00988 ✅ PASS

所有测试误差 < 0.05(容差),验证成功!

六、关键经验

  1. 仔细阅读 TFLM 头文件debug_log.h 明确定义了 DebugLog 的签名

  2. 理解调用链MicroPrintfVMicroPrintfDebugLog(va_list)

  3. 嵌入式适配要点

    • 提供平台相关的 DebugLogDebugVsnprintf
    • 使用静态内存分配
    • 使用 osal_printk 而非 printf
  4. 量化模型处理:理解 scalezero_point 的反量化公式

参考资源

阅读全文

毕业设计 (1) - WS63 (Hi3863) 完成 C++ 支持过程

WS63 SDK 添加 C++ 编译支持完整指南

概述

本文档记录了在海思 WS63 SDK 中添加 C++ 编译支持的完整过程,包括遇到的问题、解决方案和最终配置。适用于需要在 WS63 芯片上使用 TensorFlow Lite Micro (TFLM) 或其他 C++ 库的开发者。

开发环境

  • 芯片: WS63 (RISC-V 架构,riscv31)

  • 工具链: riscv32-musl-105-fp (支持浮点)

  • SDK 路径: G:\HiSpark_SDK\fbb_ws63

  • TFLite Micro: G:\HiSpark_SDK\fbb_ws63\tflite-micro


背景与目标

初始状态

WS63 SDK 原生只支持 C 语言编译,缺少 C++ 支持:

  • 没有 cxxflags 配置

  • 构建系统未配置 C++ 编译器选项

  • CMake 构建脚本未区分 C/C++ 编译选项

目标

  1. 添加基本的 C++ 编译支持

  2. 支持浮点运算(与 C 代码 ABI 兼容)

  3. 能够编译和运行 C++ 测试程序

  4. 为集成 TFLite Micro 做准备


配置步骤

步骤 1: 添加架构级别的 CXXFLAGS

文件: src/build/config/target_config/common_config.py

问题: arch_config 中所有架构只定义了 ccflags,缺少 cxxflags

解决方案: 为每个架构添加 cxxflags 字段

# ===== CUSTOM BEGIN: 添加 common_cxxflags =====
common_cxxflags = [
    '-std=gnu++17',            # C++17 标准
    '-Wall',
    '-Werror',
    '-Wextra',
    '-Winit-self',
    '-Wpointer-arith',
    # '-Wstrict-prototypes',  # C++ 不支持此选项,已移除
    '-Wno-type-limits',
    '-Wno-unused-parameter',  # TFLM 头文件中有未使用参数
    '-fno-strict-aliasing',
    '-Os',
    '-fno-unwind-tables',
]
# ===== CUSTOM END =====

为每个架构添加 cxxflags(以 riscv31 为例,WS63 使用此架构):

'riscv31': {
    'ccflags': [...],
    # ===== CUSTOM BEGIN: 为 riscv31 架构添加 C++ 编译标志 =====
    'cxxflags': [
        '-ffreestanding',
        '-fdata-sections',
        '-Wno-implicit-fallthrough',
        '-ffunction-sections',
        '-nostdlib',
        '-pipe',
        '-mabi=ilp32',
        '-march=rv32imc',
        '-fno-tree-scev-cprop',
        '-fno-common',
        '-mpush-pop',
        '-msmall-data-limit=0',
        '-fno-ipa-ra',
        '-Wtrampolines',
        '-Wlogical-op',
        # '-Wjump-misses-init',  # C only, 已移除
        "-Wa,-enable-c-lbu-sb",
        "-Wa,-enable-c-lhu-sh",
        "-fimm-compare",
        "-femit-muliadd",
        "-fmerge-immshf",
        "-femit-uxtb-uxth",
        "-femit-lli",
        "-femit-clz",
        "-fldm-stm-optimize",
        '-g',
    ],
    # ===== CUSTOM END =====
    'linkflags': [...],
    ...
}

关键点:

  • C++ 移除了 -Wjump-misses-init(仅 C 支持)

  • C++ 移除了 -Wstrict-prototypes(C++ 不支持)


步骤 2: 配置 CMake 区分 C/C++ 编译选项

文件: src/build/cmake/build_component.cmake

问题: CMake 使用相同的编译选项编译 C 和 C++ 文件

解决方案: 使用 CMake 生成器表达式区分语言

# 原代码:
target_compile_options(${COMPONENT_NAME}
    PRIVATE
    ${COMPILE_CCFLAGS}
    $<$<BOOL:${USE_KCONFIG}>:-include${PROJECT_BINARY_DIR}/mconfig.h>
)

# 修改为:
target_compile_options(${COMPONENT_NAME}
    PRIVATE
    # ===== CUSTOM BEGIN: 为 C++ 语言添加单独的编译选项 =====
    # 原代码:${COMPILE_CCFLAGS} (适用于所有语言)
    # 修改原因:C++ 需要使用不同的编译标志(如 -std=gnu++17 而非 -std=gnu99)
    $<$<COMPILE_LANGUAGE:C>:${COMPILE_CCFLAGS}>
    $<$<COMPILE_LANGUAGE:CXX>:${CXXFLAGS}>
    # ===== CUSTOM END: 原代码 ${COMPILE_CCFLAGS} 已被上面的生成器表达式替换 =====
    $<$<BOOL:${USE_KCONFIG}>:-include${PROJECT_BINARY_DIR}/mconfig.h>
)

步骤 3: 初始化 CXXFLAGS 变量

文件: src/build/cmake/global_variable.cmake

问题: CXXFLAGS 变量未初始化

解决方案:

# ===== CUSTOM BEGIN: 添加 CXXFLAGS 默认值 =====
# 如果从 Python 构建脚本传入了 CXXFLAGS,则使用该值;否则使用 CCFLAGS 作为默认值
if(NOT DEFINED CXXFLAGS)
    set(CXXFLAGS "${CCFLAGS}")
endif()
# ===== CUSTOM END =====

步骤 4: 为 WS63 添加浮点支持(关键)

文件: src/build/config/target_config/ws63/target_config.py

问题: C++ 代码使用软浮点 ABI (-mabi=ilp32),而 C 代码使用单精度浮点 ABI (-mabi=ilp32f),导致链接时 ABI 不兼容

错误信息:

can't link soft-float modules with single-float modules
failed to merge target specific data of file

解决方案:

4.1 为 C++ 添加浮点和优化选项

'cxxflags': [
    *fp_flags,  # 应用浮点 ABI 替换
    *codesize_flags['ccflags'],
    # 禁用异常和 RTTI 以减少运行时依赖
    '-fno-exceptions',
    '-fno-rtti',
    # 解决未定义引用问题
    '-fno-threadsafe-statics',
],

4.2 修改 Python 构建脚本使用 pretend

文件: src/build/script/enviroment.py

def merge_common_config(self, com_config):
    """ 将公共配置项合并到config中 """
    self.pretend('ccflags', com_config.get_ram_ccflags())
    # ===== CUSTOM BEGIN: 为 cxxflags 也使用 pretend =====
    # 原代码:self.extend('cxxflags', com_config.get_ram_cxxflags())
    # 修改原因:确保 target_config 中定义的 cxxflags(包括 fp_flags)能够替换 common_cxxflags 中的选项
    self.pretend('cxxflags', com_config.get_ram_cxxflags())
    # ===== CUSTOM END =====
    self.extend('rom_ccflags', com_config.get_rom_ccflags())
    ...

4.3 添加 C++ 标准库

# 原代码:'std_libs': ['m', 'c', 'gcc'],
# ===== CUSTOM BEGIN: 添加 C++ 标准库支持 =====
# 原代码:'std_libs': ['m', 'c', 'gcc'],
'std_libs': ['m', 'c', 'gcc', 'stdc++'],
# ===== CUSTOM END =====

4.4 添加链接器选项

'linkflags': [
    *codesize_flags['linkflags'],
    # ===== CUSTOM BEGIN: 添加 C++ 链接器选项 =====
    # 允许未定义引用(TFLM 的某些函数可能不需要)
    '-Wl,--allow-shlib-undefined',
    # '-Wl,--unresolved-symbols=ignore-in-link-files',  # 此选项不支持,已移除
    # ===== CUSTOM END =====
],

步骤 6: 创建 C++ 测试程序

文件: src/application/samples/tflm_hello_world/test_cpp.cc

目的: 验证基本的 C++ 编译和运行

/**
 * Simple C++ test file for WS63
 * Tests basic C++ compilation support
 */

// 在包含 C 头文件之前,为 C++ 定义正确的 NULL
#ifdef NULL
#undef NULL
#endif
#define NULL 0

#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"

// 简单的 C++ 类测试
class CppTest {
private:
    int value;

public:
    constexpr CppTest(int v) : value(v) {}
    ~CppTest() = default;

    int getValue() const { return value; }
    void setValue(int v) { value = v; }

    CppTest& operator+(const CppTest& other) {
        value += other.value;
        return *this;
    }
};

// 模板函数测试
template<typename T>
constexpr T add(T a, T b) {
    return a + b;
}

static constexpr CppTest global_test(42);

extern "C" void tflm_cpp_test_entry(void)
{
    CppTest test1(10);
    CppTest test2(20);

    test1 + test2;
    int result = add(5, 3);

    osal_printk("C++ Test: global_test = %d, result = %d\r\n",
              global_test.getValue(), result);
    osal_printk("C++ Test: test1.getValue() = %d, test2.getValue() = %d\r\n",
              test1.getValue(), test2.getValue());
}

app_run(tflm_cpp_test_entry);

遇到的问题与解决方案

问题 1: C++ 特有编译选项导致错误

错误:

cc1plus.exe: error: command line option '-Wjump-misses-init' is valid for C/ObjC but not for C++

解决: 从 cxxflags 中移除 C 特有选项

  • 移除 -Wjump-misses-init

  • 移除 -Wstrict-prototypes

问题 2: 浮点 ABI 不兼容

错误:

can't link soft-float modules with single-float modules
failed to merge target specific data of file

解决:

  1. cxxflags 应用 fp_flags(浮点选项替换)

  2. 使用 pretend() 方法合并配置,确保替换生效

问题 3: 函数签名不匹配

错误:

error: invalid conversion from 'int (*)()' to 'init_call_t {aka void (*)()}'

解决: 将 tflm_cpp_test_entry 返回类型从 int 改为 void

问题 4: 未使用参数警告

错误:

error: unused parameter 'size' [-Werror=unused-parameter]

解决: 在 common_cxxflags 中添加 -Wno-unused-parameter

问题 5: 头文件找不到

错误:

fatal error: flatbuffers/flatbuffers.h: No such file or directory
fatal error: fixedpoint/fixedpoint.h: No such file or directory

解决: 添加 TFLM 及其依赖的头文件路径


最终配置总结

关键修改文件列表

  1. src/build/config/target_config/common_config.py

    • 添加 common_cxxflags
    • 为所有架构添加 cxxflags
  2. src/build/cmake/build_component.cmake

    • 使用生成器表达式区分 C/C++ 编译
  3. src/build/cmake/global_variable.cmake

    • 初始化 CXXFLAGS 变量
  4. src/build/config/target_config/ws63/target_config.py

    • 添加 cxxflags 并应用浮点和优化选项
    • 添加 C++ 标准库
    • 添加链接器选项
  5. src/build/script/enviroment.py

    • 修改 cxxflags 使用 pretend 而非 extend
  6. src/application/samples/CMakeLists.txt

    • 添加 TFLM 头文件路径
  7. src/application/samples/tflm_hello_world/CMakeLists.txt

    • 添加测试源文件

验证结果

测试的 C++ 特性

✅ 类和对象
✅ 构造函数/析构函数
✅ 成员函数
✅ 运算符重载
✅ 模板函数
✅ constexpr 变量
✅ extern “C” 链接


总结

通过以上配置,WS63 SDK 现在完全支持 C++ 编译。主要成果:

  1. ✅ 实现了 C/C++ 混合编译

  2. ✅ 解决了浮点 ABI 兼容性问题

  3. ✅ 成功编译并运行 C++ 测试程序

  4. ✅ 为集成 TFLM 或其他 C++ 库奠定了基础


文档版本: 1.0
最后更新: 2025-02-08
作者: Claude (协助配置)
适用 SDK 版本: WS63 SDK 1.10.106
参考链接: WS63添加C++经验总结及讨论

阅读全文

最近想实现的东西

2026/1/21

1、drogon有一个csp的功能,如果搭配ai,能否做到渲染页面的时候根据内容自动排版呢?

2、我的hi3861有一个i2s接口,正在尝试设计模块去实现智能语音

3、hello-agents课程

阅读全文

20251215记录

2025/12/15

关于我使用开发板连接wifi但是没有分配地址去花一天解决为什么无法使用mqtt连接服务器的问题

居然不知道ping一下连通性。。主要是不太信任不熟悉的代码,比如那没有一点注释的mqtt代码,就一直以为是mqtt的问题。结果就是dhcp的问题。

阅读全文

DECA实践

2025/10/13
阅读全文

hispark_pegasus/hi3861/华为云/Mqtt

MQTT 接入所需条件

1. 接入凭证

  • Client ID
  • 用户名
  • 密码
    2. 接入信息
  • 域名/地址:在 MQTT 接入信息中查找域名,通过 ping 命令获取其 IP 地址。
  • 端口:使用 1883
    3. 主题
  • 在平台的“Topic 管理”中查找。
  • 常用类型:
    • 消息上报:用于上报设备消息。
    • 属性上报:用于上报设备属性。
      4. 连接与订阅
  • 设备端连接:使用 MQTT 客户端库,并填入上述的地址、端口、Client ID、用户名和密码进行连接。
  • 规则引擎
    • 在配置规则引擎时,会设置一个 Topic 作为消息转发的目标。
    • 设备端(如 PC)可以使用接入信息的 IP + 端口 + 这个 Topic 来订阅消息。
阅读全文

解决redhat echo $http_proxy无法找到文件地址的方法及redhat虚拟机终端按键哒哒哒声音的问题探讨

问题1:当echo的时候查找~/.bashrc文件,发现脚本会继续寻找bashrc.d目录下的proxy文件并进行运行,也就是启动bash的时候就会运行那个proxy.sh文件,而proxy.sh文件可能是之前旧的代理地址,只要删除里面的内容就好了。

问题2:redhat虚拟机终端按键哒哒哒的问题主要是虚拟机的问题,并不是自己键盘的问题,可以在搜索关键词进行寻找解决方案

阅读全文
avatar
lAn_rEd

Description
山的背面是未知,天空没有尽头

问答