打印

LM3S811板子设计的四足机器人之ucos-ii篇——(四)

[复制链接]
5460|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
guozhiyang|  楼主 | 2011-12-10 22:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
自己很早都开始学习ucosii这个嵌入式操作系统,这个是非常小型的操作系统,相比linux来说真是没有办法比较。
并且这个操作系统在Cortex-M3上跑起来真是不错,感觉就是为它设计的一样。对于LM3S811也是Cortex-M3的芯片
所以用起来很是方便。对于ucosii也在不断地成长,现在ucos-iii也出来了,我看了看里面给增加了=很多的东西,
比ucos-ii有强大了很多。
在本次收到21IC的LM3S811板子后,自己用它做了个四足的机器人,这个是自己很长时间的想法了。所以在本次设计的
四足机器人上将用这操作系统。我见论坛上也有坛友发有关ucosii的帖子,但是自己看了看,讲的都非常的简单,
就是粘贴一些源码,使用的价值很少并且对于ucosii只是一个浅浅的了解。所以趁着本次机器人的需要,我来说说ucosii
的学习,并且结合本次设计运用ucosii的需求,希望对刚接触这个操作系统的人有一点作用。增加咱21ic论坛的人气
和大家分享的精神。
对于ucosii我个人认为应该知道的是:时间管理,ucosii的初始化,任务控制块,时间控制块,任务调度方法,中断的控制
和嵌套,移植的汇编编写。
1、时间管理
在ucosii的时间管理上,都有一个定时器来给ucosii产生定时的中断,让它运行起来。这都需要硬件一个定时器来产生
系统时钟
static  void  tickInit (void)
{
    SysTickPeriodSet((INT32U)(SysCtlClockGet() / OS_TICKS_PER_SEC) -1 );
    SysTickEnable();
    SysTickIntEnable();
}
中断服务程序
void  tickISRHandler (void)
{
    #if OS_CRITICAL_METHOD == 3
        OS_CPU_SR cpu_sr;
    #endif
    OS_ENTER_CRITICAL();                        
    OSIntNesting++;
    OS_EXIT_CRITICAL();
    OSTimeTick();                                                       /*  Call uC/OS-II's OSTimeTick()*/
    OSIntExit();                                 
}
里面的 OSTimeTick();来给ucosii产生系统时钟,这个大家需要看一下源码,太多了我就在说了
2、ucosii的初始化
  在ucosii的初始化中有时间,就绪表,任务就绪表,事件等待表,事件组、内存、消息队列、定时器、
  钩子函数的初始化等的初始化操作
void  OSInit (void)
{
    OSInitHookBegin();                                           /* Call port specific initialization code   */
    OS_InitMisc();                                               /* Initialize miscellaneous variables       */
    OS_InitRdyList();                                            /* Initialize the Ready List                */
    OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */
    OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
    OS_FlagInit();                                               /* Initialize the event flag structures     */
#endif
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
    OS_MemInit();                                                /* Initialize the memory manager            */
#endif
#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
    OS_QInit();                                                  /* Initialize the message queue structures  */
#endif
    OS_InitTaskIdle();                                           /* Create the Idle Task                     */
#if OS_TASK_STAT_EN > 0u
    OS_InitTaskStat();                                           /* Create the Statistic Task                */
#endif
#if OS_TMR_EN > 0u
    OSTmr_Init();                                                /* Initialize the Timer Manager             */
#endif
    OSInitHookEnd();                                             /* Call port specific init. code            */
#if OS_DEBUG_EN > 0u
    OSDebugInit();
#endif
}
3、任务控制块
任务控制块是个链表结构,在任务申请的时候来创建。用它来管理每一个创建的任务,
typedef struct os_tcb {
    OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */
#if OS_TASK_CREATE_EXT_EN > 0u
    void            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */
    OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */
    INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */
    INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */
    INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
#endif
    struct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */
    struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */
#if (OS_EVENT_EN)
    OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
#endif
#if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)
    OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
#endif
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
    void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
#endif
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
#if OS_TASK_DEL_EN > 0u
    OS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
#endif
    OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
#endif
    INT32U           OSTCBDly;              /* Nbr ticks to delay task or, timeout waiting for event   */
    INT8U            OSTCBStat;             /* Task      status                                        */
    INT8U            OSTCBStatPend;         /* Task PEND status                                        */
    INT8U            OSTCBPrio;             /* Task priority (0 == highest)                            */
    INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */
    INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */
    OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */
    OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
#if OS_TASK_DEL_EN > 0u
    INT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
#endif
#if OS_TASK_PROFILE_EN > 0u
    INT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */
    INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */
    INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */
    OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */
    INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
#endif
#if OS_TASK_NAME_EN > 0u
    INT8U           *OSTCBTaskName;
#endif
#if OS_TASK_REG_TBL_SIZE > 0u
    INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
#endif
} OS_TCB;
里面包括了任务的优先级,任务的状态,任务的堆摘的大小,其实位置和结束位置。
特别是任务切换的时候优先级的切换
    INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */
    INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */
    OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */
    OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
