51 - 自写操作系统

news2024/11/27 12:32:21

简易OS

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

🔗 //--------- 参考视频链接 ------//

通过实验, 学习理解操作系统的大概;

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 ---------------------------//

内存分配:
1

实验结果:LED0波形
1

问题:为什么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();
}

代码没实现:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL入门指南】外键约束使用详解

一、为什么需要外键&#xff1f; MySQL是一种关系型数据库&#xff0c;现实中的业务往往是相互关联的&#xff0c;这也就决定了数据库中的表也是存在相互关系的。而表与表之间的相互关系就是通过外键来维护的。给大家举一个现实的例子来帮助理解&#xff1a; 现在我们需要在数据…

IPsec中IKE与ISAKMP过程分析(主模式-消息1)

IPsec协议族中IKE&#xff08;Internet Key Exchange&#xff09;是一种基于ISAKMP的协议&#xff0c;它为建立IPSec安全通信隧道提供了一种无痕密钥交换的机制。简单来说&#xff0c;IKE就是ISAKMP的扩展&#xff0c;为ISAKMP提供了更加高效、灵活和安全的密钥协商机制。 GMT …

什么是大数据?如何入门学习大数据?

什么是大数据&#xff1f;在互联网技术快速发展的今天&#xff0c;大量日常生活和经营活动中产生的数据都已经信息化。我们产生的数据量相比以前有了爆炸式的增长&#xff0c;传统的数据处理技术已经无法胜任&#xff0c;需求催生技术&#xff0c;一套用来处理海量数据的软件工…

TCP报文结构以及三次握手以及四次握手

源端口号: 目标端口号:用来标识接收方计算机的具体应用进程 序号&#xff1a;发送数据组的第一个字节的序号。在TCP传送的流中&#xff0c;每一个字节一个序号。例如&#xff1a;一个报文段的序号为300&#xff0c;此报文段数据部分共有100字节&#xff0c;则下一个报文段的序…

vmware安装redhat 8

vmware安装redhat 8 1、下载镜像文件1.1 镜像文件 2、安装系统2.1、选择自定义安装2.2、兼容性选择2.3、选择镜像文件导入2.4、设置用户名密码2.5、选择虚拟机在磁盘上的位置2.6、选择处理器数量2.7、选择内存大小2.8、选择桥接或NAT2.9、选择SCSI控制器类型2.10、选择虚拟机磁…

第十二章 Transform组件(下)

上一章节中我们介绍了Transform组件的属性和方法。我们发现 Transform 中有right&#xff0c;up和forward&#xff0c;而 Vector3 类中也有right&#xff0c;up和forward&#xff0c;他们是一回事嘛&#xff1f;我们使用Forward来说明两者之间的区别。我们知道&#xff0c;改变…

【web基础】html常用标签+作品展示

前言 小亭子正在努力的学习编程&#xff0c;接下来将开启javaEE的学习~~ 分享的文章都是学习的笔记和感悟&#xff0c;如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话&#xff0c;烦请点赞关注支持一波, 感激不尽~~ 说明&#xff1a;此部分是java web基础知识&a…

Python游戏利器pygame,疯狂赛车(34)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 欢迎和猫妹一起&#xff0c;趣味学Python。 今日主题 你玩过游戏吗&#xff1f; 你喜欢玩游戏吗&#xff1f; 手机游戏&#xff0c;电脑游戏&#xff0c;还是游戏机游戏…

java基础入门-02

Java基础入门-02 8、面向对象8.1. 类和对象8.1.1 类和对象的理解8.1.2 类的定义8.1.3 对象的使用8.1.4 学生对象-练习 8.2. 对象内存图8.2.1 单个对象内存图8.2.2 多个对象内存图 8.3. 成员变量和局部变量8.3.1 成员变量和局部变量的区别 8.4. 封装8.4.1 封装思想8.4.2 private…

智能网联汽车城市化的进程和思考

4月19日&#xff0c;工信部官网显示&#xff0c;支持湖北&#xff08;襄阳&#xff09;、浙江&#xff08;德清&#xff09;、广西&#xff08;柳州&#xff09;创建国家级车联网先导区。至此&#xff0c;车联网国家级先导区正式扩容&#xff0c;由4个增至7个。智能网联作为新生…

