嵌入式内核学习笔记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函数一次,否则将出现程序奔溃。 标记,回头看看,貌似不错哦 ***********************
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。 竟然还是动态的,支持楼主分享 market!!!!!!!!!!! 好直观啊!!!{:smile:} 这是基于51的? 竟然会动!赞 **********************
复习一下
**********************
课件是利用 TKSTUDIO 和 SDCC 完成的,改成 KEIL 问题也不大,暂时忽略 编译器和IDE。
课件重写了 SETJMP 和 LONGJMP ,从学习角度上看,重新这两个函数 有些早而且 增加了难度,放在后面可能效果更好。所以,这里暂时不分析 怎样重新这两个函数。
然后,下一步就利用 这两个 函数 实现 最简单的 多任务 系统。 不错!!! 好强大的样子 好强大的图 {:lol:}露珠竟然看这个,谢谢分享 oldbeginner 发表于 2014-4-14 07:54
**********************
复习一下
**********************
************************
最简单的多任务模型
************************
把课件形式改了一下,内容没变。
程序分析
循环往复。 {:victory:}{:victory:}{:victory:}{:lol:}{:lol:}{:lol:}{:lol:} 会动的图……赞 动态图课件? 瞬间感觉高大上 确实好强大的图,楼主介绍下怎么做这样的图,我们也学习一下。 oldbeginner 发表于 2014-4-14 10:18
************************
最简单的多任务模型
**********************
让任务互不干扰
**********************
这个很好理解,每个任务使用独立的堆栈
本帖最后由 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就是预设值,启动作用,有点像种子基金。
mark!{:smile:} 很好 记着 tinyos51 本帖最后由 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-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:33 编辑
楼主,你那个gif的课件用啥做的···好奇···
好吧,我好像没有关注帖子的重点···· 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 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-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-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部曲,
不顶部行哦,这么好的笔记,难得哦。 记号,这个图文不错 谢谢楼主分享 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();
}
邮箱实现的细节暂时 不打算深入,看了一下不难。
因为目标是简单 的 实时系统,其实 在信号量引入之前就已经实现了。 赞,mark 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的调式还可以,准备单步调式一下。看看执行过程。 竟然还是动态的,支持楼主分享 mark,很好的东西,不错不错。 本帖最后由 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 来回切换,过程大概就是这个样子。
赞 支持楼主! 支持楼主! 感谢分享{:victory:} 很详细。。 学习了,谢谢楼主! setjmp和longjmp,哈哈我的菜,freertos实现了协程,rtx却没有,打算好好学习下这两个函数,争取在rtx中也添加协程支持 看起来很不错! 这个操作系统不能用KEIL编译吗? 用心, 感谢 666啊,MARK了回头来看看
666啊,MARK了回头来看看
页:
[1]