这四个变量,要多关注一下。
4、事件控制块
  是自己所申请的事件:信号量,队列,邮箱,事件组,互斥锁等所具有控制信息,任务等待的时间没有发生而在
时间控制块进行注册。
5、任务调度的方法
一个操作系统核心,任务的调度方法。很显然ucosii是一优先级进行调度的
INT8U  const  OSUnMapTbl[256] = {
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
};这个数组能看懂,基本上就懂了它的任务是如何切换的。并且在就需态下选择优先级最高的任务运行。
主要用到OSRdyGrp和OSRdyTbl两个数组来完成就绪态任务选取。
还有中断的控制和移植的汇编程序,以后在跟大家补充。
发个信号量用的例子
#include<include.h>
static  OS_STK   Task_StartStk[TASK_START_STK_SIZE];                    /*  The stack of start task     */
                                                                        /*  启动任务的堆栈              */
static  OS_STK   Task_AStk[TASK_A_STK_SIZE];
static  OS_STK   Task_BStk[TASK_B_STK_SIZE];
//static  OS_STK   Task_CStk[TASK_C_STK_SIZE];
//INT16U box[]="我是中国人,和必须额外问";
OS_EVENT *DispSem;
OS_EVENT *ReMbox;
/*********************************************************************************************************
   FUNCTION PROTOTYPES 函数声明
*********************************************************************************************************/
static  void     taskStart           (void  *parg);                     /*  The start task  启动任务    */
static  void     taskA               (void  *parg);
static  void     taskB               (void  *parg);
//static  void     taskc               (void  *parg);
static  void     taskCreate          (void);
int main (void)
{
    SysCtlLDOSet(SYS_LDO);                                              /* 设置LDO的输出为2.50v         */
    intDisAll();                                                        /* Disable all the interrupts   */
                                                                        /* 关闭所有中断                 */
    OSInit();                                                           /*  Initialize the kernel of uC */
                                                                        /*  OS-II 初始化uC/OS-II的内核  */
    OSTaskCreate ( taskStart,
     (void *)0,
      &Task_StartStk[TASK_START_STK_SIZE - 1],
      TASK_START_PRIO );                                  /*  Initialize the start task   */
                                                                        /*  初始化启动任务              */
    OSStart();                                                          /*  Start uC/OS-II 启动uC/OS-II */
    return(0) ;
}
static  void  taskStart (void  *parg)
{
    (void)parg;
    targetInit();                                                       /*  Initialize the target's MCU */
  //  SSIInit();
   // usart_init();
    #if OS_TASK_STAT_EN > 0
        OSStatInit();                                                   /*  Enable statistics           */
                                                                        /*  使能统计功能                */
    #endif
    /*
     *  Create the other tasks here. 在这里创建其他任务
     */
    taskCreate();
while (1) {
        OSTaskSuspend(OS_PRIO_SELF);                                    /*  The start task can be pended*/
                                                                        /*  here. 启动任务可在这里挂起  */
    }
}
static  void  taskCreate (void)
{
    DispSem = OSSemCreate(1);
  //  ReMbox=OSMboxCreate(NULL);
    OSTaskCreate (taskA, (void *)0,
    &Task_AStk[TASK_A_STK_SIZE-1], TASK_A_PRIO);
    OSTaskCreate (taskB, (void *)0,
    &Task_BStk[TASK_B_STK_SIZE-1], TASK_B_PRIO);
  //  OSTaskCreate (taskc, (void *)0,
  //  &Task_CStk[TASK_C_STK_SIZE-1], TASK_C_PRIO);
}
static void taskA(void *parg)
{
    INT8U err;
   // INT8U box[100];
    (void)parg;
    while (1) {
        OSSemPend(DispSem, 0, &err);                                 /*  等待信号量                  */
        ledToggle(1);
        OSTimeDly(20);
   //     box[]=OSMboxPend(ReMbox,0,&err);
     //   UARTSend(box[],sizeof(box[]));
    }
}
static void taskB(void  *parg)
{
    INT8U uckey;
    (void)parg;
    while (1) {
        uckey = keyRead( );
        switch (uckey) {
        case 0xFE:                                                      /*  KEY1按下                    */
            while (keyRead( ) == 0xFE) {                                /*  等待按键释放,消除按键抖动  */
                ;
            }
            OSSemPost(DispSem);                                  /*  发送信号量                  */
            break;
        default:
            break;
        }
        ledToggle(3);
        OSTimeDly(2);
    }
}
/*void taskc(void *parg)
{
  INT8U err;
  parg=parg;
// OSSemPend(DispSem,0,&err);
//  UARTSend(box[],sizeof(box[]));
  OSTimeDly(20);
}*/

