oldbeginner 发表于 2014-4-11 21:01:59

嵌入式内核学习笔记00(回头看周立功Tinyos51)_2014_4_11

*********************
非局部远程跳转

引入 相关概念
*********************

笔记01 学到一半,感觉还是从 Tinyos51开始比较好,原因,
1、周立功开源相关资料,并且有《项目驱动——单片机应用设计基础》及其课件;
2、感觉适合循序渐进。

《项目驱动》推荐的学习顺序,                        




<setjmp.h>头文件                                             




<setjmp.h>头文件 声明 setjmp()和 longjmp() ,使用它们可以实现非局部跳转;同时<setjmp.h> 还声明了jmp_buf数据类型。

<setjmp.h>提供了以下机制:

[*]jmp_buf:数组类型变量,用于保存恢复调用环境所需的上下文信息。
[*]setjmp:将程序上下文信息保存到跳转“缓冲区(jmp_buf类型的数组)”。
[*]longjmp:将程序上下文信息从“缓冲区”恢复,实现非局部远程跳转。




小知识:局部变量bp                                                   











jmp_buf                                                                  



注意事项
setjmp和longjmp函数必须协同使用,在调用longjmp函数之前必须保证至少调用setjmp函数一次,否则将出现程序奔溃。

renpeng009 发表于 2014-4-11 21:21:11

标记,回头看看,貌似不错哦

oldbeginner 发表于 2014-4-11 21:55:27

***********************
setjmp.h 应用范例

***********************



还需要找其它例子来辅助理解,
http://www.cnblogs.com/lq0729/archive/2011/10/23/2222117.html





再补充几个简单的,
http://blog.csdn.net/wzzfeitian/article/details/9325061

http://blog.csdn.net/wykwdy007/article/details/6535322


不同的编译器(优化或不优化)对结果有影响,原因就是 bp。

gyd0317 发表于 2014-4-11 22:01:18

竟然还是动态的,支持楼主分享

babyhua 发表于 2014-4-11 22:09:09

market!!!!!!!!!!!

hongguan 发表于 2014-4-11 22:10:08

好直观啊!!!{:smile:}

SNOOKER 发表于 2014-4-11 22:46:08

这是基于51的?

msm2009 发表于 2014-4-11 22:48:27

竟然会动!赞

oldbeginner 发表于 2014-4-14 07:54:43

**********************
复习一下
**********************

课件是利用 TKSTUDIO 和 SDCC 完成的,改成 KEIL 问题也不大,暂时忽略 编译器和IDE。

课件重写了 SETJMP 和 LONGJMP ,从学习角度上看,重新这两个函数 有些早而且 增加了难度,放在后面可能效果更好。所以,这里暂时不分析 怎样重新这两个函数。









然后,下一步就利用 这两个 函数 实现 最简单的 多任务 系统。

jianbo513 发表于 2014-4-14 08:10:07

不错!!!

yue6315018 发表于 2014-4-14 08:10:23

好强大的样子

fy024 发表于 2014-4-14 09:01:45

好强大的图

cmheia 发表于 2014-4-14 09:02:34

{:lol:}露珠竟然看这个,谢谢分享

oldbeginner 发表于 2014-4-14 10:18:41

oldbeginner 发表于 2014-4-14 07:54
**********************
复习一下
**********************


************************
最简单的多任务模型

************************

把课件形式改了一下,内容没变。









程序分析                                           











循环往复。

hqgboy 发表于 2014-4-14 10:52:16

{:victory:}{:victory:}{:victory:}{:lol:}{:lol:}{:lol:}{:lol:}

落叶知秋 发表于 2014-4-14 11:04:20

会动的图……赞

huangqi412 发表于 2014-4-14 11:48:43

动态图课件?

huangqi412 发表于 2014-4-14 11:49:05

瞬间感觉高大上

Eric_Xue 发表于 2014-4-15 11:50:43

确实好强大的图,楼主介绍下怎么做这样的图,我们也学习一下。

oldbeginner 发表于 2014-4-15 12:08:20