Qt5.9学习笔记-事件(一)

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

哪些问题是ChatGPT不会回答我的?

哪些问题是ChatGPT不会回答我的&#xff1f; 文章目录 哪些问题是ChatGPT不会回答我的&#xff1f;前言ChatGPT擅长回答哪些问题呢&#xff1f;结语 前言 对于chatgpt&#xff0c;人们或许会觉得无论什么问题&#xff0c;它都能够答复&#xff0c;但对于以下这些问题&#xff…

云和恩墨荣获2023数字中国创新大赛·信创赛道“最具发展潜力奖”等4个奖项

4月27日&#xff0c;作为2023数字中国创新大赛信创赛道系列活动之一的“信创与开源技术论坛”在福州召开&#xff0c;信创赛道全国总决赛颁奖仪式在该论坛上举行。云和恩墨的参赛作品“安稳易用的企业级国产数据库 - MogDB”经过层层筛选、角逐&#xff0c;最终荣获“信创赛道最…

2023五一数学建模竞赛选题人数公布

数据来源自&#xff0c;各个平台人数投票统计&#xff0c;仅供参考。 具体数值比例为&#xff1a; 题号人数A504B1174C1905 目前&#xff0c;五一数模竞赛C题半成品论文基本完成制作&#xff08;累计35页&#xff0c;10000字&#xff09;&#xff0c;注&#xff1a;蓝色字体…

面向画布(Canvas)的JavaScript库

面向画布(Canvas)的JavaScript库 总结 每个库各有特色&#xff0c;根据需求选择 学习要点 面向画布(Canvas)的JavaScript库 EaselJS 是一个封装了 HTML5 画布(Canvas) 元素的 JavaScript 库。jCanvaScript面向HTML5画布&#xff08;canvas&#xff09;的Javascript类库&…

【C++】switch 语句

1、缘起 最近在 BiliBili 黑马程序员学习 C 编程语言&#xff0c;今天学习到了 switch 语句。为了加强记忆&#xff0c;所以就写了一篇关于 switch 语句的博客。 switch 语句是 C 中的一种流程控制语句&#xff0c;它可以根据变量的值来执行不同的代码块。这种语句通常用于替代…

《LearnUE——基础指南:上篇—0》——UE架构

UE架构 给我一个UObject&#xff0c;我可以翘起整个UE 思考&#xff1a;如果让我们自己来设计一款3D渲染引擎&#xff0c;你会怎么设计整个架构&#xff1f; 目前&#xff0c;市面上有众多的3D游戏引擎和各种三维渲染引擎&#xff0c;像常见的渲染库OpenGL&#xff0c;WebGL&…

垃圾邮件、欺骗和网络钓鱼,如何防止电子邮件安全意识的威胁

网络安全意识月已过半&#xff0c;本周我们的重点是电子邮件安全意识。 在我们最近为此活动撰写的一篇博客中&#xff0c;我们介绍了电子邮件系统面临的一些最严重的危险——包括垃圾邮件、欺骗和网络钓鱼——以及用户如何主动在他们的电子邮件中发现这些攻击并采取措施避免它…

【Python】如此轻松就能PDF和word互转?【详细教程来啦】

文章目录 前言一、pdf2docx功能二、限制三、安装四、案例总结 前言 可将 PDF 转换成 docx 文件的 Python 库。该项目通过 PyMuPDF 库提取 PDF 文件中的数据&#xff0c;然后采用 python-docx 库解析内容的布局、段落、图片、表格等&#xff0c;最后自动生成 docx 文件。 一、…

A-star算法自学

搜索过程 广度优先搜索&#xff08;BFS&#xff09;算法与Dijsktra算法的结合&#xff0c;可以得出最短的路径。 将地图信息通过划分为方形或者其他多边形格子的方法进行表示&#xff0c;便于利用二维数组存放地图信息&#xff0c;每个格子有可行和不可行两种状态&#xff1b;…