相关帖子

沙发
guozhiyang|  楼主 | 2011-12-10 22:53 | 只看该作者
明天再能把,今天有点晚了。大家共同学习啊!

使用特权

评论回复
板凳
shimx| | 2011-12-11 10:53 | 只看该作者
只有四?别的呢

使用特权

评论回复
地板
guozhiyang|  楼主 | 2011-12-11 13:27 | 只看该作者
都有,网往后看看

使用特权

评论回复
5
guozhiyang|  楼主 | 2011-12-12 12:57 | 只看该作者
在这里说一下ucosii的信号量、邮箱、消息队列。这三个都是来实现任务见通信和切换的方法。但是在使用之前要知道它们的用法。下面介绍一下最常用的函数。
1、信号量:
   信号量就是来进行任务切换的。
   *OSSemCreate (INT16U cnt);来创建信号量并且赋予初始值。
   OSSemPend (OS_EVENT  *pevent,INT32U  timeout,INT8U *perr)这个可以挂起一个任务,来切换任务,运行就绪表中优先级最高的任务,并且信号量数减一。
  INT8U  OSSemPost (OS_EVENT *pevent);这个函数是来发送信号量信息,并且信号量值加以,在事件就绪表中查找被信号量阻塞的最高优先级的任务来让其就绪,在查找任务就绪表中任务最高的任务并且让其运行。
  一般的这三个函数最常用。
2、邮箱:
   邮箱可以切换任务,并且也可以在任务之间传递信息。
   OS_EVENT  *OSMboxCreate (void *pmsg)用来创建邮箱并且可以初始化邮箱中的信息。
   void  *OSMboxPend (OS_EVENT  *pevent,INT32U timeout,INT32U *perr)
这个函数是用来获取邮箱的信息,如果在规定的时间内邮箱中没有信息就阻塞任务,在任务就绪表中查找最高级的任务并且执行它。
   INT8U  OSMboxPost (OS_EVENT  *pevent,void      *pmsg)用来向消息中发送信息,并且通过查找事件就绪表中申请邮箱的最高优先级的任务就绪,并且在查找任务就绪表中优先级的任务让其运行。
一般情况下这三个函数最常用。
3、消息队列:
  消息队列就好比多个邮箱一样,可以存储多个信息。
  OS_EVENT  *OSQCreate (void    **start,INT16U    size) 来创建消息队列,并且给出消息队列的首地址和队列的尺寸值。
  void  *OSQPend (OS_EVENT  *pevent,INT32U     timeout,INT8U     *perr)
这个函数用来获取消息队列的信息,当在设定的时间内仍然没有获得消息队列的信息,就挂起当前的任务。查找就绪表中优先级最高的任务并且运行它。
INT8U  OSQPost (OS_EVENT  *pevent,void      *pmsg)这个函数是向消息队列中发送信息并且查找事件就绪表中被消息队列挂起的最高优先级的任务,和就绪表中最高优先级的任务比较优先级值,越小的任务的优先级越高。并且运行其中最高优先级的任务。
  一般情况下这三个函数最常用。
下面给出我写的在ucosii下的uart的驱动程序用到了上面三种,并且用中断的程序来发送消息队列的信息。
#include <includes.h>
#include "UART_INIT.h"
#include "Main.h"
OS_EVENT  *SEM_UART;     //信号量指针
OS_EVENT  *BOX_UART;     //邮箱指针
OS_EVENT  *Q_UART;        //消息队列指针
void *Point;
uint8 shuju[]={"实践出真理\r\n"};
uint8 changdu,fuhao=1;
uint8 Q_LED[]={0xFF};
int main()
{
  SysCtlLDOSet(SYS_LDO);
  intDisAll();
// targetInit();
  OSInit();                          //ucosii初始化
  OSTaskCreate(taskstart,0,&STKSTART[TASK_START_STK_SIZE-1],TASK_START_PRIO);
  OSStart();
  return (0);
}
void taskstart(void *parg)
{
  parg=parg;
  targetInit();
  Uart_Init();
  ledInit();
  keyInit();
  SEM_UART=OSSemCreate(0);                 //信号量创建
  BOX_UART=OSMboxCreate(0);               //邮箱的创建
  Q_UART=OSQCreate(Point,10);               //信号量创建
  #if OS_TASK_STAT_EN>0
  OSStatInit();
  #endif
  OSTaskCreate(taskuart,0,&STKUART[UART_STK_SIZE-1],UART_PRIO);
  OSTaskCreate(TaskLed,0,&STKLED[LED_STK_SIZE-1],LED_PRIO);
  while(1)
  {
    ledOn(4);
   // OSTimeDly(10);
     OSTaskSuspend(OS_PRIO_SELF);
  }
}
void taskuart(void *prag)
{
  prag=prag;
  uint8 err,*Mbox_Data,err1,*Q_DATA;
// changdu=sizeof()
  OSSemPend(SEM_UART,0,&err);                //申请信号量值
  ledOff(4);
  Mbox_Data=OSMboxPend(BOX_UART,0,&err1);  //要邮箱信息
  while(*Mbox_Data)
  {
    Q_DATA=OSQPend(Q_UART,0,&err);              //要消息队列的信息
   if(*Q_DATA)
    {
      ledOn(3);
      ledOff(2);
      OSSemPend(SEM_UART,0,&err);               //申请信号量值
      OSTimeDly(100);
   //   break;
    }
  //   *Mbox_Data=0;
   //  ledOff(3);
     //OSTimeDly(10);
   //  break;
  }
}
void TaskLed(void *prag)
{
  prag=prag;
  uint8 KEY;
  OSSemPost(SEM_UART);                       //释放信号量值
  KEY=GPIOPinRead(KEY1_GPIO_PORT,KEY1_PIN);
  while(KEY)
  {
    ledOn(1);
    OSMboxPost(BOX_UART,&fuhao);       //发送邮箱信息
    UART_Send(shuju,(sizeof(shuju)-1));
    ledOn(2);
  //  ledOff(3);
//   delayms(100);
   // OSTimeDlyHMSM(0,0,0,30);
  }
//  OSTimeDly(100);
}