oldbeginner 发表于 2014-4-14 10:18
************************
最简单的多任务模型



**********************
让任务互不干扰


**********************











这个很好理解,每个任务使用独立的堆栈

oldbeginner 发表于 2014-4-15 13:16:21

本帖最后由 oldbeginner 于 2014-4-15 13:17 编辑

oldbeginner 发表于 2014-4-15 12:08
**********************
让任务互不干扰



*********************
任务切换的 第一原动力


*********************



用setTaskJmp()模拟任务调用setjmp()               





setTaskJmp()的指针有些绕,

搜一下,http://zhidao.baidu.com/link?url=vB9aBcgJXZf5KkJz2yWRztrPoQfnVnrIDRiM1gDnkP2otZYlkYL_Ai30pcp9ppFKqoqs5oRiHPJ0mxs6MDqeRq
c语言*p++是什么意思?

i=*p++ 相当于 i=*(p++),又相当于 i=*p; p++。

(我以后不会用这种写法的,*p++ 是个不好的用法。)

然后,就可以了,





setTaskJmp就是预设值,启动作用,有点像种子基金。


vctor、南 发表于 2014-4-15 13:27:51

mark!{:smile:}

haphard 发表于 2014-4-15 21:35:53

很好   记着   tinyos51

oldbeginner 发表于 2014-4-16 12:52:10

本帖最后由 oldbeginner 于 2014-4-16 12:55 编辑

haphard 发表于 2014-4-15 21:35
很好   记着   tinyos51
***********************
先把 第一版 仿真出来
***********************

有点波折,尝试用KEIL不成功,bp 指针不会作。只能下载了TKStudio,10秒搞定,工程文件等什么配置都不需要,直接打开工程就可以编译。
http://www.embedtools.com/pro_tools/emluator/studio.asp






虽然第一次使用,界面感觉还是挺熟悉的。


修改了两个任务,各加了一个端口输出。



main.c 还是蛮清爽的,

void task0 (void)
{
    while (1) {
      __GucTask0++;
                P1=1<<(__GucTask0%8);
      
      tnOsSched();
    }
}

void task1 (void)
{
    while (1) {
      __GucTask1++;
                P2=1<<(__GucTask1%8);
      tnOsSched();
    }
}

void main (void)
{
    tnOsInit();
    tnOsTaskCreate(task0, __GucTaskStks);
    tnOsTaskCreate(task1, __GucTaskStks);
    tnOsStart();
}

oldbeginner 发表于 2014-4-17 12:12:15

oldbeginner 发表于 2014-4-16 12:52
***********************
先把 第一版 仿真出来
***********************


********************
内核组成函数

*******************

任务的识别                                             







/*********************************************************************************************************
任务控制块
*********************************************************************************************************/
struct tn_os_tcb {
    jmp_buf       jbTaskContext;                                        /*任务上下文                  */
    unsigned char ucTaskStat;                                           /*任务状态                  */
};
typedef struct tn_os_tcb    TN_OS_TCB;

static data TN_OS_TASK_HANDLE __GthTaskCur;   
变量名中的 tn 表示 tiny,th 表示 task handle 任务句柄,_G 可能表示 静态变量,uc表示 unsigned char

内核API                                                





void tnOsInit (void)
{
    TN_OS_TASK_HANDLE thTask;                                           /*操作的任务                  */
   
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      __GtcbTasks.ucTaskStat = __TN_TASK_FLG_DEL;             /*任务处于删除状态            */
    }
    __GthTaskCur = 0;                                                   /*初始任务为0号任务         */
}


