系统数据:
1)当前的PID
2)所有的进程总数PAMOUNT
//多任务系统的初始化
1. 设置MSP值
2. 设置临时的PSP值(因为这段实际上只在启动定时器之前有效)
3. 设置CONTROL[1]=1(设置双堆栈),马上会自动切换到PSP上
4. 设置PID=0,设置PAMOUNT=0
5. 设置为进程信息表起始地址到PLIST(注意进程信息表是向上生长的)
6. 设置为进程堆栈分配的起始地址到PSTACK(堆栈是向下生长的)
//调用AddTask添加任务
1. 写入配置信息到任务表中(任务起始地址,堆栈地址(首次进行分配)xPSR等等)
a) 至于输入参数,就存放在任务的堆栈中
//启动任务切换
1. 设置SysTick定时器的详细配置(定时间,开中断)
2. 启动定时器
//进程的切换过程
1. 当中断触发时,硬件会按照下表自动进行寄存器的入栈(如果当响应异常时,当前的代码正在使用PSP,则压入PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。一旦进入了服务例程,就将一直使用主堆栈),通常Systick中断都是在PSP的情况下发生的,所以数据压入PSP中。
2. 入栈完毕,将堆栈切换到MSP,开始执行SysTick服务程序:
a) 将当前的堆栈切换为MSP
b) 取得进程堆栈指针,将堆栈指针备份到PID备份区域
c) 将R4到R11存储到PID对应的程序堆栈区域
d) 开始执行程序计算下一个PID=PID+1>PAMOUNT?1: PID+1;
e) 读取下一个PID进程堆栈指针,并将堆栈中的备份恢复到到R4到R11寄存器中
f) 将下一个进程PID的堆栈地址(弹出r4-r11后的地址)写到psp中
g) 进行中断返回,同时这样会触发中断返回的硬件过程,硬件会将堆栈切换为PSP并将PSP的内容恢复到相应寄存器中
3. 出栈完毕,硬件自动清除NVIC寄存器
4. 现场恢复完毕,继续执行任务
//任务的退出
1. 在写入进程信息的时候,已经把lr寄存器的值设置成为了ExitTask函数的地址,所以,当函数退出之后,会自动执行收尾。
补充资料:
1. 关于堆栈
a) 堆栈是向下生长的
b) 堆栈指针总是指向最后被压入堆栈的数据
c) 只有在CONTROL[1]=1时,才会使用双堆栈
d) 因为 C M 3 使用的是向下生长的满栈,所以 MSP 的初始值必须是堆栈内存的末地址加1 。
举例来说,如果你的堆栈区域在 0x20007C00 ‐ 0x20007FFF 之间,那么 MSP 的初始值就必须是0 x 20008000。
e) 在handler模式下CONTROL[1]不可以写入1,但是可以读取PSP的值
f) 堆栈的地址指针会和4对齐,例如:向sp写入0x20006001或者0x20006002或者0x20006003时,会自动变成0x20006000,而写入0x20006004就是直接写入0x20006004
g) 堆栈总是指向最后一个元素的,当写入四个字节的字时,会先指针减去4,再进行入栈。当堆栈指针为sp=0x20006004时,写入一个字 0x12345678时,会在0x20006000字节、0x20006001字节、0x20006002字节、0x20006003字节分别写入0x78、0x56、0x34、0x12。要想执行类似的操作可以使用存储指令(例如str)将数据存到0x20006000单元,会达到相同的效果