标签:
我在MSP430单片机上调试的控制4个灯闪烁, 把原理搞清楚了一通则百通,可以举一返三;注:以下所讲的堆栈即栈,因为堆栈说习惯了 ,堆是堆栈是栈;
下面简单的介绍一下我的系统,其实还不能叫系统,因为太简单了,只有心脏在跳动,还没长大发育;
大家都知道切换任务时要保存现场,那现场到底要保存什么呢?
首先介绍一下我的任务栈怎么用的,在创建任务时先定义一个全局数组,如TASK1_TACK_SIZE[128];
|-------------------------------------------------------------------|
|func | ..... | ...... | ....... | task stack...... |
|-------------------------------------------------------------------|
高地址 ----> 低地址
栈从高地址往低地址增长。
(1)创建任务时要传入 任务的入口地址 和 任务栈底地址。
任务入口地址(func)会存放在栈底部,任务创建好之后从第一个任务开始运行,第一个任务就是从任务栈里面取出func并调用,第一个任务就开始跑起来了。
(2)第二个任务怎么调用呢,当发生定时器中断时:
第一:首先自动把PC和SR压进当前的任务栈里;
|-------------------------------------------------------------------|
|func| ...|....|pc|sr | ....... | task stack...... |
|---------------- --- |----------------------------------------------|
高地址 | --------> 低地址
|
|
|SP (此时栈指针可能指向这里)
第二:进入中断子函数,手动保存所有工作寄存器,除了中断会自动保存的PC和SR不需要保存其他都要保存,当然有人会问局部变量哪去了,需要保存吗,答应是不需要保存,因为局部变量就在栈里,在压入pc之前的就是局部变量或调用的子程序还没返回时被压栈的信息;
|-----------------------------------------------------------------------|
|func| ...|....|pc|sr |r4|r5|r6|r7|r8|r9| ...r14|r15|.... | task stack..... |
|---------------- -------------------------------- |---------------------|
高地址 --------> | 低地址
|
|
|SP (此时栈指针指向这里)
此时需要保存当前的SP指针,把它指向的地址保存到任务块里的pTaskStk,下次恢复时只要把pTaskStk恢复到SP,我的任务描述是一个结构体;
//任务结构体
typedef struct
{
OSELK_STK *pTaskStk;// 任务栈指针
volatile TaskSysTime runTmr;//定时
volatile TaskSysTime runTmr_bak;//定时备份
unsigned char taskId;
unsigned char taskMode;//任务模式
unsigned char taskState;//任务状态
E_Task_func * func;//任务入口
}TaskFuncType, *PTaskFuncType;
第三:保存好当前任务现场时,取下一个任务块,把pTaskStk恢复给SP,sp指向哪里了呢, 怎么进入下一个任务入口呢,恢复下一个任务首先要POPALL 寄存器,任务初始化时要先初始化任务栈,pTaskStk要保存栈底地址+13,要预留12个寄存器的位置,因为切换任务时会POPALL 寄存器,栈会往高地址增长,最后POP PC时,即把PC 指向了func任务入口地址,下一个任务就被切换过来了,以此类推,每个任务切换都是这个过程,这样分时复用就开始跑起来了。
下面是中断代码:
/*<RBHead>
*************************************************************************
* *
* *
* STUTTGART *
* *
* Alle Rechte vorbehalten - All rights reserved *
* *
*************************************************************************
*************************************************************************
*
*************************************************************************
*
* $Filename__:main.c$
*
* $Author____:rao jun$
*
* $Function__:TME ISR SW task $
*
*************************************************************************
* $User______:jun.rao$
* $Date______:31.08.2015$
* $Class_____:$
* $Name______:
* $Variant___:1.0.0$
* $Revision__:0$
* $Type______:asm$
* $State_____:debug$
* $Generated_:$
*************************************************************************
*
*
*************************************************************************
* List Of Changes
*
* $History
*
*
* $
*
*************************************************************************
</RBHead>*/
#include <msp430f5247.h>
;********************************************************************************************************
;********************************************************************************************************
extern LKOS_TaskProcISR
extern pTaskStk
;********************************************************************************************************
;********************************************************************************************************
RSEG CODE ; Program code
;********************************************************************************************************
;Interrupt ISR
;********************************************************************************************************
TIMER1_ISR
dint;关闭中断
push.w r4;保存工作寄存器
push.w r5
push.w r6
push.w r7
push.w r8
push.w r9
push.w r10
push.w r11
push.w r12
push.w r13
push.w r14
push.w r15
mov.w sp,pTaskStk;保存当前的任务栈指针
calla #LKOS_TaskProcISR;调用c语言 取一下任务栈指针
mov.w pTaskStk,sp;恢复任务栈指针
pop.w r15
pop.w r14
pop.w r13
pop.w r12
pop.w r11
pop.w r10
pop.w r9
pop.w r8
pop.w r7
pop.w r6
pop.w r5
pop.w r4
eint;开中断
reti;这条指令中断返回指令,会自动执行 POP SR ,POP PC,当POP PC时即任务被切换到下一个断点了
;********************************************************************************************************
;
;
; Interrupt vectors
;********************************************************************************************************
COMMON INTVEC
ORG TIMER1_A0_VECTOR
WDT_VEC DW TIMER1_ISR
END
/**
***************************************************************************************************
* create_task
*
* \param func fun \ uint8 mode \ uint32 tmr_ms \ uint8 priority
* \return void
*
***************************************************************************************************
*/
int create_task(E_Task_func* fun , unsigned int taskId , unsigned int *pStk , unsigned char mode , TaskSysTime tmr)
{
PTaskFuncType ptask_fun;
if ((NULL == fun ) || (NULL == pStk))
return (-1);
ptask_fun = &Task_funcTab[taskId];//依次往数组里放
if (taskId < MAX_TASK)
{
if (ptask_fun->taskMode == 0)
{
ptask_fun->func = fun;
ptask_fun->taskMode = mode;
ptask_fun->runTmr = tmr;
ptask_fun->runTmr_bak =tmr;
ptask_fun->pTaskStk = LKOS_TaskStackInit(fun,pStk,taskId); //初始化堆栈
ptask_fun->taskId = taskId;
Task_Ctrl.CurTaskLens = taskId;
return (ptask_fun->taskId);
}
}
return (-1);
}
OSELK_STK * LKOS_TaskStackInit(E_Task_func* fun , OSELK_STK*stk,unsigned int taskId)
{
memset(stk,0,strlen((char const*)stk));//栈清0,调用库比较慢
*stk = (unsigned int)fun;//初始化时先把每个任务的入口地址存在栈底,切换任务的时候才能知道下一个任务在哪里
return (stk-13);//初始化完栈让SP 跳过13个字节(sr 和r4-r15) ,因为在调用下一个任务时要POP 13个字节
}
/**
***************************************************************************************************
* LKOS_TaskProcISR 中断调用的函数
* 轮询调度
* \param
* \return next_taskStk
*
***************************************************************************************************
*/
void LKOS_TaskProcISR(void)
{
PTaskFuncType next_task=NULL;
*Task_Ctrl.ppTaskStk = pTaskStk;//保存当前任务的SP
if(Task_Ctrl.task_cnt < Task_Ctrl.CurTaskLens)
Task_Ctrl.task_cnt++;
else
Task_Ctrl.task_cnt = 0;
next_task = LKOS_GetTaskTCBPtr(Task_Ctrl.task_cnt);
Task_Ctrl.ppTaskStk = &next_task->pTaskStk;//一下个任务栈
pTaskStk = next_task->pTaskStk;
ConfigureTimerA();
}
/*----------------------------------------------------------------------------------------
* TASK
*----------------------------------------------------------------------------------------
*/
unsigned int TASK1_STACK_SIZE[200]={0,0,};
unsigned int TASK2_STACK_SIZE[200]={0,0,};
unsigned int TASK3_STACK_SIZE[200]={0,0,};
unsigned int TASK4_STACK_SIZE[200]={0,0,};
void LKOS_Task1(void);
void LKOS_Task2(void);
void LKOS_Task3(void);
void LKOS_Task4(void);
/*----------------------------------------------------------------------------------------
* MAIN
*----------------------------------------------------------------------------------------
*/
int main( void )
{
TaskSysTime taskTmr;
sys_BspInit();
LKOS_TaskInit();
taskTmr.ms = 15;
create_task(LKOS_Task1,LKOS_Task1_ID,&TASK1_STACK_SIZE[200-1],TMR_MODE,taskTmr);
taskTmr.ms = 25;
create_task(LKOS_Task2,LKOS_Task2_ID,&TASK2_STACK_SIZE[200-1],TMR_MODE,taskTmr);
taskTmr.ms = 25;
create_task(LKOS_Task3,LKOS_Task3_ID,&TASK3_STACK_SIZE[200-1],TMR_MODE,taskTmr);
taskTmr.ms = 15;
create_task(LKOS_Task4,LKOS_Task4_ID,&TASK4_STACK_SIZE[200-1],TMR_MODE,taskTmr);
LKOS_FirstTaskInit();
while(1);
return 0;
}
/**
***************************************************************************************************
* LKOS_Task1
*
* \param void
* \return void
*
***************************************************************************************************
*/
void LKOS_Task1(void)
{
int b = 6;
int c = 1;
int buf[10]={1,2,3,4,5,6,7,8,9};
while(1)
{
if(b++<20)
led1_on();
Delayms(500);
led1_off();
Delayms(500);
}
}
/**
***************************************************************************************************
* LKOS_Task2
*
* \param void
* \return void
*
***************************************************************************************************
*/
void LKOS_Task2(void)
{
int a = 6;
int c = 9;
int tmp[10]={1,2,3,4,5,6,7,8,9};
_EINT();
while(1)
{
a++;
led2_on();
Delayms(100);
a++;
led2_off();
Delayms(100);
}
}
标签:
原文地址:http://my.oschina.net/u/2457912/blog/505190