/*********************************************************************************************************
** Function name:         tnOsTaskCreate
** Descriptions:            创建任务
** input parameters:      pfuncTask: 任务函数
**                        pucStk:    堆栈位置,堆栈至少要16个字节
** output parameters:       none
** Returned value:          任务句柄, -1为失败
*********************************************************************************************************/
TN_OS_TASK_HANDLE tnOsTaskCreate (void (*pfuncTask)(void), idata unsigned char *pucStk)
{
    TN_OS_TASK_HANDLE thRt;                                             /*返回值                      */
   
    /*
   *搜索空闲的任务控制块
   */
    for (thRt = 0; thRt < TN_OS_MAX_TASKS; thRt++) {
      if (__GtcbTasks.ucTaskStat == __TN_TASK_FLG_DEL) {

            /*
             *搜索到,创建任务
             */
            setTaskJmp(pfuncTask, pucStk, __GtcbTasks.jbTaskContext);
            __GtcbTasks.ucTaskStat = __TN_TASK_FLG_RDY;         /*任务就绪                  */
            return thRt;
      }
    }
    return -1;
}变量名中的 rt 表示 return,puc 表示 指向 unsigned char 的指针,



/*********************************************************************************************************
** Function name:         tnOsStart
** Descriptions:            启动操作系统
** input parameters:      none
** output parameters:       none
** Returned value:          none
*********************************************************************************************************/
void tnOsStart (void)
{
    longjmp(__GtcbTasks.jbTaskContext);                              /*执行0号任务               */
}



/*********************************************************************************************************
** Function name:         tnOsSched
** Descriptions:            任务调度:执行下一个任务
** input parameters:      none
** output parameters:       none
** Returned value:          none
*********************************************************************************************************/
void tnOsSched (void)
{
    TN_OS_TASK_HANDLE   thTask;                                       /*操作的任务                  */
    char                cTmp1;
    TN_OS_TASK_HANDLE   thTmp2;
    volatile data char *pucTmp3 = (void *)0;
   
    thTmp2 = __GthTaskCur;
   
    /*
   *执行下一个任务
   */
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      thTmp2++;
      if (thTmp2 >= TN_OS_MAX_TASKS) {
            thTmp2 = 0;
      }
      if ((__GtcbTasks.ucTaskStat & __TN_TASK_FLG_RDY) != 0) {
            
            cTmp1 = setjmp(__GtcbTasks.jbTaskContext);    /*保存当前任务上下文          */
            if (cTmp1 == 0) {         
                __GthTaskCur = thTmp2;
                longjmp(__GtcbTasks.jbTaskContext);             /*执行指定任务                */
            }
            return;
      }
    }

    /*
   *等待本任务就绪
   */
    pucTmp3 = (volatile data char *)(&(__GtcbTasks.ucTaskStat));
    while ((*pucTmp3 & __TN_TASK_FLG_RDY) == 0) {
    }
}
变量名中 Tmpx 表示 第x个 临时变量。



/*********************************************************************************************************
** Function name:         tnOsTaskDel
** Descriptions:            删除任务
** input parameters:      thTask: 任务句柄, -1为删除自身
** output parameters:       none
** Returned value:          none
*********************************************************************************************************/
void tnOsTaskDel (TN_OS_TASK_HANDLE thTask)
{
    /*
   *检查参数
   */
    if (thTask == -1) {
      thTask = __GthTaskCur;
    }
    if (thTask >= TN_OS_MAX_TASKS || thTask < 0) {
      return;
    }

    /*
   *删除任务
   */
    __GtcbTasks.ucTaskStat = __TN_TASK_FLG_DEL;

    /*
   *删除自身,则执行下一个任务
   */
    if (thTask == __GthTaskCur) {
      tnOsSched();
    }
}
变量命名规则很重要,要不然看起来很累。


kalo425 发表于 2014-4-17 12:31:12

本帖最后由 kalo425 于 2014-4-17 12:33 编辑

楼主,你那个gif的课件用啥做的···好奇···

好吧,我好像没有关注帖子的重点····

oldbeginner 发表于 2014-4-17 12:58:09

oldbeginner 发表于 2014-4-17 12:12
********************
内核组成函数



************************
细节理解

************************



static   dataTN_OS_TASK_HANDLE    __GthTaskCur;

voidthOsInit(void)
{
       TN_OS_TASK_HANDLE    thTask;

       for(thTask=0;thTask<TN_OS_MAX_TASKS;thTask++){

               __GtcbTasks.ucTaskStat=TN_TASK_FLG_DEL;
       }

       __GthTaskCur    =   0;

}







