1.功能
添加要给简单的多任务功能可以避免裸板软件只能单任务的弊端,可以多个任务同时执行;也可以作为操作系统学习的模板;在后续功能晚上中也可以继续添加系统功能,例如信号量,文件系统,驱动分层等。
2.背景
在实现指纹功能时我发现单任务很难实现相关功能,但是鉴于代码量比较大所以只能挑选堆栈占用较小的系统,后来发现网上有比较好的多任务demo.
3.实现
多任务的实现基于cortex-m3的pendsv和systick中断,当systick中断发生时出发置起pendsv中断,在pendsv中断中实现上下文切换。
4.代码
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stdio.h"
#include "misc.h"
#define HW32_REG(ADDRESS) (*((volatile unsigned long *)(ADDRESS)))
void task0(void) ;
unsigned char flag=1;
uint32_t curr_task=0; // 当前执行任务
uint32_t next_task=1; // 下一个任务
uint32_t task0_stack[17];
uint32_t task1_stack[17];
uint32_t PSP_array[4];
u8 task0_handle=1;
u8 task1_handle=1;
void task0(void)
{
while(1)
{
if(task0_handle==1)
{
printf("task0\n");
task0_handle=0;
task1_handle=1;
}
}
}
void task1(void)
{
while(1)
{
if(task1_handle==1)
{
printf("task1\n");
task1_handle=0;
task0_handle=1;
}
}
}
__asm void SetPendSVPro(void)
{
NVIC_SYSPRI14 EQU 0xE000ED22
NVIC_PENDSV_PRI EQU 0xFF
LDR R1, =NVIC_PENDSV_PRI
LDR R0, =NVIC_SYSPRI14
STRB R1, [R0]
BX LR
}
__asm void TriggerPendSV(void)
{
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
}
int main(void)
{
SetPendSVPro();
printf("OS test\n");
PSP_array[0] = ((unsigned int) task0_stack) + (sizeof task0_stack) - 16*4;
//PSP_array中存储的为task0_stack数组的尾地址-16*4,即task0_stack[1023-16]地址
HW32_REG((PSP_array[0] + (14<<2))) = (unsigned long) task0; /* PC */
//task0的PC存储在task0_stack[1023-16]地址 +14<<2中,即task0_stack[1022]中
HW32_REG((PSP_array[0] + (15<<2))) = 0x01000000; /* xPSR */
PSP_array[1] = ((unsigned int) task1_stack) + (sizeof task1_stack) - 16*4;
HW32_REG((PSP_array[1] + (14<<2))) = (unsigned long) task1; /* PC */
HW32_REG((PSP_array[1] + (15<<2))) = 0x01000000; /* xPSR */
/* 任务0先执行 */
curr_task = 0;
/* 设置PSP指向任务0堆栈的栈顶 */
__set_PSP((PSP_array[curr_task] + 16*4));
SysTick_Config(9000000);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//72/8=9MHZ
/* 使用堆栈指针,非特权级状态 */
__set_CONTROL(0x3);
/* 改变CONTROL后执行ISB (architectural recommendation) */
__ISB();
/* 启动任务0 */
task0();
while(1);
}
__asm void PendSV_Handler(void)
{
// 保存当前任务的寄存器内容
MRS R0, PSP // 得到PSP R0 = PSP
// xPSR, PC, LR, R12, R0-R3已自动保存
STMDB R0!,{R4-R11}// 保存R4-R11共8个寄存器得到当前任务堆栈
// 加载下一个任务的内容
LDR R1,=__cpp(&curr_task)
LDR R3,=__cpp(&PSP_array)
LDR R4,=__cpp(&next_task)
LDR R4,[R4] // 得到下一个任务的ID
STR R4,[R1] // 设置 curr_task = next_task
LDR R0,[R3, R4, LSL #2] // 从PSP_array中获取PSP的值
LDMIA R0!,{R4-R11}// 将任务堆栈中的数值加载到R4-R11中
//ADDS R0, R0, #0x20
MSR PSP, R0 // 设置PSP指向此任务
// ORR LR, LR, #0x04
BX LR // 返回
// xPSR, PC, LR, R12, R0-R3会自动的恢复
ALIGN 4
}
void SysTick_Handler(void)
{
flag=~flag;
LED0=flag;
if(curr_task==0)
next_task=1;
else
next_task=0;
TriggerPendSV();
}