C51 - 自写操作系统

这篇具有很好参考价值的文章主要介绍了C51 - 自写操作系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

内存分配:
C51 - 自写操作系统

实验结果:LED0波形
C51 - 自写操作系统

问题:为什么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

到了这里,关于C51 - 自写操作系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 自学笔记:89c51单片机最小系统Protues篇

    什么是单片机最小系统? 单片机最小系统:单片机最小应用系统,使用 最少 的元件组成单片机能够工作的系统。 单片机最小系统 三要素 :电源模块,(晶振/时钟)振荡电路模块,复位电路模块 1,电源模块:供电。VCC端接正向5V电压,GND端接地。 51单片机全部引脚图(百

    2024年02月04日
    浏览(45)
  • 基于AT89C51单片机的电子万年历系统

    点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87708258 源码获取 主要内容: 电子万年历系统以实时时钟芯片DS1302和AT89C52单片机为主要研究对象,着重进行51单片机控制系统的设计研究和如何读取DS1302内部时钟信息的研究。以及运用18B20进行实

    2024年02月09日
    浏览(52)
  • C51单片机中断系统与定时器/计时器(理论部分)

    前言 文章的开始,有人可能会问,我们在前几节课学了延时函数,为什么不用延时函数来取代定时器,而必须学定时器呢?这里,有如下几个理由: 1.使用delay函数时,单片机的cpu做不了其他事情,只能等待延时结束。 2.用了定时器cpu就可以做其他的事情,只要定时器溢出时

    2023年04月15日
    浏览(40)
  • 基于AT89C51单片机的智能浇花系统设计

    目 录 摘 要 I ABSTRACT I 1 绪论 1 1.1 课题背景和意义 1 1.1.1选题背景 1 1.1.2研究意义 1 1.2 国内外发展现状及趋势 2 1.3研究主要内容 3 2 总体设计方案 5 2.1 浇灌系统设计的需求 5 2.2 总体设计方案选定 5 3 系统硬件设计 6 3.1 LCD1602显示接口线路 6 3.2 ADC0832简介 6 3.3 湿度传感器介绍 7

    2024年02月05日
    浏览(47)
  • 基于AT89C51单片机的温度控制系统报警器

    点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87771724?spm=1001.2014.3001.5503 源码获取 单片机读取温度传感器当前的温度值并在LCD液晶显示屏上的第一行显示当前的温度值,单片机读取按键状态并通过人为手动按键部分来设置最低温度到最高温度

    2024年02月07日
    浏览(49)
  • 单片机设计_自动追光系统、光源跟踪系统(AT89C51 光敏电阻 步进电机)

    想要更多项目私wo!!!          51 单片机双轴自动追光系统主要由 STC89C52RC + 5516 光敏电阻 + ADC0832 + ULN2803 + 步进电机 + LCD1602 显示屏组成。         1.通过子电路板的上、下、左、右四个光敏电阻来感受四个方向的光强,自 动寻找光强最强的方向。四个光敏电阻的分压电

    2024年02月11日
    浏览(43)
  • 在学习c51单片机实验七(双机通信及pcb设计)操作Keil uVision4和protus的时候遇到的两个坑

    特别是懒的时候,对于新手,每次用一个工程文件,因为创建不熟练,就容易出现这个问题 Build target \\\'Target 1\\\' linking... *** ERROR L104: MULTIPLE PUBLIC DEFINITIONS     SYMBOL:  MAP     MODULE:  shiyan6right.obj (SHIYAN6RIGHT) *** ERROR L104: MULTIPLE PUBLIC DEFINITIONS     SYMBOL:  MAIN     MODULE:  shi

    2024年02月03日
    浏览(98)
  • 【C51】10-基础51单片机的小车项目(51完结)

    10.1小车的安装 10.2电机模块的开发(L9110S) 接通 VCC , GND 模块电源指示灯亮, 以下资料来源官方,但是不对,根据下节课实际调试 IA1 输入高电平, IA1 输入低电平,【 OA1 OB1 】电机正转; IA1 输入低电平, IA1 输入高电平,【 OA1 OB1 】电机反转; IA2 输入高电平, IA2 输入低

    2024年02月08日
    浏览(56)
  • C51实现流水灯

    1、 先八盏灯从左至右依次点亮,同一时刻仅有一盏灯处于被点亮状态,每盏灯亮0.5s,然后八盏灯从右至左依次点亮,同一时刻仅有一盏灯处于被点亮状态,每盏灯亮0.5s,循环两遍; 2、 八盏灯同时闪烁,亮1s,灭0.5s;,实现4次; 3、 上述过程周而复始的循环运行; 代码如

    2024年02月07日
    浏览(43)
  • 基于C51控制蜂鸣器

    🐋 前言:本实验基于STC89C52RC单片机,根据电路原理图编程控制蜂鸣器。由于51系列单片机结构大同小异,读者可根据此博客举一反三,实现所需完成的功能。 🐬 目录: 一、蜂鸣器介绍 二、电路原理图分析 三、实现蜂鸣器鸣叫 🐇 实验所选单片机及结构展示(以普中C51为例,

    2024年02月05日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包