完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
FSC_STOS(点击下载@px86 )是专为STM32芯片编写的多任务系统内核。 *任务之间相互独立,互不干扰。 *提供三种运行模式:时间轮询模式、纯优先级模式、时间片+优先级混合模式。 *采用时间切片轮询方式,每个任务运行周期(CPU占用时间)可控,从而实现相对优先级(顺序模式)。 *可由OS_delayMs()函数决定任务运行周期。 *提供OSTimer、OSFlag、OSMutex、OSMBox给任务之间进行延时、同步、互斥、通讯等。 *提供任务状态控制函数、任务状态获取函数、任务跳转函数、任务切换上锁与解锁函数、手动任务调度切换等函数。 *提供串口指令,控制任务及查看系统状态信息。 一、文件结构 1. (1)APP.c ---任务实体代码 (2)FSC_STOS.h ---内核声明代码 (3)FSC_STOS.c ---内核主体代码 (4)FSC_STOS.asm ---汇编代码 2.使用方法 (1)用户代码指定在APP.c任务内编写,也可以在其他C文件处写好,调用到任务内来运行。 (2)在用到系统提供的函数的地方文件#include "fsc_stos.h"。 (3)运行前检查FSC_STOS.h内配置是否满足。 3.关于移植(在M0、M3和M4上移植,其他内核后续更新) (1)在main.c中包含系统头文件:include "fsc_stos.h" ,剪切main文件内的所有#include ""到APP.c中,剪切main()函数全部内容到APP.c某个任务内,从APP.c复制OS_MAIN() 函数到main()中作为系统入口。 (2)删除原工程有关SysTick和PendSV的代码。一般,与这两个有关的,一是delay延时函数,二是stm32fxxx_it.c中断函数c文件中的SysTick_Handler和PendSV_Handler中断函数。解决办法是把工程中的delay.c/.h移除,在stm32fxxx_it.c中注释掉这两个中断函数。原来工程中的延时函数改为系统提供的延时函数。 (3)做完以上两点,基本就完成移植了。(注意添加头文件路径,需要用到系统指令的还要把系统指令接收数据入口函数放到串口中断函数中) 二、代码解析 1.系统文件 1>APP.c 存放系统运行入口和用户任务实体函数,用户只能在Task()任务函数里进行用户代码编写。 2>FSC_STOS.h 存放系统设置参数 3>FSC_STOS.c 存放系统c代码 4>FSC_STOS.asm 存放系统汇编代码 2.系统运行流程(可抢断顺序运行模式) (1).OS_MAIN()函数运行,系统初使化(包含初使化系统定时器)、将用户函数加入系统、启动系统(包含启动系统定时器)。 || / (2).OS_TaskIdle()空闲任务运行,经过1个时间切片后,产生系统定时中断。 || / (3).进入OS_Timer_Handler()中断函数,运行TCB指向下一个任务的TCB,并且经过OSPendSVPulse()函数触发一次PendSV中断,PendSV中断函数内容在汇编代码里,因为PendSV中断优先级别最低,所以触发后不会立即跳到中断函数,而是等待PendSV触发之前所有在等待响应的中断全部响应完后才会响应PendSV中断(这个机制是硬件机制),所以在系统定时器中断函数里触发了一次PendSV也不会立即跳到PendSV中断函数,系统定时器中断函数内容全部运行完成跳出后,硬件会检测PendSV之前的中断是否全部响应完成,没有则等待,都完成了就会响应PendSV中断,在PendSV中断函数中以汇编代码方式实现了把当前任务的所有临时数据和程序PC断点等信息保存(入栈)到任务堆栈数组中,然后获取下一个任务的TCB,将下一个任务的TCB的数据装载到当前的运行寄存器中(第一次运行时,堆栈中的数据是无用的,无需保存),PendSV中断函数运行完后跳出,PC会从下一任务PC值处开始运行,正常情况下下一任务是OS_TaskManage()。ps:想深入了解上下文(汇编代码部分)具体细节的请查看《Cortex-M3权威指南》中文版。 || / (4).OS_TaskManage()任务管理器任务运行,运行至OS_delayMs()函数时,触发PendSV中断,如当前无其他中断产生则会立即响应PendSV中断并进入PendSV中断函数,完成上下文切换(环境切换,参考上面过程),正常情况下下一任务是Task1()。 || / (5).Task1()用户任务1运行,如果Task1()中没有调用OS_delayMs()函数,则经过1个时间切片后,产生系统定时器中断,完成下一任务交换和上下文切换(过程参考上面)。如果有调用OS_delayMs()并且运行到了OS_delayMs()函数,则参考步骤(4)。正常情况下下一任务是Task2() || / (6).Task2()用户任务2运行,以此类推,直到最后一个用户任务运行完再从空闲任务开始循环运行。如果在循环运行过程中,在系统定时器中断函数中检测到某个任务的OS_delayMs()延时时间完成,则会将下一任务临时改成此任务,让延时完成的任务立即获得1次运行,运行完成时,下一运行任务将会恢复到被打断之前的下一任务继续正常运行。当多个任务的延时同时完成时,先创建的任务优先获得运行,后创建的任务紧跟着依次等待运行。 4.源码体验 (0)FSC_STOS.asm 任务上下文切换 ;//启动代码 包含对SysTick和PendSV的初使化、SP指针初使化 OSStartUp ;//void OSStartUp(void){} CPSID I ;//关闭全局中断 LDR R0,=OSPendSVInit ;//等价C语言:运行OSPendSVInit(); 函数,PendSV初使化 BLX R0 ;//return LDR R0,=OSSysTickInit ;//同理, SysTick定时器初使化 BLX R0 ;//同理 LDR R4,=0x0 ;//R4装载立即数0(不直接给PSP赋值0而是经进R寄存器作为媒介是因为PSP只能和R寄存器打交道) MSR PSP, R4 ;//PSP(process stack pointer)程序堆栈指针赋值0。PSP属用户级(特级权下为MSP),双堆栈结构。 LDR R0,=OSPendSVPulse ;//OSPendSVPulse();触发一次PendSV中断(全局中断没打开,会悬挂起) BLX R0 ;//return CPSIE I ;//打开全局中断(此时若没有其他中断在响应,则立即进入PendSV中断函数) OSStartPending ;//原地等待标号。 B OSStartPending ;//返回OSStartPending,即原地等待,等价C语言:while(1); //等待进入PendSV中断函数 ;//PendSV中断函数 PendSV_Handler ;//PendSV中断函数(移植时需要注释原工程的PendSV_Handler中断函数) CPSID I ;//关闭全局中断 MRS R0, PSP ;//读取PSP堆栈指针的值到R0 ;+++++++++++++++++++++Save Context Code(PageUp)+++++++++++++++++++;//任务环境保存开始 ;------------------------Cortex_M3 Code Start---------------------; //M3及以上内核有关代码开始 CBZ R0, OSFirstEnter ;//判断R0是否等0,等于就直接跳转OSFirstEnter,此指令M3及以上才有,比CMP快捷。 ;//等价C语言:if(R0==0){goto OSFirstEnter;} SUB R0, R0, #0x20 ;//R0=R0-0x20,0x20=32,32byte的数据=8个32位寄存器的值 ;//此处PSP指针操作的地址单位是byte,这里需要把PSP的指针往栈底 ;//方向移动32byte的长度,用来模拟8个32位寄存器压栈后的指针变化。 STM R0, {R4-R11} ;//批量保存R4-R11的值到内存(以R0(PSP)为首地址),方便快捷,M0不支持。 ;//以上两行代码模拟了将R4-R11压栈过程。 ;------------------------Cortex_M3 Code End-----------------------; //M3及以上内核有关代码结束 ;+++++++++++++++++++Save Context Code(PageDown)+++++++++++++++++++; //任务环境保存结束 ;+++++++++++++++++++++Read Context Code(PageUp)+++++++++++++++++++;//任务环境恢复开始 LDR R0, =OSTCBCur ;//读取OSTCBCur指针值到R0 LDR R1, =OSTCBNext ;//读取OSTCBNext指针值到R1 LDR R2 , [R1] ;//读取内存数据到R2(以R1为地址的内存)//R2=OSTCBNext->StkPtr; STR R2 , [R0] ;//保存R2数据内存到(以R0为地址的内存)//OSTCBCur->StkPtr=OSTCBNext->StkPtr;//切换堆栈指针 LDR R0 , [R2] ;//读取内存数据到R0(以R2为地址的内存)//R0=OSTCBNext->StkPtr; ;------------------------Cortex_M3 Code Start---------------------;//M3及以上内核有关代码开始 LDM R0, {R4-R11} ;//批量从内存读取R4-R11的值到R4-R11寄存器 ADD R0, R0, #0x20 ;//R0=R0+0x20 (即8个0x04),PSP指针往栈顶方向移动8个寄存器长度 ;------------------------Cortex_M3 Code End-----------------------;//M3及以上内核有关代码结束 ;+++++++++++++++++++Read Context Code(PageDown)+++++++++++++++++++;//任务环境恢复结束 (1)main.c main()函数(一般裸机工程中有,属于移植修改对象) <1>main函数内容全部剪切到Task中(其中一个) #include "fsc_stos.h" //使用多任务内核 int main(void) //主要用于进入系统 { OS_MAIN();//系统入口函数 } (2)APP.c 用户任务 /*任务名称任意,此处为方便直观取名为Task1-5 */ /******************************创建任务参数*************************/ void Task1(void); //任务1声明 void Task2(void); //任务2声明 void Task3(void); //任务3声明 void Task4(void); //任务4声明 void Task5(void); //任务5声明 /*******************************************************************/ void OS_MAIN(void) { /*------------------------------全局初使化区-----------------------------*/ /*推荐把所有任务都使用到的初使化放在此处,Task独立用到的初使化放在Task内*/ SWON_JTAGOFF(); //仅使用SW功能。示例代码,使用其他调试模式时删除 USART1_Config(9600); //串口1初使化。示例代码,不使用串口1时删除 /*-----------------------------------------------------------------------*/ OSInit(); //系统初使化 /********************************在系统中创建任务***********************************/ //动态创建(系统自动根据栈参数生成空间,注意设置好内存池的大小) /* 任务名 任务函数名 栈大小 时间片 任务优先级 任务状态*/ OSTaskCreate("Task1", Task1, 128, 10, 4, TASK_RUNNING); //OS创建任务1 OSTaskCreate("Task2", Task2, 128, 10, 6, TASK_RUNNING); //OS创建任务2 OSTaskCreate("Task3", Task3, 128, 10, 5, TASK_RUNNING); //OS创建任务3 OSTaskCreate("Task4", Task4, 128, 10, 5, TASK_RUNNING); //OS创建任务4 OSTaskCreate("Task5", Task5, 128, 10, 3, TASK_RUNNING); //OS创建任务5 //任务如超过5个请自行添加 /***********************************************************************************/ OSStartUp();//系统启动 } /************************************************用户任务代码编程区*************************************************/ /*----------------------全局变量及宏定义区--------------------------*/ /*------------------------------------------------------------------*/ //多个任务(等同裸机下的main())并发运行,提高cpu使用效率。 void Task1(void) //任务1 { PC13_OUT; //stm32f103c8t6最小系统板板载LED IO口初使化:推挽输出模式 ,示例代码,使用时删除 while(1) { PC13=~PC13; //LED闪烁,示例代码,使用时删除 OSprintf("Task1 is runningrn");//示例代码,使用时删除 OS_delayMs(500);//闪烁频率:1S,示例代码,使用时删除 } } void Task2(void) //任务2 { while(1) { OSprintf("Task2 is runningrn");//示例代码,使用时删除 OS_delayDHMS(0,0,0,1); //示例代码,使用时删除 } } void Task3(void) //任务3 { while(1) { OS_delayMs(0);//为0时不运行 } } void Task4(void) //任务4 { while(1) { OS_delayMs(0); } } void Task5(void) //任务5 { while(1) { OS_delayMs(0); } } (3)FSC_STOS.h 系统配置 /****************************************用户可自定义***********************************************************/ #define OS_RINNING_MODE 2 //OS运行模式:0-时间片运行 1-优先级运行 2-时间片+优先级运行 #define OS_MAX_TASKS 8 //任务数=用户任务数+2 任务数:3-65535 用户根据实际需要的任务数量修改 #define OS_MEMORYPOOL_SIZE 4*1024*1 //内存池大小,单位:Byte #define OS_UCGUI_SUPPORT 0 //uCGUI支持多任务 0-不支持 1-支持 (建议单任务使用GUI,即设为0) /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #define TIMER_SIZE 2 //系统虚拟定时器数量 (根据需要配置,1-65535,以下同) #define FLAG_SIZE 2 //标志数量 #define FLAG_GROUP_SIZE 2 //标志群数量 #define MUTEX_SIZE 2 //互斥数量 #define MBOX_SIZE 2 //邮箱数量 #define MBQ_SIZE 2 //邮箱队列缓存数量 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #define OS_CMD_ALL_ENABLE 1 //剪裁全部系统指令(0-剪裁,1-不剪裁,下同) #define OS_CMD_NSY_ENABLE 1 //剪裁系统无关的指令 #define OS_CMD_DISP_SYS_ENABLE 1 //剪裁系统状态信息指令显示(关闭显示能节省大量内存) #define OS_CMD_DISP_NSY_ENABLE 1 //剪裁系统状态信息以外无关的指令显示 #define OS_TIMECOUNTSYS_ENABLE 1 //剪裁系统时间统计系统(剪裁可提高时间片模式下的内核切换速度) #define OS_GET_SET_SW_ENABLE 1 //剪裁任务设置、获取、跳转 #define OS_SIGN_PP_ENABLE 1 //剪裁信号量(可提高内核切换速度) #define OS_TIM_SYS_ENABLE 1 //剪裁系统虚拟定时器(可提高内核切换速度) #define OS_SYSTIME_ENABLE 0 //剪裁系统时间RTC #define OS_REMOTE_ENABLE 0 //剪裁不常用函数 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #define TASK_NAME_LEN 32 //任务名 字符最大长度(如任务名超过32个字符请修改) #define OS_CMD_STR_LEN 64 //系统指令 字符最大长度 #define OS_CMD_NUM_LEN 8 //系统指令字符串允许包含数字个数("prio=89//"为一个数字) #define OS_FAULT_BACKUP_DATA_LEN 1 //系统故障数据备份长度 /*----------------------------系统任务参数--------------------------------*/ #define OS_TIMESLICE_IDLE 1 //空闲任务时间切片,单位:微秒 ms #define OS_TIMESLICE_MANAGER 1 //任务管理器任务时间切片,单位:微秒 ms #define TaskIDLE_StkSize 32 //空闲任务堆栈大小 #define TaskManager_StkSize 160 //任务管理器任务堆栈大小 #define TaskIDLE_Prio 0 //空闲任务优先级(不能修改,放在此是为了让用户了解空闲任务的优先级) #define TaskManager_Prio 1 //任务管理器任务优先级(如想指令能快速反应可提高优先级) /*----------------------------故障检测系统--------------------------------*/ #define OS_FAULT_ENABLE 0 //剪裁故障检测系统(需要在stm32fxxx_it.c中注释掉HardFault_Handler()中断函数) #define OS_FAULT_DISP_ENABLE 0 //剪裁故障检测系统提示 #define OS_Reset_Delay_Time 10 //系统重启延时,单位:s #define OS_Shutdown_Delay_Time 10 //系统关闭延时,单位:s #define OS_EndlessLoops_Times 1000 //连续运行该次数,则认为该任务陷入死循环无法切换到其他任务运行,单位:次数 #define OSFAULT_Reset_Enable 1 //是否允许故障后自动重启,0-不允许 1-允许 /***************************************************************************************************************/ /*+++++++++++++++++++++++++++++++++++++++++++++状态定义区+++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /*-------------------------------------用户可调用与系统有关函数-------------------------------------------------*/ /*--------------常用系统功能函数-------------*/ void OSTimeSliceCounterReset(void); //时间切片计数器清0(手动切换任务时,可选择需要将时间切片计数清0) void OSSchedSwitch(void); //任务调度并切换任务(如不将时间切片清0,切换后的任务运行可能少于完整时间片时间) void OSContextExchangeToTask(OS_TCB* tcb); //切换到指定任务,输入参数,任务控制块 (该函数效率比OSTaskSwitch高) void OSScheduler_Process(void); //任务调度器(只调度不切换) void OSContextExchange(void); //上下文切换(切换任务环境,下个任务运行OSTCBCur指向的任务) void OSSchedLock(void); //任务切换上锁函数(进入半裸机模式,OS部分功能失效) void OSSchedUnlock(void); //任务切换解锁函数 void OSTaskSwitchBack(void* Taskx); //任务跳转 带返回 void OSTaskSwitch(void* Taskx); //任务跳转 不带返回 void OSTaskPrioSet(void* Taskx,INT16U Taskprio);//任务优先级设置函数(每调用一次就会自动备份一次) void OSTaskPrioBackup(void* Taskx); //任务优先级备份函数 void OSTaskPrioBackupSet(void* Taskx,INT16U Taskprio);//任务备份优先级设置函数 INT16U OSTaskPrioBackupGet(void* Taskx); //获取任务备份优先级 INT8U OSTaskStateSet(void* Taskx,INT8U TaskState);//设置任务状态,返回OS_FALSE-设置失败,OS_TRUE-设置成功 void OSEnterDriver(void); //开始保护驱动程序 驱动代码开头调用 (进入准裸机模式,此时只有delay_us()函数能正常使用) void OSExitDriver(void); //结束保护驱动程序 驱动代码结尾调用 (恢复OS模式) void OSSleep(void); //系统休眠 void OSAwaken(void); //系统唤醒 (4)FSC_STOS.C 系统部分核心源码 void OSSchedSwitch(void) //任务调度并切换任务 { OSScheduler_Process();//调度管理器程序,经调度后获取下个要运行的任务 if(OSTCBRun!=OSTCBCur) //如果下个要运行的任务与当前的任务不一致则 { OSContextExchange(); //进行任务环境切换(切换到下个任务运行) } } /*---------------------------------任务切换关键函数(Page Down)---------------------------------------*/ //可悬挂软件触发中断PendSV中断中进行上下文 //只需能以软件方式触发的中断均可,将上下文汇编代码放在中断函数中,用软件方式触发上下文切换。 void OSContextExchange(void) //上下文切换(任务环境切换,下个任务运行OSTCBCur指向的任务) { OS_INT_ENTER();//关中断 /*Enter PendSV_Handler*/ //PendSV方式 OSPendSVPulse(); //触发一次PendSV中断 /*Enter SVC_Handler*/ //SVC方式 //OSSVCPulse(); OS_INT_EXIT();//开中断 } void OSScheduler_Process(void) //任务调度器(只调度不切换) { if((OS_System.LockNesting == 0) && (OS_System.IntNesting == 0)) { #if (OS_FAULT_ENABLE == 1) RESCHEDULE: #endif #if (OS_GET_SET_SW_ENABLE == 1) #endif { #if (OS_SIGN_PP_ENABLE == 1) OSFlagGroupHandler();//OSFlagGroup有关 #endif if(OS_System.RuningMode==0)//运行模式:Order { /*******************************************可抢断顺序运行*******************************************/ #if (OS_SIGN_PP_ENABLE == 1) OSMutexHandler_Order();//OSMutex有关函数 #endif if(OSTaskSwitchCheck_Order()==OS_FALSE) //是否有跳转发生完成 { OSRunModeProcess_Order();//时间片轮询内核程序 } } else { /*******************************************抢断式优先级******************************************/ #if (OS_SIGN_PP_ENABLE == 1) OSMutexHandler_Prio();//OSMutex有关函数 #endif OSRunModeProcess_Prio();//任务优先级内核程序 OSTaskSwitchCheck_Prio();//任务跳转检测(跳转后还要再次核查最高优先级) } } } #if (OS_FAULT_ENABLE == 1) OS_Fault.tcblast=OSTCBCur; //错误检测系统备份 OS_Fault.tcbnext=OSTCBNext;//错误检测系统备份 if(OS_FAULT_STK_CHECK(OSTCBCur)==OS_TRUE)//当前任务的栈溢出检测 { goto RESCHEDULE; //如果溢出则关闭当前任务并重新查找最高优先级任务或下个可运行的任务 } #endif } /*---------------------------------任务切换关键函数(Page Up)---------------------------------------*/ void OS_SysTick_Handler(void) //任务切换核心函数 { if(OS_System.Running==OS_TRUE)//系统状态检测 { OS_System.ClockCnt++; //系统时钟节拍计数器累加 OSTaskTimeDelayCount_Process();//任务延时计数程序 #if (OS_TIMECOUNTSYS_ENABLE == 1) OSRunTimeCount_Process(); //系统运行时间统计程序 #endif #if (OS_TIM_SYS_ENABLE == 1) OSTimerCount_Process(); //虚拟定时器计数程序 #endif OS_System.TimeSliceCnt++; //时间切片计数器(以时钟节拍为基准) if(OS_System.TimeSliceCnt>=OSTCBCur->TaskTimeSlice)//时间切片溢出判断(决定一个时间切片等于多少个时钟节拍) { OS_System.TimeSliceCnt=0; //时间切片计数器清0,准备重新计数 if(OS_System.RuningMode!=1)//非纯优先级运行模式情况下 { OSSchedSwitch(); //调度并切换任务 } #if (OS_FAULT_ENABLE == 1) OSTaskEndlessLoopsCheck_Process();//任务卡死检测程序(原理:任务连续不断运行N次则视为卡死) #endif #if (OS_CMD_ALL_ENABLE == 1) OSTaskCPUOccRateCount_Process();//任务占用率统计程序 #endif } } else if(OS_System.Running==OS_FALSE) { OS_StopRun_Process();//系统关闭程序 } } ———————————————— 版权声明:本文为CSDN博主「Angle_145」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_37007823/article/details/88234743 三、系统指令 1.串口发送cmd/help//可获取所有指令帮助信息。 2.更多指令请查看FSC_STOS.c。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1618 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1545 浏览 1 评论
979 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1597 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
645浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
516浏览 3评论
532浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
505浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 16:24 , Processed in 0.623602 second(s), Total 74, Slave 58 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号