2021年12月7日23点52分:
目前这个调度器已成型
https://blog.csdn.net/qq_42907191/article/details/121772005.
学习单片机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);
或者直接让线程函数返回
//让指针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;
}