TN_OS_TASK_HANDLE tnOsTaskCreate (void (*pfuncTask)(void), idata unsigned char *pucStk)
{
    TN_OS_TASK_HANDLE thRt;                                             /*返回值                      */
   
    /*
   *搜索空闲的任务控制块
   */
    for (thRt = 0; thRt < TN_OS_MAX_TASKS; thRt++) {
      if (__GtcbTasks.ucTaskStat == __TN_TASK_FLG_DEL) {

            /*
             *搜索到,创建任务
             */
            setTaskJmp(pfuncTask, pucStk, __GtcbTasks.jbTaskContext);
            __GtcbTasks.ucTaskStat = __TN_TASK_FLG_RDY;         /*任务就绪                  */
            return thRt;
      }
    }
    return -1;
}






voidtnOsStart (void)
{
    longjmp(__GtcbTasks.jbTaskContext);                                     // 执行0号任务
}






void tnOsSched (void)
{
    TN_OS_TASK_HANDLE   thTask;                                       /*操作的任务                  */
    char                cTmp1;
    TN_OS_TASK_HANDLE   thTmp2;
    volatile data char *pucTmp3 = (void *)0;
   
    thTmp2 = __GthTaskCur;
   
    /*
   *执行下一个任务
   */
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      thTmp2++;
      if (thTmp2 >= TN_OS_MAX_TASKS) {
            thTmp2 = 0;
      }
      if ((__GtcbTasks.ucTaskStat & __TN_TASK_FLG_RDY) != 0) {
            
            cTmp1 = setjmp(__GtcbTasks.jbTaskContext);    /*保存当前任务上下文          */
            if (cTmp1 == 0) {         
                __GthTaskCur = thTmp2;
                longjmp(__GtcbTasks.jbTaskContext);             /*执行指定任务                */
            }
            return;
      }
    }

    /*
   *等待本任务就绪
   */
    pucTmp3 = (volatile data char *)(&(__GtcbTasks.ucTaskStat));
    while ((*pucTmp3 & __TN_TASK_FLG_RDY) == 0) {
    }
}





void tnOsTaskDel (TN_OS_TASK_HANDLE thTask)
{
    /*
   *检查参数
   */
    if (thTask == -1) {
      thTask = __GthTaskCur;
    }
    if (thTask >= TN_OS_MAX_TASKS || thTask < 0) {
      return;
    }

    /*
   *删除任务
   */
    __GtcbTasks.ucTaskStat = __TN_TASK_FLG_DEL;

    /*
   *删除自身,则执行下一个任务
   */
    if (thTask == __GthTaskCur) {
      tnOsSched();
    }
}

oldbeginner 发表于 2014-4-18 14:47:11

本帖最后由 oldbeginner 于 2014-4-18 15:00 编辑

************************
时间片轮询多任务操作系统

************************



void task0 ( void )
{
    TMOD = ( TMOD & 0xF0 ) | 0x01;
    TL0 = 0x00;
    TH0 = 0x00;
    TR0 = 1;
    ET0 = 1;
    TF0 = 0;
                            //允许time0中断

    while ( 1 )
    {
      __GucTask0 ++;
    }
}

void task1 ( void )
{
    while ( 1 )
    {
      __GucTask1 ++;
    }
}

void timer0ISR( void ) __interrupt 1      //时钟节拍中断服务程序
{
    tnOSTimeTick();                            //时钟节拍处理程序
}

void main ( void )
{
    tnOsInit ();
    tnOsTaskGreate ( task0, __GucTaskStks );
    tnOsTaskGreate ( task1, __GucTaskStks );
    tnOsStart ();
}








