DSP实时多任务调度的解决方案

电子说

1.2w人已加入

描述

DSP处理器是一种嵌入式处理器(embedded microprocessor),它专门用于数字信号处理,其在系统结构和指令算法方面进行了特殊设计,具有很高的编译效率和指令执行速度。

嵌入式系统在当今是一种非常活跃的应用,在工业、服务业、消费电子等领域的应用范围都不断扩大。为了方便嵌入式系统的开发,人们研发了许多嵌入式操作系统,如WinCE、uc/OS、嵌入式Linux、VxWorks、pSOS、QNX、Palm OS等,由于嵌入式系统往往用于一些较为实时性的用途,这些操作系统也往往被称为实时多任务操作系统(RTOS, Real Time Operation System)。这些系统往往被用在通用嵌入式处理器上(如ARM等)。

DSP系统和通用嵌入式系统的区别

虽然说DSP也是一种嵌入式系统,但是由于其“专用于数字信号处理”的特点,其系统架构也会同通用嵌入式系统略有区别(当然,只是“略有区别”而已)。DSP往往用来跑高速的数学算法,而不牵涉到人机界面、数据库、高层应用等功能(从PC角度来理解的话,DSP在一个嵌入式系统中的功能类似于底层驱动,例如3D图象的演算、环绕声的演算、网络协议处理等)。

因此,从这样的认识角度来看,DSP中的各种任务的调度在过程上相对“单纯”些。DSP中的任务更加侧重于“实时性”和“并行性”。实际上,对于单核的DSP芯片来说,并不可能存在真正的“并行计算”,所谓的并行只不过是通过高速切换几个“串行的线程”来实现。而对于“实时性”,则要求尽可能多的将CPU时间用于计算,并且不同的线程间不能有阻塞的现象发生(从软件角度看,就是执行任务的代码执行时间要短,如果是复杂的算法,就需要对算法进行优化使得算法可以“分步执行”)。

DSP实时多任务调度的解决方案

根据以上的分析,我们可以大致的得出一个简单的DSP RTOS的雏形,它的核心就是创建一种可以实时执行的线程。这种线程被称为“PRD Task”(period task),指的是这种线程一旦创建,就由系统内核自动的周期性调用,而调度周期可以保证相当高的时间精度。管理这个机制的部件叫做“PRD模块”。

创建PRD任务的方法是在初始化的时候向内核注册一个PRD任务。

int Thread_PRD_Append(long TimeSlinceCount/*执行周期

*/,void (*CallBackHandle)()/* 函数句柄*/)

返回值: TRUE / FALSE

函数功能:向系统注册一个新的线程,指定执行周期以及需要调度的函数句柄,系统便会在指定的时间间隔自动调用这个函数。

这个注册任务等待执行的方式叫做“回调”,注册的时候向内核提交一个指定的执行周期和任务函数的入口函数指针。内核在通过计时,在达到执行周期的时候通过函数指针调用任务函数。

内核为了管理这个功能,需要一张“PRD任务表”。它的定义如下:

typedef struct {

long TimeSlice_Current; //当前时间片

long TimeSlice_Count; //总时间片

void (*Callback_Handle)(); //调用句柄

} Type_PRD_Table;

Type_PRD_Table PRD_Table[SYS__THREAD_PRD_TABLE_SIZE];//PRD表

int PRD_Table_ItemCount=0;//当前的PRD表最大项目数

TimeSlice_Current和TimeSlice_Count两个变量构成了一个软件定时器,内核通过对于TimeSlice_Current的操作即可知道何时可以调用相关的任务函数。

相关的代码如下。这是一个典型的减法计时器的代码。

for(i=0;i 0)

{ PRD_Table[i].TimeSlice_Current --; //递减时间片 }}

而在另外一处,需要判断时间片计数器是否已经计到0。

if((PRD_Table[i].TimeSlice_Current) == 0) {//调用相应的句柄

PRD_Table[i].TimeSlice_Current=PRD_Table[i].TimeSlice_Count;//恢复时间片

(*(PRD_Table[i].Callback_Handle))();//调用任务入口函数指针 }

接着,需要将减法计时器的代码放入一个硬件定时器中断中,这样便能保证这个减法计时的高度精确性。这个硬件定时器依赖于硬件,因此要求DSP芯片硬件上必须提供这样一个定时器,否则这个DSP内核便无法在这个DSP芯片上执行(幸好基本上不太会存在没有硬件定时器的DSP)。

而对于第二段代码,判别时间片计数是否“已经到点”的代码则应

当放在一个死循环中。例如在main函数中放置一个死循环。

Void Main() {

for(;;) { …。。判断时间片计数器是否已经计到0的代码}

…}

显然,除了计时是对“到点”的判别以外,我们还需要初始化和添加任务的函数。

int Thread_PRD_Append(long TimeSlinceCount,void *CallbackHandle)

{//给PRD表添加任务

if(PRD_Table_ItemCount

责任编辑:lq6

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分