手写最简单的单片机操作系统

1.前言

本文参考自网上各大神的代码,在此基础上做修改,写了一份更适合本人开发的裸机操作系统代码。

在此特别感谢“无际单片机”,他的代码框架讲解真的很不错。

后面还有很多地方需要完善,读者如有想法可以一起探讨。

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

2.原理讲解

我理解的操作系统(OS),就是在系统资源有限的情况下,通过OS进行调度处理,例如对进程、设备、文件、存储资源进行管理。

为什么我想写这个简单操作系统呢?因为个人裸机开发不需要用到FreeRTOS、RT-Thread等来开发,一来是简单任务简单处理,二是当遇到bug时不至于无从下手。

这里的操作系统,其实是放在定时器里(周期1毫秒),每个任务的计数值大于任务周期时,任务将被调度,然后在主循环里进行任务的执行。

外部的命令/数据进入单片机,还是用中断/DMA来处理,可以保证一定能接收。同时,为了保证命令/数据一定能被执行,需要写一个消息队列(后面有空再写一下,给自己挖坑),当系统空闲时对命令/数据进行处理。

这里的话,只要其他函数不进行阻塞(像Delay函数),基本上能保持任务的实时进行。

任务初始化时处于睡眠状态,任务创建时处于就绪状态,当定时时间一到,任务处于运行态,然后在主函数进行轮询处理。

系统可以随时让任务暂停(睡眠)、启动(就绪)。

在下面的演示中,我创建了两个任务,一是LED灯闪烁,二是串口不断发心跳包。

在testPro里,随时可以让任务启停。

3.os.h

#ifndef _OS_H
#define _OS_H
#include "includes.h"

typedef enum {
	Task1,
	Task2,
	TaskNum,
}OS_Task_S;

typedef enum {
	OS_Sleep,
	OS_Ready,
	OS_Run,
}OS_Task_State_S;

typedef struct {
	void (* task)(void);	//任务函数指针
	OS_Task_State_S state;	//任务运行状态
	u32	u32Period;			//任务周期
	u32 u32Tick;			//任务计时器,计时时间超过任务周期时,任务将被执行	
	u32 u32ExeTimes;		//任务被执行次数
} OS_S;

void OS_TaskInit(void);
void OS_TaskCreat(u8 ID, void (* pFunCallBack)(void), u32 period);
void OS_TaskInterruptHandler(void);
void OS_TaskRun(void);
void OS_TaskSleep(u8 ID);
void OS_TaskReady(u8 ID);
void OS_TaskDestroy(u8 ID);

#endif

4.os.h

#include "includes.h"

__IO OS_S OS_Task[TaskNum];

/********************************************
*	@函数名	OS_TaskInit
*	@描述	系统任务初始化
*	@参数	无
*	@返回值	无
*	@注意	该函数放在main函数里while(1)前
********************************************/
void OS_TaskInit(void)
{
	for (u8 i=0; i<TaskNum; i++)
	{
		OS_Task[i].task = 0;
		OS_Task[i].state = OS_Sleep;
		OS_Task[i].u32Period = 0;
		OS_Task[i].u32Tick = 0;
		OS_Task[i].u32ExeTimes = 0;
	}
}

/********************************************
*	@函数名	OS_TaskCreat
*	@描述	创建一个任务
*	@参数	
			ID	任务ID
			(* pFunCallBack)(void)	任务的回调函数
			period	任务周期
*	@返回值	无
*	@注意	无
********************************************/
void OS_TaskCreat(u8 ID, void (* pFunCallBack)(void), u32 period)
{
	if (!OS_Task[ID].task)	//任务为空
	{
		OS_Task[ID].task = pFunCallBack;
		OS_Task[ID].u32Period = period;
		OS_Task[ID].state = OS_Ready;
	}
}

/********************************************
*	@函数名	OS_TaskInit
*	@描述	系统任务调度函数
*	@参数	无
*	@返回值	无
*	@注意	为了保证任务的实时性,
			该中断处理函数必须放在定时器或者系统时钟中断里
********************************************/
void OS_TaskInterruptHandler(void)
{
	for (u8 i=0; i<TaskNum; i++)
	{
		if (OS_Task[i].task)
		{	
			if(OS_Task[i].state != OS_Sleep)
			{
				OS_Task[i].u32Tick++;
				if (OS_Task[i].u32Tick>=OS_Task[i].u32Period)
				{
					OS_Task[i].u32Tick = 0;
					OS_Task[i].state = OS_Run;
				}
			}
		}
	}
}