由于TCB增加了一个uiTicks,则在tnOsInit()中进行初始化。
voidtnOsInit (void)
{
       TN_OS_TASK_HANDLEthTask;                                                      // 操作的任务

       for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
               __GtcbTasks.ucTaskStat = __TN_TASK_FLG_DEL;   // 任务初始处于删除状态
               __GtcbTasks.uiTicks      = 0;                                       // 设置初值
       }
      
      __GthTaskCur = 0;                                                                                 // 初始运行0号任务
}


      由于tnOsTaskCreate()要操作TCB,而时钟节拍中断中也要操作TCB,因此tnOsTaskCreate()中操作TCB的代码为临界区代码,要避免被时钟节拍中断打断。
      TinyOS51中采用开/关中断的方式解决此问题。

TN_OS_TASK_HANDLEtnOsTaskCreate(void (*pfuncTask)(void),
                                     idataunsgined char *pucStk)
{
TN_OS_TASK_HANDLEthRt;

for (thRt = 0; thRt < TN_OS_MAX_TASKS; thRt++) {
      EA = 0;                                                                                                         // 禁止中断
      if (__GtcbTasks.ucTaskStat == __TN_TASK_FLG_DEL) {
                        setTaskJmp(pfuncTask, pucStk, __GtcbTasks.jbTaskContext);
                        __GtcbTask.ucTaskStat = __TN_TASK_FLG_RDY;
                EA = 1;                                                                                                   // 允许中断
                return thRt;
      }
      EA = 1;                                                                                                         // 允许中断
}      




变量命名规则,_ 表示内部引用。

       在TinyOS51 V1.1中,如果不允许中断,则时钟节拍中断服务程序不会运行,因此,在tnOsStart()中增加允许中断的代码。

       voidtnOsStart (void)
       {
      EA = 1;                                                                      // 允许中断
      longjmp (__GtcbTask.jbTaskContext);                // 执行0号任务
      }


大多数操作系统中的延时管理和中断服务程序中的任务切换功能,分别是用两个函数实现的,由于TinyOS51 V1.1是纯粹的时间片轮询操作系统,非时钟节拍中断的中断服务程序不进行任务切换操作,因此将二者合二为一

static void __tnOsSched (void)
{
    TN_OS_TASK_HANDLE   thTask;                                       /*操作的任务                  */
    char                cTmp1;
    TN_OS_TASK_HANDLE   thTmp2;
    volatile data char *pucTmp3 = (void *)0;

    thTmp2 = __GthTaskCur;

    /*
   *执行下一个任务
   */
    EA = 0;
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      thTmp2++;
      if (thTmp2 >= TN_OS_MAX_TASKS) {
            thTmp2 = 0;
      }
      if ((__GtcbTasks.ucTaskStat & __TN_TASK_FLG_RDY) != 0) {

            cTmp1 = setjmp(__GtcbTasks.jbTaskContext);    /*保存当前任务上下文          */
            if (cTmp1 == 0) {
                __GthTaskCur = thTmp2;
                longjmp(__GtcbTasks.jbTaskContext);             /*执行指定任务                */
            }
            EA = 1;
            return;
      }
    }
    EA = 1;

    /*
   *等待本任务就绪
   */
    pucTmp3 = (volatile data char *)(&(__GtcbTasks.ucTaskStat));
    while ((*pucTmp3 & __TN_TASK_FLG_RDY) == 0) {
    }
}
       在中断中切换任务,不能再使用longjmp(),因为中断需要使用专用返回指令RETI,非RET指令。
//在SDCC51编译器中,若使用__naked修饰函数,则说明此函数无保护函数
char longjmpInISR( jmp_buf jbBuf ) __naked
{
    unsigned char ucSpSave;            //用于保存堆栈指针的变量
    data unsigned char *pucBuf = ( data void * )0;            //指向上下文信息存储位置的指针

    pucBuf = ( data unsigned char * )jbBuf;
    ucSpSave = *pucBuf ++;
    bp = *pucBuf ++;
    *( ( data unsigned char * )ucSpaSave ) = *pucBuf ++;
    *( ( data unsigned char * )( ( char )( unSpSave - 1 ) ) ) = *pucBuf;
    SP = ucSpSave;

    DPL = 1;
    __asm
    RETI
    __endasm;   
}

