Skip to content

概念先知道

  • DSP 库:一组优化的数学函数(滤波、FFT、矩阵、三角函数等),比手写循环更快,是信号处理的地基。
  • Q15/Q31 定点数:嵌入式没有浮点单元(或为速度不用浮点),用 16/32 位整数表示小数,q31 表示 1.31 格式的定点数。
  • FIR 滤波器:有限冲激响应滤波器,输出是输入序列的加权和,广泛用于去噪、整形。
  • FFT(快速傅里叶变换):把时域信号变到频域,rfft 是实数输入版本,用于频谱分析。
  • 参考值比对:每个用例都有预先生成的 ref_result 数组,结果一致才打印成功,保证算法实现正确。

例程功能简介

本页对应博流官方 SDK 的 dsp 例程(examples/dsp),演示 RISC-V DSP 扩展数学库(csi_math)

  • main.c 通过宏重命名技巧把 5 个用例文件(cases/ 目录)逐个编译进来并依次调用;
  • 每个用例从 src 输入数组计算,与 ref_result 参考数组逐点比对,不一致立即打印失败并停住;
  • 覆盖 5 类典型算法:FIR 滤波、矩阵乘法、实数 FFT、正弦、方差;
  • 全部通过后打印 Example all successfully!,随后死循环等待。
  • 典型应用:音频均衡/降噪(FIR)、振动频谱分析(FFT)、传感器校准(矩阵/方差)。

提示

BL616/BL618 的 RISC-V 内核支持 DSP 扩展指令,SDK 的 csi_math 库自动使用这些指令加速;本页验证的就是这条“硬件加速数学”路径。

操作步骤

1
进入例程目录

在终端进入 SDK 的 DSP 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/dsp
2
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
3
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
运行验证

打开串口助手(波特率 2000000)。例程依次运行 5 组算法验证:FIR 滤波(riscv_dsp2_fir_q15)、矩阵乘法(matrix_q31)、实数 FFT(rfft_q15)、正弦(sin_q31)、方差(variance_q15),每组输入与预计算参考值比对,全部通过打印 Example all successfully!

代码执行流程

例程从启动到验证完成的流程如下:

例程调用的 API 介绍

csi_sin_q31(x)

计算正弦(Q31 定点输入输出),例程用它验证 128 个输入点的结果与参考值一致。

参数

  • x:Q31 格式角度值

返回值:Q31 格式正弦值

riscv_dsp2_fir_q15 / riscv_dsp2_rfft_q15 等用例()

cases/ 目录下各用例的入口函数(由 main.c 通过宏改名后调用),内部使用 csi_math.h 的 FIR/FFT/矩阵/方差 API 计算并与 ref_result 比对。

参数:无

返回值0;失败时停在 while(1)

csi_math.h(头文件)

DSP 库公共头文件,声明 csi_sin_q31csi_fir_q15csi_rfft_q15csi_mat_q31csi_var_q15 等函数。工程中 #include "csi_math.h" 即可使用。

参数:—

返回值:—

完整代码

以下为 dsp/main.c 与其中一个用例(riscv_dsp2_sin_q31.c)的完整源码,与官方示例一致,默认折叠,点击展开(其余 4 个用例结构相同,位于 cases/ 目录):

📜 点击展开 dsp/main.c 完整代码
c
#include "bflb_mtimer.h"
#include "board.h"

#define DBG_TAG "MAIN"
#include "log.h"

#define main       riscv_dsp2_fir_q15
#define ref_result ref_result_riscv_dsp2_fir_q15
#define result     result_riscv_dsp2_fir_q15
#define src        src_riscv_dsp2_fir_q15
#include "cases/riscv_dsp2_fir_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE

#define main       riscv_dsp2_matrix_q31
#define ref_result ref_result_riscv_dsp2_matrix_q31
#define result     result_riscv_dsp2_matrix_q31
#define src        src_riscv_dsp2_matrix_q31
#include "cases/riscv_dsp2_matrix_q31.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE

#define main       riscv_dsp2_rfft_q15
#define ref_result ref_result_riscv_dsp2_rfft_q15
#define result     result_riscv_dsp2_rfft_q15
#define src        src_riscv_dsp2_rfft_q15
#include "cases/riscv_dsp2_rfft_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE

#define main       riscv_dsp2_sin_q31
#define ref_result ref_result_riscv_dsp2_sin_q31
#define result     result_riscv_dsp2_sin_q31
#define src        src_riscv_dsp2_sin_q31
#include "cases/riscv_dsp2_sin_q31.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE

