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 里,问题解决。
中断配置流程
- 在 DSP/BIOS 配置文件中声明 HWI 对象,绑定中断向量号。
- 编写中断服务函数(ISR),注意用
HWI_enter/HWI_exit宏保护。 - 在 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 知识体系总览
下面这张图把本章的核心逻辑串起来了。你一看就明白:中断是“触发器”,信号量和邮箱是“通信管道”,任务调度是“执行引擎”。
4.6 综合避坑清单
最后,我把这些年用 DSP/BIOS 踩过的坑整理成一张表。你写代码时对照着看,能省不少调试时间。
| 场景 | 常见错误 | 正确做法 |
|---|---|---|
| HWI 中调用阻塞函数 | 系统死锁 | HWI 只做 SEM_post / SWI_post |
| 任务栈太小 | 栈溢出,系统异常 | 至少 1KB,复杂任务 4KB+ |
| 信号量忘记初始化 | 任务永远阻塞 | 创建时设好初始值 |
| 邮箱消息体过大 | 内存浪费 | 传指针,数据放共享区 |
| 优先级设置不合理 | 低优先级任务饿死 | 用优先级继承或调整数值 |
嗯,这一章的内容就到这儿。DSP/BIOS 的调度、中断、信号量和邮箱,说白了就是一套“分工协作”的规则。你把规则用对了,系统就跑得稳;用错了,调试能调到头秃。我当年就是一边看手册一边试错,慢慢才摸出门道。希望这些经验能帮你少走弯路。