/********************************************
*	@函数名	OS_TaskRun
*	@描述	当任务时间到,任务状态在定时中断处被置为运行态时,执行任务
*	@参数	无
*	@返回值	无
*	@注意	该函数放置于主函数while(1)里面
********************************************/
void OS_TaskRun(void)
{
	for (u8 i=0; i<TaskNum; i++)
	{
		if (OS_Task[i].state == OS_Run)
		{
			OS_Task[i].state = OS_Ready;
			OS_Task[i].u32Tick = 0;
			(* OS_Task[i].task)();
			OS_Task[i].u32ExeTimes++;
		}
	}
}

/********************************************
*	@函数名	OS_TaskSleep
*	@描述	让任务处于休眠态
*	@参数	ID	任务的ID号
*	@返回值	无
*	@注意	无
********************************************/
void OS_TaskSleep(u8 ID)
{
	if (OS_Task[ID].task)
	{
		OS_Task[ID].state = OS_Sleep;
		OS_Task[ID].u32Tick = 0;
	}
}

/********************************************
*	@函数名	OS_TaskReady
*	@描述	让任务处于就绪态
*	@参数	ID	任务的ID号
*	@返回值	无
*	@注意	无
********************************************/
void OS_TaskReady(u8 ID)
{
	if (OS_Task[ID].task)
	{
		OS_Task[ID].state = OS_Ready;
		OS_Task[ID].u32Tick = 0;
	}
}

/********************************************
*	@函数名	OS_TaskDestroy
*	@描述	销毁任务
*	@参数	ID	任务的ID号
*	@返回值	无
*	@注意	需再次创建任务才能让任务执行
********************************************/
void OS_TaskDestroy(u8 ID)
{
	if (OS_Task[ID].task)
	{
		OS_Task[ID].task = 0;
	}
}


/********************************************
*	@函数名	TIM4_IRQHandler
*	@描述	定时器每隔1ms进一次中断
*	@参数	无
*	@返回值	无
*	@注意	无
********************************************/
void TIM4_IRQHandler(void)
{
	if ( TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET ) 
	{		
		OS_TaskInterruptHandler();
		TIM_ClearITPendingBit(TIM4 , TIM_FLAG_Update);  		 
	}	
}

5.main.c

#include "includes.h"

u8 TestTemp = 0;
static void testPro(void);

int main(void)
{ 
	TimerOsInit();
	LED_Init();
	UartInit();	
	OS_TaskInit();
	OS_TaskCreat(Task1, LED1_Toggle, 200);
	OS_TaskCreat(Task2, UartSendHeartBeat, 500);
	while (1){
		UartLoopTask();
		testPro();
		ledLoopTask();
		OS_TaskRun();
	}
}

static void testPro(void)
{
    if(!TestTemp){return;}
    switch(TestTemp){	
		case 1:
			UaetSendHeartBeat();			
			break;
		case 2:
			LED1_Toggle();
			break;
		case 3:
			OS_TaskSleep(Task1);
			break;
		case 4:
			OS_TaskReady(Task1);
			break;		
	}
	TestTemp = 0;
}

6.演示视频

 

简单的操作系统演示

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您想手写一个浏览器插件,最简单的方式可能是使用JavaScript编写一个简单的浏览器扩展,例如Chrome扩展或Firefox扩展。以下是实现最简单浏览器插件的步骤: 1. 创建一个manifest文件 创建一个名为manifest.json的文件,并将其放置在您的插件目录中。在manifest文件中,您需要指定插件的名称、版本、描述、图标等信息。 例如,以下是一个最简单的manifest文件: ``` { "name": "My Extension", "version": "1.0", "description": "My first extension", "manifest_version": 2 } ``` 2. 添加一个背景脚本 在manifest文件中,您可以指定一个背景脚本,用于处理插件的逻辑。您可以使用JavaScript编写背景脚本,处理插件的功能和事件。 例如,以下是一个最简单的背景脚本: ``` chrome.browserAction.onClicked.addListener(function(tab) { alert('Hello, world!'); }); ``` 这个代码片段会在点击插件图标时弹出一个警告框。 3. 添加一个图标 在manifest文件中,您可以指定插件的图标。您可以使用PNG、JPEG等格式的图像文件作为插件图标。 例如,以下是在manifest文件中指定图标的示例: ``` { "name": "My Extension", "version": "1.0", "description": "My first extension", "manifest_version": 2, "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" } } ``` 4. 安装插件 将您的插件打包为一个zip文件,并通过浏览器的开发者模式安装插件。 在Chrome浏览器中,您可以在“扩展程序”页面中启用开发者模式,然后选择“加载已解压的扩展程序”,选择您的插件目录即可。 在Firefox浏览器中,您可以在“附加组件”页面中选择“调试附加组件”,然后选择“临时加载附加组件”,选择您的插件目录即可。 需要注意的是,这只是一个最简单的浏览器插件示例,如果您想实现更复杂的功能,您需要学习更多的知识和技术。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值