ucos系统的精华提炼

发布者:数字翻飞最新更新时间:2015-10-19 来源: eefocus关键字:ucos系统  线程 手机看文章 扫描二维码
随时随地手机看文章
前言:线程不是什么神秘的东西,当你理解后你会有一种茅塞顿开的感觉,其实它本身就很简单。

  第一节:程序代码运行条件
  回想一下:
  1.一个链接过的程序由以下组成:代码段,只读数据段,可读写数据段。
  2.单片机上常使用的两个资源:Flash(只读),RAM(可读写)
  3.对于单片机,我们习惯于一种模式,代码段和只读数据放在FLASH上,可读写数据放在RAM的起始地址,栈从RAM中最高地址向下开始运行。
  4.处理器从代码段提取代码,有三种方式,顺序,跳转,调用。所有代码段必须放在正确的位置上。
  5.处理器上数据段是通过地址来处理数据,所以数据也必须放在正确的位置上。
  6.处理上临时变量通过栈指针的向下偏移来提取变量,所以临时变量的地址是不固定的。
  7.至于堆,那是C语言的技巧,不列入条件范围内。
  总结:1.程序运行需要代码段,数据段和栈区,而代码段和数据段都必须放在编辑时对应的地址上,只有栈区是可以设置的。那么在不使用临时变量地址作为计算因数的情况下,就算改变栈顶的位置,程序的运行结果相同。

  第二节:多程序运行原理
  多线程的原理其实很简单,系统为每个线程提供一片内存作为栈区。然后选取FLASH中一点作为线程代码的起始地址,最后调转到线程代码的起始地址开始执行。线程代码可以随意操作被分配的栈中的临时变量而不会干扰到任何其他线程。除非你的临时变量过大超过了分配的栈区,这个就要你使用线程的经验和感觉有关了,一般人都不会去仔细的计算使用多大临时变量空间。线程也有个缺点,就是线程在访问栈区以外的地址时,包括数据区,都会存在这样一种可能,多个线程同时读取和修改一个数据区中的数据时,就会发生边界现象,即多线程共管的数据。当然同时是相对的,相对我们的感觉,现在举例说明:A线程在从Addr地址处读取出数据data后,恰巧被另外一线程B交接,而B线程也读取了Addr地址处的data数据并修改了data的一位,然后...,当再次运行到A线程时,A线程也修改了data并写回到Addr地址处,这样就存在了一个bug,B线程修改的位就被A线程的写回覆盖了。这点其实我们不担心,因为系统一般都会提供很多避免这种现象的机制。
  如果你对多线程还是不了解的话,我估计你应该就是对栈的认识不够准确,可参考相关资料。


  第三节:ucos系统介绍
  关于ucos的广告部分我已经屏蔽,我直接进入正题,ucos是一个抢占式系统,抢占式是指任务以抢占式的方式来运行。把Ucos中的任务当成进程来理解是不恰当的,这会影响我们对Windows进程和Linux进程的理解。Ucos中的任务只能相当于线程的角色。Ucos内容包括两大部分,一个是系统部分:包括任务操作,时间操作,事件操作,内存操作,这些不随处理器的不同而不同。另外一个是接口部分:由汇编和C语言组成。提供任务切换函数,定时器接口,CPU寄存器保存和读取,及中断处理等硬件相关操作函数。

  第四节:创建ucos任务
  使用下面函数创建一个任务:
  INT8UOSTaskCreate(void(*task)(void*p_arg),void*p_arg,OS_STK*ptos,INT8Uprio);

  创建函数设置任务函数(任务代码首地址)和任务参数,分配栈顶(ptos),和优先级。Ptos的传入做法在可读写数据区分配一个数据OS_STKStk【size】.然后把stk最高地址传送给ptos。而stk数组就是默认的分配给任务的栈区,任务task运行后使用stk存储临时变量。
  另外,stk还有个缩水就是系统需要从stk最高位减去一部分空间用来存储寄存器信息,对于ARM是16个unsignedlong长度,用来存储该任务的r0-r15,CPSR.
  系统也会为每一个创建的任务分配一个任务控制块(TCB)。TCB管理者任务的状态和信息。另外还有一个TCB指针指向当前正在运行的任务。TCB控制着当前进程是否在运行,如果不是在运行是否是因为事件阻塞,当任务运行时,从什么地方找到上次运行时保存的信息等等。
  对于每个创建后或运行的任务,都有两个重要的部分,一个是TCB,一个是栈头(栈区顶上保留的空间)。任务开始调度的第一步就是找到该任务的TCB,然后从TCB中找到栈头地址,然后使用栈头保存的数据复制到CPU寄存器上和CPSR上,最后跳转到栈头上上次运行保存的地址处开始执行。当运行的任务被调度时,一样是首先找到TCB所指向的栈头,然后把CPU所有寄存器内容和CPSR及当前地址全部复制过去,再去找到另外一个被任务是应该运行的进程,然后调度那个进程。
  多任务的背景来自一点,其实我们写的大部分程序其实都有太多的延迟,对于没有系统的程序,真正执行效率(即不做无效循环)的时间可能只占到处理器运行的5%都不到。插入一句,如果你善于处理器编程的话,你看代码不应该只看到代码的长度,而是这段代码运行占用了多长时间,和占用哪些资源和多大空间。系统的引入会让我们重新认识任务运行时间,我们不希望程序长时间做无效循环,我们要利用这段时间去做其他的事,从而提高处理器的效率。所以不让认为系统会占用你的资源,系统会帮助你努力收回那95%以上效率。实际上收回全部资源是不可能的。这就看你如何使用架构,和你的任务级别了。每个项目可能都会不同。


  第四节:抢占式调度(ucos的经典)
  调度的意思就是从所有的任务队列中找到最应该运行的任务,然后运行该任务。而调度的方式决定了系统的性能。Ucos的经典就来自于它只用了一个数组采取了最单纯的行为来进行任务调度,同时也占用了最小的资源。所以,即使是8位处理器,很多人也会使用ucos系统。
  上面说过,ucos是抢占式调度。抢占式调度的概念就是,只有一个CPU,所有线程以抢占的方式占有CPU,然后运行任务,除非他主动让出,或他被其他任务抢占,否则,他会一直占用CPU.UCOS的抢占方式是比较优先级,每个任务都需要分配一个且唯一的优先级。每次调度就是比较所有任务的优先级,找到优先级最高的任务(这点其实不复杂,下段介绍),然后调度该任务并运行,最高优先级的任务需要自己主动退出,否则,永远是这一个在运行。当这个任务运行到延迟或等待事件时,系统函数就会把这个任务从运行队列屏蔽掉,然后重新调度,再次搜索最高优先级的任务,这样就找到了另外一个优先级的任务,然后运行该任务,到这个任务睡眠或等待事件时,也会睡眠,然后再次调度,这时,如果前面睡眠的最高优先级的任务被唤醒,那么他将也会被放到优先级队列中。否则,再进入下一个优先级。
  关于任务的队列,睡眠,运行等概念都是一种理解概念,实际上ucos在这点是很简单的,也是很经典的。现在说明下ucos的调度队列。首先,我们需要知道下面个数组是干什么用的。
  INT8UconstOSUnMapTbl[256]={
  0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  6,0,1,0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
  };

  随意给一个8位数data,这个数组的作用就是以查表的方式最快的速度找到data数据中从低位到高位中为第一个为1的数的位置(bit0为0)。即代替下面的函数的作用。使用方法:prto= OSUnMapTbl[data]
  for (i=0; i<8; i++)
  {
   If ( data & (1<    Break;
  }
  理解了那个数组后我们再引入两个变量,
  OS_EXTINT8U OSRdyGrp;
  OS_EXTINT8U OSRdyTbl[OS_RDY_TBL_SIZE];
  每个任务的优先级对应于OSRdyTbl数组中的一个位。对应关系是OSRdyTbl[prio/8]中的的第(prio%8)位,(prio/8)和(prio%8)使用任务控制块TCB中的->OSTCBX和->OSTCBY表示。将OSRdyTbl数组中任务对应的位置置一表示该任务准备妥当,可参加抢占运行, OSRdyTbl数组中任务对应的位置为0表示该任务不存在,或该任务当前被阻塞无法运行。OSRdyGrp变量的作用是使用8个位依次对应OSRdyTbl数组的前8个字节,对第n位为0,代表 OSRdyTbl[n]全部为0,如果第n位为1,代表对应的OSRdyTbl[n]至少有一个为1;
  然后调度工具就开始使用下面机制来得到最高优先级的任务,即优先级号最低的那个任务
  y = OSUnMapTbl[OSRdyGrp]; 
  OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
  第一行代码,通过查表找到OSRdyTbl数组中不为0的最低的一个数组。然后通过第二条代码的OSUnMapTbl[OSRdyTbl[y]]);找到对应的OSRdyTbl[y]中的最低的一个是1的位置。然后与(y << 3)相加,就得到了OSRdyTbl数组中从低到高的最低的为1的1位的位置。即最高优先级任务的优先级,然后根据优先级找到对应的TCB进行调度。
  每次调度时都是关闭了前一个进程,因此ucos需要队列中至少有一个可运行的程序,为此UCOS制作了一个IDLE任务,这个任务优先级最低,在最高位,目的是所有任务进程都睡眠时,让系统仍然有任务可调,不至于崩溃。另外一个用作是统计CPU使用率。如果IDLE任务从没调用过,那就说明的任务抢占度过高,优先级高的任务有需要释放一些空间让优先级低的任务运行。

  第五节:UCOS的实时性能
  按我理解,UCOS的实时性能是一种设想,让所有的任务等处于等待信号阶段,当有中断触发时,执行中断处理函数,通过信号唤醒进程,来完成任务,完成后可以继续睡眠。即使有多个中断响应,只要中断函数能及时响应,那么任务就排着队来完成后续工作。这就是我的UCOS设想。
  所以,我们需要在两个地方调度,一个是中断,每次进入中断后关闭调度,但是允许信号唤醒任务,然后在最后一个中断嵌套完成后退出时进行调度,检测有没有被唤醒的可执行任务。另外一个就是定时中断。每次tick完成后都进行一次重调度。目的是当低优先级执行时没有释放资源,而高优先级的任务已被唤醒。特别是IDLE任务,除非你问它要,否则他不会给你释放资源的。

  第六节:事件处理
  UCOS的事件主要包括SEMAPHORE,Mutex,和Mbox,Q,使用起来都很简单,一般都只适用三个函数创建,挂起等待,释放。
  SEMAPHORE作用是当某个任务运行到必须得到某种资源时进行挂起等待资源满足,其他的任务或中断发送SEMAPHORE表示资源已经建立,你可以运行了,如果是任务在发送SEMAPHORE时发现有任务因此被挂起,会唤醒并调度到该任务上执行。
   Mutex有一种锁的概念,当得到一个东西后,就立马对其上锁,其他的任务就只能等待该任务完成后打开锁才能运行。这里有一个问题就是一旦低优先级的任务占用锁后而高优先级的就必须等待,而恰巧低优先级的又被中优先级的任务抢去执行就会发生,高优先级等低优先级,低优先级等中优先级的现象称为优先级翻转,所有Mutex有一个机制就是高优先级想得到锁的话就临时提高低优先级的优先级,使低优先级尽快完成完成释放锁。
  邮箱MBox基本和SEMPAPHORE相同,只是SEMPAPHORE被当做一个信号标志来传送,Mbox也可以被当做SEMPAPHORE使用,但是会返回一个地址指针。
  Q消息队列没有用过,看样子是首先初始化一个数组,然后对数组使用FIFO的方式发送和接受信件。
  我一般还会再加上一些原子读写函数atom_read/wirte,主要针对边界变量。其实很简单就是读取前关中断,读取后开中断而已。

  第七节:tick
   Tick是系统时间,他和定时的概念是不同的,如OSTimeDly (OS_TICKS_PER_SEC/100),实际上不是严格的延迟了OS_TICKS_PER_SEC/100秒,存在0-1/OS_TICKS_PER_SEC之间的误差。Tick相当于钟表在不停的跑,秒表变化的瞬间被称为tick,而我们是不可能从tick那一瞬间开始计时的。所以这是一个概念是要分清的。
   
  第八节:ucos的缺陷
  UCOS毕竟是一个小系统,甚至可以在8位处理器上运行,所以对于我们完成更复杂的任务和对系统效率更高的要求的话,它是存在一定的局限性的。如:
  1. 系统和应用,中断等关系密切,开发人员需要熟悉系统特性,如。任务被创建后是不能直接退出的,必须使用API函数销毁它。
  2. 调度方式过于单一,任务较少时可以达到平衡,任务较多时,高优先级的和低优先级的运行时间就会存在严重不平衡,并且会增加考虑调度问题。
  3. 缺少异步读取机制,如我想向串口发送数据,而此时串口缓存已满,我们就需要放弃资源调度其他任务。串口可以通过多开缓存来弥补,但是对于TCP,退出就需要至少等待下一个Tick,时间就显得有些长久了,这个机制其实我一直在考虑。 

  第九节:写后
  不喜欢LPC21xx和周立功的UCOS系统还有个原因就是LPC21xx的中断机制看起来不错,但实际上已经能够影响了我们代码的发挥。也可能我自己懒惰的原因,没有来及在LPC上改造ucos。周立功的中断函数使用__irq声明,这一点已经和上面第五节所说内容想违背。
  两外,周立功的关中断函数和开中断函数使用swi中断,我觉得是不如原版的较好。原版的函数是保存寄存器关中断函数和恢复寄存器内容。我本来考虑着周立功可能是考虑软中断可直接进入中断来避免中断干扰,而原版的在关中断函数中间仍有可能被中断,如下 
  MRSR0, CPSR;//复制CPSR,执行后可能被中断
  ORR R1, R0, #0xC0;//计算,也有可能被中断
  MSRCPSR_c, R1;//这个代码完成才真正关闭中断
  但后来相通之后,觉得周立功是多此一举,即使关中断前被中断也没有什么的,因为中断后它会原模原样的返回给你。还是不喜欢周立功的UCOS和LPC
  后来在三星的s3c2440上也架构了一个ucos,并且搭配了TFTP传输和TCP对话,感觉用起来要比LPC的好用很多。
  当然,这只是个人用法和感觉,每个芯片只要写好了软件应该也是不错的。下面稍微提下个人用法,我一般如下定义main函数
  int main(void)
  {
   OSInit();
   OSTaskCreate(MainTask,(void *)1,&MainTaskStk[MainTaskStkLengh-1], MainTaskPrio);
   OSStart(); 
   return 0;
  }
  直接创建一个MainTask任务,然后在MainTask中进行初始化硬件和创建任务,事件
  void MainTask(void *pdata)
  {
   u8 err,iLed=0;
   TargetInit(); 
   env_init();
   PrintMutux=OSMutexCreate(MutexPrintPrior,&err);
   OSTaskCreate (Consoler,(void *)0, &ConsolerStk[ConsolerStkLengh - 1], ConsolerPrio);
   OSTaskCreate(NetConsole,(void *)0, 
   &NetConsoleStk[NetConsoleStkLengh - 1],
   NetConsolePrio);
   while(1){
   iLed++;
   Led_Display(iLed);
   OSTimeDly (400);
   rtcDisplayTime();
   }

  }

关键字:ucos系统  线程 引用地址:ucos系统的精华提炼

上一篇:2440启动代码分析心得体会
下一篇:STM32F10x在环境下的开发过程

推荐阅读最新更新时间:2024-03-16 14:36

时分多线程在单片机系统中的应用研究
随着IT技术的飞速发展,单片机应用系统几乎覆盖了社会生活的各个角落,从消费电子、通信网络、工业控制、汽车到军事等领域皆可觅其踪影;而在硬件、软件以及网络技术日益成熟的今天,其应用形式正呈现多样性和复杂性。尤其是SoC、可配置内核等性能的出现,其可裁剪性使系统设计成本大大降低,减小了系统设计工作量,为单片机应用提供了便利,在产品设计、更新换代等应用方面也备受青睐。 为适应这些纷繁的应用需求.本文就时分多线程技术在单片机中的应用进行了介绍。该方法为构建低成本、高效、便于维护的单片机系统提供了良好的体系框架结构和设计思想。 1 时分多线程结构应用 通常,在单片机应用的各种控制系统中,都或多或少地存在着诸如现场数据
[单片机]
龙芯3A6000处理器将首次支持同步多线程:性能赶上10代酷睿、Zen 2
在Linux 6.5的最新补丁,龙芯方面确认,3A6000处理器将支持SMT(同步多线程),每个物理核心拥有两个逻辑核心。 这意味着,四核的3A6000将支持八个线程,32核的3D6000数据中心处理器将支持64线程。 除了支持超线程,补丁文件还提到,新CPU支持128bit矢量处理器扩展指令(LSX)和256位高级适量处理扩展指令(LASX),启用后将带来新的性能增益。 此前,龙芯董事长胡伟武宣布,下一代龙芯3B6000处理器将会采用4个大核+4个小核的8核CPU架构,并且会集成龙芯自研的GPU(通用图形处理器),预计将于2024年一季度流片。 此前,龙芯中科还在互动平台表示:“基于我们目前的判断,我们认为3A6
[嵌入式]
基于uCOS-II的任务池/线程池 模块
前言 线程池在软件开发中应用的很广泛,其很适合处理需要并行(并发)处理大量类似任务的情况。比如在web服务器端,需要为不同的socket(用户)同时提供服务,典型的模型就是为每个socket分别分配一个线程,一对一进行服务,这就涉及到大量的线程创建和销毁操作。当然,作为一个嵌入式软件,尤其还是以uCOS为操作系统的,一般是不会拿来作为高性能web服务器的。但是还是有很多时候会需要大量动态的线程。 线程池避免了创建和销毁线程带来的大量开销,使得软件效率得到了极大的提升。 uCOS-II中没有线程这个概念,而是使用了基于优先级的任务,同一时间只会运行所有准备好的任务中优先级最高的那个。但是有的时候又有使用类似线程池功能的需求,
[单片机]
基于<font color='red'>uCOS</font>-II的任务池/<font color='red'>线程</font>池 模块
基于单片机的LCD1602控制总线程
    第一行显示"Welcome";第二行显示="Happy day";若要显示其他字符,请直接往数组 LCMLineOne 和LCMLineTwo 填充相应的代码。直接上图,仿真图如下:     源程序如下,可以对比时序方式,理解总线的操作方法。     #include reg51.h //#include absaCC.h #define uchar  unsigned char #define uint  unsigned int #define busy  0x80 uchar  xdata  LCMWriteCOM  _at_ 0x80ff;  //写指令寄存器 uchar  xdata  LCMRea
[单片机]
基于单片机的LCD1602控制总<font color='red'>线程</font>序
MiniARM2300电脑自动打铃器设计与实现ucos操作系统
单片机源程序如下: /****************************************Copyright (c)**************************************************** ** Guangzhou ZHIYUAN electronics Co.,LTD. ** ** **--------------File Info--------------------------------------------------------------------------------- ** File name
[单片机]
基于TCP/IP的多线程通信及其在远程监控系统中的应用
      摘 要: 提出了一种在Windows NT下基于TCP/IP协议的多线程通信的设计与实现方法,在此基础上给出了多线程通信在蓄电池远程监控系统中的应用实例。       关键词: 多线程 实时性 TCP/IP协议 远程监控系统        传统的应用程序都是单线程的,即在程序运行期间,由单个线程独占CPU的控制权,负责执行所有任务。在这种情况下,程序在执行一些比较费时的任务时,就无法及时响应用户的操作,影响了应用程序的实时性能。在监控系统,特别是远程监控系统中,应用程序往往不但要及时把监控对象的最新信息反馈给监视客户(通过图形显示),还要处理本地机与远程机之间的通信以及对控制对象的实时控
[应用]
Intel Skylake新架构的秘密:逆超线程 单核猛增
    英特尔Skylake系列处理器已经公开了相当长一段时间,作为“Tock”部分,新架构的细节英特尔实在是透露的太少。在即将召开的IDF大会之前,来 自德媒Heise.de的一份实测报告终于让我们得以有机会管中窥豹,之前传了很久的“逆超线程(inverse hyper threading)”终于是浮出水面。 我们先来了解一下多核、超线程的意义。芯片的工作频率(时钟频率)1990年代及 2000年 代早期一直在稳步提升,但是主频太快会导致芯片出现功耗过大和过热的问题,因此英特尔等芯片制造商开始走多核化的路线,即限制单个微处理器的主频,通过集成多个处理器内核来提高处理性能。 多(超)线程(HT)指的是一个物理核心可以驾
[手机便携]
用VisualC++实现工控设备多线程控制程序
    摘要: 提出多线程工控制序中最适宜采用工作者线程和事件同步方式,并给出了一个通用的工控程序框架。     关键词: 多线程 同步 事件 多线程技术的引入,不仅可以挖掘潜在的CPU空闲时间,而且还可以提高应用程序的瓜速度,其优点在有多个任务需要完成、有巨大数据流量的程序中反映得尤为明显。而随着Visual C++的引入,其灵活的线程实现机制使得程序员从繁琐的Windows编程中解脱出来。关于多线程基本机理和实现方法近年来有许多文章介绍,这里不再赘述。本文将侧重于比较在工控程序中采用各种线程类型和同步方法的优劣,并给出一个实用的、有较广适应性的程序主体框架。 1 各种线程类型和同步方法
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved