精华内容
下载资源
问答
  • ucosiii

    2018-12-05 13:30:07
    版权声明:本文为博主原创文章,允许转载,但希望标注转载来源。 ... UCOSIII任务调度可剥夺型任务调度任务调度就是中止当前正在运行的任务转而去执行其他...
    版权声明:本文为博主原创文章,允许转载,但希望标注转载来源。 https://blog.csdn.net/qq_38410730/article/details/80753631

    UCOSIII任务调度

    可剥夺型任务调度

    任务调度就是中止当前正在运行的任务转而去执行其他的任务。

    UCOSIII是可剥夺型内核,因此当一个高优先级的任务准备就绪,并且此时发生了任务调度,那么这个高优先级的任务就会获得CPU的使用权!

    UCOSIII中的任务调度是由任务调度器来完成的,任务调度器有2种:任务级调度器和中断级调度器。

    • 任务级调度器为函数OSSched();
    • 中断级调度器为函数OSIntExit(),当退出外部中断服务函数的时候使用中断级任务调度。

    任务级调度器函数OSSched()

    先看一下这个函数的定义,该函数代码在os_core.c文件中:

    1. void OSSched (void)
    2. {
    3. CPU_SR_ALLOC();
    4. if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* 检查是否在中断服务函数中调用 */
    5. return; /* 任务型调度函数,不能在中断中使用 */
    6. }
    7. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* 检查调度器是否加锁 */
    8. return;
    9. }
    10. CPU_INT_DIS();
    11. OSPrioHighRdy = OS_PrioGetHighest(); /* 获取任务就绪表中就绪了的最高优先级任务 */
    12. OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
    13. if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task is still highest priority task? */
    14. CPU_INT_EN(); /* Yes ... no need to context switch */
    15. return;
    16. }
    17. #if OS_CFG_TASK_PROFILE_EN > 0u
    18. OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches to this task */
    19. #endif
    20. OSTaskCtxSwCtr++; /* Increment context switch counter */
    21. #if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    22. OS_TLS_TaskSw();
    23. #endif
    24. OS_TASK_SW(); /* 执行任务切换 */
    25. CPU_INT_EN();
    26. }

    在OSSched()中真正执行任务切换的是宏OS_TASK_SW()(在os_cpu.h中定义),宏OS_TASK_SW()就是函数OSCtxSW(),OSCtxSW()是os_cpu_a.asm中用汇编写的一段代码,OSCtxSW()要做的就是将当前任务的CPU寄存器的值保存在任务堆栈中,也就是保存现场,保存完当前任务的现场后将新任务的OS_TCB中保存的任务堆栈指针的值加载到CPU的堆栈指针寄存器中,最后还要从新任务的堆栈中恢复CPU寄存器的值。

    中断级调度器函数OSIntExit()

    调用OSIntExit()时,中断应该是关闭的。先看一下这个函数的定义:

    1. void OSIntExit (void)
    2. {
    3. CPU_SR_ALLOC();
    4. if (OSRunning != OS_STATE_OS_RUNNING) { /* 判断UCOSIII是否运行 */
    5. return;
    6. }
    7. CPU_INT_DIS();
    8. if (OSIntNestingCtr == (OS_NESTING_CTR)0) { /* 中断嵌套计数器 */
    9. CPU_INT_EN();
    10. return;
    11. }
    12. OSIntNestingCtr--;
    13. if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* 还有中断发生,跳回到中断服务程序中,不做任务切换 */
    14. CPU_INT_EN();
    15. return;
    16. }
    17. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* 检查调度器是否加锁 */
    18. CPU_INT_EN();
    19. return;
    20. }
    21. OSPrioHighRdy = OS_PrioGetHighest(); /* Find highest priority */
    22. OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* Get highest priority task ready-to-run */
    23. if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
    24. CPU_INT_EN(); /* Yes */
    25. return;
    26. }
    27. #if OS_CFG_TASK_PROFILE_EN > 0u
    28. OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches for this new task */
    29. #endif
    30. OSTaskCtxSwCtr++; /* Keep track of the total number of ctx switches */
    31. #if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    32. OS_TLS_TaskSw();
    33. #endif
    34. OSIntCtxSw(); /*执行任务切换 */
    35. CPU_INT_EN();
    36. }

    在中断级调度器中真正完成任务切换的就是中断级任务切换函数OSIntCtxSW(),与任务级切换函数OSCtxSW()不同的是,由于进入中断的时候现场已经保存过了,所以OSIntCtxSW()不需要像OSCtxSW()一样先保存当前任务现场,只需要做OSCtxSW()的后半部分工作,也就是从将要执行的任务堆栈中恢复CPU寄存器的值。

    其中:OSIntNestingCtr为中断嵌套计数器,进入中断服务函数后我们要调用OSIntEnter()函数,在这个函数中会将OSIntNestingCtr加1,用来记录中断嵌套的次数。而OSIntExit()是在退出中断服务函数时调用的,因此中断嵌套计数器要减1。如果减1之后,OSIntNestingCtr还大于0,说明还有其他的中断发生,那么就跳回到中断服务程序中,不需要做任务切换。

    任务调度点

    知道了任务调度的过程,那么什么时候发生任务调度呢?

    • 使用延时函数OSTimeDly()或者OSTimeDlyHMSM();
    • 创建任务;
    • 删除任务;
    • 任务通过调用OSTaskSuspend()将自身挂起;
    • 任务解挂某个挂起的任务;
    • 用户调用OSSched();
    • 释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度;
    • 任务等待的事情还没发生(等待信号量,消息队列等);
    • 任务取消等待;
    • 删除一个内核对象;
    • 任务改变自身的优先级或者其他任务的优先级;
    • 退出所有的嵌套中断;
    • 通过OSSchedUnlock()给调度器解锁;
    • 任务调用OSSchedRoundRobinYield()放弃其执行时间片。

    这么多么多调度点中,前六个尤其重要!

    调度器上锁和解锁

    有时候我们并不希望发生任务调度,因为始终有一些代码的执行过程是不能被打断的。此时我们就可以使用函数OSSchedLock()对调度器加锁,当我们想要恢复任务调度的时候就可以使用函数OSSchedUnlock()给已经上锁的任务调度器解锁。

    时间片轮转调度

    UCOSIII允许一个优先级下有多个任务,要使用这个功能我们需要定义OS_CFG_SCHED_ROUND_ROBIN_EN为1,这些任务的调度是一个值得考虑的问题。不过这不是我们要考虑的,貌似说了一句废话。

    在UCOSIII中允许一个任务运行一段时间(时间片)后让出CPU的使用权,让拥有同优先级的下一个任务运行,这种任务调度方法就是时间片轮转调度。

    我们先看一个例子:


    (1) 任务3正在运行,这时一个时钟节拍中断发生,但是任务3的时间片还没完成;

    (2) 任务3的时钟片用完;

    (3) UCOSIII切换到任务1,任务1是优先级N下的下一个就绪任务;

    (4) 任务1连续运行至时间片用完;

    (5) 任务3运行;

    (6) 任务3调用OSSchedRoundRobinYield()(在os_core.c文件中定义)函数放弃剩余的时间片,从而使优先级X下的下一个就绪的任务运行;

    (7) UCOSIII切换到任务1;

    (8) 任务1执行完其时间片。

    时间片轮转调度器为:OS_SchedRoundRobin(),函数代码如下:

    1. void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
    2. {
    3. OS_TCB *p_tcb;
    4. CPU_SR_ALLOC();
    5. if (OSSchedRoundRobinEn != DEF_TRUE) { /* 检查时间片轮转调度是否允许 */
    6. return;
    7. }
    8. CPU_CRITICAL_ENTER();
    9. p_tcb = p_rdy_list->HeadPtr; /* 获取某一优先级下就绪任务列表中的第一个任务 */
    10. if (p_tcb == (OS_TCB *)0) {                            //没有任务就绪那就直接返回
    11. CPU_CRITICAL_EXIT();
    12. return;
    13. }
    14. if (p_tcb == &OSIdleTaskTCB) {                //为空闲任务的TCB,那么也就直接返回
    15. CPU_CRITICAL_EXIT();
    16. return;
    17. }
    18. if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {                //TimeQuantaCtr字段表示当前任务的时间片还剩多少
    19. p_tcb->TimeQuantaCtr--;
    20. }
    21. if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* 说明任务的时间片还没用完,那么就不能进行任务切换 */
    22. CPU_CRITICAL_EXIT();
    23. return;
    24. }
    25. if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { /* NbrEntries字段表示某一优先级下的任务数量 */
    26. CPU_CRITICAL_EXIT(); /* NbrEntries是否小于2,如果任务数小于2就不需要做任务切换 */
    27. return;
    28. }
    29. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* 判断调度器是否上锁,如果上锁的话就直接返回 */
    30. CPU_CRITICAL_EXIT();
    31. return;
    32. }
    33. OS_RdyListMoveHeadToTail(p_rdy_list); /* 将当前任务的OS_TCB从双向链表头移到链表尾 */
    34. p_tcb = p_rdy_list->HeadPtr; /* 获取新的双向链表头,也就是下一个要执行的任务 */
    35. if (p_tcb->TimeQuanta == (OS_TICK)0) { /* 要为下一个要执行的任务装载时间片值 */
    36. p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
    37. } else {
    38. p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time */
    39. }
    40. CPU_CRITICAL_EXIT();
    41. }

    通过上面的程序我们可以清晰的看到,如果某一优先级下有多个任务话,这些任务是如何被调度和运行的:先判断该任务的时间片是否有剩余,如果有就直接返回不切换;如果没有,就进行切换。每次任务切换后运行的都是处于就绪任务列表OSRdyList[]链表头的任务,当这个任务的时间片用完后这个任务就会被放到链表尾,然后再运行新的链表头的任务。


    UCOSIII任务切换

    当UCOSIII需要切换到另外一个任务时,它将保存当前任务的现场到当前任务的堆栈中,主要是CPU寄存器值,然后恢复新的现场并且执行新的任务,这个过程就是任务切换。

    在上文的任务调度函数中,我们提到:

    • 任务级调度器函数OSSched()中真正执行任务切换的是函数OSCtxSW();
    • 中断级调度器函数OSIntExit()中真正完成任务切换的是函数OSIntCtxSW()。

    其实后面这两个函数就是UCOSIII的任务切换函数。任务切换分为两种:任务级切换和中断级切换。

    • 任务级切换函数为:OSCtxSw();
    • 中断级切换函数为:OSIntCtxSw()。

    这两个函数都是用汇编语言来编写的,以寻求最快的运行速度:

    1. OSCtxSw
    2. LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
    3. LDR R1, =NVIC_PENDSVSET
    4. STR R1, [R0]
    5. BX LR
    1. OSIntCtxSw
    2. LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
    3. LDR R1, =NVIC_PENDSVSET
    4. STR R1, [R0]
    5. BX LR

    # 欢迎使用Markdown编辑器

    你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

    新的改变

    我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

    1. 全新的界面设计 ,将会带来全新的写作体验;
    2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
    3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
    4. 全新的 KaTeX数学公式 语法;
    5. 增加了支持甘特图的mermaid语法1 功能;
    6. 增加了 多屏幕编辑 Markdown文章功能;
    7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
    8. 增加了 检查列表 功能。

    功能快捷键

    撤销:Ctrl/Command + Z
    重做:Ctrl/Command + Y
    加粗:Ctrl/Command + B
    斜体:Ctrl/Command + I
    标题:Ctrl/Command + Shift + H
    无序列表:Ctrl/Command + Shift + U
    有序列表:Ctrl/Command + Shift + O
    检查列表:Ctrl/Command + Shift + C
    插入代码:Ctrl/Command + Shift + K
    插入链接:Ctrl/Command + Shift + L
    插入图片:Ctrl/Command + Shift + G

    合理的创建标题,有助于目录的生成

    直接输入1次#,并按下space后,将生成1级标题。
    输入2次#,并按下space后,将生成2级标题。
    以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

    如何改变文本的样式

    强调文本 强调文本

    加粗文本 加粗文本

    标记文本

    删除文本

    引用文本

    H2O is是液体。

    210 运算结果是 1024.

    插入链接与图片

    链接: link.

    图片: Alt

    带尺寸的图片: Alt

    当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

    如何插入一段漂亮的代码片

    博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

    // An highlighted block
    var foo = 'bar';
    

    生成一个适合你的列表

    • 项目
      • 项目
        • 项目
    1. 项目1
    2. 项目2
    3. 项目3
    • 计划任务
    • 完成任务

    创建一个表格

    一个简单的表格是这么创建的:

    项目 Value
    电脑 $1600
    手机 $12
    导管 $1

    设定内容居中、居左、居右

    使用:---------:居中
    使用:----------居左
    使用----------:居右

    第一列 第二列 第三列
    第一列文本居中 第二列文本居右 第三列文本居左

    SmartyPants

    SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

    TYPE ASCII HTML
    Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
    Quotes "Isn't this fun?" “Isn’t this fun?”
    Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

    创建一个自定义列表

    Markdown
    Text-to-HTML conversion tool
    Authors
    John
    Luke

    如何创建一个注脚

    一个具有注脚的文本。2

    注释也是必不可少的

    Markdown将文本转换为 HTML

    KaTeX数学公式

    您可以使用渲染LaTeX数学表达式 KaTeX:

    Gamma公式展示 Γ(n)=(n1)!nN\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N 是通过欧拉积分

    Γ(z)=0tz1etdt&ThinSpace;. \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

    你可以找到更多关于的信息 LaTeX 数学表达式here.

    新的甘特图功能,丰富你的文章

    Mon 06Mon 13Mon 20已完成 进行中 计划一 计划二 现有任务Adding GANTT diagram functionality to mermaid
    • 关于 甘特图 语法,参考 这儿,

    UML 图表

    可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

    张三李四王五你好!李四, 最近怎么样?你最近怎么样,王五?我很好,谢谢!我很好,谢谢!李四想了很长时间,文字太长了不适合放在一行.打量着王五...很好... 王五, 你怎么样?张三李四王五

    这将产生一个流程图。:

    链接
    长方形
    圆角长方形
    菱形
    • 关于 Mermaid 语法,参考 这儿,

    FLowchart流程图

    我们依旧会支持flowchart的流程图:

    Created with Raphaël 2.2.0开始我的操作确认?结束yesno
    • 关于 Flowchart流程图 语法,参考 这儿.

    导出与导入

    导出

    如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

    导入

    如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
    继续你的创作。


    1. mermaid语法说明 ↩︎

    2. 注脚的解释 ↩︎

    展开全文
  • UCOSIIIUCOSIII的初始化和启动

    千次阅读 2018-06-22 18:32:26
    在使用UCOSIII之前我们必须先初始化UCOSIII,函数OSInit()用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括OSStart()。 一般UCOSIII的main函数遵循以下的格式编写: int main(void) { ...

    UCOSIII系统初始化

    在使用UCOSIII之前我们必须先初始化UCOSIII,函数OSInit()用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括OSStart()。

    一般UCOSIII的main函数遵循以下的格式编写:

    int main(void)
    {
       OS_ERR err;
       ……
       //其他函数,一般为外设初始化函数
       ……
       OSInit(&err);
       ……
       //其他函数,一般为创建任务函数
       ……
       OSStart(&err);
    }

    也就是说:

    • 最先肯定是要调用OSInit()初始化UCOSIII;
    • 创建任务,一般我们在main()函数中只创建一个start_task任务,其他任务都在start_task任 务 中 创 建, 在 调 用OSTaskCreate()函 数 创 建 任 务 的 时 候 一 定 要 调 用OS_CRITICAL_ENTER()函数进入临界区,任务创建完以后调用OS_CRITICAL_EXIT()函数退出临界区;
    • 最后调用OSStart()函数开启UCOSIII。

    需要注意:我们在调用OSStart()开启UCOSIII之前一定要至少创建一个任务,其实我们在调用OSInit()函数初始化UCOSIII的时候已经创建了一个空闲任务和时钟节拍任务。

    接下来,我们看一下UCOSIII的初始化函数OSInit()函数:

    void  OSInit (OS_ERR  *p_err)
    {
        CPU_STK      *p_stk;
        CPU_STK_SIZE  size;
    
    #ifdef OS_SAFETY_CRITICAL
        if (p_err == (OS_ERR *)0) {
            OS_SAFETY_CRITICAL_EXCEPTION();
            return;
        }
    #endif
    
        OSInitHook();                                           /* 钩子函数    */
    
        OSIntNestingCtr                 = (OS_NESTING_CTR)0;    /* Clear the interrupt nesting counter   */
    
        OSRunning                       =  OS_STATE_OS_STOPPED; /* UCOSIII正在运行的标志  */
    
        OSSchedLockNestingCtr           = (OS_NESTING_CTR)0;    /* Clear the scheduling lock counter   */
    
        OSTCBCurPtr                     = (OS_TCB *)0;          /* 指向正在运行任务控制块的指针 */
        OSTCBHighRdyPtr                 = (OS_TCB *)0;        //指向最高优先级就绪任务控制块的指针
    
        OSPrioCur                       = (OS_PRIO)0;           /* 正在运行任务的优先级 */
        OSPrioHighRdy                   = (OS_PRIO)0;            //最高优先级别的就绪任务的优先级
        OSPrioSaved                     = (OS_PRIO)0;
    
    #if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
        OSSchedLockTimeBegin            = (CPU_TS)0;
        OSSchedLockTimeMax              = (CPU_TS)0;
        OSSchedLockTimeMaxCur           = (CPU_TS)0;
    #endif
    
    #ifdef OS_SAFETY_CRITICAL_IEC61508
        OSSafetyCriticalStartFlag       =  DEF_FALSE;
    #endif
    
    #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
        OSSchedRoundRobinEn             = DEF_FALSE;
        OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
    #endif
    
        if (OSCfg_ISRStkSize > (CPU_STK_SIZE)0) {
            p_stk = OSCfg_ISRStkBasePtr;                        /* Clear exception stack for stack checking. */
            if (p_stk != (CPU_STK *)0) {
                size  = OSCfg_ISRStkSize;
                while (size > (CPU_STK_SIZE)0) {
                    size--;
                   *p_stk = (CPU_STK)0;
                    p_stk++;
                }
            }
        }
    
    #if OS_CFG_APP_HOOKS_EN > 0u
        OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0;          /* Clear application hook pointers    */
        OS_AppTaskDelHookPtr    = (OS_APP_HOOK_TCB )0;
        OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB )0;
    
        OS_AppIdleTaskHookPtr   = (OS_APP_HOOK_VOID)0;
        OS_AppStatTaskHookPtr   = (OS_APP_HOOK_VOID)0;
        OS_AppTaskSwHookPtr     = (OS_APP_HOOK_VOID)0;
        OS_AppTimeTickHookPtr   = (OS_APP_HOOK_VOID)0;
    #endif
    
    #if OS_CFG_TASK_REG_TBL_SIZE > 0u
        OSTaskRegNextAvailID    = (OS_REG_ID)0;
    #endif
    
        OS_PrioInit();                                          /* 优先级初始化  */
    
        OS_RdyListInit();                                       /* 任务就绪列表初始化  */
    
        
    #if OS_CFG_FLAG_EN > 0u                                     /* Initialize the Event Flag module  */
        OS_FlagInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_MEM_EN > 0u                                      /* Initialize the Memory Manager module  */
        OS_MemInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if (OS_MSG_EN) > 0u                                        /* Initialize the free list of OS_MSGs  */
        OS_MsgPoolInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_MUTEX_EN > 0u                                    /* Initialize the Mutex Manager module   */
        OS_MutexInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_Q_EN > 0u
        OS_QInit(p_err);                                        /* Initialize the Message Queue Manager module            */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_SEM_EN > 0u                                      /* Initialize the Semaphore Manager module                */
        OS_SemInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
        OS_TLS_Init(p_err);                                     /* Initialize Task Local Storage, before creating tasks  */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
        OS_TaskInit(p_err);                                     /* Initialize the task manager     */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    
    
    #if OS_CFG_ISR_POST_DEFERRED_EN > 0u
        OS_IntQTaskInit(p_err);                                 /* Initialize the Interrupt Queue Handler Task            */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
        
        OS_IdleTaskInit(p_err);                                 /* 空闲任务初始化  */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    
    
        OS_TickTaskInit(p_err);                                 /* 时钟节拍任务初始化 */
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    
    
    #if OS_CFG_STAT_TASK_EN > 0u                                /* Initialize the Statistic Task     */
        OS_StatTaskInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_TMR_EN > 0u                                      /* Initialize the Timer Manager module   */
        OS_TmrInit(p_err);
        if (*p_err != OS_ERR_NONE) {
            return;
        }
    #endif
    
    
    #if OS_CFG_DBG_EN > 0u
        OS_Dbg_Init();
    #endif
    
    
        OSCfg_Init();
    }

    可以看出,函数OSInit()主要负责建立任务控制链表、就绪任务表等一些数据结构,并对系统使用的全局变量进行初始化。

    我们之前讲5个系统任务的时候说到,空闲任务、时钟节拍任务是必须创建的任务;而统计任务、定时任务、中断服务管理任务则是可选任务。怎么保证这一点呢?

    在这里就得到解答了。在UCOSIII的系统初始化函数OSInit()中,前两个任务OS_IdleTaskInit()和OS_TickTaskInit()都是一定要运行的,而后三个任务OS_StatTaskInit()、OS_TmrInit()和OS_IntQTaskInit()都采用条件编译的方式。只有在OS_CFG.h文件中配置开启后,才会运行这些任务。

    既然是任务,那么就一定有优先级。那么这5个系统任务的优先级有事怎么分布的呢?

    UCOSIII中以下优先级用户程序不能使用,因为这些优先级分配给了UCOSIII的5个系统内部任务:

    • 优先级0:中断服务服务管理任务 OS_IntQTask();
    • 优先级1:时钟节拍任务 OS_TickTask();
    • 优先级2:定时任务 OS_TmrTask();
    • 优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask();
    • 优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()。

     

    UCOSIII系统启动

    使用函数OSStart()来启动UCOSIII,函数如下:

    void  OSStart (OS_ERR  *p_err)
    {
    #ifdef OS_SAFETY_CRITICAL
        if (p_err == (OS_ERR *)0) {
            OS_SAFETY_CRITICAL_EXCEPTION();
            return;
        }
    #endif
    
        if (OSRunning == OS_STATE_OS_STOPPED) {
            OSPrioHighRdy   = OS_PrioGetHighest();              /* 寻找有就绪任务的最高优先级 */
            OSPrioCur       = OSPrioHighRdy;
            OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;        //寻找该优先级的任务控制块
            OSTCBCurPtr     = OSTCBHighRdyPtr;
            OSRunning       = OS_STATE_OS_RUNNING;
            OSStartHighRdy();                                   /* Execute target specific code to start task    */
           *p_err           = OS_ERR_FATAL_RETURN;              /* OSStart() is not supposed to return   */
        } else {
           *p_err           = OS_ERR_OS_RUNNING;                /* OS is already running  */
        }
    }

    在函数OSStart()中,用到了表示内核是否处于运行状态的变量OSRunning。若该变量为FALSE,则意味着内核处于未运行状态;若该变量为TRUE,则意味着内核处于运行状态。

    由于在调用函数OSInit()进行初始化之后,已经将该值初始化为FALSE,所以在OSStart()函数中首先对OSRunning进行判断。如果该值为假,则查找最高优先级的就绪任务开始运行,并将OSRunning赋值为TRUE;否则,意味着内核已经处于运行状态了,函数OSInit()就什么工作也不做地返回。

     

    展开全文
  • UCOSIIIUCOSIII软件定时器

    万次阅读 2018-06-29 19:15:40
    在学习STM32的时候会使用定时器来做很多定时任务,这个定时器是单片机自带的,也就是硬件定时器,在UCOSIII中提供了软件定时器,我们可以使用这些软件定时器完成一些功能,本文我们就讲解一下UCOSIII软件定时器。...

    在学习STM32的时候会使用定时器来做很多定时任务,这个定时器是单片机自带的,也就是硬件定时器,在UCOSIII中提供了软件定时器,我们可以使用这些软件定时器完成一些功能,本文我们就讲解一下UCOSIII软件定时器。

     

    UCOSIII软件定时器简介

    定时器其实就是一个递减计数器,当计数器递减到0的时候就会触发一个动作,这个动作就是回调函数,当定时器计时完成时就会自动的调用这个回调函数。因此我们可以使用这个回调函数来完成一些设计。比如,定时10秒后打开某个外设等等,在回调函数中应避免任何可以阻塞或者删除定时任务的函数。

    如果要使用定时器的话需要将宏OS_CFG_TMR_DEL_EN定义为1。

    定时器的分辨率由我们定义的系统节拍频率OS_CFG_TICK_RATE_HZ决定,比如我们定义为200,系统时钟周期就是5ms,定时器的最小分辨率肯定就是5ms。但是定时器的实际分 辨 率 是 通 过 宏OS_CFG_TMR_TASK_RATE_HZ定 义 的 , 这 个 值 绝 对 不 能 大 于OS_CFG_TICK_RATE_HZ。比如我们定义OS_CFG_TMR_TASK_RATE_HZ为100,则定时器的时间分辨率为10ms。有关UCOSIII定时器的函数都在os_tmr.c文件中。

    什么是回调函数呢?

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

     

    UCOSIII软件定时器API函数

    UCOSIII软件定时器API函数
    函数 含义
    OSTmrCreate() 创建定时器并制定运行模式
    OSTmrDel() 删除定时器
    OSTmrRemainGet() 获取定时器的剩余时间
    OSTmrStart() 启动定时器计数
    OSTmrStateGet() 获取当前定时器状态
    OSTmrStop() 停止计数器倒计时

    创建一个定时器

    如果我们要使用定时器,肯定需要先创建一个定时器,使用OSTmrCreate()函数来创建一个定时器,这个函数也用来确定定时器的运行模式,OSTmrCreate()函数原型如下:

    void  OSTmrCreate (OS_TMR               *p_tmr,                //指向定时器的指针,宏OS_TMR是一个结构体
                       CPU_CHAR             *p_name,                //定时器名称
                       OS_TICK               dly,                    //初始化定时器的延迟值
                       OS_TICK               period,                    //重复周期的延迟值
                       OS_OPT                opt,                        //定时器运行选项
                       OS_TMR_CALLBACK_PTR   p_callback,                //指向回调函数的名字
                       void                 *p_callback_arg,            //回调函数的参数
                       OS_ERR               *p_err)                    //调用此函数以后返回的错误码
    {
        CPU_SR_ALLOC();
    
        OS_CRITICAL_ENTER();
        p_tmr->State          = (OS_STATE           )OS_TMR_STATE_STOPPED;     /* Initialize the timer fields             */
        p_tmr->Type           = (OS_OBJ_TYPE        )OS_OBJ_TYPE_TMR;
        p_tmr->NamePtr        = (CPU_CHAR          *)p_name;
        p_tmr->Dly            = (OS_TICK            )dly;
        p_tmr->Match          = (OS_TICK            )0;
        p_tmr->Remain         = (OS_TICK            )0;
        p_tmr->Period         = (OS_TICK            )period;
        p_tmr->Opt            = (OS_OPT             )opt;
        p_tmr->CallbackPtr    = (OS_TMR_CALLBACK_PTR)p_callback;
        p_tmr->CallbackPtrArg = (void              *)p_callback_arg;
        p_tmr->NextPtr        = (OS_TMR            *)0;
        p_tmr->PrevPtr        = (OS_TMR            *)0;
    
        OSTmrQty++;                                             /* Keep track of the number of timers created             */
    
    
        OS_CRITICAL_EXIT_NO_SCHED();
       *p_err = OS_ERR_NONE;
    }
    

    opt参数:定时器运行选项,这里有两个模式可以选择。OS_OPT_TMR_ONE_SHOT单次定时器,OS_OPT_TMR_PERIODIC周期定时器。

    软件定时器工作模式

    单次定时器

    使用OSTmrCreate()函数创建定时器时把参数opt设置为OS_OPT_TMR_ONE_SHOT,就是创建的单次定时器。创建一个单次定时器以后,我们一旦调用OSTmrStart()函数定时器就会从创建时定义的dly开始倒计数,直到减为0调用回调函数并停止。单次定时器的定时器只执行一次。

    上图展示了单次定时器在调用OSTmrStart()函数后开始倒计数,将dly减为0后调用回调函数的过程,到这里定时器就停止运行,不再做任何事情了,我们可以调用OSTmrDel()函数来删除这个运行完成的定时器。其实我们也可以重新调用OSTmrStart()函数来开启一个已经运行完成的定时器,通过调用OSTmrStart()函数来重新触发单次定时器,如下图所示。

    周期模式(无初始延迟)

    使用OSTmrCreate()函数创建定时器时把参数opt设置为OS_OPT_TMR_PERIODIC,就是创建的周期定时器。当定时器倒计数完成后,定时器就会调用回调函数,并且重置计数器开始下一轮的定时,就这样一直循环下去。如果使用OSTmrCreate()函数创建定时器的时候,参数dly为0的话,那么定时器在每个周期开始时计数器的初值就为period,如下图所示。

    周期定时器(有初始化延迟)

    在创建定时器的时候也可以创建带有初始化延时的,初始化延时就是OSTmrCreate()函数中的参数dly就是初始化延迟,定时器的第一个周期就是dly。当第一个周期完成后就是用参数period作为周期值,调用OSTmrStart()函数开启有初始化延时的定时器,如下图所示。

     

    UCOSIII实际例程

    例程要求:本例程新建两个任务:任务A和任务B,任务A用于创建两个定时器:定时器1和定时器2,任务A还创建了另外一个任务B。其中定时器1为周期定时器,初始延时为200ms,以后的定时器周期为1000ms,定时器2为单次定时器,延时为2000ms。

    任务B作为按键检测任务,当KEY_UP键按下的时候,打开定时器1;当KEY0按下的时候打开定时器2;当KEY1按下的时候,同时关闭定时器1和2;任务B还用来控制LED0,使其闪烁,提示系统正在运行。

    定时器1定时完成以后调用回调函数刷新其工作区域的背景,并且在LCD上显示定时器1运行的次数。定时器2定时完成后也调用其回调函数来刷新其工作区域的背景,并且显示运行次数,由于定时器2是单次定时器,我们通过串口打印来观察单次定时器的运行情况。

    例子:

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "lcd.h"
    #include "key.h"
    #include "includes.h"
    
    //UCOSIII中以下优先级用户程序不能使用,ALIENTEK
    //将这些优先级分配给了UCOSIII的5个系统内部任务
    //优先级0:中断服务服务管理任务 OS_IntQTask()
    //优先级1:时钟节拍任务 OS_TickTask()
    //优先级2:定时任务 OS_TmrTask()
    //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
    //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
    
    //任务优先级
    #define START_TASK_PRIO		3
    //任务堆栈大小	
    #define START_STK_SIZE 		128
    //任务控制块
    OS_TCB StartTaskTCB;
    //任务堆栈	
    CPU_STK START_TASK_STK[START_STK_SIZE];
    //任务函数
    void start_task(void *p_arg);
    
    //任务优先级
    #define TASK1_TASK_PRIO		4
    //任务堆栈大小	
    #define TASK1_STK_SIZE 		128
    //任务控制块
    OS_TCB Task1_TaskTCB;
    //任务堆栈	
    CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
    void task1_task(void *p_arg);
    
    OS_TMR 	tmr1;		//定时器1
    OS_TMR	tmr2;		//定时器2
    void tmr1_callback(void *p_tmr, void *p_arg); 	//定时器1回调函数
    void tmr2_callback(void *p_tmr, void *p_arg);	//定时器2回调函数
    
    int lcd_discolor[14]={	WHITE, RED,   BLUE,  BRED,                          //LCD刷屏时使用的颜色
    						GRED,  GBLUE, BLACK,   MAGENTA,       	 
    						GREEN, CYAN,  YELLOW,BROWN, 			
    						BRRED, GRAY };
    
    int main(void)                                    //主函数
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	
    	delay_init();  //时钟初始化
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
    	uart_init(115200);   //串口初始化
    	LED_Init();         //LED初始化	
    	LCD_Init();			//LCD初始化	
    	KEY_Init();			//按键初始化
    	
    	POINT_COLOR = RED;
    	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
    	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 9-1");
    	LCD_ShowString(30,50,200,16,16,"KEY_UP:Start Tmr1");
    	LCD_ShowString(30,70,200,16,16,"KEY0:Start Tmr2");
    	LCD_ShowString(30,90,200,16,16,"KEY1:Stop Tmr1 and Tmr2");
    	
    	LCD_DrawLine(0,108,239,108);		//画线
    	LCD_DrawLine(119,108,119,319);		//画线
    	
    	POINT_COLOR = BLACK;
    	LCD_DrawRectangle(5,110,115,314); 	//画一个矩形	
    	LCD_DrawLine(5,130,115,130);		//画线
    	
    	LCD_DrawRectangle(125,110,234,314);     //画一个矩形	
    	LCD_DrawLine(125,130,234,130);		//画线
    	POINT_COLOR = BLUE;
    	LCD_ShowString(6,111,110,16,16,	 "TIMER1:000");
    	LCD_ShowString(126,111,110,16,16,"TIMER2:000");
    	
    	OSInit(&err);		    	//初始化UCOSIII
    	OS_CRITICAL_ENTER();	//进入临界区			 
    	//创建开始任务
    	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
    				 (CPU_CHAR	* )"start task", //任务名字
                     (OS_TASK_PTR )start_task, 			//任务函数
                     (void		* )0,				//传递给任务函数的参数
                     (OS_PRIO	  )START_TASK_PRIO,     //任务优先级
                     (CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址
                     (CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位
                     (CPU_STK_SIZE)START_STK_SIZE,		//任务堆栈大小
                     (OS_MSG_QTY  )0,			//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                     (OS_TICK	  )0,			//当使能时间片轮转时的时间片长度,为0时为默认长度,
                     (void   	* )0,			//用户补充的存储区
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                     (OS_ERR 	* )&err);		//存放该函数错误时的返回值
    	OS_CRITICAL_EXIT();	            //退出临界区	 
    	OSStart(&err);                      //开启UCOSIII
    }
    
    void start_task(void *p_arg)                //开始任务函数
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	p_arg = p_arg;
    	
    	CPU_Init();
    #if OS_CFG_STAT_TASK_EN > 0u
       OSStatTaskCPUUsageInit(&err);  	    //统计任务                
    #endif
    	
    #ifdef CPU_CFG_INT_DIS_MEAS_EN		    //如果使能了测量中断关闭时间
        CPU_IntDisMeasMaxCurReset();	
    #endif
    	
    #if	OS_CFG_SCHED_ROUND_ROBIN_EN      //当使用时间片轮转的时候
    	                     //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
    	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
    #endif	
    	
    	//创建定时器1
    	OSTmrCreate((OS_TMR	*)&tmr1,		//定时器1
                    (CPU_CHAR	*)"tmr1",		//定时器名字
                    (OS_TICK	 )20,			//20*10=200ms
                    (OS_TICK	 )100,          //100*10=1000ms
                    (OS_OPT		 )OS_OPT_TMR_PERIODIC, //周期模式
                    (OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
                    (void	    *)0,			//参数为0
                    (OS_ERR	    *)&err);		//返回的错误码
    				
    	//创建定时器2
    	OSTmrCreate((OS_TMR	*)&tmr2,		
                    (CPU_CHAR	*)"tmr2",		
                    (OS_TICK	 )200,			//200*10=2000ms	
                    (OS_TICK	 )0,   					
                    (OS_OPT		 )OS_OPT_TMR_ONE_SHOT, 	//单次定时器
                    (OS_TMR_CALLBACK_PTR)tmr2_callback,	//定时器2回调函数
                    (void	    *)0,			
                    (OS_ERR	    *)&err);	
    				
    	OS_CRITICAL_ENTER();	//进入临界区
    	//创建TASK1任务
    	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		
    		 (CPU_CHAR	* )"Task1 task", 		
                     (OS_TASK_PTR )task1_task, 			
                     (void		* )0,					
                     (OS_PRIO	  )TASK1_TASK_PRIO,     
                     (CPU_STK   * )&TASK1_TASK_STK[0],	
                     (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                     (CPU_STK_SIZE)TASK1_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,  					
                     (void   	* )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR 	* )&err);				 
    	OS_CRITICAL_EXIT();	//退出临界区
    	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
    }
    
    void task1_task(void *p_arg)            //任务1的任务函数
    {
    	u8 key,num;
    	OS_ERR err;
    	while(1)
    	{
    		key = KEY_Scan(0);
    		switch(key)
    		{
    			case WKUP_PRES:     //当key_up按下的话打开定时器1
    				OSTmrStart(&tmr1,&err);	//开启定时器1
    				printf("开启定时器1\r\n");
    				break;
    			case KEY0_PRES:		//当key0按下的话打开定时器2
    				OSTmrStart(&tmr2,&err);	//开启定时器2
    				printf("开启定时器2\r\n");
    				break;
    			case KEY1_PRES:		//当key1按下话就关闭定时器
    				OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err);	//关闭定时器1
    				OSTmrStop(&tmr2,OS_OPT_TMR_NONE,0,&err);	//关闭定时器2
    				printf("关闭定时器1和2\r\n");
    				break;	
    		}
    		num++;
    		if(num==50) //每500msLED0闪烁一次
    		{
    			num = 0;
    			LED0 = ~LED0;	
    		}
    		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
    	}
    }
    
    void tmr1_callback(void *p_tmr, void *p_arg)                    //定时器1的回调函数
    {
    	static u8 tmr1_num=0;
    	LCD_ShowxNum(62,111,tmr1_num,3,16,0x80); //显示定时器1的执行次数
    	LCD_Fill(6,131,114,313,lcd_discolor[tmr1_num%14]);//填充区域
    	tmr1_num++;		//定时器1执行次数加1
    }
    
    void tmr2_callback(void *p_tmr,void *p_arg)                    //定时器2的回调函数
    {
    	static u8 tmr2_num = 0;
    	tmr2_num++;		//定时器2执行次数加1
    	LCD_ShowxNum(182,111,tmr2_num,3,16,0x80);  //显示定时器1执行次数
    	LCD_Fill(126,131,233,313,lcd_discolor[tmr2_num%14]); //填充区域
    	LED1 = ~LED1;
    	printf("定时器2运行结束\r\n");
    }
    
    

     

    展开全文
  • UCOSIIIUCOSIII系统内部任务

    千次阅读 2018-06-25 18:04:35
    之前讲到UCOSIII默认有5个系统任务: 空闲任务:UCOSIII创建的第一个任务,UCOSIII必须创建的任务,此任务有UCOSIII自动创建,不需要用户手动创建; 时钟节拍任务:此任务也是必须创建的任务; 统计任务:可选...

    之前讲到UCOSIII默认有5个系统任务:

    • 空闲任务:UCOSIII创建的第一个任务,UCOSIII必须创建的任务,此任务有UCOSIII自动创建,不需要用户手动创建;
    • 时钟节拍任务:此任务也是必须创建的任务;
    • 统计任务:可选任务,用来统计CPU使用率和各个任务的堆栈使用量。此任务是可选任务,由宏OS_CFG_STAT_TASK_EN控制是否使用此任务;
    • 定时任务:用来向用户提供定时服务,也是可选任务,由宏OS_CFG_TMR_EN控制是否使用此任务;
    • 中断服务管理任务:可选任务,由宏OS_CFG_ISR_POST_DEFERRED_EN控制是否使用此任务。

     

    UCOSIII空闲任务

    我们首先来看一下空闲任务:OS_IdleTask(),在os_core.c文件中定义。任务OS_IdleTask()是必须创建的,不过不需要手动创建,在调用OS_Init()初始化UCOS的时候就会被创建。打开OS_Init()函 数 , 可 以 看 到 , 在OS_Init()中 调 用 了 函 数OS_IdleTaskInit(),打开函数OS_IdleTaskInit(),函数代码如下:

    void  OS_IdleTaskInit (OS_ERR  *p_err)
    {
    #ifdef OS_SAFETY_CRITICAL
        if (p_err == (OS_ERR *)0) {
            OS_SAFETY_CRITICAL_EXCEPTION();
            return;
        }
    #endif
    
        OSIdleTaskCtr = (OS_IDLE_CTR)0;
                                                                /* ---------------- CREATE THE IDLE TASK ---------------- */
        OSTaskCreate((OS_TCB     *)&OSIdleTaskTCB,
                     (CPU_CHAR   *)((void *)"uC/OS-III Idle Task"),
                     (OS_TASK_PTR)OS_IdleTask,
                     (void       *)0,
                     (OS_PRIO     )(OS_CFG_PRIO_MAX - 1u),
                     (CPU_STK    *)OSCfg_IdleTaskStkBasePtr,
                     (CPU_STK_SIZE)OSCfg_IdleTaskStkLimit,
                     (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
                     (OS_MSG_QTY  )0u,
                     (OS_TICK     )0u,
                     (void       *)0,
                     (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
                     (OS_ERR     *)p_err);
    }

    从上面的代码可以看出,函数OS_IdleTaskInit()很简单,只是调用了OSTaskCreate()来创建一 个 任 务 , 这 个 任 务 就 是 空 闲 任 务 。 任 务 优 先 级 为OS_CFG_PRIO_MAX –1,OS_CFG_PRIO_MAX是一个宏,在文件os_cfg.h中定义,OS_CFG_PRIO_MAX定义了UCOSIII可用的任务数。

    空闲任务堆栈大小为OSCfg_IdleTaskStkSize,OSCfg_IdleTaskStkSize也是一个宏,在os_cfg_app.c文件中定义,默认为128,则空闲任务堆栈默认为128*4=512字节。

    空闲任务的任务函数为任务函数为OS_IdleTask(),OS_IdleTask()函数代码如下:

    void  OS_IdleTask (void  *p_arg)
    {
        CPU_SR_ALLOC();
    
        p_arg = p_arg;                                          /* 消除警告   */
    
        while (DEF_ON) {
            CPU_CRITICAL_ENTER();                            //进入临界代码保护
            OSIdleTaskCtr++;
    #if OS_CFG_STAT_TASK_EN > 0u                                //开启了统计任务
            OSStatTaskCtr++;
    #endif
            CPU_CRITICAL_EXIT();
    
            OSIdleTaskHook();                                   /* 钩子函数    */
        }
    }

    可以看到:每进入一次空闲任务,OSIdleTaskCtr就加一。我们可以通过查看OSIdleTaskCtr变量的递增速度来判断CPU执行应用任务的繁忙程度,如果递增的快的话说明应用任务花费时间少,很快就执行完了。

    空闲任务特点:

    • 空闲任务是UCOSIII创建的第一个任务;
    • 空闲任务是UCOSIII必须创建的;
    • 空闲任务优先级总是为OS_CFG_PRIO_MAK-1;
    • 空闲任务中不能调用任何可使空闲任务进入等待态的函数!

     

    UCOSIII时钟节拍任务

    另一个必须创建的任务时钟节拍任务:OS_Ticktask(),在OS_Init()中调用了一个函数OS_TickTaskInit(),函数代码如下:

    void  OS_TickTaskInit (OS_ERR  *p_err)
    {
    #ifdef OS_SAFETY_CRITICAL
        if (p_err == (OS_ERR *)0) {
            OS_SAFETY_CRITICAL_EXCEPTION();
            return;
        }
    #endif
    
        OSTickCtr         = (OS_TICK)0u;                        /* Clear the tick counter         */
    
        OSTickTaskTimeMax = (CPU_TS)0u;
    
        OS_TickListInit();                                      /* Initialize the tick list data structures    */
    
                                                                /* ---- CREATE THE TICK TASK ---- */
        if (OSCfg_TickTaskStkBasePtr == (CPU_STK *)0) {
           *p_err = OS_ERR_TICK_STK_INVALID;
            return;
        }
    
        if (OSCfg_TickTaskStkSize < OSCfg_StkSizeMin) {
           *p_err = OS_ERR_TICK_STK_SIZE_INVALID;
            return;
        }
    
        if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {     /* Only one task at the 'Idle Task' priority      */
           *p_err = OS_ERR_TICK_PRIO_INVALID;
            return;
        }
    
        OSTaskCreate((OS_TCB     *)&OSTickTaskTCB,
                     (CPU_CHAR   *)((void *)"uC/OS-III Tick Task"),
                     (OS_TASK_PTR )OS_TickTask,
                     (void       *)0,
                     (OS_PRIO     )OSCfg_TickTaskPrio,
                     (CPU_STK    *)OSCfg_TickTaskStkBasePtr,
                     (CPU_STK_SIZE)OSCfg_TickTaskStkLimit,
                     (CPU_STK_SIZE)OSCfg_TickTaskStkSize,
                     (OS_MSG_QTY  )0u,
                     (OS_TICK     )0u,
                     (void       *)0,
                     (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
                     (OS_ERR     *)p_err);
    }
    

    可以看到在函数OS_TickTaskInit()的最后调用OSTaskCreate()来创建了一个任务,任务函数为OS_TickTask(),所以说时钟节拍任务是UCOSIII必须创建的,同样,不需要我们手工创建。时钟节拍任务的任务优先级为OSCfg_TickTaskPrio,时钟节拍任务的优先级尽可能的高一点,一般设置时钟节拍任务的任务优先级为1。

    时钟节拍任务的作用是跟踪正在延时的任务,以及在指定时间内等待某个内核对象的任务,OS_TickTask()任务函数代码如下:

    void  OS_TickTask (void  *p_arg)
    {
        OS_ERR  err;
        CPU_TS  ts;
    
        p_arg = p_arg;                                          /* Prevent compiler warning        */
    
        while (DEF_ON) {
            (void)OSTaskSemPend((OS_TICK  )0,                        //请求信号量
                                (OS_OPT   )OS_OPT_PEND_BLOCKING,
                                (CPU_TS  *)&ts,
                                (OS_ERR  *)&err);               /* Wait for signal from tick interrupt                    */
            if (err == OS_ERR_NONE) {
                if (OSRunning == OS_STATE_OS_RUNNING) {
                    OS_TickListUpdate();                        /* 信号量请求成功的话就调用       */
                }
            }
        }
    }

    我们先来了解时钟节拍任务中一个重要的概念:时钟节拍列表。

    时钟节拍列表是由一个数据表OSCfg_TickWheel(在os_cfg_app.c中定义)和一个计数器OSTickCtr组 成 , 表OSCfg_TickWheel是 一 个 数 组 , 数 组 元 素 个 数 由 宏OS_CFG_TICK_WHEEL_SIZE定义,宏OS_CFG_TICK_WHEEL_SIZE在os_cfg_app.h中定义了。表OSCfg_TickWheel中的元素为os_tick_spoke类型的,os_tick_spoke是一个结构体,结构体定义如下:

    struct  os_tick_spoke {
        OS_TCB              *FirstPtr;                          /* 指针变量,在表头上并属于该表     */
        OS_OBJ_QTY           NbrEntries;                        /* 表示在该表项上等待的任务的数目  */
        OS_OBJ_QTY           NbrEntriesMax;                     /* 表示在该表项上等待的任务的最大数目 */
    };

    在使用时钟节拍列表时需要先初始化时钟节拍列表,在OS_TickTaskInit()函数中会调用OS_TickListInit()来初始化时钟节拍列表,函数OS_TickListInit()代码如下:

    void  OS_TickListInit (void)
    {
        OS_TICK_SPOKE_IX   i;
        OS_TICK_SPOKE     *p_spoke;
    
        for (i = 0u; i < OSCfg_TickWheelSize; i++) {
            p_spoke                = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
            p_spoke->FirstPtr      = (OS_TCB        *)0;
            p_spoke->NbrEntries    = (OS_OBJ_QTY     )0u;
            p_spoke->NbrEntriesMax = (OS_OBJ_QTY     )0u;
        }
    }

    初始化时钟节拍列表十分简单,就是将OSCfg_TickWheel[]中的每个变量都清零。

     

    UCOSIII统计任务

    在UCOSIII中统计任务可用来统计CPU的使用率、各个任务的CPU使用率和各任务的堆栈使用情况,默认情况下统计任务是不会创建的,如果要使能统计任务的话需要将宏OS_CFG_STAT_TASK_EN置1,宏OS_CFG_STAT_TASK_EN在os_cfg.h文件中有定义。

    当我们将宏OS_CFG_STAT_TASK_EN置1以后,OSinit()函数中有关统计任务的代码就可以编译了。OS_StatTaskInit()函数用来创建统计任务,统计任务的优先级通过宏OS_CFG_STAT_TASK_PRIO设 置 ,一般将统计任务的优先级设置为OS_CFG_PRIO_MAX-2,也就是倒数第二。

    如果要使用统计任务的话就需要在main()函数创建的第一个也是唯一一个应用任务中调用OSStatTaskCPUUsageInit()函数。也就是在start_task()任务中调用函数:

    void start_task(void *p_arg)
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	p_arg = p_arg;
    
    	CPU_Init();
    #if OS_CFG_STAT_TASK_EN > 0u
       OSStatTaskCPUUsageInit(&err);  	//统计任务                
    #endif
    	
    #ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间
        CPU_IntDisMeasMaxCurReset();	
    #endif
    	
    #if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
    	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
    	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
    #endif		
    	
    	OS_CRITICAL_ENTER();	//进入临界区
            ...                //函数省略			 
    	OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err);		//挂起开始任务			 
    	OS_CRITICAL_EXIT();	//进入临界区
    }

    CPU的总的使用率会保存在变量OSStatTaskCPUUsage中,我们可以通过读取这个值来获取CPU的使用率。从V3.03.00版本起,CPU的使用率用一个0~10000之间的整数表示(对应0.00~100.00%),在这之前的版本,CPU使用率是0~100之间的整数表示。

    如果将宏OS_CFG_STAT_TASK_STK_CHK_EN置1 的话表示检查任务堆栈使用情况,那么统计任务就会调用OSTaskStkChk()函数来计算所有已创建任务的堆栈使用量,并将检测结果写入到每个任务的OS_TCB中的StkFree和StkUsed中。

     

    UCOSIII定时任务

    UCOSIII提供软件定时器功能,定时任务是可选的,将宏OS_CFG_TMR_EN设置为1就会使能定时任务,在OSInit()中将会调用函数OS_TmrInit()来创建定时任务。定时任务的优先级通过宏OS_CFG_TMR_TASK_PRIO定义,一般将定时器任务优先级设置为2。

     

    UCOSIII中断服务管理任务

    当把os_cfg.h文件中的宏OS_CFG_ISR_POST_DEFERRED_EN置1就会使能中断服务管理任务,UCOSIII会创建一个名为OS_IntQTask()的任务,该任务负责“延迟”在ISR中调用的系统post服务函数的行为。中断服务管理任务的任务优先级永远是最高的,为0!

    在UCOS中可以通过关闭中断和任务调度器上锁两种方式来管理临界段代码,如果采用后一种,即调度器上锁的方式来管理临界段代码的话,那么在中断服务函数中调用的“post”类函数就不允许操作诸如任务就绪表、等待表等系统内部数据结构。

    当ISR(中断服务函数)调用UCOSIII提供的“post”函数时,要发送的数据和发送的目的地都会存入一个特别的缓冲队列中,当所有嵌套的ISR都执行完成以后UCOSIII会做任务切换,运行中断服务管理任务,该任务会把缓存队列中存放的信息重发给相应的任务。这样做的好处就是可以减少中断关闭的时间,否则,在ISR中还需要把任务从等待列表中删除,并把任务放入就绪表,以及做一些其他的耗时操作。

     

    UCOSIII钩子函数

    钩子函数一般主要是用来扩展其他函数(任务)功能的,钩子函数有如下几个:

    • OSIdleTaskHook(),空闲任务调用这个函数,可以用来让CPU进入低功耗模式;
    • OSInitHook(),系统初始化函数OSInit()调用此函数;
    • OSStatTaskHook(),统计任务每秒中都会调用这个函数,此函数允许你向统计任务中添加自己的应用函数;
    • OSTaskCreateHook(),任务创建的钩子函数;
    • OSTaskDelHook(),任务删除的钩子函数;
    • OSTaskReturnHook(),任务意外返回时调用的钩子函数,比如删除某个任务;
    • OSTaskSwHook(),任务切换时候调用的钩子函数;
    • OSTimeTickHook(),滴答定时器调用的钩子函数。

    本质上讲:钩子函数实际就是事件,就是你可以不改动UCOSIII源代码结构,在需要的地方加入自己代码!

    下面以OSIdleTaskHook()为例介绍一下:

    void  OS_IdleTask (void  *p_arg)
    {
        CPU_SR_ALLOC();
    
        p_arg = p_arg;                                          /* 消除警告   */
    
        while (DEF_ON) {
            CPU_CRITICAL_ENTER();                            //进入临界代码保护
            OSIdleTaskCtr++;
    #if OS_CFG_STAT_TASK_EN > 0u                                //开启了统计任务
            OSStatTaskCtr++;
    #endif
            CPU_CRITICAL_EXIT();
    
            OSIdleTaskHook();                                   /* 钩子函数    */
        }
    }

    函数OSIdleTaskHook(),代码如下:

    void  OSIdleTaskHook (void)
    {
    #if OS_CFG_APP_HOOKS_EN > 0u
        if (OS_AppIdleTaskHookPtr != (OS_APP_HOOK_VOID)0) {
            (*OS_AppIdleTaskHookPtr)();
        }
    #endif
    }

    从 上 面 的 函 数 代 码 中 可 以 看 出 要 使 用 空 闲 任 务 钩 子 函 数 的 话 需 要 将 宏OS_CFG_APP_HOOKS_EN置1,即允许使用空闲任务的钩子函数。当时使能空闲任务的钩子函数以后每次进入空闲任务就会调用指针OS_AppIdleTaskHookPtr,而这个指针是一个指向函数的指针。OS_AppIdleTaskHookPtr是何方神圣?

    打开os_app_hooks.c文 件 , 在 文 件 中 有 个 函 数App_OS_SetAllHooks(),函数代码如下:

    void  App_OS_SetAllHooks (void)
    {
    #if OS_CFG_APP_HOOKS_EN > 0u
        CPU_SR_ALLOC();
    
        CPU_CRITICAL_ENTER();
        OS_AppTaskCreateHookPtr = App_OS_TaskCreateHook;
        OS_AppTaskDelHookPtr    = App_OS_TaskDelHook;
        OS_AppTaskReturnHookPtr = App_OS_TaskReturnHook;
    
        OS_AppIdleTaskHookPtr   = App_OS_IdleTaskHook;
        OS_AppStatTaskHookPtr   = App_OS_StatTaskHook;
        OS_AppTaskSwHookPtr     = App_OS_TaskSwHook;
        OS_AppTimeTickHookPtr   = App_OS_TimeTickHook;
        CPU_CRITICAL_EXIT();
    #endif
    }

    显示将App_OS_IdleTaskHook赋值给OS_AppIdleTaskHookPtr。那么问题来了,OS_AppIdleTaskHookPtr又是何方神圣?我们仔细查看os_app_hooks.c文件,会发现App_OS_IdleTaskHook是一个函数,代码如下:

    void  App_OS_IdleTaskHook (void)
    {
    
    }

    到这里我们基本就懂了空闲任务的钩子函数OSIdleTaskHook()是怎么工作了,在OSIdleTaskHook中最终调用的是函数App_OS_IdleTaskHook(),也就是说如果我们要想在空闲任务的钩子函数中做一些其他处理的话就需要将程序代码写在App_OS_IdleTaskHook()函数中。

    注意:在空闲任务的钩子函数中不能调用任何可以使空闲进入等待态的代码!

    原因很简单,CPU总是在不停的运行,需要一直工作,不能让CPU停下来,哪怕是执行一些对应用没有任何用的代码,比如简单的将一个变量加一。在UCOS中为了让CPU一直工作,在所有应用任务都进入等待态的时候CPU会执行空闲任务,我们可以从空闲任务的任务函数OS_IdleTask()看出,在OS_IdleTask()中没有任何可以让空闲任务进入等待态的代码。如果在OS_IdleTask()中有可以让空闲任务进入等待态的代码的话有可能会在同一时刻所有任务(应用任务和空闲任务)同时进入等待态,此时CPU就会无所事事了,所以在空闲任务的钩子函数OSIdleTaskHook()中不能出现可以让空闲任务进入等待态的代码!这点很重要,一定要谨记!

     

    展开全文
  • UCOSIIIUCOSIII的任务管理

    千次阅读 2018-06-23 21:16:31
    UCOSIII任务创建 UCOSIII是多任务系统,那么肯定要能创建任务,创建任务就是将任务控制块、任务堆栈、任务代码等联系在一起,并且初始化任务控制块的相应字段。在UCOSIII中我们通过函数OSTaskCreate()来创建任务,...
  • uCOSIII以一系列函数的形式为应用程序代码提供服务。用户通过调用这些函数可以完成各种操作。uCOSIII提供的服务函数可以管理信号量、消息队列、互斥信号量等。对于应用程序的开发来说,调用uCOSIII提供的函数和其他...
  • UCOSIII的任务简介 在UCOSIII中任务是以何种面貌存在的呢? 在UCOSIII中任务就是程序实体,UCOSIII能够管理和调度这些小任务(程序)。UCOSIII中的任务由三部分组成:任务堆栈、任务控制块和任务函数。 任务堆栈...
  • UCOSIIIUCOSIII的事件标志组

    万次阅读 2018-07-05 19:10:09
    UCOSIII事件标志组 前面讲述了UCOSIII的信号量、互斥信号量,它们都可以完成任务的同步。但是有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制: “或”...
  • UCOSIII学习之UCOSIII系统移植

    万次阅读 多人点赞 2017-01-24 18:18:47
    UCOSIII在STM32F103上的移植之前断断续续学习了UCOSIII,在脑子里已经对他有了一定的认识。在这里趁着这段时间空闲,将之前有关UCOSIII的所学整理整理,巩固学习的同时也和大家交流分享
  • UCOSIIIUCOSIII的任务调度和切换

    万次阅读 2018-06-22 18:27:44
    UCOSIII任务调度 可剥夺型任务调度 任务调度就是中止当前正在运行的任务转而去执行其他的任务。 UCOSIII是可剥夺型内核,因此当一个高优先级的任务准备就绪,并且此时发生了任务调度,那么这个高优先级的任务就会...
  • UCOSIIIUCOSIII的中断和时间管理

    万次阅读 2018-06-28 19:36:03
    UCOSIII的中断管理 UCOSIII中断处理过程 在STM32中是支持中断的,中断是一个硬件机制,主要用来向CPU通知一个异步事件发生了,这时CPU就会将当前CPU寄存器值入栈,然后转而执行中断服务程序,在CPU执行中断服务...
  • UCOSIIIUCOSIII的消息传递

    万次阅读 2018-07-04 18:12:33
    UCOSIII任务间通信 一个任务或者中断服务程序有时候需要和另一个任务交流信息,这个就是消息传递的过程就叫做任务间通信,任务间的消息传递可以通过两种途径:一是通过全局变量,二是通过发布消息。 使用全局变量...
  • UCOSIIIUCOSIII的存储管理

    千次阅读 2018-07-05 20:40:02
    UCOSIII内存管理简介 作为一个RTOS操作系统,内存管理是必备的功能,因此UCOSIII也就内存管理能力。通常应用程序可以调用ANSI C编译器的malloc()和free()函数来动态的分配和释放内存,但是在嵌入式事实操作系统中...
  • 1、 UCOSIII系统初始化在使用UCOSIII之前我们必须先初始化UCOSIII,即函数OSInit()就是用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括OSStart()。int main(void){OS_ERR err;CPU_SR_ALLOC...
  • UCOSIII资料

    2018-08-27 19:51:30
    ucosiii移至程序ucosiii移至程序
  • UCOSIIIUCOSIII的互斥信号量

    千次阅读 2018-07-02 21:18:54
    信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用(不太清楚的可以参考链接:【UCOSIIIUCOSIII的信号量)。   优先级反转 优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种...
  • UCOSIII同时等待多个内核对象 前面讲述了UCOSIII的信号量(一个任务与另一个任务同步)、事件标志组(一个任务与多个任务同步),它们都可以完成任务的同步。同时,信号量(保证全局变量)、消息队列,它们都可以...
  • uCOSIII配套例程

    2018-07-03 10:50:59
    例10-1 UCOSIII消息传递 例11-1 UCOSIII事件标志组 例1-1 UCOSII移植 例12-1 UCOSIII内存管理 例4-1 UCOSIII移植 例6-1 UCOSIII任务创建和删除 例6-2 UCOSIII任务挂起和恢复 例6-3 UCOSIII时间片轮转调度 例8-1 UCOS...
  • UCOSIIIUCOSIII的任务内嵌信号量

    千次阅读 2018-07-03 18:11:24
    我们一般使用信号量时都需要先创建一个信号量,不过在UCOSIII中每个任务都有自己的内嵌的信号量,这种功能不仅能够简化代码,而且比使用独立的信号量更有效。任务信号量是直接内嵌在UCOSIII中的,任务信号量相关代码...
  • UCOSIII程序

    2018-10-23 10:31:03
    UCOSIII程序
  • 文 章 导 读 今天给大家整理汇总了下UCOSIII任务的基本状态与转换状态相关的内容,希望对小伙伴们有所帮助哈,!公众号新增了“读者讨论”功能,新...1UCOSIII任务的基本状态 UCOSIII的基本状态主要有休眠态、就绪...
  • ucosIII

    2015-06-22 19:40:00
    /* ************************************************************************************************************************ * uC/OS-III *...
  • UCOSIII学习笔记(一) 文章目录UCOSIII学习笔记(一)UCOSII简介前后台系统:RTOS系统:可剥夺型内核:UCOSIII内核文件uC/OS-III中独立于CPU的源文件\Cfg\Template\SourceμC/OS-III,CPU专用源代码\compilerμC/...
  • ucosII和ucosIII

    2018-01-17 14:11:50
    压缩包包含ucosII 和 ucosIII 两种源码,供网友在要源码移植时下载使用,均是本人在www.micrium.com下载的原压缩包。
  • 复制例4-1 UCOSIII文件夹到工程目录下 向工程中添加分组 我们在上面已经准备好了所需的文件,我们还要将这些文件添加到我们的工程中,我们在 KEIL 工程中新建如图 4.3.5 所示的分组。 PendSV错误。删除下面...
  • 压缩包里面是ucosii和ucosiii的官方资料: 100-uC-OS-II-002.pdf 100-uC-USB-D-Renesas-RX63N-001.pdf 100-uCOS-II-Freescale-Kinetis-001.pdf 100-uCOS-III-NXP-LPC1768-001.pdf 600-uCOS-III-UsersGuide-004.pdf ...
  • UCOSIII移植

    千次阅读 2019-05-06 20:40:31
    1.新建UCOSIII文件夹,在此文件夹下将正点原子资料的UCOSIII-LIB,UCOSIII-CPU,UCOSIII复制过来,再新建UCOSIII-BSP,UCOSIII-CONFIG 2.将EvalBoards->ucosiii下的文件除app.c和stm32f10x_conf.h外的八个文件复制...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,749
精华内容 1,099
关键字:

ucosiii