4. 算法部署流程

DSP/BIOS 这东西,说白了就是给 DSP 芯片配的一个轻量级“管家”。我早年刚接触时也觉得,不就是个调度器嘛,后来踩的坑多了才明白——它管的不光是任务排队,还有中断响应、资源争抢这些硬骨头。这一章咱们就把它掰开揉碎了讲。

4.1 任务调度机制

DSP/BIOS 的任务调度,核心是抢占式优先级调度。每个任务都有一个优先级,数字越小优先级越高(0 最高)。高优先级任务就绪时,会立刻打断正在运行的低优先级任务。

关键点:任务有四种状态——READY、RUNNING、BLOCKED、TERMINATED。调度器只在 READY 队列里选最高优先级的任务执行。

我做过一个音频处理项目,三个任务:采集、处理、输出。采集优先级最高,处理次之,输出最低。一开始没注意任务栈大小,结果采集任务栈溢出,系统直接挂掉。嗯,这里要注意——每个任务都要单独分配栈空间,别省。

任务创建示例

TSK_Handle taskHandle;
TSK_Attrs taskAttrs = TSK_ATTRS;
taskAttrs.priority = 5;   // 优先级 5
taskAttrs.stackSize = 0x1000;  // 4KB 栈

taskHandle = TSK_create((Fxn)myTaskFunc, &taskAttrs, NULL);
if (taskHandle == NULL) {
    // 创建失败处理
}

任务函数本身是个无限循环,里面调用 TSK_sleep() 或等待信号量来让出 CPU。千万别写成死循环不释放——那系统就卡死了。

4.2 中断管理

DSP/BIOS 的中断管理,我习惯把它分成两层:硬件中断(HWI)和软件中断(SWI)。

  • HWI:直接响应硬件事件,优先级最高,执行时间要极短。
  • SWI:由 HWI 触发,优先级低于 HWI,但高于普通任务。适合做“中断下半部”处理。

为什么要有 SWI?你想想看,HWI 里如果做大量计算,会阻塞其他中断。我遇到过一例:ADC 中断里做了个 FIR 滤波,结果串口中断响应延迟,丢数据了。后来把滤波挪到 SWI 里,问题解决。

中断配置流程

  1. 在 DSP/BIOS 配置文件中声明 HWI 对象,绑定中断向量号。
  2. 编写中断服务函数(ISR),注意用 HWI_enter/HWI_exit 宏保护。
  3. 在 ISR 中通过 SWI_post() 触发 SWI,把耗时操作交给 SWI。

个人经验:ISR 里只做三件事——读数据、清标志、发 SWI。其他一概不干。这样中断延迟可控,系统稳定。

4.3 信号量

信号量是任务同步的“交通灯”。DSP/BIOS 支持两种:计数信号量和二值信号量。

类型 初始值 典型用途
二值信号量 0 或 1 互斥访问、事件通知
计数信号量 ≥0 资源池管理(如缓冲区数量)

我曾经在一个多通道数据采集系统里,用计数信号量管理 DMA 缓冲区。每个缓冲区就绪时 SEM_post() 一次,处理任务 SEM_pend() 等待。这样缓冲区数量就是信号量的初始值,逻辑清晰得很。

信号量使用示例

SEM_Handle semHandle;
SEM_Attrs semAttrs = SEM_ATTRS;
semAttrs.count = 3;  // 初始 3 个资源

semHandle = SEM_create(3, &semAttrs);
if (semHandle == NULL) {
    // 创建失败
}

// 任务 A:等待资源
SEM_pend(semHandle, SYS_FOREVER);

// 任务 B:释放资源
SEM_post(semHandle);

避坑指南:我曾经在 ISR 里调用 SEM_pend(),结果系统死锁。记住——SEM_pend() 会阻塞任务,不能在 HWI 里用。ISR 里只能用 SEM_post()。

4.4 邮箱通信

邮箱(Mailbox)是任务间传递数据的“信箱”。和信号量不同,邮箱能携带具体数据,而不仅仅是计数。

DSP/BIOS 的邮箱是固定长度、固定容量的队列。发送方把消息放入邮箱,接收方从邮箱取出。如果邮箱满了,发送方可以选择阻塞等待或立即返回。

邮箱操作函数

  • MBX_create(bufSize, nBufs, attrs):创建邮箱,指定每条消息大小和缓冲区数量。
  • MBX_post(mbx, msg, timeout):发送消息,可设置超时。
  • MBX_pend(mbx, msg, timeout):接收消息,可设置超时。

我做过一个雷达信号处理项目,采集任务把原始数据打包成结构体,通过邮箱发给处理任务。邮箱容量设成 8,刚好匹配乒乓缓冲的深度。这样即使处理任务偶尔卡顿,数据也不会丢。

邮箱使用示例

typedef struct {
    int16_t data[256];
    uint32_t timestamp;
} MsgPacket;

MBX_Handle mbxHandle;
MsgPacket txMsg, rxMsg;

// 创建邮箱,每条消息大小 sizeof(MsgPacket),容量 8
mbxHandle = MBX_create(sizeof(MsgPacket), 8, NULL);

// 发送任务
MBX_post(mbxHandle, &txMsg, BIOS_WAIT_FOREVER);

// 接收任务
MBX_pend(mbxHandle, &rxMsg, BIOS_WAIT_FOREVER);

我建议:邮箱消息结构体尽量紧凑。我见过有人把整个音频帧(1024 个 float)塞进邮箱,结果内存暴涨。其实传个指针就行,数据放共享缓冲区。

4.5 知识体系总览

下面这张图把本章的核心逻辑串起来了。你一看就明白:中断是“触发器”,信号量和邮箱是“通信管道”,任务调度是“执行引擎”。

DSP/BIOS 核心机制关系图 硬件中断 HWI 优先级最高,执行极短 软件中断 SWI 中断下半部处理 任务调度 抢占式优先级 触发 唤醒 信号量 同步 / 互斥 邮箱 数据传递 共享缓冲区 数据存储 同步/数据流 写入 💡 设计原则 • HWI 只做最小必要操作,其余交给 SWI 或任务 • 信号量用于任务间同步,邮箱用于传递数据 • 共享缓冲区配合信号量/邮箱使用,避免数据竞争

4.6 综合避坑清单

最后,我把这些年用 DSP/BIOS 踩过的坑整理成一张表。你写代码时对照着看,能省不少调试时间。

场景 常见错误 正确做法
HWI 中调用阻塞函数 系统死锁 HWI 只做 SEM_post / SWI_post
任务栈太小 栈溢出,系统异常 至少 1KB,复杂任务 4KB+
信号量忘记初始化 任务永远阻塞 创建时设好初始值
邮箱消息体过大 内存浪费 传指针,数据放共享区
优先级设置不合理 低优先级任务饿死 用优先级继承或调整数值

嗯,这一章的内容就到这儿。DSP/BIOS 的调度、中断、信号量和邮箱,说白了就是一套“分工协作”的规则。你把规则用对了,系统就跑得稳;用错了,调试能调到头秃。我当年就是一边看手册一边试错,慢慢才摸出门道。希望这些经验能帮你少走弯路。