解剖8051内核如何进行多任务切换

最近在玩新唐单片机,这个跟我之前用的51内核是一样的,然后今天觉得跑下多任务,自己研究了下,跟几个同学还讨论了,发现有些人对切换过程还不是十分明白,所以发个文章出来。

直接上代码

#include "MS51_16K.h"/** UART0 initial setting* include sys.c in Library for modify HIRC value to 24MHz* include uart.c in Library for UART initial setting*/
void initialize_UART0(void)
{MODIFY_HIRC(HIRC_24);P06_PUSHPULL_MODE;UART_Open(24000000,UART0_Timer3,115200);ENABLE_UART0_PRINTF;
}void delay_ms(unsigned int n)
{unsigned int i;for(i=0;i<n;i++)_delay_();
}void Timer_ISR (void)   interrupt 17     //ISR for self wake-up timer
{_push_(SFRS);clr_WKTF;                                   //clear interrupt flag_pop_(SFRS);
}void initialize_Timer(void)
{WKCON = 0x00;                     //timer base 10k, Pre-scale = 1/16//RWK = 0XFF;                     //  if prescale is 0x00, never set RWK = 0xffRWK = 0X00;ENABLE_WKT_INTERRUPT;             // enable WKT interruptENABLE_GLOBAL_INTERRUPT;set_EIPH1_PWKTH;set_WKCON_WKTR;
}#define MAX_TASKS 2       /*任务槽个数.必须和实际任务数一至*/
#define MAX_TASK_DEP 100   /*最大栈深.最低不得少于2个,保守值为12*/
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];/*任务堆栈.*/
unsigned int task_id;    /*当前活动任务号*/
unsigned int max_task = 0;unsigned char idata task_sp[MAX_TASKS];void task_switch()
{task_sp[task_id] = SP;if(++task_id == max_task)task_id = 0;SP = task_sp[task_id];
}void task_load(unsigned int fn, int tid)
{task_sp[tid] = task_stack[tid]+1;task_stack[tid][0] = (unsigned int)fn & 0xff;task_stack[tid][1] = (unsigned int)fn >> 8;++max_task;
}void task1()
{static unsigned char i;printf("task,SP:%x\n",(int)SP);while(1){i++;printf("task#1\n");delay_ms(100);task_switch();}
}void task2()
{static unsigned char j;while(1){j+=2;printf("task#2\n");delay_ms(100);task_switch();}
}void switch_to(unsigned int tid)
{task_id = tid;SP = task_sp[tid];return;
}void main(void)
{initialize_UART0();Disable_WDT_Reset_Config();printf("~~~~~~~~~~~~~~~~MainStart...\n");task_load(task1, 0);//将task1函数装入0号槽task_load(task2, 1);//将task2函数装入1号槽switch_to(0);printf("~~~~~~~~~~~~~~~~MainEnd...\n"); 
}

单片机运行输出

代码很简单,就是两个任务进行不断的切换,每个任务进行相应时间的延迟。

先说下第一个函数

把任务函数转载到二维数组保存起来,而且任务函数地址和任务的tid编号要对应。

void task_load(unsigned int fn, int tid)
{task_sp[tid] = task_stack[tid]+1;task_stack[tid][0] = (unsigned int)fn & 0xff;task_stack[tid][1] = (unsigned int)fn >> 8;++max_task;
}

task_sp 用来表示任务的数组

task_stack 用来保存任务函数的地址

说下这行代码

task_sp[tid] = task_stack[tid]+1;

后面的 + 1 ,不知道大家有没有疑惑。

+1 简单理解就是指向了下一个位置

再解剖第二个函数,等下你就知道这个作用的奇特

用来做任务的切换,先保存之前运行的任务函数地址,再改变任务id,把对应任务id的函数地址赋值给SP。

void task_switch()
{task_sp[task_id] = SP;if(++task_id == max_task)task_id = 0;SP = task_sp[task_id];
}

它是妙处不是在这个函数的本身,而是只有比较深入的了解函数调用的过程,才明白其中的奥妙。