***************************
已经开始变复杂了。



可以参考 http://bbs.eeworld.com.cn/thread-311494-1-1.html 辅助理解。感觉这篇文章更容易理解些

oldbeginner 发表于 2014-4-20 11:07:39

oldbeginner 发表于 2014-4-18 14:47
************************
时间片轮询多任务操作系统



******************
学而时习之

******************

都学了些什么?

1、月光宝盒:setjmp 和 longjmp,通过这对函数,实现不同时空(函数)之间的跳跃。

   感觉就像 去游泳,游泳前 要把换衣服,把衣服存到衣柜里,衣服就是上下文信息,衣柜就是堆栈。

   顺便再学一学堆栈,

http://xd.ccec.edu.cn/mcu/uploads/media/flash/2-3.swf





同时发现这个课件的一个大缺点:居然无视 PC 的存在。





2、jmp_buf 是 上下文信息,相当于 封装了 要保存的信息,类似 三件套 工作服(上衣、衬衫,裤子,bp sp pc),不担心穿错。



3、然后就是一个白领 在一天中做不同的事情,任务调换

利用了1和2中的原理,





4、解决一个问题,调用任务前,初始化任务。



5、然后就是 TINYOS51 第v1.0,有几个更新,

增加了 TCB ,用来标示任务



另外,增加了几个正规的函数名称
A、void tnOsInit (void),初始任务为0号任务
B、tnOsTaskCreate ( ),主要调用 setTaskJmp( ),创建任务
C、void tnOsStart (void),执行0号任务
D、void tnOsSched (void),任务调度:执行下一个任务

删除任务其实还用不到。


5、然后 在 V1.0 基础上,增加了时间轮询



*********************
还是有点难度的。



oldbeginner 发表于 2014-4-21 11:51:37

oldbeginner 发表于 2014-4-20 11:07
******************
学而时习之



*******************
信号量

*******************



/*********************************************************************************************************
** Function name:         tnOsSemCreate
** Descriptions:            创建一个信号量
** input parameters:      posSem: 指向信号量变量的指针
**                        cCount: 信号量初始值
** output parameters:       none
** Returned value:          参考tiny_os_51.h关于返回值的定义
*********************************************************************************************************/
char tnOsSemCreate (data TN_OS_SEM *posSem, char cCount)
{
    if (posSem == (data TN_OS_SEM *)0) {
      return TN_OS_PAR_ERR;
    }
    posSem->cCount = cCount;
    return TN_OS_OK;
}


/*********************************************************************************************************
** Function name:         tnOsSemPend
** Descriptions:            等待一个信号量
** input parameters:      posSem:   指向信号量变量的指针
**                        uiDlyTicks: 等待的时间,0为无限等待。以时钟节拍为单位
** output parameters:       none
** Returned value:          信号量当前值
*********************************************************************************************************/
char tnOsSemPend (data TN_OS_SEM *posSem, unsigned int uiDlyTicks)
{
    unsigned char cCount;                                             /*信号量计数值                */

    if (posSem == (data TN_OS_SEM *)0) {
      return 0;
    }

    EA = 0;
    if (posSem->cCount > 0) {                                           /*有信号量,直接获得          */
      posSem->cCount--;
      cCount = posSem->cCount;
      EA = 1;
      return cCount;
    }

    /*
   *等待信号量
   */
    __GtcbTasks.uiTicks    = uiDlyTicks;
    __GtcbTasks.ucTaskStat = __TN_TASK_FLG_SEM;
    __GtcbTasks.pvEvent    = (data void *)posSem;

    EA = 1;
    __tnOsSched();
    EA = 0;

    if (__GtcbTasks.ucTaskStat == __TN_TASK_FLG_RDY) {    /*等到信号量                  */
      cCount = posSem->cCount;
      EA = 1;
      return cCount;
    }

    /*
   *没有等到信号量
   */
    __GtcbTasks.ucTaskStat = __TN_TASK_FLG_RDY;
    __GtcbTasks.pvEvent    = (data void *)0;
    EA = 1;
    return TN_OS_TIME_OUT;
}



