从零开始手写嵌入式实时操作系统

这是B站【黑马程序员】的课程《黑马程序员嵌入式操作系统视频教程,嵌入式入门必学的操作系统教程,从理论到动手实践一套通关》,这套课程的前10节课介绍了一些理论知识,很简单生动,从第11节课开始写代码,从零开始手写嵌入式实时操作系统。这篇笔记主要针对嵌入式实时操作系统demo代码编写。


目录

[1 声明两个任务的堆栈指针和任务堆栈](#1 声明两个任务的堆栈指针和任务堆栈)

[2 定义操作系统的loadtask函数](#2 定义操作系统的loadtask函数)

[3 通过修改SP,来修改调用的函数](#3 通过修改SP,来修改调用的函数)

[4 任务调度器的实现](#4 任务调度器的实现)

[5 任务切换的上下文混淆问题](#5 任务切换的上下文混淆问题)

[6 多任务访问外设操作(LED)](#6 多任务访问外设操作(LED))

[7 为什么延时1秒会出现睡眠2秒的问题](#7 为什么延时1秒会出现睡眠2秒的问题)

[8 改用硬件delay实现sleep函数](#8 改用硬件delay实现sleep函数)

[9 操作系统监管定期时间片轮转](#9 操作系统监管定期时间片轮转)

[10 最终代码](#10 最终代码)


1 声明两个任务的堆栈指针和任务堆栈

cs 复制代码
#include <stc8h.h>

#define MAX_TASKS 2    //为了简化,假设当前操作系统只有两个task
#define MAX_TASKS_DEP 32    //每个task任务的堆栈深度为32

unsigned char task_sp[MAX_TASKS];    //任务的堆栈指针,即当前系统的两个堆栈指针
unsigned char task_stack[MAX_TASKS][MAX_TASKS_DEP];

2 定义操作系统的loadtask函数

cs 复制代码
#include <stc8h.h>

#define MAX_TASKS 2 
#define MAX_TASKS_DEP 32 

//idata关键字指明这两个变量在stc8单片机访问最快的内部内存中
unsigned char idata task_sp[MAX_TASKS];
unsigned char idata task_stack[MAX_TASKS][MAX_TASKS_DEP];

unsigned char task_id;    //当前任务号,从0开始

//第0号任务
void task0()
{
    unsigned int a = 3;
    while (1)
    {
        a = a + 3;
    }
}

//第1号任务
void task1()
{
    unsigned int b = 5;
    while (1)
    {
        b = b + 5;
    }
}

//操作系统加载任务的函数
//参数说明:
//    fn:是一个函数的指针,数据类型为int,stc8单片机中函数指针为16位
//    tid:表示task id
//函数作用:
//    把一个task的函数指针放入对应的堆栈空间里面
void task_load(unsigned int fn, unsigned char tid)
{
    task_sp[tid] = task_stack[tid] + 1;    //把任务的指针往下一个空间挪一格

    task_stack[tid][0] = fn & 0xff;    //task_stack[tid][0]放上fn函数指针的低8位
    task_stack[tid][1] = fn >> 8;    //task_stack[tid][1]放上fn函数指针的高8位
}

3 通过修改SP,来修改调用的函数

cs 复制代码
#include <stc8h.h>

#define MAX_TASKS 2 
#define MAX_TASKS_DEP 32 

unsigned char idata task_sp[MAX_TASKS];
unsigned char idata task_stack[MAX_TASKS][MAX_TASKS_DEP];

unsigned char task_id; 

void task0()
{
    unsigned int a = 3;
    while (1)
    {
        a = a + 3;
    }
}

void task1()
{
    unsigned int b = 5;
    while (1)
    {
        b = b + 5;
    }
}

void task_load(unsigned int fn, unsigned char tid)
{
    task_sp[tid] = task_stack[tid] + 1;

    task_stack[tid][0] = fn & 0xff;
    task_stack[tid][1] = fn >> 8;
}

int main()
{
    task_load(task0, 0);    //把task0装载到内存中
    task_id = 0;
    SP = task_sp[0];    //修改StackPointer寄存器的值为task_sp[0]的地址,执行task0函数
}

4 任务调度器的实现

cs 复制代码
#include <stc8h.h>

#define MAX_TASKS 2 
#define MAX_TASKS_DEP 32 

unsigned char idata task_sp[MAX_TASKS];
unsigned char idata task_stack[MAX_TASKS][MAX_TASKS_DEP];

unsigned char task_id; 

//定义一个任务切换的函数(任务调度器)
void task_switch()
{
    task_sp[task_id] = SP;    //把当前系统的堆栈指针存放到某个任务的task_sp里
    task_id = task_id + 1;
    
    //防止task_id溢出
    if (task_id == MAX_TASKS)
    {
        task_id = 0;
    }

    SP = task_sp[task_id];
}

void task0()
{
    unsigned int a = 3;
    while (1)
    {
        a = a + 3;

        task_switch();    //任务切换
    }
}

void task1()
{
    unsigned int b = 5;
    while (1)
    {
        b = b + 5;

        task_switch();    //任务切换
    }
}

void task_load(unsigned int fn, unsigned char tid)
{
    task_sp[tid] = task_stack[tid] + 1;

    task_stack[tid][0] = fn & 0xff;
    task_stack[tid][1] = fn >> 8;
}

int main()
{
    task_load(task0, 0);
    task_id = 0;
    SP = task_sp[0];
}

5 任务切换的上下文混淆问题

cs 复制代码
void task0()
{
    static unsigned int a = 3;    //编译器将其放在静态数据区域,这样不会出现数据脏读的现象
    while (1)
    {
        a = a + 3;

        task_switch();
    }
}

void task1()
{
    static unsigned int b = 5;    //编译器将其放在静态数据区域,这样不会出现数据脏读的现象
    while (1)
    {
        b = b + 5;

        task_switch(); 
    }
}

6 多任务访问外设操作(LED)

cs 复制代码
#include <intrins.h>

//软件生成
//24MHZ的主频下,延时1s的函数
void Delay1000ms()
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 122;
    j = 193;
    k = 128;
    do
    {
        do
        {    
            while(--k);
        }while (--j)
    } while (--i)
}

void task0()
{
    //亮灯
    P5M0 = 0x00;
    P5M1 = 0x00;
    P53 = 1;

    while (1)
    {
        //延迟1s
        Delay1000ms();

        //熄灯
        P53 = ~P53;

        task_switch();
    }
}

void task1()
{
    //亮灯
    P4M1 = 0x00;
    P4M0 = 0x00;
    P2M1 = 0x00;
    P2M0 = 0x00;
    P27 = 1;

    while (1)
    {
        //延迟1s
        Delay1000ms();

        //熄灯
        P27 = ~P27;

        task_switch(); 
    }
}

int main()
{
    task_load(task0, 0);
    task_load(task1, 1);
    task_id = 0;
    SP = task_sp[0];
}

7 为什么延时1秒会出现睡眠2秒的问题

因为软件实现的软delay占用了cpu的时间。

8 改用硬件delay实现sleep函数

cs 复制代码
#include <intrins.h>

//枚举任务状态
typedef enum {
    TASK_RUNNING,    //运行状态
    TASK_SUSPENDED,  //挂起状态
} TaskStatus;

typedef struct {
    unsigned char id;    //任务id
    TaskStatus status;   //任务状态
    unsigned int delay_count;    //延迟计数器
    unsigned int delay_duration; //延迟时间
} Task;

//两个任务都是默认运行状态,不延时
Task idata tasks[MAX_TASKS] = {
    {0, TASK_RUNNING, 0, 0},
    {0, TASK_RUNNING, 0, 0}
};

//原型函数
void Timer0_init();
void sleep(unsigned int , unsigned int );

//睡眠函数
void sleep(unsigned int task_id, unsigned int delay_ms)
{
    tasks[task_id].status = TASK_SUSPENDED;
    tasks[task_id].delay_count = 0;
    tasks[task_id].delay_duration = delay_ms;
}

//软件生成
//24MHZ主频下,硬件delay1ms
void Timer0_init()
{
    AUXR |= 0x80;
    TMOD &= 0xF0;
    TL0 = 0x40;
    TH0 = 0xA2;
    TF0 = 2;
    TR0 = 1;
}

//系统的定时器中断,每隔1ms就执行一次中断函数
void Timer0_ISR(void) interrupt 1
{
    unsigned char i = 0;

    for (i = 0; i < MAX_TASKS; i++)
    {
        //睡眠状态
        if (tasks[i].status == TASK_SUSPENDED)
        {
            tasks[task_id].delay_count++;
        }

        //睡眠结束
        if (tasks[i].delay_count == tasks[task_id].delay_duration)
        {
            //睡眠结束
            tasks[i].status = TASK_RUNNING;
            tasks[i].delay_count = 0;
        }
    }
}

void task0()
{
    P5M0 = 0x00;
    P5M1 = 0x00;
    P53 = 1;

    while (1)
    {
        //检查自己的状态,如果自己是睡眠状态,就应该交给别的task去做
        if (tasks[0].status == TASK_SUSPENDED)
        {
            task_switch();
            continue();
        }

        //硬件延迟1s
        sleep(0, 1000);

        P53 = ~P53;

        task_switch();
    }
}

void task1()
{
    P4M1 = 0x00;
    P4M0 = 0x00;
    P2M1 = 0x00;
    P2M0 = 0x00;
    P27 = 1;

    while (1)
    {
        //检查自己的状态,如果自己是睡眠状态,就应该交给别的task去做
        if (tasks[1].status == TASK_SUSPENDED)
        {
            task_switch();
            continue();
        }

        //硬件延迟1s
        sleep(1, 1000);

        P27 = ~P27;

        task_switch(); 
    }
}

int main()
{
    Timer0_init();
    task_load(task0, 0);
    task_load(task1, 1);
    task_id = 0;
    SP = task_sp[0];
}

9 操作系统监管定期时间片轮转

cs 复制代码
//在24Mhz下,设置定时器为100ms
void Timer1_Init(void)
{
    AUXR |= 0x40;
    TMOD &= 0x0F;
    TL1 = 0xA0;
    TH1 = 0xF6;
    TF1 = 0;
    TR1 = 1;
}

//在timer1中的中断,每100ms进行任务切换
void Timer1_ISR(void) interrupt 1
{
    task_switch(); 
}

int main()
{
    Timer0_init();
    Timer1_init();
    task_load(task0, 0);
    task_load(task1, 1);
    task_id = 0;
    SP = task_sp[0];
}

10 最终代码

cs 复制代码
#include <intrins.h>

#include <stc8h.h>

#define MAX_TASKS 2 
#define MAX_TASKS_DEP 32 

unsigned char idata task_sp[MAX_TASKS];
unsigned char idata task_stack[MAX_TASKS][MAX_TASKS_DEP];

unsigned char task_id; 

typedef enum {
    TASK_RUNNING,   
    TASK_SUSPENDED, 
} TaskStatus;

typedef struct {
    unsigned char id;  
    TaskStatus status;  
    unsigned int delay_count;   
    unsigned int delay_duration; 
} Task;

Task idata tasks[MAX_TASKS] = {
    {0, TASK_RUNNING, 0, 0},
    {0, TASK_RUNNING, 0, 0}
};


void task_switch()
{
    task_sp[task_id] = SP;  
    task_id = task_id + 1;
    
    if (task_id == MAX_TASKS)
    {
        task_id = 0;
    }

    SP = task_sp[task_id];
}

void Timer0_init();
void sleep(unsigned int , unsigned int );

void sleep(unsigned int task_id, unsigned int delay_ms)
{
    tasks[task_id].status = TASK_SUSPENDED;
    tasks[task_id].delay_count = 0;
    tasks[task_id].delay_duration = delay_ms;
}

void Timer0_init()
{
    AUXR |= 0x80;
    TMOD &= 0xF0;
    TL0 = 0x40;
    TH0 = 0xA2;
    TF0 = 2;
    TR0 = 1;
}

void Timer0_ISR(void) interrupt 1
{
    unsigned char i = 0;

    for (i = 0; i < MAX_TASKS; i++)
    {
        if (tasks[i].status == TASK_SUSPENDED)
        {
            tasks[task_id].delay_count++;
        }

        if (tasks[i].delay_count == tasks[task_id].delay_duration)
        {
            tasks[i].status = TASK_RUNNING;
            tasks[i].delay_count = 0;
        }
    }
}

void Timer1_Init(void)
{
    AUXR |= 0x40;
    TMOD &= 0x0F;
    TL1 = 0xA0;
    TH1 = 0xF6;
    TF1 = 0;
    TR1 = 1;
}

void Timer1_ISR(void) interrupt 1
{
    task_switch(); 
}

void task0()
{
    P5M0 = 0x00;
    P5M1 = 0x00;
    P53 = 1;

    while (1)
    {
        if (tasks[0].status == TASK_SUSPENDED)
        {
            task_switch();
            continue();
        }

        sleep(0, 1000);

        P53 = ~P53;

        task_switch();
    }
}

void task1()
{
    P4M1 = 0x00;
    P4M0 = 0x00;
    P2M1 = 0x00;
    P2M0 = 0x00;
    P27 = 1;

    while (1)
    {
        if (tasks[1].status == TASK_SUSPENDED)
        {
            task_switch();
            continue();
        }

        sleep(1, 1000);

        P27 = ~P27;

        task_switch(); 
    }
}

int main()
{
    Timer0_init();
    Timer1_init();
    task_load(task0, 0);
    task_load(task1, 1);
    task_id = 0;
    SP = task_sp[0];
}
相关推荐
铲子Zzz42 分钟前
Java使用接口AES进行加密+微信小程序接收解密
java·开发语言·微信小程序
YTao_G1 小时前
STM32模块:022 PWR电源控制
stm32·单片机·嵌入式硬件
小小小新人121231 小时前
C语言 ATM (4)
c语言·开发语言·算法
zhuiQiuMX1 小时前
字节面试手撕中等题但还没做出来
面试
Two_brushes.1 小时前
【linux网络】网络编程全流程详解:从套接字基础到 UDP/TCP 通信实战
linux·开发语言·网络·tcp/udp
小白学大数据1 小时前
R语言爬虫实战:如何爬取分页链接并批量保存
开发语言·爬虫·信息可视化·r语言
争不过朝夕,又念着往昔1 小时前
Go语言反射机制详解
开发语言·后端·golang
Azxcc01 小时前
C++异步编程入门
开发语言·c++
Biaobiaone2 小时前
Java中的生产消费模型解析
java·开发语言
我命由我123452 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js