51单片机 - 自写操作系统

在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();
}

代码没实现:

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

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值