uC/OS II 提供给用户通用接口函数都在Ucos_ii.h中【uC/GUI 提供给用户通用接口函数都在INC包含的各个头文件中,使用时参考官方的手册用就好了,有中文版的】;
INT8U const [url=]OSUnMapTbl[256] = {[/url]
[url=] 0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u
}
【这个表的通俗用法就是 给出 给任意一个长度不超过8位的二进制值a, 将这个a做为该数组的索引, OSUnMapTb[a]就表示二进制值a中第一个位1出现的位置】
这个数组的 索引值 从 0~255;
[/url][url=]这个数组的每个值 表示的是它对应索引的[/url][url=]二进制形式从右到左第一次出现1的位置。[/url]
[url=]即通过这个数组 可以直接获取0~255各值的二进制形式从右到左第一次出现1的位置
Ucosii 中任务的优先级管理方法(假设最多64个任务):
因为OSUnMapTbl是256个元素,也就是索引是8位的二进制值,这个索引的每一位的0 1 值代表的是对应的任务是否就绪;
因此管理任务的最小单位为INT8U(也就是8个任务用8个位来组成一个INT8U);
依次类推:可用一个INT8U来表示某个最小单元中是否有就绪态的任务(即该INT8U是否为0)
这样就是 8X8 = 64个任务;
依次往上类推,8X8X….X8,理论上在空间足够的情况下可以管理无限个任务;
具体的优先级值的算法就是:
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
其实Ucosii 任务数大于64个时,他是16个任务为一组,组成INT16U来管理的
同样往上推,也是16组来管理的
16X16
if ((*ptbl & 0xFFu) != 0u) {
//如果低8位不为0,都不用考虑高8位了,直接在低8位中去寻找第一次出现1的地方
OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]);
} else {
//如果低8位为0,直接在高8位中去寻找第一次出现1的地方,然后加8 就表示整个16位的INT16U中第一次出现1的地方
OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u);
}
[/url]
[url=]一个任意长度的二进制值(n位),我们都可以将这个n位化成m个段,用一个2的m次方个成员的数组 去存放他的特征值。代码中可以以m位为单位求得特征值。
编程技巧,比如在用一个int(32位)的整数的30位的各个位的置位来表示对于的消息到来,且0-30位,优先级一次降低...
假如某个时刻同时来了很多个消息,很多位置1,那么怎么来获取这时优先级最高的捏?
就用上面的方法举一反三
我们可以一次处理 用一个16个单元的数组,
a[16] = {}; 来存放 4位 二进制能表示的16个数字 中 各数第一位出现1的位置,
每4位 4位依次处理, 就可以利用该数组快速获取最高优先级的消息....
[/url]
[url=]
//这里用了一个很小的数组,来处理
//这里GetOnePos 的算法 对于 很多位的大数值来说,可能效率不高,但是对于 只有4位的值来说,就是很快了
//其实这里只是一个简单的例子而已,ucos中用的是8 位 也就是256个值的数组,
//这个就看你的需求,你来选择了具体的数组的大小了, 看具体的情况来 用时间 换 空间, 或用空间换时间
unsigned int g_unHighPrio[16] =
{
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
};
char GetOnePos(int data)
{
if(data & 0x0000000F)
{
return(0 + g_unHighPrio[(data & 0x0000000F)>>0]);
}
else if(data & 0x000000F0)
{
return(4 + g_unHighPrio[(data & 0x000000F0)>>4]);
}
else if(data & 0x00000F00)
{
return(8 + g_unHighPrio[(data & 0x00000F00)>>8]);
}
else if(data & 0x0000F000)
{
return(12 + g_unHighPrio[(data & 0x0000F000)>>12]);
}
else if(data & 0x000F0000)
{
return(16 + g_unHighPrio[(data & 0x000F0000)>>16]);
}
else if(data & 0x00F00000)
{
return(20 + g_unHighPrio[(data & 0x00F00000)>>20]);
}
else if(data & 0x0F000000)
{
return(24 + g_unHighPrio[(data & 0x0F000000)>>24]);
}
else if(data & 0xF0000000)
{
return(28 + g_unHighPrio[(data & 0xF0000000)>>28]);
}
else
{
return 0;
}
}
int main(void)
{
unsigned int nTst = 0;
int npos = 0;
while(1)
{
nTst = 0;
printf("Input Tst = ");
scanf("%u",&nTst);
printf("The First 1 position is %d\n",GetOnePos(nTst));
getchar();
}
}
实时操作系统RTOS
实时操作系统包括软实时与硬实时,软实时要求各个任务尽快地运行,而不要求限定某一个任务在多长的时间内完成,而硬实时系统不仅须执行无误而且要做到准时,需要在规定的时间内完成相关操作;
大多数实时系统是两者的综合;
非实时操作系统指操作系统无法保证哪怕是最高优先级任务开始执行的最后时限。软实时操作系统指的是操作系统只能保证在xx时间内执行最高优先级的用户代码,但用户软件是否能及时完成操作,操作系统不管!
RTlinux就是linux的硬实时操作系统,它能够创建精确运行的符合POSIX.1B标准的实时进程;
并非所有的嵌入式系统都需要实时操作系统,只有在一些特定的场合,对时间比较敏感的应用才会使用实时操作系统。实时操作系统必须及时响应所要求的任务,在限定时间内完成任务。非实时的操作系统,多时间不是很敏感,对所要求的任务只是会保证完成,但在什么时候完成,或用多长的时间完成就不一定了。例如:手机它不需要实时性。我们发短信时,系统对它的处理早1秒或者晚1秒都不会影响到我们的使用。而对于导弹这样的应用必须具有实时性。导弹被发射出去锁定目标后要不断修正飞行方向,以保证击中目标,如果它的实时性不好的话,从传感器传来的信号没有及时响应,即使完了1毫秒的时间,那误差就会很大。用这样的导弹攻打敌方目标的话,目标很可能没有击中,美国大使馆倒是有可能被炸掉。
另外一般linux不具有实时性,它是分时操作系统一般是面向用户的,但是因为它的源代码是公开的,它是可以改造成实时系统的,但即使是这样它的实时性也不会很好,毕竟它最初的设计并不是为了实时性。我们在Linux上面同时运行好几个程序,它们会被并发的执行。我们会发现同时多运行几个程序可能会比只允许一个程序慢,这是因为操作系统把处理器按时间片分给了每一个程序。自然会慢一些。而实时操作系统,一般不同的任务会有不同的优先级,他会把拥有最高的优先级的程序一次性执行完毕。然后再执行次一级的程序。这样的系统只适用于控制,不适合一般的应用。
任务划分
目标:
满足实时性要求;
任务数目合理;-->合理使用系统的软硬件资源
简化软件系统;-->合理规划任务,降低对操作系统的服务要求,使操作系统功能得到裁剪,简化系统
降低资源需求;
目前,各功能单独成立为一个任务,比如显示功能成为显示任务,文件系统通过一个任务来管理,这样文件系统任务要显示时,就向显示任务的消息队列里面发送显示消息,而不是直接在本任务中调用显示函数。
0:设备依赖性任务的划分
从系统的结构框图中,我们可以看到系统的输入输出各个设备。并发性是任务的基本特性,而控制输出、输出的设备的程序具有先天的并发性,把他们分别封装为不同的任务是合理的,这样就可以划分出第一批任务,键盘任务,显示任务,数据采集任务,控制输出任务和通信任务。
1:系统关键任务划分:关键是指某种功能在应用系统中的重要性,如果该功能不能正常实现,则将造成重大影响,甚至引发灾难性的后果;包含该关键功能的任务为关键任务,关键任务必须得到运行机会即使遗漏一次也不行; 对于关键功能,必须尽可能与其他的功能剥离,独立成为一个任务,通过通信的方式再触发其他的任务,完成后续操作。例如:火灾检测系统中,烟雾传感器的检测功能就是一个关键功能,必须将其与其他的报警、灭火等功能剥离。OSMboxPostOpt()消息发送函数,具有广播功能: O在uC/OS II的较新版本中,消息发送函数OSMboxPostOpt()具有广播功能,发送一条消息就可以使所有等待该消息的任务进入就绪状态。2:紧迫任务划分:某种功能必须在规定的时间内得到运行权(及时运行),并在规定的时刻前执行完毕(按时完成),这类功能有严格的实时性要求。大多数的紧迫任务是通过异步事件来触发,这些异步事件一般能够引发某种中断。在这种情况下,将紧迫任务安排在相应的ISR中是最有效的办法,如果不能安排在中断任务中,那么可竟尽可能提高优先级来解决“及时性”。对于按时完成,需要对紧迫任务进行瘦身,尽可能剥离不太紧迫的操作,只剩下必须立刻做的操作,被剥离的不太紧迫的操作另外封装成一个任务。例如:能谱分析仪,紧迫任务放在外部中断服务程序中,完成对脉冲峰值的采样,并将采样结果放入消息队列中。紧迫任务不一定是关键任务,所以遗漏一两次执行会导致工作品质下降,但是不会造成严重后果。
3:数据处理任务划分:用户程序中消耗时间最多的是各种数据处理程序单元,这些单元不止一个,且分别为不同的功能服务;所以需要将他们划分出来,分别包装为不同的任务,因为他们的处理较耗时,所以他们的优先级须安排低,这样让他们使用其他任务的剩余时间来进行数据处理(如果有时间片轮转,可以安排他们为同一优先级,利用时间片来轮转他们)。模拟时间片轮转:假如有3个数据处理任务A、B、C;我们可以将他们细分为A1、A2、A3,B1、B2、B3,C1、C2、C3,再交叉安排优先级就可以了。A1->1,B1->2,C1->3,A2->4,B2->5...
4:功能聚合任务划分:将关系密切的若干功能组合为一个任务,达到功能聚合的效果,关系密切:数据关联和时序关联,如果他们分开,有可能会使用大量的通信资源,造成较大的负担。
5:触发条件相同的任务划分:
如果若干个功能由相同的事件触发,则可以将这些功能组合成为一个任务,从而免除将事件分发给多个任务的工作量。这样做的条件是:当以某顺序执行这些功能时,各个功能的实时性要求仍然可以得到满足,且各个功能在执行过程中不会出现问题,例如:火警检测系统中,拨打电话、启动喷淋灭火系统、保持火警记录,这些任务是不能合在一起做为一个任务的,否则其中一些功能有误其他的任务就会被耽搁。
符合本类任务的通常是内部事件,例如通过运算处理产生某个结果,根据这个结果需要执行若干功能,这些功能可组合为一个任务。
6:运行周期相同的任务划分:
绝大多数功能都需要不停的重复执行,如果重复执行的条件是固定的时间间隔,则这个功能具有周期性。将周期相同的功能组合在一起封装为一个任务,就可以避免一个时间事件触发几个任务,省去事件分发操作和他们的之间的通信。
7:顺序操作任务划分:
如果若干个功能按固定的顺序运行流水作业,相互之间完全没有并发现,则应该将这个功能组合为一个任务。
任务设计
任务函数结构
所设计的任务函数至少有一次对操作系统服务函数的调用,这样才能让低优先级的任务得到执行。
1:单次执行的任务
任务创建后,得到执行,执行完毕后自我删除;任务基本分为三大部分:1:准备工作代码(定义变量以及初始化工作)2:任务实体代码(完成具体的功能,一般都可以被中断)3:调用删除函数。例如启动任务(如果采用启动任务去启动各个任务,那启动任务的优先级需要比它创建的任务的优先级高,一般系统中通常将启动任务所做的事情交给系统的一个实质任务去做,节省资源);
采用“创建任务”的方式来启动任务,不仅可以省去通常的通信手段激活任务的麻烦,还可以通过*pdata来传递参数,是没有启动具有不同的工作状态(比如串口波特率),但是这样的话实时性会比较差,每一次任务的启动都需要创建,发费较多的时间,还有可能在删除时引起不必要的后遗症(如共享资源释放、任务关联);
所以通过“创建任务”来启动的任务一般是孤立的任务,他们不和其他的任务进行通信(ISR除外),只使用共享资源来获取信息和输出信息。
2:周期性执行的任务
周期性执行的任务,通常在代码中调用系统延时函数,OSTimeDly或OSTimeDlyHMSM来调整执行周期。但是这两个函数有延时误差,至少有一个或小于一个时钟节拍的误差,如需精确的定时需采用独立的定时器。
3:事件触发执行的任务
任务的实体代码的执行需要等待某种事件的发生,在相关事件发生之前,任务被挂起,相关事件发生一次,任务执行一次。当触发条件是“时间间隔”(定时器触发)时,它既是周期任务。
如触发条件是某个信号(信号量等),那么这个触发条件仅仅是触发任务的执行。
如触发条件是某个信息(邮箱等),那么这个触发条件除了启动该任务外,还为任务提供原始数据和资料。
任务优先级安排
uC/OS II共有64个优先级:0~63。在OS_CFG.h中设置OS_LOWEST_PRIO来确定系统实际使用的优先级范围,#define OS_LOWEST_PRIO 18 ->系统裁剪到只有19个优先级,节省资源开销。
OS_LOWEST_PRIO-->空闲任务; OS_LOWEST_PRIO-1-->统计任务;
OS_LOWEST_PRIO-2-->系统保留; OS_LOWEST_PRIO-3-->系统保留;
系统最高的4个优先级(0、1、2、3)保留。
[/url]
|
|