SP 是堆栈指针,用来保存当前堆栈的位置

上面的函数是在进入函数的时候,把当前堆栈的值保存在 stak_sp 中,然后更改stak_sp 的值,再赋值给SP。

说如何切换吧

void switch_to(unsigned int tid)
{task_id = tid;SP = task_sp[tid];return;
}

调用函数 switch_to(0) 之前 堆栈和PC指针是这样的

调用函数 switch_to(0) 之后

我们需要把PC之前的值,保存在SP里面,然后呢,PC就开始执行switch_to函数体里面的内容。

然后,改变SP的值,让SP的值等于需要执行函数的地址

函数退出的时候,PC指针又会从SP堆栈位置拿到之前保存的那个地址「实际上已经被我们修改了」,去继续执行。

就是通过这样不断的切换,完成了多个函数交换执行。

这是最基本的多任务系统,代码也不是非常完整,喜欢研究的同学,可以再看看网上的例程。

我这次用的是芯唐 MS51FB9AE 芯片。

有做这方便的同学,欢迎一起讨论~


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

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

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

相关文章

孙鑫6菜单

孙鑫6&#xff0c;遇到的问题解决方法 https://blog.csdn.net/caichao1234/article/details/8431921 C中 error C2593: “operator ”不明确的错误解决方法 https://blog.csdn.net/yaosir1993/article/details/52497890?utm_sourceblogxgwz4

java与.net平台之间进行RSA加密验证

RSA加密算法虽然不分平台&#xff0c;标准都是一样的&#xff0c;但是各个平台的实现方式都不尽相同&#xff0c;下面来我来说说java与.net平台之间该如何进行RSA加密验证&#xff0c;即java端加密-》.net端验证和.net端加密-》java端验证。我对RSA算法也只是停留在应用的层面&…

渡虎谷告诉你CSS的结构和规则

基本语法规则选择符任何HTML元素都可以是一个CSS1的选择符。选择符仅仅是指向特别样式的元素。例如&#xff0c; P { text-indent: 3em }当中的选择符是P。 类选择符单一个选择符能有不同的CLASS(类)&#xff0c;因而允许同一元素有不同样式。例如&#xff0c;一个网页制作者也…

阿莫电子为啥越来越差?

我是在上大学接触到这个论坛的&#xff0c;当时我们创新基地的学长们都注册了这个论坛的账号。记得有一次看见我旁边的学长在浏览一个技术论坛&#xff0c;我就问「这个是什么网站&#xff0c;看起来好牛逼的样子」。我记得他说了一句「阿莫电子你都不知道&#xff0c;你是不是…

TQ210——文件系统