/*********************************************************************************************************
** Function name:         tnOsSemPost
** Descriptions:            发送一个信号量
** input parameters:      posSem:指向信号量变量的指针
** output parameters:       none
** Returned value:          参考tiny_os_51.h关于返回值的定义
*********************************************************************************************************/
char tnOsSemPost (data TN_OS_SEM *posSem)
{
    TN_OS_TASK_HANDLE thTask;                                           /*操作的任务                  */

    if (posSem == (data TN_OS_SEM *)0) {
      return TN_OS_PAR_ERR;
    }

    EA = 0;

    /*
   *信号量增加
   */
    if (posSem->cCount < 0x7f) {
      posSem->cCount++;
    }

    /*
   *查找等待的任务
   */
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      if (__GtcbTasks.ucTaskStat == __TN_TASK_FLG_SEM) {
            if (__GtcbTasks.pvEvent == (data void *)posSem) {
                break;
            }
      }
    }

    if (thTask >= 0 && thTask < TN_OS_MAX_TASKS) {

      /*
         *激活等待的任务
         */
      posSem->cCount--;
      __GtcbTasks.ucTaskStat = __TN_TASK_FLG_RDY;
      __GtcbTasks.pvEvent    = (data void *)0;
    }

    if (posSem->cCount < 0x7f) {
      EA = 1;
      return TN_OS_OK;
    }
    EA = 1;
    return TN_OS_EVENT_FULL;
}



示例,                                             

3部曲,







yanghc 发表于 2014-4-21 13:30:44

不顶部行哦,这么好的笔记,难得哦。

zjsx133 发表于 2014-4-21 15:37:47

记号,这个图文不错

LJW 发表于 2014-4-22 16:59:36

谢谢楼主分享

oldbeginner 发表于 2014-4-22 17:33:58

oldbeginner 发表于 2014-4-21 11:51
*******************
信号量



*****************
消息邮箱

*****************

其实 感觉 消息邮箱和信号量 差不多,是协调任务之间信息沟通的。







static idata unsigned char        __GucTaskStks;        // 分配任务堆栈
static unsigned char                __GucTask0;                // 任务0测试变量
static unsigned char                __GucTask1;                // 任务1测试变量
static TN_OS_MSG                __GomMsg;                // 定义消息邮箱

void task0 (void)
{
      TMOD= (TMOD & 0xf0) | 0x01;
      TL0          = 0x0;
      TH0          = 0x0;
      TR0          = 1;
      ET0          = 1;
      TF0          = 0;                                        // 允许Timer0中断

      tnOsMsgCreate(&__GomMsg, 0);                        // 创建消息邮箱
      while (1) {
            __GucTask0 = tnOsMsgPend(&__GomMsg, 0);        // 等待消息到来
      }
}

void task1 (void)
{
      while (1) {
            __GucTask1++;
            tnOsMsgPost(&__GomMsg, __GucTask1);                // 发送消息到邮箱
            tnOsTimeDly(10);
      }
}
void time0ISR (void) __interrupt 1
{
       tnOsTimeTick();                                // 时钟节拍处理程序
}

void main (void)
{
      tnOsInit();
      tnOsTaskCreate(task0, __GucTaskStks);
      tnOsTaskCreate(task1, __GucTaskStks);
      tnOsStart();
}


邮箱实现的细节暂时 不打算深入,看了一下不难。
因为目标是简单 的 实时系统,其实 在信号量引入之前就已经实现了。

jzhang123 发表于 2014-4-22 17:49:43

赞,mark                  

oldbeginner 发表于 2014-4-22 18:24:07

oldbeginner 发表于 2014-4-22 17:33
*****************
消息邮箱



*********************
被忽视的调度器

*********************

其实,调度器就是核心,会调度器基本上就懂了TINYOS51,这方面课件衔接的并不好。



