这个任务调度模块的实现是形成于毕设项目中的,用在STM32中,断断续续跨度2个月实现了一些基本功能,可能后面再做其他项目时会一点点完善起来,也会多学习相关知识来强化模块的实用性和高效性,毕竟用自己自主实现出来的功能还是蛮舒心的。
整体上的结构属于线性结构,结合链表和定时器来实现,我使用的是sysTick这个滴答时钟,1ms的频率,功能比较简单,容易理解。
分片的模式,主要体现在函数分片和时间分片在我之前就有使用在函数中,主要的思路是,把函数功能切片,分为几个小部分,每次执行时按次序执行小部分,对于没有时序要求的函数来说,可以把一个占用CPU大的功能分摊开来实现,从而避免有些地方耗时长的问题。对于时间分片,其实就是定时器的一种应用,实际上,函数分片在执行的时候已经是一种时间分片了,不过现在加上人为的控制在里面了。
下面是函数分片的一般结构:
void func(char *fos,...){
static char step=0;//顺序控制变量,自由度比较高,可乱序,可循环,可延迟执行
switch(step){
case 0:{
//...
step++;
break;
}
case 1:{
//...
step++;
break;
}
//...
default:{
//step++;//可以借助default实现延时的效果,即跳过几次空白step
break;
}
}
return;
}
其中添加的参数变量*fos是必要的,因为就是通过传入每个任务的这个标志位来判断是否运行结束,而其他的参数,就得基于具体任务做不一样的处理了。
可以看到这个框图是一个头尾相连的闭环结构,从头节点依次运行到尾节点后再从头循环往复执行下去。
void loop_task(void){
static Task_Obj *tasknode;
tasknode=task_curnode->next;//repoint the curnode to the next
if(tasknode==NULL){//tasknode is null,only the headnode have the attr
return;//express the task space is none
}
else if(tasknode->task_type==TYPE_HEAD){//tasknode is headnode
task_curnode=tasknode;
return;
}
else{
if(tasknode->run_type == RUN_WAIT){
//等待型任务,通过ready标志来确定是否执行,否则就跳过
if(!tasknode->ready){
if(task_curnode->next !=NULL){
task_curnode=task_curnode->next;
return;
}
}
}
if(tasknode->task_status==STATUS_INIT){
tasknode->tickstart=HAL_GetTick();//获取tick
tasknode->task_status=STATUS_RUN;
}
else if(tasknode->task_status==STATUS_RUN){
if((HAL_GetTick() - tasknode->tickstart) > (uint32_t)tasknode->task_tick){
tasknode->task_name(&(tasknode->task_fos));//run the step task,transfer the fos
tasknode->tickstart+=(uint32_t)tasknode->task_tick;//update the tickstart
}
}
}
if(tasknode->task_fos==FOS_FLAG){
tasknode->ready=0;
if(tasknode->waittask!=NULL){
//置位该任务绑定的等待的任务准备运行标志位,标识可以准备运行了
tasknode->waittask->ready=1;
}
//运行结束就删掉该任务
delete_task(tasknode);
}
else if(tasknode->task_fos==FOC_FLAG){
//循环运行该任务
tasknode->task_status=STATUS_INIT;//continue running from start
tasknode->task_fos=0;//RESET fos
}
if(task_curnode->next !=NULL){
if(task_curnode->next->run_type==RUN_FORCE) return;//force-type's task
else task_curnode=task_curnode->next;
}
}
其中有几个运行态和标志位
#define FOS_FLAG 99//运行结束标志
#define FOC_FLAG 100//运行结束后再次执行,相当于循环运行
#define TYPE_NOMAL 0//标识一般任务类型
#define TYPE_HEAD 1//标识头任务类型
#define TYPE_END 2//标识尾任务类型
#define RUN_NORMAL 0//一般轮询模式
#define RUN_FORCE 1//强制运行该任务,运行结束才继续下一个任务
#define RUN_WAIT 2//等待指定的任务结束,才可以被运行
#define STATUS_INIT 0//任务的准备阶段,用于获取起始时间
#define STATUS_RUN 1//任务运行阶段
#define STATUS_UNVAILED 2//无效状态
运行时对时间间隔tick的把握还有点问题,这个等待后面有机会优化下。
typedef struct TASK_CLASS{
void (*task_name)(char *taskfos,...);//任务函数
int task_tick;//任务的时间分片间隔
uint32_t tickstart;//起始时间点,每次执行完须加上一个tick
char task_fos;//运行结束标志
char task_type;//任务类型变量
char task_status;//任务状态
char run_type;//运行状态
char ready;//准备运行标志位
struct TASK_CLASS *next;//下一任务
struct TASK_CLASS *waittask;//等待执行的任务
} Task_Obj;
添加任务
void add_task(void (*taskname)(char *,...),int tasktick,int runtype){//可变参,这里未做处理
Task_Obj *tasknode,*tmpnode;
char i;
tasknode = (Task_Obj*)malloc(sizeof(Task_Obj));
tasknode->task_name=taskname;
tasknode->task_tick=tasktick;
tasknode->task_fos=0;
tasknode->task_status=STATUS_INIT;//initial status
tasknode->task_type=TYPE_END; //set the new node to endnode
tasknode->run_type=runtype;
tasknode->next=&task_headnode;//the endnode point to the headnode
tmpnode=&task_headnode;
if(task_num==0){
tmpnode->next=tasknode;
task_num++;
return;
}
for(i=0;i<task_num;i++){
tmpnode=tmpnode->next;//reach the endnode
}
tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node
tmpnode->next=tasknode;
task_num++;
}
void add_wait_task(void (*taskname)(char *),void (*waitname)(char *),int tasktick){
Task_Obj *tmpnode,*tasknode;
char i,pos;
tmpnode=&task_headnode;
for(i=0;i<task_num;i++){
tmpnode=tmpnode->next;//reach the endnode
if(tmpnode->task_name==taskname){
pos=i;//获取要等待任务的位置
break;
}
}
tasknode = (Task_Obj*)malloc(sizeof(Task_Obj));
tasknode->task_name=waitname;
tasknode->task_tick=tasktick;
tasknode->task_fos=0;
tasknode->task_status=STATUS_INIT;//initial status
tasknode->task_type=TYPE_END; //set the new node to endnode
tasknode->run_type=RUN_WAIT;//任务为等待运行
tasknode->ready=0;
tasknode->next=&task_headnode;//the endnode point to the headnode
tmpnode->waittask=tasknode;//获取新建的等待执行的任务地址,在运行结束后把等待执行的任务的准备运行标志位置1
tmpnode=&task_headnode;
if(task_num==0){
tmpnode->next=tasknode;
task_num++;
return;
}
for(i=0;i<task_num;i++){
tmpnode=tmpnode->next;//reach the endnode
}
tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node
tmpnode->next=tasknode;
task_num++;
}
删除任务
void delete_task(Task_Obj *taskobj){
if(task_curnode->task_type==TYPE_HEAD && task_num < 2){//if curnode is headnode,and tasknum=1
task_curnode->next=NULL;
}
else{
task_curnode->next=taskobj->next;//repoint the curnode next
}
free(taskobj);//free the space of where the taskobj pointed
task_num--;
}
void delete_task_withname(void (*taskname)(char *)){
Task_Obj *tmpnode,*tmpnode2;
char i,pos;
tmpnode=&task_headnode;
for(i=0;i<task_num;i++){
tmpnode=tmpnode->next;//reach the endnode
if(tmpnode->task_name==taskname){
pos=i;
break;
}
}
if(i==task_num) return;
tmpnode=&task_headnode;
for(i=0;i<pos+1;i++){
tmpnode2=tmpnode;
tmpnode=tmpnode->next;
}
if(tmpnode->next==NULL){//if tmpnode is endnode
tmpnode2->next=&task_headnode;
}
else{
tmpnode2->next=tmpnode->next;//repoint the curnode next
}
task_num--;
free(tmpnode);
}
初始化任务空间
void non_task(char *taskfos){
return;
}
void init_taskspace(void){
task_headnode.task_name=non_task;
task_headnode.task_type=TYPE_HEAD;
task_headnode.task_status=STATUS_UNVAILED;
task_headnode.next=NULL;
task_curnode=&task_headnode;//头节点是没有任务需要执行的
task_num=0;
}
add_task(task1,500,RUN_NORMAL);//500ms执行一次task1任务
add_wait_task(task1,task2,500);//task2等待task1结束才会执行,运行的时间间隔为500ms
delete_task_withname(task1);//删除task1任务
while(1){
//...
loop_task();//任务轮询
}
整体实现说难不难,说简单不简单,但也是我第一次尝试这种偏向系统级应用的代码,而且都没有参照任何其他的资料和代码,完全以自己的对任务的理解和具体项目的需求来一点点实现,希望后面会把这个调度的代码进一步完善成一个通用型的调度方式,也方便后面项目的使用了。
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复