最简OS

  • 1> 版本1:任务建立与切换
  • 2> 版本2:定时器切换
    • 2.1> main.c
    • 2.2> task.c
    • 2.3> sleep.c
  • 3> 版本3:加时间片轮转

在51单片机上,实现操作系统最简模型, 学习理解操作系统的基本概念;

//———– 参考视频链接 (15集) ———–//

1> 版本1:任务建立与切换

#include #include sbit LED_0 = P0^0;sbit LED_1 = P0^1;#define MAX_TASKS2// 任务个数:task0,task1;#define MAX_TASK_DEP32// 任务最大栈深度:任务切换时保存现场;unsigned char idata task_sp[MAX_TASKS];// 任务堆栈指针数组;unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];// 任务堆栈, 2个任务,每个任务分配32Byte空间;unsigned char task_id;/*-- CPU Delay --*/void Delay1000ms()//@22.1184MHz{unsigned char i, j, k;_nop_();i = 15;j = 2;k = 235;do{do{while (--k);} while (--j);} while (--i);}/*** @brief任何切换函数(任务调度)* @paramNone* @retval None*/void task_switch(){task_sp[task_id] = SP;// 保存当前的SP;task_id = task_id + 1;if (task_id == MAX_TASKS) {task_id = 0;}SP = task_sp[task_id];// 把下一个task的sp放入到当前的SP}/*** @brief任务0;* @paramNone* @retval None*/void task0(){LED_0 = 0;while (1) {LED_0 = ~LED_0;Delay1000ms();task_switch();// 任务切换 }}/*** @brief任务1;* @paramNone* @retval None*/void task1(){LED_1 = 0;while (1) {LED_1 = ~LED_1;Delay1000ms();task_switch();// 任务切换 }}// 函数的地址(指针)占16bit;// fn:存放函数的地址;// tid:task id,0或1;void task_load(unsigned int fn, unsigned char tid){// 51单片机中,堆栈向上增长;task_sp[tid] = task_stack[tid] + 1; // 将任务堆栈指针设置为下一个空闲位置,预留2个Byte用来存放task的函数地址;// 存放task0或task1函数的首地址task_stack[tid][0] = fn & 0xff;task_stack[tid][1] = fn >> 8;}void main(){task_load(task0, 0);task_load(task1, 1);task_id = 0;// 把当前任务设置为task0;SP = task_sp[0];// 执行task0; }//----------------------------------- End ---------------------------//

内存分配:

实验结果:LED0波形

问题:为什么LED0和LED1会亮2s,灭2s呢,如何改为想要亮1s,灭1s

void Delay1000ms(): 是CPU在,不干其他活,傻延时,所以LED0在等的同时LED1也在等;


2> 版本2:定时器切换

使用51内部,定时器0硬件资源来定时,让CPU释放;


2.1> main.c

#include "main.h"void main(){Timer0_Init();task_load(task0, 0);task_load(task1, 1);task_id = 0;// 把当前任务设置为task0;SP = task_sp[0];// 执行task0; }

main.h

#ifndef __MAIN_H__#define __MAIN_H__#include sbit LED_0= P0^0;sbit LED_1= P0^1;#define MAX_TASKS2// 任务个数:task0,task1;#define MAX_TASK_DEP32// 任务最大栈深度:任务切换时保存现场;#include "sleep.h"#include "task.h"#endif

2.2> task.c

