在51单片机上,实现操作系统最简模型, 学习理解操作系统的基本概念;
🔗 //----------- 参考视频链接 (15集) -----------//
1> 版本1:任务建立与切换
#include <STC89C5xRC.H>
#include <intrins.h>
sbit LED_0 = P0^0;
sbit LED_1 = P0^1;
#define MAX_TASKS 2 // 任务个数:task0,task1;
#define MAX_TASK_DEP 32 // 任务最大栈深度:任务切换时保存现场;
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 任何切换函数(任务调度)
* @param None
* @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;
* @param None
* @retval None
*/
void task0()
{
LED_0 = 0;
while (1) {
LED_0 = ~LED_0;
Delay1000ms();
task_switch(); // 任务切换
}
}
/**
* @brief 任务1;
* @param None
* @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 <STC89C5xRC.H>
sbit LED_0 = P0^0;
sbit LED_1 = P0^1;
#define MAX_TASKS 2 // 任务个数:task0,task1;
#define MAX_TASK_DEP 32 // 任务最大栈深度:任务切换时保存现场;
#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 任何切换函数(任务调度)
* @param None
* @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;
* @param None
* @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;
* @param None
* @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.1184MHz
void 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; // 任务id
TaskStatus 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();
}
代码没实现:文章来源:https://www.toymoban.com/news/detail-432233.html
任务的优先级;
任务之间没有信号量,消息机制;
文件管理;
内存管理;文章来源地址https://www.toymoban.com/news/detail-432233.html
到了这里,关于C51 - 自写操作系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!