1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
写这个线程调度器的起因
学习单片机2年了,一直都是用的裸机开发。不过随着工程任务越来越复杂,觉得是时候上个系统了。 很多时候使用一个while(1)大循环+状态机就可以实现很多的功能,曾经我认为在单片机里加入系统,系统的任务切换会占用一定的cpu资源,感觉效率不如状态机来的高。 (好吧,当时我还没有意识到系统的强大) 不过在尝试使用了系统之后,发现很多事情方便了很多,状态机需要花费很多功夫来进行任务分布。例如我需要每个一段时间调用一个按键扫描程序,我的做法是在大循环里检查时间tick(在stm32的hal库中就使用HAL_GetTick()来获取时间),到时间了就执行按键扫描一次。 但是使用系统后只需要新建一个任务,里面加个循环直接用系统的延时函数就能实现相同功能。这个还不是系统最强大的地方,系统还可以通过对中断的响应,从一个任务切换到另一个任务(这是我觉得系统最强大的地方,也是系统能充分利用cpu的来源)。这个特性在代码中的体现就是中断对邮箱发送消息,然后中断结束之后系统就会立即吧任务切换到读取邮箱的任务中。(这个特性在我做的一个小玩意中让我省了一个定时器,原本裸机程序中这个定时器的中断是用来当作比while(1)优先级高,比USB中断优先级低的线程使用的,用来处理USB的数据) 不过我并不打算使用现成的操作系统,我打算尝试自己写一个,我最先从 Cortex-M3权威指南 来研究(先不管标题为什么是M4,先从简单的做起),这玩意有中文翻译版,学习起来比盯着满天英文舒服的很多,里面还要一些汇编指令的说明,为我写任务调度器提供了支持(任务调度内核还是离不开汇编的),然后我发现还有 ARM Cortex-M3与Cortex-M4权威指南 这个“葵花宝典”,这个也是中文的,因为M4多了FPU,内核编写需要注意浮点寄存器的处理(FPU这个鬼玩意个地方比较坑 FPU->FPCAR这个寄存器似乎于MSP有关联)。 实现参考 我先用STM32CubeMX生成了个带freertos的工程(不用自己移植简直不要太方便),先从freertos入手,研究freertos的源码。发现这个rtos临界区满天飞,而且区是由屏蔽全局中断来实现的(吐了一口老血),cortex强大的中断系统,中断这么一掐,心里总感觉不舒服,感觉这种系统用起来很不踏实(个人感觉)。然后我寻找有没有不掐中断的系统,结果还真找到了,它就是RTX,不过它凭什么不需要使用关中断来实现数据同步呢,在keil的目录中有RTX的源码,研究之后发现,其实,RTX在cortex中能实现不关中断又能实现数据同步,是因为它使用了LDREX/STREX这两个指令 RTX源码中有个宏定义 __USE_EXCLUSIVE_ACCESS 决定了使不使用这个特性 利用这两个指令的特性可以保证写入数据的原子特性,也可以得知,如果RTX用在没有类似于LDREX/STREX特性的平台上,它还是要乖乖关中断。 不过我实现的这个线程调度器并不想依赖这个特性(不想过多依赖硬件特性),当时我又不想屏蔽中断(中断是不可能掐的,这辈子都是不可能掐的),这使得我不得不将有关于中断的os功能削弱一点,在freertos中一个邮箱应该是能接收多个中断的发送请求的,但我的os的邮箱只能接收一个(没有关中断的支持也没办法啊)。 不过一般情况下邮箱只会有一个发送者,这个削弱影响不大。 虽然中断的邮箱发送只能单发送,但是邮箱的发送者都是线程的话,邮箱支持多发送者,这使得邮箱可以一种特殊的方法接收多个中断的邮箱,每个中断都有一个线程来接收中断的邮箱,这些线程再把消息转发到同一个线程的邮箱上,勉强实现了线程接收多个中断的邮箱(这个方法有点费内存啊 为了不关中断而费点内存,不亏 )。 这个线程调度器实现的功能 这个线程调度器的源码贴在后面,第一次写这玩意,是边学边写的,现学现卖,代码写的比较奔放(命名,结构混乱),后面再慢慢把代码整理了。不过得益于奔放的代码,源码才4个文件,加上内存管理,总共7个文件。 目前这个代码我在STM32F411和STM32H750使用过,未发现问题。F1系列的我没有测试,但用在上面的话只需要将FPU相关的现场保护,相关寄存器操作去掉应该就能用了。 调度器占用的资源有: (1)内存 (废话) (2)SysTick中断 (只需要在中断中调用一个函数就行不需要完全占用) (3)PendSV中断 (在Cortex中 这个中断可是任务切换利器) 这个调度器没有使用到SVC中断(我终于可以用它干一些奇怪的事情了) 目前调度器实现的功能有: (1)线程:这个都不能实现还算什么线程调度器,线程可创建,可删除(仅支持线程删除自己本身)。 (2)线程休眠:线程可以释放cpu,休眠一段时间在继续运行(不就是延时嘛,但好像不完全是) (3)邮箱:中断仅支持单发单收,线程中支持多发单收,支持限定时间的发送/接收操作,支持尝试发送/接收的操作,中断仅支持尝试发送(你说什么?多发多收!好像没有这么干的必要)。 (4)锁:可用于保证资源互斥访问,一个线程获得锁之后,其他线程将不能获得这个锁,直到锁的拥有线程释放了锁,锁支持尝试获取,限时尝试获取,获取失败阻塞线程,锁仅用在线程之间 (5)CPU占用率统计:如果使用,这个功能将占用一个高精度定时器(要求实现一个方法,返回上次调用这个方法于这次调用这个方法所过去的时间) 目前实现的功能不是很多,不过这些功能已经可以完成很多工作了,我认为线程调度器的核心功能就是于各种中断打交道,以实现任务调度,目前调度器于中断打交道的功能只有邮箱,邮箱似乎是万能的(这里的各种中断指的是除了PendSV SysTick之外的中断,例如USB,串口等) 如何把这个线程调度器加入自己的工程中 以STM32F4的HAL工程为例: 先修改stm32f4xx_it.c文件 里面构造了个全局变量SysTick_IRQ_Call,用于存放中断回调,线程调度器会在合适的时候调用SysTick_SetIrqCallback()来设置这个中断回调,以防止线程调度器的Systick处理被提前调用,线程调度器并不会完全占用systick中断,其内部依然可以加入用户的处理代码,例如HAL库的HAL_IncTick() (这样可以不影响HAL库的工作,至于图片里为什么不是HAL_IncTick()?是因为我把HAL库的延时部分修改了) PendSV_Handler被注释掉了,因为它在H_task_port.s中被定义了,线程调度器将完全占用PendSV 一些头文件包含需要修改 stm32f4xx_hal.h可以替换为stm32f4xx.h 根据平台决定 Peripheral.h是用户头文件 编译的时候缺什么加什么就行。 H_task_test.c文件的开始调度方法需要修改 其实也不算什么修改 里面就是使能fpu 设置PendSV中断优先级为最低 不同平台可能方法不同,修改为对应平台的就可以。 最后保证其他中断的优先级不为最低即可(即不能让PendSV能够抢占其他中断),因为PendSV退出后必须是退出到线程模式的。 上面都是必须要的操作,下面还有一些不是必要操作,根据需求而定。 在H_task_test.c中可以配置内存大小,内存来源于Mem这个数组并且内存在H_taskInit()中进行初始化,可以根据需要修改 H_task_test.h中一个获取时间的方法,这个是用于CPU占有率统计的,如果不使用的话,直接返回0即可 现在这个线程调度器就可以使用了 调度器的使用 具体流程为: (初始化调度器)->(创建信号量,锁)->(初始化外设,创建任务)->(开始调度) 创建信号量、锁在初始化外设之前,这是为了避免中断在邮箱创建之前就向邮箱发送消息。 这是一个示例,User_Init()是用来初始化外设的。建议main函数一开始就调用H_TaskInit()方法来初始化线程调度器。 线程/任务 所谓线程它的本体就是一个函数 void delegate_task(void* v){ void* dv[2]; void (*_Void_voidPtr)(void*); while(1){ H_task_Msg_Read(v,dv); _Void_voidPtr=dv[0]; _Void_voidPtr(dv[1]); } } 这个是线程从邮箱里读数据,邮箱每个消息固定传2个指针,这个例子中这两个指针是一个方法和其传入的参数 在这个线程调度器中,线程的本体代码需要能传入一个指针,无返回参数(其实不需要严格遵守) 目前线程调度器仅支持线程结束自己的线程,如果想结束线程,只需要调用 //退出当前线程 并释放线程占用的资源 void TaskExit(void); 1 2 或者直接让线程函数返回 //让指针v指向的无符号整型数每毫秒加1,循环1000次后退出 void _add(void* v){ int i; for (i = 0; i < 1000; i++) { ((unsigned int*)v)[0]++; TaskSleep(1); } } 其实线程返回后就跳到TaskExit()方法里了。 调度器的初始化 线程调度器的初始化使用 void H_TaskInit(void* v,int StackSize) v 是空闲任务传入参数(其实就是一个指针),这个指针现在对于空闲任务来说似乎并没有什么用(用户也基本碰不到空闲任务) StackSize 空闲任务堆栈大小,单位:字节,如果传入大小不为4的倍数,会转换为大于它并且最接近它的4的倍数。 线程的创建 创建任务使用方法 void CreateTask(H_task_info_def** taskPtr,void (* Code)(void*),void* v,int StackSize,int Priority) taskPtr 如果想获取任务句柄,此段就为容纳句柄的指针,如果不想获取,传入NULL即可 Code 线程运行的代码 v 线程传入参数 StackSize 线程堆栈大小 Priority 优先级 数字越小优先级越高 可设置为除0x7FFFFFFF(int的最大值,被空闲任务使用)外的所有数(int类型),除空闲任务外,其他任务的优先级可以设置为相同,但仅仅是传入的Priority相同了,内部还是会将Priority的优先级进行区分。但是Priority不同的任务之间的优先级大小是确定的 数字小的优先级大。 开始任务调度 void StartScheduler() 调用即可,无脑操作。它后面的代码不会执行到 线程休眠 调用 //线程休眠一段时间 void TaskSleep(int NumOfTick); 后触发线程调度,NumOfTick个SysTick中断后再将线程置为就绪态 锁 我之前用c#做过上位机,发现锁是个好东西,我的这个线程调度器也要整一个。 这个锁仅支持线程之间使用 锁相关方法: //新建一个锁 H_task_Lock_Def* new_H_task_Lock(void); //锁定Lock 如果Lock已被其他线程占用 则阻塞当前线程并启动任务调度 void H_task_Lock(H_task_Lock_Def* lock); //尝试锁定一个锁 返回0:成功 int H_task_tryLock(H_task_Lock_Def* lock); //尝试锁定一个锁 超时检测 int H_task_tryLockTimeOut(H_task_Lock_Def* lock,int TimeOut); //释放锁 void H_task_Unlock(H_task_Lock_Def* lock); //删除锁 void H_task_Lock_Delete(H_task_Lock_Def* lock); 邮箱 邮箱有线程之间使用的邮箱(函数名不带irq),还有线程于中断之间使用的邮箱(函数名带irq),但邮箱的类型都是H_task_Msg_irq 邮箱相关方法: //新建邮箱 H_task_Msg_irq* new_H_task_Msg_irq(int NumMsg); //中断向邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_irq_Send(H_task_Msg_irq* msg,void* v0,void* v1); //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_irq_tryRead(H_task_Msg_irq* msg,void** dv); //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_irq_Read(H_task_Msg_irq* msg,void** dv); //删除邮箱 void H_task_Msg_irq_Delete(H_task_Msg_irq* msg); //新建邮箱 H_task_Msg_irq* new_H_task_Msg(int NumMsg); //邮箱发送消息 void H_task_Msg_Send(H_task_Msg_irq* msg,void* v0,void* v1); //邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_trySend(H_task_Msg_irq* msg,void* v0,void* v1); //尝试向邮箱发送消息 超时失败 返回 0:成功 其他:失败 int H_task_Msg_trySendtimeOut(H_task_Msg_irq* msg,void* v0,void* v1,int TimeOut); //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_tryRead(H_task_Msg_irq* msg,void** dv); //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_Read(H_task_Msg_irq* msg,void** dv); //尝试读取消息 阻塞 超时返回失败 返回 0:成功 其他:失败 int H_task_Msg_tryReadtimeOut(H_task_Msg_irq* msg,void** dv,int TimeOut); //删除邮箱 void H_task_Msg_Delete(H_task_Msg_irq* msg); 能在中断调用的方法只有H_task_Msg_irq_Send(),其他的方法只能在线程里调用(其实能被用户中断调用的方法在这个线程调度器中也只有这个)。H_task_Msg_irq_Send()是线程调度器与用户中断交互的唯一通道,信息只能从中断发送到线程,然后线程再操作外设。(要不然中断直接操作外设,但是这样就没线程调度器的事情了) 还有一些细节没有详尽的说明,例如邮箱的使用细节,但是如果使用过其他os,使用这个应该不麻烦,我相信源码能说明一切。 源码 每个代码段都是一个文件,复制粘贴保存为对应的文件名即可,每段代码段都有注明文件名。这些文件可放在同一个文件夹上 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 文件名:H_task_test.h #ifndef __H_task_test_H_ #define __H_task_test_H_ #include "H_Malloc.h" typedef struct _H_task_info_def { void* parent; void* StackPointer; void* Stack; Hsize StackSize; struct _H_task_info_def* next; struct _H_task_info_def* DelayNext;//用于延时列表的指针 int Priority;//优先级 仅影响线程在列表中的排列 值越小 在列表的位置就靠前 优先级高 若有两个线程优先级相同 实际优先级也是一高一低 取决于创建线程时线程加入列表的方式 volatile int Status;//线程状态 0:就绪 -1:线程结束标记 -2:被邮箱阻塞 -3:被锁阻塞 -4:发送时被邮箱阻塞 大于0:因延时导致的阻塞 此段还表示为延时的tick数 其他:阻塞态 volatile void* BlockObject;//阻塞对象 当Status为被中断邮箱阻塞时 此段为中断邮箱指针 当被锁阻塞时 此段为锁的指针 }H_task_info_def; //锁 typedef struct { volatile H_task_info_def* thread; volatile int isHighPriorityLockReq;//是否有更高优先级的锁请求 }H_task_Lock_Def; //用于中断向线程发送消息的消息句柄 typedef struct { H_task_info_def* thread;//正在尝试读取邮箱的线程 struct{ void** MsgArray; int I_Offset; int O_Offset; int MsgArrayNum; }Msgs; }H_task_Msg_irq; typedef struct { H_task_info_def* root; H_task_info_def* DelayRoot; void*** RunThread; volatile int SchedulerSuspend;//当此段不为0时挂起线程调度 volatile int SchedulerForIrq;//挂起线程调度时 中断产生了调度请求后 此段被置为非0 int isDelete;//是否有待删除的任务 此段由删除任务函数设置 空闲任务清除 int CPU_Utilization;//1000为百分百占用 负数表示cpu占有率获取未实现或者还未得到第一个占有率 Huint32 idleTime;//线程空闲时间 Huint32 runTime;//线程运行时间 struct { void* (*Malloc)(Hsize); void (*Free)(void*); }Mem; }H_task_test; //初始化 传入空闲任务参数和空闲任务堆栈大小 void H_TaskInit(void* v,int StackSize); //创建任务 void CreateTask(H_task_info_def** taskPtr,void (*Code)(void*),void* v,int StackSize,int Priority); //线程休眠一段时间 void TaskSleep(int NumOfTick); //退出当前线程 并释放线程占用的资源 void TaskExit(void); //开始调度 void StartScheduler(void); //新建邮箱 H_task_Msg_irq* new_H_task_Msg_irq(int NumMsg); //中断向邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_irq_Send(H_task_Msg_irq* msg,void* v0,void* v1); //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_irq_tryRead(H_task_Msg_irq* msg,void** dv); //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_irq_Read(H_task_Msg_irq* msg,void** dv); //删除邮箱 void H_task_Msg_irq_Delete(H_task_Msg_irq* msg); //新建邮箱 H_task_Msg_irq* new_H_task_Msg(int NumMsg); //邮箱发送消息 void H_task_Msg_Send(H_task_Msg_irq* msg,void* v0,void* v1); //邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_trySend(H_task_Msg_irq* msg,void* v0,void* v1); //尝试向邮箱发送消息 超时失败 返回 0:成功 其他:失败 int H_task_Msg_trySendtimeOut(H_task_Msg_irq* msg,void* v0,void* v1,int TimeOut); //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_tryRead(H_task_Msg_irq* msg,void** dv); //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_Read(H_task_Msg_irq* msg,void** dv); //尝试读取消息 阻塞 超时返回失败 返回 0:成功 其他:失败 int H_task_Msg_tryReadtimeOut(H_task_Msg_irq* msg,void** dv,int TimeOut); //删除邮箱 void H_task_Msg_Delete(H_task_Msg_irq* msg); //新建一个锁 H_task_Lock_Def* new_H_task_Lock(void); //锁定Lock 如果Lock已被其他线程占用 则阻塞当前线程并启动任务调度 void H_task_Lock(H_task_Lock_Def* lock); //尝试锁定一个锁 返回0:成功 int H_task_tryLock(H_task_Lock_Def* lock); //尝试锁定一个锁 超时检测 int H_task_tryLockTimeOut(H_task_Lock_Def* lock,int TimeOut); //释放锁 void H_task_Unlock(H_task_Lock_Def* lock); //删除锁 void H_task_Lock_Delete(H_task_Lock_Def* lock); //获取cpu占用率 int H_task_getCPU_Utilization(void); #endif //__H_task_test_H_ +++++++++++++++++++++++++++++++++++++++++++++++++++++ 文件名:H_task_test.c #include "H_task_test.h" #include "stm32f4xx_hal.h" H_task_test* H_task_tcp; __align(8) static Hbyte Mem[16*1024]; extern H_task_info_def* new_Task(void (*Code)(void*),void* v,int StackSize,int Priority); extern void port_startFirstThread(void); static void* Malloc(Hsize Size){ void* r; r=H_Malloc(Mem,Size); if(r==NULL){ for(;;) { //无效内存 } } return r; } static void Free(void* p){ H_Free(Mem,p); } int GetMemBfb(){ H_Malloc_Info_Def info; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 H_Malloc_GetInfo(Mem,&info); H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return 100*info.UseSize/(info.UseSize+info.FreeSize); } static void tryDeleteTask(){ H_task_info_def* t; H_task_info_def* p; H_task_info_def* p_last; p=H_task_tcp->root; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 while(p!=NULL){ if(p->Status!=-1){ break; }else{ t=p; p=p->next; H_task_tcp->Mem.Free(t->StackPointer); H_task_tcp->Mem.Free(t->Stack); H_task_tcp->Mem.Free(t); } } H_task_tcp->root=p; if(H_task_tcp->root!=NULL){ p_last=H_task_tcp->root; p=p_last->next; while(p!=NULL) { if(p->Status!=-1) { p_last=p; p=p->next; }else{ p_last->next=p->next; t=p; p=p->next; H_task_tcp->Mem.Free(t->StackPointer); H_task_tcp->Mem.Free(t->Stack); H_task_tcp->Mem.Free(t); } } }else{ for(;;){ //根节点不可能为空 } } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } void EmptyTask(void* v){ for(;;) { if(H_task_tcp->isDelete) { H_task_tcp->isDelete=0; tryDeleteTask(); } } } //初始化 传入空闲任务参数和空闲任务堆栈大小 void H_TaskInit(void* v,int StackSize){ H_task_info_def* t; H_Malloc_Init(Mem,16*1024); H_task_tcp=Malloc(sizeof(H_task_test)); H_task_tcp->Mem.Malloc=Malloc; H_task_tcp->Mem.Free=Free; H_task_tcp->RunThread=NULL; H_task_tcp->DelayRoot=NULL; H_task_tcp->isDelete=0; H_task_tcp->SchedulerForIrq=0; H_task_tcp->CPU_Utilization=-1; H_task_tcp->idleTime=0; H_task_tcp->runTime=0; t=new_Task(EmptyTask,v,StackSize,0x7FFFFFFF); t->next=NULL; t->parent=H_task_tcp; H_task_tcp->root=t; } //创建任务 void CreateTask(H_task_info_def** taskPtr,void (*Code)(void*),void* v,int StackSize,int Priority){ H_task_info_def* t; H_task_info_def* p; H_task_info_def* p_last; H_task_info_def* _this; if(Priority==0x7FFFFFFF){ //不能使用这个优先级 return; } H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 t=new_Task(Code,v,StackSize,Priority); t->next=NULL; t->parent=H_task_tcp; if (taskPtr!=NULL) { *taskPtr=t; } if(H_task_tcp->root->Priority>Priority){ t->next=H_task_tcp->root; H_task_tcp->root=t; }else{ p_last=H_task_tcp->root; p=p_last->next; while(p!=NULL){ if(p->Priority>Priority){ t->next=p; p_last->next=t; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 }else{ if(H_task_tcp->RunThread!=NULL){ _this=(*H_task_tcp->RunThread)[1]; if(_this->Priority>Priority){ //新建的任务优先级比当前任务优先级更高 触发调度 SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } } return; } p_last=p; p=p->next; } t->next=NULL; p_last->next=t; } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 }else{ if(H_task_tcp->RunThread!=NULL){ _this=(*H_task_tcp->RunThread)[1]; if(_this->Priority>Priority){ //新建的任务优先级比当前任务优先级更高 触发调度 SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } } } //线程休眠一段时间 void TaskSleep(int NumOfTick){ H_task_info_def* _this; H_task_info_def* p; if(NumOfTick<=0){ //进来个不大于0的家伙 是不是不想活了 return; } _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 _this->Status=NumOfTick; if(H_task_tcp->DelayRoot==NULL){ _this->DelayNext=NULL; H_task_tcp->DelayRoot=_this; }else{ p=H_task_tcp->DelayRoot; while (p->DelayNext!=NULL) { p=p->DelayNext; } _this->DelayNext=NULL; p->DelayNext=_this; } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 while(_this->Status){ //等待阻塞结束 } } //直接终止一个线程 并释放线程占用的资源 如果传入的为NULL 则为终止调用它的线程 void TaskAbort(H_task_info_def* Thread){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; if(Thread!=NULL){ if(_this!=Thread){ //终止的不是本线程 H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 Thread->Status=-1;//线程结束标记 H_task_tcp->isDelete=-1; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return; } } //终止的是本线程 H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 _this->Status=-1;//线程结束标记 H_task_tcp->isDelete=-1; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 for(;;){ } } //退出当前线程 并释放线程占用的资源 void TaskExit(){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 _this->Status=-1;//线程结束标记 H_task_tcp->isDelete=-1; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 for(;;){ } } //开始调度 void StartScheduler(){ FPU->FPCCR|=FPU_FPCCR_ASPEN_Msk|FPU_FPCCR_LSPEN_Msk; HAL_NVIC_SetPriority(PendSV_IRQn, 15U, 0U); port_startFirstThread(); } //新建邮箱 H_task_Msg_irq* new_H_task_Msg_irq(int NumMsg){ H_task_Msg_irq* r; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 r=H_task_tcp->Mem.Malloc(sizeof(H_task_Msg_irq)); r->Msgs.MsgArray=H_task_tcp->Mem.Malloc(sizeof(void*)*2*NumMsg); r->thread=NULL; r->Msgs.MsgArrayNum=NumMsg; r->Msgs.I_Offset=0; r->Msgs.O_Offset=0; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return r; } //中断向邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_irq_Send(H_task_Msg_irq* msg,void* v0,void* v1){ void** v; int EmptyNum; //判断是否有足够空间 if(msg->Msgs.I_Offset < msg->Msgs.O_Offset){ EmptyNum = msg->Msgs.O_Offset - msg->Msgs.I_Offset; }else{ EmptyNum = msg->Msgs.MsgArrayNum - msg->Msgs.I_Offset + msg->Msgs.O_Offset; } //至少要空出一个容量 if(EmptyNum<2){ return -1; } v=&msg->Msgs.MsgArray[2*msg->Msgs.I_Offset]; v[0]=v0; v[1]=v1; if(msg->Msgs.I_Offset==(msg->Msgs.MsgArrayNum-1)){ msg->Msgs.I_Offset=0; }else{ msg->Msgs.I_Offset++; } if(msg->thread!=NULL){ if(msg->thread->Status==-2){ //邮箱阻塞了线程 if(H_task_tcp->SchedulerSuspend){ H_task_tcp->SchedulerForIrq=1; }else{ SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } } return 0; } //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_irq_tryRead(H_task_Msg_irq* msg,void** dv){ void** v; if(msg->Msgs.I_Offset!=msg->Msgs.O_Offset){ v=&msg->Msgs.MsgArray[2*msg->Msgs.O_Offset]; dv[0]=v[0]; dv[1]=v[1]; if(msg->Msgs.O_Offset==(msg->Msgs.MsgArrayNum-1)){ msg->Msgs.O_Offset=0; }else{ msg->Msgs.O_Offset++; } return 0; } return -1; } //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_irq_Read(H_task_Msg_irq* msg,void** dv){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; if(0!=H_task_Msg_irq_tryRead(msg,dv)){ //阻塞 _this->BlockObject=msg; _this->Status=-2;//被邮箱阻塞 msg->thread=_this; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 while(0!=H_task_Msg_irq_tryRead(msg,dv)){ //等待读取到数据 } } } //删除邮箱 void H_task_Msg_irq_Delete(H_task_Msg_irq* msg){ H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 H_task_tcp->Mem.Free(msg->Msgs.MsgArray); H_task_tcp->Mem.Free(msg); H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } //新建邮箱 H_task_Msg_irq* new_H_task_Msg(int NumMsg){ return new_H_task_Msg_irq(NumMsg); } static int msgisNoEmpty(H_task_Msg_irq* msg){ int EmptyNum; //判断是否有足够空间 if(msg->Msgs.I_Offset < msg->Msgs.O_Offset){ EmptyNum = msg->Msgs.O_Offset - msg->Msgs.I_Offset; }else{ EmptyNum = msg->Msgs.MsgArrayNum - msg->Msgs.I_Offset + msg->Msgs.O_Offset; } //至少要空出一个容量 if(EmptyNum<2){ return -1; } return 0; } //邮箱发送消息 void H_task_Msg_Send(H_task_Msg_irq* msg,void* v0,void* v1){ void** v; int EmptyNum; H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 //判断是否有足够空间 if(msg->Msgs.I_Offset < msg->Msgs.O_Offset){ EmptyNum = msg->Msgs.O_Offset - msg->Msgs.I_Offset; }else{ EmptyNum = msg->Msgs.MsgArrayNum - msg->Msgs.I_Offset + msg->Msgs.O_Offset; } //至少要空出一个容量 if(EmptyNum<2){ //阻塞 _this->BlockObject=msg; _this->Status=-4;//被邮箱阻塞 msg->thread=_this; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 while(msg->thread!=_this){ } H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 } v=&msg->Msgs.MsgArray[2*msg->Msgs.I_Offset]; v[0]=v0; v[1]=v1; if(msg->Msgs.I_Offset==(msg->Msgs.MsgArrayNum-1)){ msg->Msgs.I_Offset=0; }else{ msg->Msgs.I_Offset++; } if(msg->thread!=NULL){ if(msg->thread->Priority<_this->Priority){ //读取线程优先级更高 H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 return; } } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return; } //邮箱发送消息 返回 0:成功 其他:失败 int H_task_Msg_trySend(H_task_Msg_irq* msg,void* v0,void* v1){ void** v; int EmptyNum; H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 //判断是否有足够空间 if(msg->Msgs.I_Offset < msg->Msgs.O_Offset){ EmptyNum = msg->Msgs.O_Offset - msg->Msgs.I_Offset; }else{ EmptyNum = msg->Msgs.MsgArrayNum - msg->Msgs.I_Offset + msg->Msgs.O_Offset; } //至少要空出一个容量 if(EmptyNum<2){ H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return -1; } v=&msg->Msgs.MsgArray[2*msg->Msgs.I_Offset]; v[0]=v0; v[1]=v1; if(msg->Msgs.I_Offset==(msg->Msgs.MsgArrayNum-1)){ msg->Msgs.I_Offset=0; }else{ msg->Msgs.I_Offset++; } if(msg->thread!=NULL){ if(msg->thread->Priority<_this->Priority){ //读取线程优先级更高 H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 return 0; } } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return 0; } //尝试向邮箱发送消息 超时失败 返回 0:成功 其他:失败 int H_task_Msg_trySendtimeOut(H_task_Msg_irq* msg,void* v0,void* v1,int TimeOut){ int r; r=H_task_Msg_trySend(msg,v0,v1); if(r==0){ return 0; } while(TimeOut){ TimeOut--; TaskSleep(1); r=H_task_Msg_trySend(msg,v0,v1); if(r==0){ return 0; } } return 0; } //尝试读取消息 不阻塞 返回 0:成功 其他:邮箱空 int H_task_Msg_tryRead(H_task_Msg_irq* msg,void** dv){ return H_task_Msg_irq_tryRead(msg,dv); } //读取消息 如果消息为空则阻塞 直到获取到消息 void H_task_Msg_Read(H_task_Msg_irq* msg,void** dv){ H_task_Msg_irq_Read(msg,dv); } //尝试读取消息 阻塞 超时返回失败 返回 0:成功 其他:失败 int H_task_Msg_tryReadtimeOut(H_task_Msg_irq* msg,void** dv,int TimeOut){ int r; r=H_task_Msg_tryRead(msg,dv); if(r==0){ return 0; } while(TimeOut){ TimeOut--; TaskSleep(1); r=H_task_Msg_tryRead(msg,dv); if(r==0){ return 0; } } return r; } //删除邮箱 void H_task_Msg_Delete(H_task_Msg_irq* msg){ H_task_Msg_irq_Delete(msg); } //新建一个锁 H_task_Lock_Def* new_H_task_Lock(){ H_task_Lock_Def* r; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 r=H_task_tcp->Mem.Malloc(sizeof(H_task_Lock_Def)); r->isHighPriorityLockReq=0; r->thread=NULL; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return r; } //锁定Lock 如果Lock已被其他线程占用 则阻塞当前线程并启动任务调度 void H_task_Lock(H_task_Lock_Def* lock){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 if((lock->thread!=NULL)&&(lock->thread!=_this)){ //已有其他线程使用这个锁 if(lock->thread->Priority>_this->Priority){ //占用锁的优先级低 lock->isHighPriorityLockReq=-1;//标记请求 已便于低优先级的线程释放锁时能触发任务调度 } _this->BlockObject=lock; _this->Status=-3;//被锁阻塞 H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 while(lock->thread!=_this){ } return; }else{ lock->thread=_this;//锁定 } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } //尝试锁定一个锁 返回0:成功 int H_task_tryLock(H_task_Lock_Def* lock){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 if((lock->thread!=NULL)&&(lock->thread!=_this)){ //已有其他线程使用这个锁 H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return -1; }else{ lock->thread=_this;//锁定 } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } return 0; } //尝试锁定一个锁 超时检测 int H_task_tryLockTimeOut(H_task_Lock_Def* lock,int TimeOut){ int r; r=H_task_tryLock(lock); if(r==0){ return 0; } while(TimeOut){ TimeOut--; TaskSleep(1); r=H_task_tryLock(lock); if(r==0){ return 0; } } return r; } //释放锁 void H_task_Unlock(H_task_Lock_Def* lock){ H_task_info_def* _this; _this=(*H_task_tcp->RunThread)[1]; H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 if(lock->thread==_this){ lock->thread=NULL; if(lock->isHighPriorityLockReq){ //存在更高优先级的线程尝试占用这个锁 lock->isHighPriorityLockReq=0; H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 return; } } H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } //删除锁 void H_task_Lock_Delete(H_task_Lock_Def* lock){ H_task_tcp->SchedulerSuspend=-1;//挂起任务调度 H_task_tcp->Mem.Free(lock); H_task_tcp->SchedulerSuspend=0;//允许任务调度 if(H_task_tcp->SchedulerForIrq){ H_task_tcp->SchedulerForIrq=0; SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;//触发调度 } } //获取cpu占用率 int H_task_getCPU_Utilization(){ return H_task_tcp->CPU_Utilization; } +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
|
|
文件名:H_task_port.c
#include "H_task_test.h" #include "Peripheral.h" extern H_task_test* H_task_tcp; void** H_Task; void* PendSV_Call; extern void* StartFirstThread; //获取上次调用于本次调用之间的时间间隔 Hint32 port_GetDt(){ static volatile Hint32 lastT=0; Hint32 nowT; Hint32 r; nowT=(Hint32)TIM5->CNT; r=nowT-lastT; lastT=nowT; return r; } //刷新H_Task 更新H_Task任务堆栈 void* H_Task_SP_Refresh(){ H_task_info_def* p; Hint32 dt; int Status; int EmptyNum; void* BlockObject; if(H_task_tcp->SchedulerSuspend){ return (void*)0;//调度已挂起 } p=H_Task[1]; if(p->Stack>H_Task[0]){ while(1){ //堆栈溢出 } } dt=port_GetDt(); H_task_tcp->runTime+=dt; if(p->next==NULL){ //下一个为NULL 为空闲线程 H_task_tcp->idleTime+=dt; } if(H_task_tcp->runTime>400000){ H_task_tcp->CPU_Utilization=1000-(1000*H_task_tcp->idleTime/H_task_tcp->runTime); H_task_tcp->runTime=0; H_task_tcp->idleTime=0; } p=((H_task_test*)p->parent)->root; while(p!=NULL){ Status=p->Status; if(Status==0){ //就绪态 if(H_Task==p->StackPointer){ return (void*)0; }else{ H_Task=p->StackPointer; return (void*)0xFFFFFFFF; } }else if(Status==-2){ BlockObject=p->BlockObject; //是否就绪由消息管理 if(((H_task_Msg_irq*)BlockObject)->Msgs.I_Offset!=((H_task_Msg_irq*)BlockObject)->Msgs.O_Offset){ //不为空 就绪 p->Status=0; ((H_task_Msg_irq*)BlockObject)->thread=NULL; if(H_Task==p->StackPointer){ return (void*)0; }else{ H_Task=p->StackPointer; return (void*)0xFFFFFFFF; } } }else if(Status==-3){ BlockObject=p->BlockObject; //是否就绪由锁管理 if(((H_task_Lock_Def*)BlockObject)->thread==NULL){ //锁已释放 就绪 p->Status=0; ((H_task_Lock_Def*)BlockObject)->thread=p; if(H_Task==p->StackPointer){ return (void*)0; }else{ H_Task=p->StackPointer; return (void*)0xFFFFFFFF; } } }else if(Status==-4){ //是否就绪由消息管理 BlockObject=p->BlockObject; //判断是否有足够空间 if(((H_task_Msg_irq*)BlockObject)->Msgs.I_Offset < ((H_task_Msg_irq*)BlockObject)->Msgs.O_Offset){ EmptyNum = ((H_task_Msg_irq*)BlockObject)->Msgs.O_Offset - ((H_task_Msg_irq*)BlockObject)->Msgs.I_Offset; }else{ EmptyNum = ((H_task_Msg_irq*)BlockObject)->Msgs.MsgArrayNum - ((H_task_Msg_irq*)BlockObject)->Msgs.I_Offset + ((H_task_Msg_irq*)BlockObject)->Msgs.O_Offset; } //至少要空出一个容量 if(EmptyNum<2){ }else{ p->Status=0; ((H_task_Msg_irq*)BlockObject)->thread=NULL; if(H_Task==p->StackPointer){ return (void*)0; }else{ H_Task=p->StackPointer; return (void*)0xFFFFFFFF; } } } p=p->next; } for(;;){ //由于有空闲任务的存在 不可能存在没有就绪任务的情况 } } void port_startFirstThread(){ PendSV_Call=&StartFirstThread; H_Task=H_task_tcp->root->StackPointer; H_task_tcp->RunThread=&H_Task; //__ISB(); //__DSB(); extern void portSysTick(void* v); SysTick_SetIrqCallback(portSysTick,NULL); SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk; for(;;){} } void portSysTick(void* v){ H_task_info_def* p; H_task_info_def* p_last; int isScheduler; if(H_task_tcp->SchedulerSuspend){ //调度已挂起 return; } isScheduler=0; p=H_task_tcp->DelayRoot; while(p!=NULL){ if(p->Status>0){ p->Status--; if(p->Status==0){ isScheduler=-1; } } p=p->DelayNext; } if(isScheduler){ p=H_task_tcp->DelayRoot; while(p!=NULL){ if(p->Status){ break; }else{ p=p->DelayNext; } } H_task_tcp->DelayRoot=p; if(H_task_tcp->DelayRoot!=NULL){ p_last=H_task_tcp->DelayRoot; p=p_last->DelayNext; while(p!=NULL) { if(p->Status) { p_last=p; p=p->DelayNext; }else{ p_last->DelayNext=p->DelayNext; p=p->DelayNext; } } } SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk; } } H_task_info_def* new_Task(void (*Code)(void*),void* v,int StackSize,int Priority){ H_task_info_def* t; void** r; unsigned int* Stack; int StackSize_div4; StackSize+=3; StackSize-=StackSize%4; StackSize_div4=StackSize/4; r=H_task_tcp->Mem.Malloc(sizeof(void*)*2); t=H_task_tcp->Mem.Malloc(sizeof(H_task_info_def)); t->Priority=Priority; t->StackSize=StackSize; t->Status=0; t->BlockObject=NULL; t->Stack=H_task_tcp->Mem.Malloc(StackSize); r[1]=t; Stack=t->Stack; Stack[StackSize_div4-1]=0x01000000;//xPSR Stack[StackSize_div4-2]=((Huint32)Code);//&0xFFFFFFFE;//PC Stack[StackSize_div4-3]=(Huint32)TaskExit;//LR // Stack[StackSize_div4-4]=0x00000000;//R12 // Stack[StackSize_div4-5]=0x00000000;//R3 // Stack[StackSize_div4-6]=0x00000000;//R2 // Stack[StackSize_div4-7]=0x00000000;//R1 Stack[StackSize_div4-8]=(Huint32)v;//R0 Stack[StackSize_div4-9]=0xFFFFFFFD;//LR 退回到线程模式 使用PSP Stack[StackSize_div4-10]=0;//R11 Stack[StackSize_div4-11]=0;//R10 Stack[StackSize_div4-12]=0;//R9 Stack[StackSize_div4-13]=0;//R8 Stack[StackSize_div4-14]=0;//R7 Stack[StackSize_div4-15]=0;//R6 Stack[StackSize_div4-16]=0;//R5 Stack[StackSize_div4-17]=0;//R4 r[0]=&Stack[StackSize_div4-17]; t->StackPointer=r; return t; } +++++++++++++++++++++++++++++++++++++++++++++++++++++ 注意这是.s的汇编文件 文件名:H_task_port.s AREA |.text|,CODE,READONLY THUMB PRESERVE8 EXPORT PendSV_Handler EXPORT ThreadSwitch EXPORT StartFirstThread EXTERN H_Task EXTERN PendSV_Call EXTERN H_Task_SP_Refresh ThreadSwitch PROC LDR R0,=H_Task LDR R12,[R0] ;读取栈指针地址 PUSH {R12,LR} BL H_Task_SP_Refresh ;切换H_Task POP {R12,LR} CMP R0,#0 ;结果是否为0 BXEQ LR ;如果为0 返回 不做任务切换 ;开始任务切换 保存和释放堆栈 MRS R0,PSP ;读线程栈指针到R0 ;ISB ;判断线程是否使用了FPU 如果使用了 保存浮点寄存器 TST LR,#0x10 VSTMDBEQ R0!, {S16-S31} STMDB R0!, {R4-R11,LR} ;保存现场 STR R0,[R12] ;保存栈指针 LDR R0,=H_Task LDR R0,[R0] ;读取栈指针地址 LDR R0,[R0] ;读取栈指针 LDMIA R0!,{R4-R11,LR} ;恢复现场 ;判断线程是否使用了FPU 如果使用了 恢复浮点寄存器 TST LR,#0x10 VLDMIAEQ R0!,{S16-S31} MSR PSP,R0 ;恢复堆栈指针 ;ISB BX LR ENDP StartFirstThread PROC LDR R0,=PendSV_Call LDR R1,=ThreadSwitch STR R1,[R0] ;LDR R0,=0xE000ED08 ;加载中断向量表寄存器地址(SCB->VTOR的地址) ;LDR R0,[R0] ;读取栈初始指针的地址 ;LDR R0,[R0] ;读取栈初始指针 ;MSR MSP,R0 ;设置主堆栈指针为初始指针 ;LDR R1,=0xE000EF38 ;加载FPU上下文寄存器地址(FPU->FPCAR的地址) MOV R0,#0 ;STR R0,[R1] ;FPU->FPCAR=0 MSR CONTROL,R0 ;设置CONTROL 特权级,使用MSP (主要是为了清除FPCA) ;ISB LDR R1,=H_Task ;加载H_Task地址 LDR R1,[R1] ;读取栈指针地址 LDR R0,[R1] ;读取栈指针 LDMIA R0!,{R4-R11,LR} ;恢复现场 MSR PSP,R0 ;恢复堆栈指针 ;ISB BX LR ENDP PendSV_Handler PROC LDR R0,=PendSV_Call LDR R0,[R0] BX R0 ENDP END +++++++++++++++++++++++++++++++++++++++++++++++++++++ 文件名:H_Type.c #ifndef APP_INCLUDE_H_TYPE_H_ #define APP_INCLUDE_H_TYPE_H_ typedef unsigned char Hbyte; typedef int Hint; typedef unsigned int Huint; typedef short Hint16; typedef unsigned short Huint16; typedef int Hint32; typedef unsigned int Huint32; typedef long long Hint64; typedef unsigned long long Huint64; typedef unsigned char* Hbyte_ptr; typedef int* Hint_ptr; typedef unsigned int* Huint_ptr; typedef short* Hint16_ptr; typedef unsigned short* Huint16_ptr; typedef int* Hint32_ptr; typedef unsigned int* Huint32_ptr; typedef long long* Hint64_ptr; typedef unsigned long long* Huint64_ptr; //与(void*) 占用空间相同的整型 typedef int Hsize; //与(void*) 占用空间相同的无符号整型 typedef unsigned int Husize; #ifndef NULL #define NULL ((void*)0) #endif // !NULL #endif /* APP_INCLUDE_H_TYPE_H_ */ +++++++++++++++++++++++++++++++++++++++++++++++++++++ 文件名:H_Malloc.h #ifndef __H_Malloc_H_ #define __H_Malloc_H_ #include "H_Type.h" //长度使用的类型(与cpu位数相同可获得更高的效率) #define H_Malloc_Size_Def Huint32 //对齐字节数 必须为2^n 并且不小于H_Malloc_Size_Def类型的储存字节数的两倍 #define H_Malloc_Align 8 typedef struct { int Result;//结果 0:未发现问题 -1:头块不应有上一个块 -2:出现问题(长度数据被修改 尾部不对齐) -3:存在连续未分配的块 -4:上下块信息不匹配 -5:与下一块信息不匹配 -6:与上一块信息不匹配 void* ErrPtr;//错误地址 H_Malloc_Size_Def UseSize;//使用内存量 H_Malloc_Size_Def FreeSize;//可申请重量 H_Malloc_Size_Def OccupySize;//实际占用量 H_Malloc_Size_Def NoOccupySize;//空闲量 }H_Malloc_Info_Def; /** * @brief 初始化内存池 * @param MemAddr 作为内存池数组的指针 * @param MemSize 数组大小(字节) * @return 无 */ void H_Malloc_Init(void* MemAddr,H_Malloc_Size_Def MemSize); /** * @brief 向指定内存池申请内存 * @param MemAddr 内存池地址 * @param Size 要申请的内存大小 * @return 申请到的内存指针 如果为NULL则为失败 */ void* H_Malloc(void* MemAddr,H_Malloc_Size_Def Size); /** * @brief 将内存释放回内存池 * @param MemAddr 内存池地址 * @param Ptr 要释放的内存指针 * @return 无 */ void H_Free(void* MemAddr,void* Ptr); /** * @brief 获取内存池状态 * @param MemAddr 内存池地址 * @param info 用于存放信息的指针 * @return 无 */ void H_Malloc_GetInfo(void* MemAddr,H_Malloc_Info_Def* info); #endif +++++++++++++++++++++++++++++++++++++++++++++++++++++ 文件名:H_Malloc.c #include "H_Malloc.h" typedef struct { H_Malloc_Size_Def* StartSize_Ptr;//用于存放第一个内存池首地址 H_Malloc_Size_Def* EntrySize_Ptr;//下次malloc时开始搜索的地址 void* EndAddr;//边界地址 为(内存池首指针+内存池大小) }H_Malloc_Header_Def; /** * @brief 初始化内存池 * @param MemAddr 作为内存池数组的指针 * @param MemSize 数组大小(字节) * @return 无 */ void H_Malloc_Init(void* MemAddr,H_Malloc_Size_Def MemSize){ H_Malloc_Header_Def* Header; H_Malloc_Size_Def offset; Hbyte_ptr ptr; Header=(H_Malloc_Header_Def*)MemAddr; offset=sizeof(H_Malloc_Header_Def); while(((offset+H_Malloc_Align)%H_Malloc_Align)!=0){ offset++; } ptr=(Hbyte_ptr)MemAddr; Header->StartSize_Ptr=(H_Malloc_Size_Def*)&ptr[offset]; Header->EntrySize_Ptr=Header->StartSize_Ptr; Header->StartSize_Ptr[0]=MemSize-offset-H_Malloc_Align; Header->StartSize_Ptr[1]=0; Header->EndAddr=(void*)&ptr[MemSize]; } /** * @brief 向指定内存池申请内存 * @param MemAddr 内存池地址 * @param Size 要申请的内存大小 * @return 申请到的内存指针 如果为NULL则为失败 */ void* H_Malloc(void* MemAddr,H_Malloc_Size_Def Size){ H_Malloc_Header_Def* Header; H_Malloc_Size_Def tSize; H_Malloc_Size_Def* EntrySize_Ptr; H_Malloc_Size_Def* After_Ptr; H_Malloc_Size_Def* New_Ptr; H_Malloc_Size_Def Msk; Hbyte_ptr ptr; Hbyte_ptr r; if(Size==0) { return NULL; } Msk=((H_Malloc_Size_Def)0x1)<<(sizeof(H_Malloc_Size_Def)*8-1); Header=(H_Malloc_Header_Def*)MemAddr; EntrySize_Ptr=Header->EntrySize_Ptr; //将大小整和成H_Malloc_Align的倍数 Size+=H_Malloc_Align-1; Size-=Size%H_Malloc_Align; do{ tSize=EntrySize_Ptr[0]; if(Msk&tSize){ //已占用 }else{ //未占用 if(tSize>Size){ //大小不等 if(tSize-Size<(H_Malloc_Align*2)){ //不可分割 *EntrySize_Ptr=tSize|Msk; ptr=(Hbyte_ptr)EntrySize_Ptr; r=&ptr[H_Malloc_Align]; EntrySize_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if(Header->EndAddr==EntrySize_Ptr){ EntrySize_Ptr=Header->StartSize_Ptr; } Header->EntrySize_Ptr=EntrySize_Ptr; return (void*)r; }else{ //可分割 ptr=(Hbyte_ptr)EntrySize_Ptr; After_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; New_Ptr=(H_Malloc_Size_Def*)&ptr[Size+H_Malloc_Align]; New_Ptr[0]=tSize-Size-H_Malloc_Align; New_Ptr[1]=Size; EntrySize_Ptr[0]=Size|Msk; if((void*)After_Ptr!=Header->EndAddr){ //有下一个 After_Ptr[1]=New_Ptr[0]; } ptr=(Hbyte_ptr)EntrySize_Ptr; Header->EntrySize_Ptr=New_Ptr; return (void*)&ptr[H_Malloc_Align]; } }else if(tSize==Size){ //大小相同 *EntrySize_Ptr=tSize|Msk; ptr=(Hbyte_ptr)EntrySize_Ptr; r=&ptr[H_Malloc_Align]; EntrySize_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if(Header->EndAddr==EntrySize_Ptr){ EntrySize_Ptr=Header->StartSize_Ptr; } Header->EntrySize_Ptr=EntrySize_Ptr; return (void*)r; } } ptr=(Hbyte_ptr)EntrySize_Ptr; EntrySize_Ptr=(H_Malloc_Size_Def*)&ptr[(tSize&(~Msk))+H_Malloc_Align]; if(Header->EndAddr==EntrySize_Ptr){ EntrySize_Ptr=Header->StartSize_Ptr; } }while(EntrySize_Ptr!=Header->EntrySize_Ptr); return NULL; } /** * @brief 将内存释放回内存池 * @param MemAddr 内存池地址 * @param Ptr 要释放的内存指针 * @return 无 */ void H_Free(void* MemAddr,void* Ptr){ H_Malloc_Header_Def* Header; H_Malloc_Size_Def tSize; H_Malloc_Size_Def* Last_Ptr; H_Malloc_Size_Def* EntrySize_Ptr; H_Malloc_Size_Def* After_Ptr; H_Malloc_Size_Def* After_After_Ptr; H_Malloc_Size_Def Msk; Hbyte_ptr ptr; if(Ptr==NULL) { return; } Msk=((H_Malloc_Size_Def)0x1)<<(sizeof(H_Malloc_Size_Def)*8-1); Header=(H_Malloc_Header_Def*)MemAddr; EntrySize_Ptr=(H_Malloc_Size_Def*)((Hbyte_ptr)Ptr-(Hbyte_ptr)H_Malloc_Align); tSize=EntrySize_Ptr[0]&(~Msk); if(EntrySize_Ptr[1]==0){ //没有上一块内存 ptr=(Hbyte_ptr)EntrySize_Ptr; After_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if((void*)After_Ptr!=Header->EndAddr){ //有下一个 ptr=(Hbyte_ptr)After_Ptr; After_After_Ptr=(H_Malloc_Size_Def*)&ptr[(After_Ptr[0]&(~Msk))+H_Malloc_Align]; if(After_Ptr[0]&Msk){ //下一个被占用 EntrySize_Ptr[0]&=(~Msk);//取消标记 }else{ //都无占用 if(After_Ptr==Header->EntrySize_Ptr){ //下次Malloc是即将要合并的位置 Header->EntrySize_Ptr=EntrySize_Ptr; } EntrySize_Ptr[0]=tSize+After_Ptr[0]+H_Malloc_Align;//取消标记 并且更新大小 if((void*)After_After_Ptr!=Header->EndAddr){ After_After_Ptr[1]=EntrySize_Ptr[0]; } } }else{ //无下一个 EntrySize_Ptr[0]&=(~Msk);//取消标记 } }else{ //有上一块内存 Last_Ptr=(H_Malloc_Size_Def*)((Hbyte_ptr)EntrySize_Ptr-(Hbyte_ptr)(EntrySize_Ptr[1]+(H_Malloc_Size_Def)H_Malloc_Align)); ptr=(Hbyte_ptr)EntrySize_Ptr; After_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if((void*)After_Ptr!=Header->EndAddr){ //有下一个 ptr=(Hbyte_ptr)After_Ptr; After_After_Ptr=(H_Malloc_Size_Def*)&ptr[(After_Ptr[0]&(~Msk))+H_Malloc_Align]; if((Last_Ptr[0]&Msk)&&(After_Ptr[0]&Msk)){ //都为已占用 EntrySize_Ptr[0]&=(~Msk);//取消标记 }else if(Last_Ptr[0]&Msk){ //仅上一个占用 合并下一个 if(After_Ptr==Header->EntrySize_Ptr){ //下次Malloc是即将要合并的位置 Header->EntrySize_Ptr=EntrySize_Ptr; } EntrySize_Ptr[0]=tSize+After_Ptr[0]+H_Malloc_Align;//取消标记 并且更新大小 if((void*)After_After_Ptr!=Header->EndAddr){ After_After_Ptr[1]=EntrySize_Ptr[0]; } }else if(After_Ptr[0]&Msk){ //仅下一个占用 合并到上一个 if(EntrySize_Ptr==Header->EntrySize_Ptr){ //下次Malloc是即将要合并的位置 Header->EntrySize_Ptr=Last_Ptr; } Last_Ptr[0]=Last_Ptr[0]+tSize+H_Malloc_Align;//并且更新大小 After_Ptr[1]=Last_Ptr[0]; }else{ //都无占用 全合并 if((EntrySize_Ptr==Header->EntrySize_Ptr)||(After_Ptr==Header->EntrySize_Ptr)){ //下次Malloc是即将要合并的位置 Header->EntrySize_Ptr=Last_Ptr; } Last_Ptr[0]=Last_Ptr[0]+tSize+After_Ptr[0]+H_Malloc_Align*2;//并且更新大小 if((void*)After_After_Ptr!=Header->EndAddr){ After_After_Ptr[1]=Last_Ptr[0]; } } }else{ //无下一个 if(Last_Ptr[0]&Msk){ //上一个被占用 EntrySize_Ptr[0]&=(~Msk);//取消标记 }else{ //都无占用 if(EntrySize_Ptr==Header->EntrySize_Ptr){ //下次Malloc是即将要合并的位置 Header->EntrySize_Ptr=Last_Ptr; } Last_Ptr[0]=Last_Ptr[0]+tSize+H_Malloc_Align;//并且更新大小 } } } } /** * @brief 获取内存池状态 * @param MemAddr 内存池地址 * @param info 用于存放信息的指针 * @return 无 */ void H_Malloc_GetInfo(void* MemAddr,H_Malloc_Info_Def* info){ H_Malloc_Header_Def* Header; H_Malloc_Size_Def* EntrySize_Ptr; H_Malloc_Size_Def* After_Ptr; //H_Malloc_Size_Def* After_After_Ptr; H_Malloc_Size_Def* Last_Ptr; H_Malloc_Size_Def Msk; H_Malloc_Size_Def tSize; Hbyte_ptr ptr; Msk=((H_Malloc_Size_Def)0x1)<<(sizeof(H_Malloc_Size_Def)*8-1); Header=(H_Malloc_Header_Def*)MemAddr; info->UseSize=0; info->FreeSize=0; info->OccupySize=0; info->NoOccupySize=0; Last_Ptr=Header->StartSize_Ptr; tSize=Last_Ptr[0]&(~Msk); if(Last_Ptr[0]&Msk){ info->UseSize+=tSize; info->OccupySize+=tSize+H_Malloc_Align; }else{ info->FreeSize+=tSize; info->NoOccupySize+=tSize+H_Malloc_Align; } if(Last_Ptr[1]!=0){ info->Result=-1; info->ErrPtr=Last_Ptr; return; } ptr=(Hbyte_ptr)Last_Ptr; EntrySize_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; while(EntrySize_Ptr!=Header->EndAddr) { if((void*)EntrySize_Ptr > Header->EndAddr){ info->Result=-2; info->ErrPtr=EntrySize_Ptr; return; } tSize=EntrySize_Ptr[0]&(~Msk); if(EntrySize_Ptr[1]==0){ //没有上一块内存 ptr=(Hbyte_ptr)EntrySize_Ptr; After_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if((void*)After_Ptr!=Header->EndAddr){ //有下一个 ptr=(Hbyte_ptr)After_Ptr; //After_After_Ptr=(H_Malloc_Size_Def*)&ptr[(After_Ptr[0]&(~Msk))+H_Malloc_Align]; if(After_Ptr[1]!=tSize){ info->Result=-5; info->ErrPtr=EntrySize_Ptr; return; } }else{ //无下一个 } }else{ //有上一块内存 Last_Ptr=(H_Malloc_Size_Def*)((Hbyte_ptr)EntrySize_Ptr-(Hbyte_ptr)(EntrySize_Ptr[1]+(H_Malloc_Size_Def)H_Malloc_Align)); ptr=(Hbyte_ptr)EntrySize_Ptr; After_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; if((void*)After_Ptr!=Header->EndAddr){ //有下一个 ptr=(Hbyte_ptr)After_Ptr; //After_After_Ptr=(H_Malloc_Size_Def*)&ptr[(After_Ptr[0]&(~Msk))+H_Malloc_Align]; if(After_Ptr[1]!=tSize){ info->Result=-5; info->ErrPtr=EntrySize_Ptr; return; } if((Last_Ptr[0]&(~Msk))!=EntrySize_Ptr[1]){ info->Result=-6; info->ErrPtr=EntrySize_Ptr; return; } }else{ //无下一个 if((Last_Ptr[0]&(~Msk))!=EntrySize_Ptr[1]){ info->Result=-6; info->ErrPtr=EntrySize_Ptr; return; } } } if(EntrySize_Ptr[0]&Msk){ info->UseSize+=tSize; info->OccupySize+=tSize+H_Malloc_Align; }else{ if((Last_Ptr[0]&Msk)==0){ //都为未占用 info->Result=-3; info->ErrPtr=Last_Ptr; return; } info->FreeSize+=tSize; info->NoOccupySize+=tSize+H_Malloc_Align; } if((Last_Ptr[0]&(~Msk))!=EntrySize_Ptr[1]){ info->Result=-4; info->ErrPtr=Last_Ptr; return; } Last_Ptr=EntrySize_Ptr; ptr=(Hbyte_ptr)Last_Ptr; EntrySize_Ptr=(H_Malloc_Size_Def*)&ptr[tSize+H_Malloc_Align]; } info->Result=0; } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1780 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1081 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1679 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 21:03 , Processed in 1.010052 second(s), Total 78, Slave 62 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号