TQ210——文件系统 1.busybox 致命错误: curses.h:没有那个文件或目录 解决办法:ubuntu系统中缺少一个套件 ncurses devel,把此套件安装下即可 $ sudo apt-get install libncurses5-dev 2.Linux 根文件系统目录结构 (1) /bin 目录 该目录下存放所有用户(包括系统管理员和一…

六自由度正反解算法

https://wenku.baidu.com/view/474595887e21af45b307a8e6.html https://wenku.baidu.com/view/8dc7d91d6d85ec3a87c24028915f804d2a168756.html https://max.book118.com/html/2017/0701/119416936.shtm

stdout字符串过滤输出

功能 把stdout的内容重定向到一个文件,然后从文件里面读取数据,再把数据对应的字符串给过滤掉,重新输出到stdout中 代码 // gcc xx.c -lpthread && ./a.out #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h&…

使用栈实现中缀表达式转为后缀表达式和后缀表达式的求解

书籍在线网址http://interactivepython.org/runestone/static/pythonds/index.html 中文翻译书籍&#xff1a;https://facert.gitbooks.io/python-data-structure-cn/ 将中缀表达式转换为后缀表达式 中缀表达式&#xff08;上图&#xff0c;一般书写&#xff09;转换为后缀表达…

C#基础:Lambda表达式

从委托的角度来看&#xff0c;Lambda表达式与匿名方法没有区别。在【C#基础&#xff1a;匿名方法】一文中&#xff0c;我使用了匿名方法来调用List<T>的FindAll方法。从C# 3.0开始&#xff0c;在使用匿名方法的地方&#xff0c;完全可以用Lambda表达式来代替。Lambda表达…

TQ210 —— 点亮LED

TQ210 —— 点亮LED 1、S5PV210 GPIO硬件简介 1.1、GPIO 【S5PV210有237个多功能IO口&#xff0c;通过设置寄存器确定某个引脚用于输入输出或者其他特殊功能】 GPIO的英文全称为General-PurposeIO ports&#xff0c;也就是通用IO接口。在嵌入式系统中常常有数量众多&#xff0…

bind1nd\bind2nd

https://www.cnblogs.com/renyuan/p/6216375.html https://blog.csdn.net/jnu_simba/article/details/9530341

拆解镍氢电池充电器,简单的电路设计令人震惊!

上班爱摸鱼&#xff0c;下班爱钓鱼&#xff0c;于是买了一个钓鱼机。不贵&#xff0c;几十块钱&#xff0c;带4条鱼竿。长这样&#xff1a;电池驱动&#xff0c;钓起鱼来乐趣无穷&#xff1a;惊喜的是&#xff0c;钓鱼机自带附赠的电池是可充电的&#xff0c;省下不少买干电池的…

设备底座几个常见固定方式

在运营商我们可以经常发现如下的固定方式&#xff0c;因为设备厂家的不同&#xff0c;以及机房上下走线方式&#xff0c;固定方式也有些差异。可以清楚看见&#xff0c;底座可以靠螺母进行微调使机柜高度&#xff0c;底部还有膨胀螺丝打入地板还有很多机柜是不加微调用的底座&a…

抽35块树莓派新品单片机送给可爱的你们

我是谁~公众号「嵌入式Linux」&#xff0c;深入解析简明扼要直白解释各种技术问题&#xff0c;包括但不局限于Linux、单片机、半导体、Android、C语言等等。号主从事嵌入式软件行业有10余年&#xff0c;建立了10余个技术微信群&#xff0c;整理了很多技术资料&#xff0c;欢迎大…

bitset类

C语言的一个类库&#xff0c;用来方便地管理一系列的bit位而不用程序员自己来写代码。 bitset除了可以访问指定下标的bit位以外&#xff0c;还可以把它们作为一个整数来进行某些统计。 可以如下声明一个该类型变量&#xff1a; bitsetvarm (M) 其中varm为变量名。 N表示该类型在…

TQ210——时钟系统

TQ210——时钟系统 1、时钟域 一般来讲&#xff0c; MCU 的主时钟来源主要是外部晶振或外部时钟&#xff0c;比较常见的是 外 部晶振。一般情况&#xff0c;系统内所使用的时钟都是高频率的时钟&#xff0c;外部晶振一般达不到那么高的频率&#xff0c;所以一般使用锁相环电路&…

Android 监听耳机的插拔事件

一般采用的是动态监听的方式来实现的&#xff1a; package com.renhui.ej;import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.supp…

ubuntu操作

下面的命令大都需要在控制台 / 终端 / shell 下输入。 控制台, 终端, 和 shell 意味着同样一件事 - 一个命令行界面&#xff0c; 他可以用来控 制系统。 打开一个控制台: 应用程序 --> 附件 --> 终端 任何一个使用 sudo 作为前缀的命令都需要拥有管理员 (或 root) 访问权…

我的文章汇总

2018年文章汇总2019年文章汇总2020年文章汇总专辑|Linux文章汇总专辑|程序人生专辑|C语言专辑|Git专辑|安卓系统专辑|嵌入式我的知识小密圈关注公众号&#xff0c;后台回复「1024」获取学习资料网盘链接。欢迎点赞&#xff0c;关注&#xff0c;转发&#xff0c;在看&#xff0c…

CMap

https://blog.csdn.net/u010879420/article/details/53914767