#define main       riscv_dsp2_variance_q15
#define ref_result ref_result_riscv_dsp2_variance_q15
#define result     result_riscv_dsp2_variance_q15
#define src        src_riscv_dsp2_variance_q15
#include "cases/riscv_dsp2_variance_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE

int main(void)
{
    board_init();

    printf("Example run riscv_dsp2_fir_q15!\r\n");
    riscv_dsp2_fir_q15();
    printf("Example run riscv_dsp2_matrix_q31!\r\n");
    riscv_dsp2_matrix_q31();
    printf("Example run riscv_dsp2_rfft_q15!\r\n");
    riscv_dsp2_rfft_q15();
    printf("Example run riscv_dsp2_fir_q15!\r\n");
    riscv_dsp2_sin_q31();
    printf("Example run riscv_dsp2_variance_q15!\r\n");
    riscv_dsp2_variance_q15();
    printf("Example all successfully!\r\n");

    while (1) {
        bflb_mtimer_delay_ms(1000);
    }
}
📜 点击展开 cases/riscv_dsp2_sin_q31.c 完整代码
c
#include <stdio.h>
#include <math.h>
#include "csi_math.h"

#define TEST_SIZE 128

static q31_t src[TEST_SIZE] = {
    0x71f40eb8, 0x1aedfac4, 0x0f3ff639, 0x40eb378a, 0x159f1974, 0x5428ca42, 0x3e990f9d, 0x209a58e6,
    0x62875be9, 0x5816212a, 0x28a6ff13, 0x7a51f6a7, 0x3ca51dcd, 0x052989e9, 0x613a6877, 0x413bbd0c,
    0x502a2298, 0x44af92d2, 0x1851b3ba, 0x531d1932, 0x5259e962, 0x36537413, 0x457bf10d, 0x6f43906c,
    0x473fcc60, 0x3ed09220, 0x2a151a9b, 0x12b52c1b, 0x3f79f6a8, 0x0aea56a5, 0x7f5ab471, 0x0ee05125,
    0x2ec0f1de, 0x2b59847b, 0x6da9fe05, 0x0aa207c9, 0x4e0db117, 0x1ad037ab, 0x78555489, 0x2005ef5a,
    0x107b129e, 0x36df9796, 0x4687e80b, 0x732d0ac1, 0x534c3c63, 0x1c0f8e39, 0x30b19f31, 0x19f18910,
    0x3850eca4, 0x10118818, 0x45a0df62, 0x4104e5c3, 0x14195691, 0x74162b17, 0x5b9f737e, 0x445d0125,
    0x1df54a61, 0x788e5af7, 0x3bc0bd34, 0x0f07e8f3, 0x211f4d64, 0x31a834bb, 0x506dd274, 0x6bb74d4b,
    0x66e132bc, 0x263e92dd, 0x5cb47ec9, 0x541f4af5, 0x2320126d, 0x58b7700e, 0x00d0828f, 0x1182b519,
    0x3d33cf33, 0x6577c618, 0x22af9e0f, 0x058ffea4, 0x2bf32d25, 0x07fb97e5, 0x7895e5f5, 0x73b5f4dd,
    0x0399834c, 0x52f68eac, 0x66311a01, 0x3690ee7d, 0x1258b0e3, 0x1d85a331, 0x28e0511a, 0x1485ae28,
    0x556e56c5, 0x690aa18a, 0x040672e5, 0x5c14209e, 0x5d8ff577, 0x61ba1095, 0x27c2e01e, 0x797a7a8f,
    0x2d957473, 0x2a0c920a, 0x5b8d5230, 0x4c5e6a70, 0x62c33c82, 0x552b8cf0, 0x52a5adae, 0x3023134c,
    0x2015d046, 0x734945c9, 0x73233bd0, 0x4e0a9921, 0x52fc390d, 0x02b26459, 0x25ea9d61, 0x4197a318,
    0x55b9ca53, 0x6eb23e44, 0x31862277, 0x7b8f7be8, 0x2c0cc008, 0x533a8b83, 0x6a35c724, 0x0a232bc0,
    0x2e6247d9, 0x3b442980, 0x1de30e64, 0x749b41b2, 0x70834a66, 0x01f8428b, 0x56356d0a, 0x4708a91b
};