void UART0_ISR (void)
{
    unsigned long ulStatus;
      #if OS_CRITICAL_METHOD == 3
          OS_CPU_SR cpu_sr;
      #endif
      OS_ENTER_CRITICAL();
      OSIntNesting++;
      OS_EXIT_CRITICAL();
    ulStatus = UARTIntStatus(UART0_BASE, true);                         /*  获得中断状态              */
    UARTIntClear(UART0_BASE, ulStatus);                                 /*  清除等待响应的中断        */
   // OSMboxPost(BOX_UART,&fuhao);
     OSQPost(Q_UART,Q_LED);                  //发送消息队列信息
     if (ulStatus & (UART_INT_TX|UART_INT_RX)) {                                       /*  检查是否有未响应的传输中断*/
        while (Uart_ulCount&&UARTSpaceAvail(UART0_BASE)) {          /*  处理传输中断              */   
            UARTCharNonBlockingPut(UART0_BASE, *Uart_pucBuffer++);         /*  发送下一个字符            */
            Uart_ulCount--;                                                /*  发送字符数自减            */
        }
    }
    OSIntExit();
}

void delayms(uint8 time)
{
  uint8 i,j;
  for(i=0;i<time;i++)
    for(j=200;j>0;j--);
}

使用特权

评论回复
6
guozhiyang|  楼主 | 2011-12-12 17:55 | 只看该作者
ucosii得驱动学习
uCOS-II所有驱动应用举例.rar (1.39 MB)
ucos-ii驱动框架.rar (45.47 KB)

使用特权

评论回复
评分
参与人数 1威望 +3 收起 理由
一路向南 + 3 很给力!
7
关小波522| | 2011-12-12 23:14 | 只看该作者
一直想学UC/OS,苦于不知道如何如门,谢谢楼主的分享,好好学习。

使用特权

评论回复
8
guozhiyang|  楼主 | 2011-12-13 12:13 | 只看该作者
先把这个操作系统学好了,在学其它的就有思想了。我会在发贴,希望大家多支持一下啊

使用特权

评论回复
9
lzlong| | 2011-12-13 15:42 | 只看该作者
一直在看楼主写的帖子 真的很好

使用特权

评论回复
10
guozhiyang|  楼主 | 2011-12-13 17:55 | 只看该作者
谢谢9楼的支持啊!

使用特权

评论回复
11
guozhiyang|  楼主 | 2011-12-13 22:41 | 只看该作者
我建了个Ti的cortex-M3和ucosii的群大家可以有兴趣可以加一下165387072

使用特权

评论回复
12
guozhiyang|  楼主 | 2011-12-13 22:55 | 只看该作者
abbr_acb61de0cd93cac35450db40d5625893.rar (2.29 MB)

使用特权

评论回复
13
唯爱盼1314| | 2013-8-11 07:56 | 只看该作者
你好  我想问一下 我想用ucos2做一个六足机器人 现在没思路 不知道动作怎么封装成任务  就是那些任务该如何切换才能达到完整的动作 还有就是 任务里面处理PWM波的方式 怎样才能实现一连串的动作
  我用的STM32  谢谢大家了

使用特权

评论回复
14
一路向南| | 2013-8-11 14:13 | 只看该作者
很好的资料,顶!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

67

帖子

3

粉丝