C51 - 自写操作系统

news2024/11/25 6:46:23

最简OS

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

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

内存分配:
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/476489.html

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

相关文章

〖Python网络爬虫实战㉑〗- 数据存储之JSON操作

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付…

912. 排序数组

1.题目&#xff1a; 2.我的代码&#xff1a; C语言&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* sortArray(int* nums, int numsSize, int* returnSize) {//希尔排序int gap numsSize;//多次预排while (gap > 1) {/…

【Linux】初识Linux

目录 &#x1f34e;一.Linux历史&#x1f34e; 1.UNIX发展的历史 2.Linux发展历史 &#x1f34f;二.开源&#x1f34f; &#x1f351;三.官网&#x1f351; &#x1f34a;四.企业应用现状&#x1f34a; 1.Linux在服务器领域的发展 2.Linux在桌面领域的发展 3.Linux在移…

自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类

目录 贝叶斯分类器何为朴素案例&#xff1a;基于SMS Spam Collection数据集的广告邮件分类SMS数据集词向量表示Laplacian平滑训练过程分类过程 完整代码 贝叶斯分类器 首先要理解贝叶斯决策的理论依据&#xff0c;引用西瓜书上的原话&#xff1a;对于分类任务&#xff0c;在所…

【小呆的力学笔记】非线性有限元的初步认识【二】

文章目录 1.2 有限元分析的数学原理1.2.1 基于最小势能原理的变分法提法1.2.1.a 弹性力学方程简化记法1.2.1.b 应变能密度和应变余能密度1.2.1.c 最小势能原理变分基础 1.2 有限元分析的数学原理 书接上回&#xff0c;我们已经回顾了线性有限元分析的理论基础——线弹性力学的…

TryHackMe-Lunizz CTF(boot2root)

Lunizz CTF 端口扫描 循例nmap Web枚举 进80&#xff0c;apache默认页面 gobuster扫一下目录 /hidden一个文件上传点, 图片上传后无权访问/hidden/uploads/ /whatever一个假的命令执行点 /instructions.txt 由 CTF_SCRIPTS_CAVE 制作&#xff08;不是真实的&#xff09;感谢…

如何看待人工智能技术的变革与未来?

人工智能是当今科技领域中最具前景的技术之一。从最初的逻辑推理到现在的深度学习&#xff0c;人工智能技术的发展已经经历了多个阶段。在本文中&#xff0c;我们将从技术的角度&#xff0c;探讨人工智能的发展历程和未来发展趋势。 一、起源和逻辑推理阶段 人工智能的起源可…

【五一创作】Java 反射

在了解反射前&#xff0c;我们先要知道一些相关知识 Class类 Class类的实例表示java应用运行时的类或接口&#xff0c;每个java类运行时都在JVM里表现为一个class对象&#xff0c;可通过类名.class、类型.getClass()、Class.forName("类名")等方法获取class对象。 …

关于 IO、存储、硬盘和文件系统

关于IO、存储、硬盘和文件系统 0.引入1.了解IO1.1.存储器IO1.2.设备IO 2.存储介质和存储类型2.1.内存2.2.硬盘2.3.固态硬盘&#xff08;SSD&#xff09;2.4.U盘 3.硬盘的工作原理3.1.磁头3.2.盘片3.3.电动机3.4.硬盘的读写操作 4.文件系统概述4.1.文件系统的类型4.2.文件系统的…

vagrant virtualbox 复制

菜鸟学习&#xff0c;记录一下 vagrant virtualbox 虚拟机复制。 目录 第一步&#xff0c;使用 virtualbox 复制虚拟机 第二步&#xff0c;复制 vagrant 文件 第三步&#xff0c;重命名相关文件夹及文件并修改配置&#xff1a; 第四步&#xff0c;注册运行复制后的虚拟机 第…

ImageJ实践——测量大小/长短(以细胞为例)

ImageJ是一款功能强大的图像处理软件。毫无疑问它在测量方面提供了十分便利的功能。下面我将以测量细胞的长短、大小&#xff08;面积&#xff09;为例&#xff0c;详细介绍ImageJ的测量操作流程。 1. ImageJ打开图像文件 在弹出的文件选择对话框中选择目标文件&#xff0c;即…

Spring Data Elasticsearch--ElasticsearchRestTemplate--使用/教程/实例

原文网址&#xff1a;Spring Data Elasticsearch--ElasticsearchRestTemplate--使用/教程/实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文用实例来介绍如何使用Spring Data Elasticsearch的ElasticsearchRestTemplate来操作ES。包括&#xff1a;索引的增删等、文档的增删改查…

【拓扑排序】课程表系列

文章目录 课程表&#xff08;环检测算法&#xff09;1. DFS2. BFS 课程表 II&#xff08;拓扑序列&#xff09;1. DFS2. BFS 课程表 IV&#xff08;记忆化搜索&#xff09;1. DFS2. BFS 课程表&#xff08;环检测算法&#xff09; 1. DFS 先修课程之间的关系可以用有向图表示&…

AI题目整理

1、网络配置时batchsize的大小怎样设置?过小和过大分别有什么特点? Batch size是指一次迭代过程中&#xff0c;输入到神经网络的样本数量。 batchsize太小的缺点&#xff1a; ①耗时长&#xff0c;训练效率低。 ②训练数据就会非常难收敛&#xff0c;从而导致欠拟合。 batch…

MySQL后台线程详解

前言 MySQL的服务实现通过后台多个线程、内存池、文件交互来实现对外服务的&#xff0c;不同线程实现不同的资源操作&#xff0c;各个线程相互协助&#xff0c;共同来完成数据库的服务。本章简单总结MySQL的一些后台线程以及主要作用。 本章收录在MySQL性能优化原理实战专栏&am…

js常见混淆加密技术

下面&#xff0c;我将通过一个案例来演示如何使用JavaScript混淆加密技术来保护你的网站。 假设你有一个网站&#xff0c;其中包含一个登录页面&#xff0c;该页面的JavaScript代码如下所示&#xff1a; function login(username, password) {if (username "admin"…

Doris(21):Doris的函数—日期函数

1 CONVERT_TZ(DATETIME dt, VARCHAR from_tz, VARCHAR to_tz) 转换datetime值dt,从 from_tz 由给定转到 to_tz 时区给出的时区,并返回的结果值。 如果参数无效该函数返回NULL。 select convert_tz(2019-08-01 13:21:03, Asia/Shanghai, America/Los_Angeles); select co…

大数据-玩转数据-初识FLINK

一、初识Flink Flink采用一只松鼠的彩色图案作为logo Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算。Flink被设计在所有常见的集群环境中运行&#xff0c;以内存执行速度和任意规模来执行计算 二、Flink的重要特点 1、事件驱动…

mysql与redis区别

一、.redis和mysql的区别总结 &#xff08;1&#xff09;类型上 从类型上来说&#xff0c;mysql是关系型数据库&#xff0c;redis是缓存数据库 &#xff08;2&#xff09;作用上 mysql用于持久化的存储数据到硬盘&#xff0c;功能强大&#xff0c;但是速度较慢 redis用于存储使…

一篇你看得懂的SNP

单核苷酸多态性&#xff0c;&#xff08;Single Nucleotide Polymorphism&#xff0c;简称SNP&#xff09;指的是由单个核苷酸—A,T,C或G的改变而引起的DNA序列的改变&#xff0c;造成包括人类在内的物种之间染色体基因组的多样性。是指在基因组上单个核苷酸的变异&#xff0c;…