比如说,任务0 以上面的方式写出,比较容易理解,然后课件 再 TINYOS51 V1.0 时,过渡得不自然,忽略了调度器的细节的讲解(虽然有代码)。



这时,应该分析调度器的核心,不过调度器代码多了很多,因为引入了任务数组,去掉干扰,



就会发现,是一样的。

调度器之所以更复杂,是因为,

调度器其它代码1

    TN_OS_TASK_HANDLE   thTask;                                       /*操作的任务                  */
    char                cTmp1;
    TN_OS_TASK_HANDLE   thTmp2;
    volatile data char *pucTmp3 = (void *)0;
   
    thTmp2 = __GthTaskCur;
   
    /*
   *执行下一个任务
   */
    for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) {
      thTmp2++;
      if (thTmp2 >= TN_OS_MAX_TASKS) {
            thTmp2 = 0;
      }
      if ((__GtcbTasks.ucTaskStat & __TN_TASK_FLG_RDY) != 0) {

这些代码是因为采用了任务数组的形式,需要给下标赋值;同时 判断 打算执行的任务 状态 是准备好的。


调度器其它代码2

            return;
      }
    }

    /*
   *等待本任务就绪
   */
    pucTmp3 = (volatile data char *)(&(__GtcbTasks.ucTaskStat));
    while ((*pucTmp3 & __TN_TASK_FLG_RDY) == 0) {
    }

这些代码是要确保要执行的任务是准备好的,如果没有准备好,就无限等下去。

TKS的调式还可以,准备单步调式一下。看看执行过程。

jiwx2011 发表于 2014-4-22 18:35:41

竟然还是动态的,支持楼主分享

xuanfong1 发表于 2014-4-22 19:35:45

mark,很好的东西,不错不错。

oldbeginner 发表于 2014-4-23 18:18:03

本帖最后由 oldbeginner 于 2014-4-23 18:21 编辑

oldbeginner 发表于 2014-4-22 18:24
*********************
被忽视的调度器



*****************
最简单 的调试

理解 最基本的内容 TINYOS51 V1.0
*****************



开始调试, SP 指向 0x 61

然后,单步,



SP 指向 0x 63 ,向上移动了 两格,因为 地址 要占 两格,地址低位在下面,高位在上面。
堆栈保存的地址应该 是 009B。009B 就是跳转前 要执行的下一行命令地址。

然后,继续单步,



跳出,回到主函数,SP 变回 0x 61,PC 的值 变成 9B。

单步,又跳跃到函数中,



这时 ,SP 变成 0x 64 ,不理解为何 加了 3格,而不是2格?

然后,再单步



因为 多了 3个局部变量,所以 SP + 3 ,变成 0x 67。

。。。。。。

原理调试是这个样子啊!

目的不在于学习 调式,而是看看 任务0 和 任务1 是如何切换的,

所以,



最后,任务0 和 任务1 来回切换,过程大概就是这个样子。

LJW 发表于 2014-4-25 14:55:38

赞 支持楼主!

jordonwu 发表于 2014-4-28 14:08:04

支持楼主!

andmain999 发表于 2014-6-11 10:04:24

感谢分享{:victory:}

siko 发表于 2014-7-13 19:39:41

很详细。。

守望者2012 发表于 2014-7-14 19:22:22

学习了,谢谢楼主!

myxiaonia 发表于 2014-7-15 14:27:11

setjmp和longjmp,哈哈我的菜,freertos实现了协程,rtx却没有,打算好好学习下这两个函数,争取在rtx中也添加协程支持

t35720864 发表于 2014-7-15 14:41:37

看起来很不错!

TimCheng 发表于 2014-9-25 15:26:19

这个操作系统不能用KEIL编译吗?

EE_Duan 发表于 2018-4-9 14:41:22

用心, 感谢

麦路客 发表于 2018-5-12 11:33:39

666啊,MARK了回头来看看

lnso 发表于 2018-5-14 13:22:14


666啊,MARK了回头来看看
页: [1]
查看完整版本: 嵌入式内核学习笔记00(回头看周立功Tinyos51)_2014_4_11