这是B站【黑马程序员】的课程《黑马程序员嵌入式操作系统视频教程,嵌入式入门必学的操作系统教程,从理论到动手实践一套通关》,这套课程的前10节课介绍了一些理论知识,很简单生动,从第11节课开始写代码,从零开始手写嵌入式实时操作系统。这篇笔记主要针对嵌入式实时操作系统demo代码编写。
目录
1 声明两个任务的堆栈指针和任务堆栈
#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函数
#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,来修改调用的函数
#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 任务调度器的实现
#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 任务切换的上下文混淆问题
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)
#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函数
#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 操作系统监管定期时间片轮转
//在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 最终代码
#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];
}