#include "task.h"unsigned char idata task_sp[MAX_TASKS];// 任务堆栈指针数组;unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];// 任务堆栈, 2个任务,每个任务分配32Byte空间;unsigned char task_id;/*** @brief任何切换函数(任务调度)* @paramNone* @retval None*/void task_switch(){task_sp[task_id] = SP;// 保存当前的SP;task_id = task_id + 1;if (task_id == MAX_TASKS) {task_id = 0;}SP = task_sp[task_id];// 把下一个task的sp放入到当前的SP}/*** @brief任务0;* @paramNone* @retval None*/void task0(){LED_0 = 0;while (1) {if (tasks[0].status == TASK_SUSPENDED) {task_switch();continue;// 如果任务处于sleep挂起状态,直接跳出}LED_0 = ~LED_0;sleep(0, 1000); // 任务0,睡眠1s;没有任何阻塞;task_switch();// 任务切换 }}/*** @brief任务1;* @paramNone* @retval None*/void task1(){LED_1 = 0;while (1) {if (tasks[1].status == TASK_SUSPENDED) {task_switch();continue;// 如果任务处于sleep挂起状态,直接跳出}LED_1 = ~LED_1;sleep(1, 1000);task_switch();// 任务切换 }}// 函数的地址(指针)占16bit;// fn:存放函数的地址;// tid:task id,0或1;void task_load(unsigned int fn, unsigned char tid){// 51单片机中,堆栈向上增长;task_sp[tid] = task_stack[tid] + 1; // 将任务堆栈指针设置为下一个空闲位置,预留2个Byte用来存放task的函数地址;// 存放task0或task1函数的首地址task_stack[tid][0] = fn & 0xff;task_stack[tid][1] = fn >> 8;}

task.h

#ifndef __TASK_H__#define __TASK_H__#include "main.h"extern unsigned char idata task_sp[MAX_TASKS];// 任务堆栈指针数组;extern unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];// 任务堆栈, 2个任务,每个任务分配32Byte空间;extern unsigned char task_id;void task0();void task1();void task_load(unsigned int fn, unsigned char tid);void task_switch();#endif

2.3> sleep.c

#include "sleep.h" Task idata tasks[MAX_TASKS] = {{0, TASK_RUNNING, 0, 0},// 任务0,默认运行状态,不延时,当前延时时间0;{0, TASK_RUNNING, 0, 0},// 任务1,默认运行状态,不延时,当前延时时间0;};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;}//1毫秒@22.1184MHzvoid Timer0_Init(void){TMOD &= 0xF0;//设置定时器模式TMOD |= 0x01;//设置定时器模式TL0 = 0xCD;//设置定时初始值TH0 = 0xF8;//设置定时初始值TF0 = 0;//清除TF0标志ET0 = 1;EA = 1;TR0 = 1;//定时器0开始计时}/*--- 定位器0中断服务函数, 1ms中断1次 ---*/void Timer0_ISR(void) interrupt 1{unsigned char i;TL0 = 0xCD;//设置定时初始值TH0 = 0xF8;//设置定时初始值for (i = 0; i < MAX_TASKS; i++) {if (tasks[i].status == TASK_SUSPENDED) {tasks[i].delay_count++;if (tasks[i].delay_count >= tasks[i].delay_duration) {tasks[i].status = TASK_RUNNING;tasks[i].delay_count = 0;}}}}

sleep.h

#ifndef __SLEEP_H__#define __SLEEP_H__#include "main.h"typedef enum {TASK_RUNNING,TASK_SUSPENDED} TaskStatus;/*--- 定义任务结构体 ---*/typedef struct {unsigned char id; // 任务idTaskStatus status;// 任务状态unsigned int delay_count;// 延时计数器unsigned int delay_duration;// 延时时间} Task;extern Task idata tasks[MAX_TASKS];void Timer0_Init(void);void sleep(unsigned int task_id, unsigned int delay_ms);#endif

3> 版本3:加时间片轮转

版本2中如果其中一个任务,不主动task_switch()切换任务,怎么办?
再用一个硬件资源Timer1,200us中断一次,并强制切换;

sleep.c 增加:

void Timer1_Init(void)//200微秒@22.1184MHz{TMOD &= 0x0F;//设置定时器模式TMOD |= 0x10;//设置定时器模式TL1 = 0xB8;//设置定时初始值TH1 = 0xEE;//设置定时初始值TF1 = 0;//清除TF1标志ET1 = 1;EA = 1;TR1 = 1;//定时器1开始计时}void Timer1_ISR(void) interrupt 3{TL1 = 0xB8;//设置定时初始值TH1 = 0xEE;//设置定时初始值task_switch();}

代码没实现:

任务的优先级;
任务之间没有信号量,消息机制;
文件管理;
内存管理;