YKK · 2022年12月08日 · 安徽

【GD32F427开发板试用】添加多任务功能

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();
}

参考: https://mp.weixin.qq.com/s/i6...

推荐阅读
关注数
1
文章数
1
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息