|
|
前段时间做了一个资源占用比较大的东西、使用的是SWI中断触发NVCI中断自动出栈入栈的操作、相当于替代了RTOS的任务切换和优先级抢占的功能
但是那个是参考nordic的调度器实现的、每次调度是丢一个函数指事和一个事件指针到队列里、然后触发SWI中断、然后在SWI中断里把事件和函数指针取出来处理、
但是实际应用下来会发现很多时候事件指针是空的、只是丢了一个函数回调进去、但是这样却要占用一个比较大的事件队列和事件池
所以想了想能不能结合OSAL的优点改造一下、因为OSAL的事件触发其实就是不需要传递事件指针的。于事有了这样的设计。附件是让AI帮我生成的代码。但是还没空移植到真实的MCU环境上去运行。
#define MAX_SWI_TCB 3
#define MAX_OSAL_SUB_TASK 7
typedef int (*SubTask_Handler)(void *me, uint8_t mIndex);
typedef struct{
uint32_t* pQuBuf; //创建SWI_TCB_t 任务控制块的时候传入一个数组作为一个消息邮箱环形缓冲、必须使用无锁FIFO、最好是使用原子操作入队出队
uint32_t mQuSize; //必是2^n
//指定任务使用的SWI 中断号、主循环main也可以跑一个SWI_TCB_t任务控制块、
//但是mSwiIrqNum设置成0xFF这样在触发事件的时候就不要触发SWI中断
uint8_t mSwiIrqNum;
uint8_t mPreemptPrio; //指定SWI中断的优先级分配
uint8_t mSubPrio;
}SWI_TCB_PARAM_t;
typedef struct{
uint16_t mMsgType; //所有消息头都是一个16位的消息类型、bit15~bit13保留和mEvtFlag一样的定义也用于区分事件发给谁
uint16_t mCount; //引用计数、用于广播消息的时候、内存块回收的时候通过统计这个引用计数检测消息是否被所有任务都处理完成
}MsgHead_t;
typedef struct {
uint32_t* pQuBuf; //消息队列缓冲区 只是用来存放消息指针
uint32_t mQuIn;
uint32_t mQuOut;
uint32_t mQuSize; //消息队列大小
//移植到RTOS下时这个事件标志每个bit对应于需要被唤醒的子任务
//例如bit0对应于mEvtFlag[0] 事件不为空、需要调用mSubTaskHook[0]处理
//当移植到RTOS环境下时不再使用SWI中断、而是每个SWI_TCB_t任务控制块绑定一个RTOS任务
RTOS_EVT_t mRtosEvtFlag;
//
// 裸机专用 1~7个任务独立事件标志、设置和清除事件标志的时候要用原子操作
// bit31 用于区分事件或消息是否广播模式、如果置1表示广播、则事件标志写入到mAdvEvtFlag、否则写入到mEvtFlag
// bit30 用于通知子任务有消息需要处理
// bit29~bit27 用于区分事件传递给那个子任务
// bit26 用于通知子任务有系统定时TICK需要处理
// bit25~0 APP子任务可以自由定义事件
uint32_t mEvtFlag[MAX_OSAL_SUB_TASK];
// 广播的事件
uint32_t mAdvEvtFlag[MAX_OSAL_SUB_TASK];
// 1~7个协作任务回调函数数组
SubTask_Handler mSubTaskHook[MAX_OSAL_SUB_TASK];
// 1~7个协议作任务各自的事件订阅列表指针
uint32_t* pEvtSubscrTab[MAX_OSAL_SUB_TASK];
// 1~7个协议作任务每个定阅事件列表大小
uint8_t mSubscrCountTab[MAX_OSAL_SUB_TASK];
// SWI中断优先级配置
uint8_t mSwiIrqNum;
uint8_t mPreemptPrio;
uint8_t mSubPrio;
uint8_t mTaskAllocate;
}SWI_TCB_t;
typedef struct{
SWI_TCB_t * pOsalTcb;
SubTask_Handler *pSubTaskHandle;
uint32_t* pEvtSubscrTab;
uint8_t mSubscrCount;
}SUB_TASK_PARAM_t;
// 协作任务私有控制块 不变
typedef struct {
SWI_TCB_t * pSwiTcb;
uint8_t mTaskSlot; // 0~6 任务下标
uint32_t mTaskStatus;
uint16_t mDelayTick;
} SubTask_t;
//将所有SWI_TCB_t控制块的指针都填到这个表格里、广播事件和消息的时候从这个表里去找
//有公共的消息队列、所有任务获取广播数据从里获取
//用户可以选择用或不用定阅分发的机制、通过宏控制
typedef struct{
uint32_t* pQuBuf;
uint32_t mQuIn;
uint32_t mQuOut;
uint32_t mQuSize;
SWI_TCB_t * mObservers[MAX_SWI_TCB];
uint8_t mTcbAllocate;
}SWI_TCB_SUBJECT_t;
int swi_osaL_run(SWI_TCB_t * pOsalTcb) //SWI中断里检查每个mEvtFlag是否为不为空、并且调用mSubTaskHook处理事件或消息
{
for(uint8_t i=0; i<; i++)
{
if(pOsalTcb->mEvtFlag[i] != 0x00000000)
{
if(pOsalTcb->mSubTaskHook[i] != NULL)
{
pOsalTcb->mSubTaskHook(pOsalTcb, i);
}
}
}
}
bool swi_osal_init(SWI_TCB_t * pOsalTcb, SWI_TCB_PARAM_t * pParam);
//创建任务的时候SWI_TCB_t * pOsalTcb = pParam->pOsalTcb;
//创建子任务时分配任务编号 pTask->mTaskSlot = pOsalTcb->mTaskAllocate++;
//同时把任务定阅的事件列表 填写到 pOsalTcb->pEvtSubscrTab[pTask->mTaskSlot] = pParam->pEvtSubscrTab;
int Task create_cotask(SubTask_t* pTask, SUB_TASK_PARAM_t *pParam);
// 传入的事件标志的时候最高5bit不可使用、或者cotask_send_evt函数内通过掩码自动屏蔽掉最高的几个bit
// 因为需要通过pTask->mTaskSlot设置 bit29~bit27 用于区分事件传递给那个子任务、bit31必须置0
// 同时通过pTask->pSwiTcb 找到任务控制块并设置相对应的 mEvtFlag 同时触发对应SWI中断、产生任务调度
int cotask_send_evt( SubTask_t* pTask, uint32_t mEvt);
// 消息使用内存池动态分配、然后发送给指定的pTask->pSwiTcb->pQuBuf消息缓冲、
// 同时利用pTask->mTaskSlot 设置 bit29~bit27 和 bit30 设置相对应的 mEvtFlag、和mMsgType同时触发对应SWI中断、产生任务调度
//
int cotask_send_msg(SubTask_t* Task, void *pMsg);
//子任务内部最好是用cotask_send_evt和cotask_send_msg、
//而为了解偶合、跨模块可以用cotask_publish_evt和cotask_publish_msg 这样不同模块之间完全不需要知道对方是谁
//发送广播事件和广播消息的时候把bit31置1、发送广播消息的时候把mMsgType的bit32和bit30都置1
int cotask_publish_evt(uint32_t mEvt);
//发送广播消息的时候把消息的引用计数清零
int cotask_publish_msg(void *pMsg);
//初始化定阅分发系统
int init_tcb_subject(void);
//将任务控制块添加进入定阅分发列表里
int add_tcb_to_subject(SWI_TCB_t * pOsalTcb);
//从SWI_TCB_SUBJECT_t消息队列取出消息、分发给mObservers里的每个任务控制块、同时把消息引用计数++
//消息分发完成后按SWI_TCB_t里的SWI中断优先级触发中断
int tcb_subject_run(void);
uint32_t sOsalQuBuf0[];
uint32_t sOsalQuBuf1[];
uint32_t sOsalQuBuf2[];
SWI_TCB_t; s_OsalTcb0; //放在main循环中使用
SWI_TCB_t; s_OsalTcb1; //放在SWI1中断使用
SWI_TCB_t; s_OsalTcb2; //放在SWI2中断使用
uint32_t s_SubscrTab_0_0[];
uint32_t s_SubscrTab_0_1[];
SubTask_t s_SubTak_0_0;
SubTask_t s_SubTak_0_1;
uint32_t s_SubscrTab_1_0[];
uint32_t s_SubscrTab_1_1[];
SubTask_t s_SubTak_1_0;
SubTask_t s_SubTak_1_1;
uint32_t s_SubscrTab_2_0[];
uint32_t s_SubscrTab_2_1[];
SubTask_t s_SubTak_2_0;
SubTask_t s_SubTak_2_1;
int sub_task_0_0_handle((void *me, uint8_t mIndex)
{
..............................
}
int sub_task_0_1_handle((void *me, uint8_t mIndex)
{
..............................
}
int sub_task_1_0_handle((void *me, uint8_t mIndex)
{
SWI_TCB_t * pOsalTcb = (SWI_TCB_t * )me;
//根据索引找到事件标志
uint32_t mEvt = pOsalTcb->mEvtFlag[mIndex];
//任务事件处理、处理一个事件标志位后就清除对应的事件标志位
//如果是在RTOS环境下对应的mEvtFlag所有bit都清空后还需要清空mRtosEvtFlag相应的标志位
..............................
//例如想要在当前子任务给task_2_0发送事件、就会触发抢占、假定SWI2中断优先级设置比SWI1中断优先级高一些
cotask_send_evt(&s_SubTak_2_0, .....);
//发送消息也是一样的
}
int sub_task_1_1_handle((void *me, uint8_t mIndex)
{
..............................
}
int sub_task_2_0_handle((void *me, uint8_t mIndex)
{
..............................
}
int sub_task_2_1_handle((void *me, uint8_t mIndex)
{
..............................
}
int main(void)
{
SWI_TCB_PARAM_t mTcbParam;
SUB_TASK_PARAM_t mTaskParam;
mTcbParam.pQuBuf = sOsalQuBuf0; //初始化消息缓冲和大小
mTcbParam.mQuSize =
mTcbParam.mSwiIrqNum = 0xFF; //指定SWI中断号0xFF、表示发送事件、消息的时候不需要触发SWI中断
...................................
swi_osal_init(&s_OsalTcb0, &mTcbParam);
mTaskParam.pOsalTcb = &s_OsalTcb0;
mTaskParam.pSubTaskHandle = sub_task_0_0_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_0_0;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_0_0)/4;
Task create_cotask(&s_SubTak_0_0, &mTaskParam);
mTaskParam.pOsalTcb = &s_OsalTcb0;
mTaskParam.pSubTaskHandle = sub_task_0_1_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_0_1;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_0_1)/4;
Task create_cotask(&s_SubTak_0_1, &mTaskParam);
mTcbParam.pQuBuf = sOsalQuBuf1; //初始化消息缓冲和大小
mTcbParam.mQuSize =
mTcbParam.mSwiIrqNum = ; //分配SWI中断号和优先级
...................................
swi_osal_init(&s_OsalTcb1, &mTcbParam);
mTaskParam.pOsalTcb = &s_OsalTcb1;
mTaskParam.pSubTaskHandle = sub_task_1_0_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_1_0;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_1_0)/4;
Task create_cotask(&s_SubTak_1_0, &mTaskParam);
mTaskParam.pOsalTcb = &s_OsalTcb1;
mTaskParam.pSubTaskHandle = sub_task_1_1_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_1_1;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_1_1)/4;
Task create_cotask(&s_SubTak_1_1, &mTaskParam);
mTcbParam.pQuBuf = sOsalQuBuf2;
...................................
swi_osal_init(&s_OsalTcb2, &mTcbParam);
mTaskParam.pOsalTcb = &s_OsalTcb2;
mTaskParam.pSubTaskHandle = sub_task_2_0_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_2_0;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_2_0)/4;
Task create_cotask(&s_SubTak_2_0, &mTaskParam);
mTaskParam.pOsalTcb = &s_OsalTcb2;
mTaskParam.pSubTaskHandle = sub_task_2_1_handle;
mTaskParam.pEvtSubscrTab = s_SubscrTab_2_1;
mTaskParam.mSubscrCount = sizeof(s_SubscrTab_2_1)/4;
Task create_cotask(&s_SubTak_2_1, &mTaskParam);
init_tcb_subject();
add_tcb_to_subject(&s_OsalTcb0);
add_tcb_to_subject(&s_OsalTcb1);
add_tcb_to_subject(&s_OsalTcb2);
while(1)
{
swi_osaL_run(&s_OsalTcb0);
tcb_subject_run(); //这个放在主循环里合适吗?还是说放在优先级最高的SWI中断里
}
}
void swi_1_irq(void)
{
swi_osaL_run(&s_OsalTcb1);
}
void swi_2_irq(void)
{
swi_osaL_run(&s_OsalTcb2);
}
|
|