static q31_t ref_result[TEST_SIZE] = {
    0xae927d72, 0x7c0dcc9a, 0x571dc1b4, 0xfa3a9ce8, 0x6fbe4766, 0x950553ac, 0x08cd75d4, 0x7ff10d88,
    0x80fc5f3e, 0x8989e528, 0x74a02914, 0xdcc60f08, 0x14fc107a, 0x2016c0b4, 0x803bb9f2, 0xf841608c,
    0xa4c37a12, 0xe2d13fdc, 0x7701f3ce, 0x98c47cba, 0x9babda5e, 0x3a861816, 0xddf4f932, 0xa247ba02,
    0xd36834fe, 0x0771665a, 0x70a392a6, 0x65b47542, 0x034a13ca, 0x4158f3d2, 0xfbf19cc6, 0x55615368,
    0x5fdfbdea, 0x6ca44370, 0x9bbb0ff4, 0x3fd0ad96, 0xae8a0976, 0x7bdf3ff8, 0xd0f56b9a, 0x7fffc56a,
    0x5c9ea4d4, 0x3771a934, 0xd7aa340e, 0xb4a477aa, 0x9816491c, 0x7d9cd9ea, 0x575fbe36, 0x7a621b14,
    0x2f245878, 0x5acfe2d2, 0xdd15a2fa, 0xf9996cf4, 0x6ac53b20, 0xb9575c24, 0x82f21454, 0xe4cb524a,
    0x7f5b34f4, 0xd2434f50, 0x1a7e13a4, 0x561a435e, 0x7fcdac80, 0x52dadf6a, 0xa39b2218, 0x94978822,
    0x873b677e, 0x7a08961a, 0x81ac072c, 0x95262c98, 0x7e7e8b94, 0x8818541c, 0x051dbcf2, 0x60f7405a,
    0x1185c64c, 0x849581a0, 0x7ee31fd6, 0x2284557a, 0x6a99f2be, 0x30e22044, 0xd26fa1a0, 0xb76248d4,
    0x167fd962, 0x9954214a, 0x85de5864, 0x392d8c3e, 0x644f642a, 0x7f0db084, 0x7409edc4, 0x6c371202,
    0x90d846b8, 0x8c670202, 0x19206920, 0x825db7b6, 0x80eac460, 0x8075db58, 0x76d279d2, 0xd7b86778,
    0x6494f30e, 0x70bd232c, 0x830a5b0a, 0xb6f92e88, 0x812cf66e, 0x91aab728, 0x9a86f676, 0x59e5afda,
    0x7fff28b2, 0xb53400a0, 0xb472be58, 0xae98fc86, 0x993ebfe8, 0x10e525f8, 0x7aa33c5c, 0xf60164b2,
    0x8feff520, 0x9fe370c6, 0x537dd3cc, 0xe4538b3e, 0x6a40ae5e, 0x98572fc8, 0x8fbe4fce, 0x3d18f9cc,
    0x61655134, 0x1d79d4fc, 0x7f4f471a, 0xbc1671de, 0xa7cc325e, 0x0c5b6644, 0x8e80c89c, 0xd4adac08
};

int main(void)
{
    q31_t result;
    int i;

    for (i = 0; i < TEST_SIZE; i++) {
        result = csi_sin_q31(src[i]);

        if (result != ref_result[i]) {
            printf("Example run failed!\r\n");
            while (1)
                ;
        }
    }

    printf("Example run successfully!\r\n");

    return 0;
}

FAQ

打印 “Example run failed!” 卡住了?

说明某个用例的计算结果与参考值不一致。多为用例文件被改动或编译优化异常;还原 cases/ 目录后重新编译,正常情况下 5 组全部通过。

为什么用 Q15/Q31 而不用 float?

定点运算不需要浮点单元(或浮点较慢),速度更快、结果确定,适合实时信号处理。需要小数时用 Q 格式表示(q31 即 1.31 格式),运算后用位移还原。

怎么在自己的工程里用 DSP 库?

在 defconfig 开启 DSP 库(CONFIG_DSP=y 或按 SDK 组件说明),代码里 #include "csi_math.h" 即可调用 csi_sin_q31csi_fir_q15csi_rfft_q15 等函数。

这 5 个用例能改成实际应用吗?

可以。把 src 换成你的采样数据、ref_result 比对逻辑改成业务处理即可;例如麦克风采集数组直接喂给 csi_fir_q15 做降噪,再输出到 I2S 播放。

Released under the MIT License. Build Time 2026-09-11 14:52:23