概念先知道
- 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 库自动使用这些指令加速;本页验证的就是这条“硬件加速数学”路径。
操作步骤
在终端进入 SDK 的 DSP 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/dsp执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。例程依次运行 5 组算法验证:FIR 滤波(riscv_dsp2_fir_q15)、矩阵乘法(matrix_q31)、实数 FFT(rfft_q15)、正弦(sin_q31)、方差(variance_q15),每组输入与预计算参考值比对,全部通过打印 Example all successfully!。
代码执行流程
例程从启动到验证完成的流程如下:
例程调用的 API 介绍
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_q31、csi_fir_q15、csi_rfft_q15、csi_mat_q31、csi_var_q15 等函数。工程中 #include "csi_math.h" 即可使用。
参数:—
返回值:—
完整代码
以下为 dsp/main.c 与其中一个用例(riscv_dsp2_sin_q31.c)的完整源码,与官方示例一致,默认折叠,点击展开(其余 4 个用例结构相同,位于 cases/ 目录):
📜 点击展开 dsp/main.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 完整代码
#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_q31、csi_fir_q15、csi_rfft_q15 等函数。
这 5 个用例能改成实际应用吗?
可以。把 src 换成你的采样数据、ref_result 比对逻辑改成业务处理即可;例如麦克风采集数组直接喂给 csi_fir_q15 做降噪,再输出到 I2S 播放。

