uCOS-III实时操作系统在MCU平台被广泛使用,在这里我们将简单的记录如何将uCOS-III实时操作系统移植到目标平台上并运行。
1、必要的准备
在开始uCOS-III实时操作系统的移植前,我们还需要做一些必要的准备,如确定目标板、准备目标工程及uCOS-III实时操作系统源码等。
1.1、获取uCOS-III源码
在移植uCOS-III之前,首先要获取它的源码。其源码可以从Micrium 的官方网站:www.micrium.com得到。为了方便移植,我们建议直接下载Micrium移植好的基于目标平台的例子。例如我们就下载了uCOS-III V3.0.4基于STM32F4的实例。
解压下载得到的压缩包,我们可以发现4个文件夹,分别是EvalBoards、uC-CPU、uC-LIB、uCOS-III,如下图所示:
其中EvalBoards文件夹下是基于该评估版的应用层实现,在我们的移植中有部分文件可以移过来使用。当然
uC-CPU文件夹这是和 CPU 紧密相关的文件,里面的一些文件很重要,都是我们需要使用的。
uC-LIB文件夹,Micrium 公司提供的官方库,诸如字符串操作、内存操作等接口,可用可不用。一般能用于代替标准库中的一些函数,使得在嵌入式中应用更加方便安全。
uCOS-III文件夹,是操作系统内核文件夹,都是系统核心文件。这些文件是我们全部需要的,移植时将这些拷贝过去就可以。
1.2、建立目标项目
在这里我们的目标MCU选用的是STM32F407ZG,所以在移植之前我们需要建立一个面向STM32F407ZG的裸机工程。当然方法有多种,我们使用STM32CubeMX工具配置硬件然后生成一个基础的项目。
2、uCOS-III的移植
我们此次移植基于STM32F407平台,使用HAL库,并使用IAR开发工具来完成。首先,我们创建一个空项目,并添加必要的HAL库函数,以及启动文件,主函数等。总之是一个可以运行的干净的项目即可。
接下来就是移植uCOS-III的过程。移植的过程并不复杂,先将必要的文件复制到我们的项目中来。一是将uC-CPU、uC-LIB、uCOS-III三个文件夹全部复制到我们的项目中。
并将EvalBoards文件夹下的EvalBoardsSTSTM32F429II-SKuCOS-III目录下的一些文件拷贝到我们的项目中。具体如下图红框中所示:
一般来说我们可以拷贝这8个文件直接使用就可以,但并不说明这8个文件是必须的。其中一些配置文件在系统中会引用到,所以文件名称不要改,而且配置参数按需设定。其他文件实际上可以根据我们的意愿修改。为简便起见,我们还可以复制两个文件,在EvalBoardsSTSTM32F429II-SKBSP目录之下的bsp文件:
其实,这两个文件与具体硬件联系紧密,一般需要自己编写,不过因为我们知识移植,所以有几个函数我们可以直接拿过来使用,我们将其复制过来加以修改。
文件已经准备好了,接下来就是将其移植到我们的项目中,将uCOS-III下的核心代码添加到项目中,如下:
同时将uC-CPU和uC-LIB文件夹下的内容添加到项目中,具体如下:
然后,将我们从例程中复制的相关文件也添加到项目中,具体如下:
然后修改项目属性中的文件引用路径:
到这了,工程项目就已经创建完成了,但并不可用,此时若编译会出现许多错误。因为例程使用的是标准库,而我们使用了HAL库,据此首先要将bsp.h文件中的 #include <stm32f4xx_conf.h>修改为:#include <stm32f4xx_hal.h>。根据需要修改bsp.c文件中的具体驱动代码。
还有一个重要的修改,那就是PendSV中断处理,在STM32F4的启动文件startup_stm32f407xx.s中定义了该中断的中断处理函数PendSV_Handler。同时uCOS-III在os_cpu_a.asm文件中也定义了该中断的中断处理函数OS_CPU_PendSVHandler。所以我们我们需要让他们统一起来,怎么办呢?可以修改startup_stm32f407xx.s文件,也可以修改os_cpu_a.asm文件。在这里我们是修改了startup_stm32f407xx.s文件。不过通常情况下,我们不建议修改别人写好的文件,事实上,原厂例程中提供的一个方法是编写一段汇编程序使用PendSV_Handler调用OS_CPU_PendSVHandler达到相应的目的。不管采用哪种方式都需要在stm32f4xx_it.c文件中注释掉PendSV_Handler函数的实现。
同样的,SysTick_Handler中断处理函数需要做类式的处理。但是由于HAL库本身也是需要使用该中断的,而且在uCOS-III中OS_CPU_SysTickHandler函数是以C代码实现的,所以我们可在stm32f4xx_it.c文件中的SysTick_Handler函数中直接调用。
到这里移植工作基本就完不成了,编译也没有错,但需要跑起来,我们还需要编写相应的多任务处理代码。
3、移植测试
在前面我们已经完成了uCOS-III移植的基本工作。接下来我们实现多任务的测试代码。在开始任务编写前,我们需要修改bsp.c文件的内容。除了具体的应用驱动外,需要实现几个与时钟相关的函数:BSP_CPU_ClkFreq、CPU_TS_TmrInit、CPU_TS_TmrRd、CPU_TS32_to_uSec和CPU_TS64_to_uSec。在我们拷贝来的实例中,其实都有,除BSP_CPU_ClkFreq外,其他都不需要修改。BSP_CPU_ClkFreq函数实现如下:
1 CPU_INT32U BSP_CPU_ClkFreq (void) 2 { 3 CPU_INT32U hclk_freq; 4 5 hclk_freq=HAL_RCC_GetHCLKFreq(); 6 7 return hclk_freq; 8 }
然后我们就可以开始具体任务的实现,在这里我们实现1个启动任务和3个普通任务,当然这些任务都非常简单,我们先声明任务控制块和任务栈如下:
1 static OS_TCB AppTaskStartTCB; 2 static CPU_STK AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE]; 3 static OS_TCB AppTaskUpdateTCB; 4 static CPU_STK AppTaskUpdateStk[APP_CFG_TASK_UPDATE_STK_SIZE]; 5 static OS_TCB AppTaskCOMTCB; 6 static CPU_STK AppTaskCOMStk[APP_CFG_TASK_COM_STK_SIZE]; 7 static OS_TCB AppTaskUserIFTCB; 8 static CPU_STK AppTaskUserIFStk[APP_CFG_TASK_USER_IF_STK_SIZE];
接着我们在主函数中创建启动任务,并启动任务调度。这时操作系统已经开始任务调度。
1 //生成启动任务 2 OSTaskCreate((OS_TCB *)&AppTaskStartTCB, 3 (CPU_CHAR *)"App Task Start", 4 (OS_TASK_PTR )AppTaskStart, 5 (void *)0, 6 (OS_PRIO )APP_CFG_TASK_START_PRIO, 7 (CPU_STK *)&AppTaskStartStk[0], 8 (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE / 10, 9 (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE, 10 (OS_MSG_QTY )0, 11 (OS_TICK )0, 12 (void *)0, 13 (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 14 (OS_ERR *)&err); 15 16 OSStart(&err); //启动任取调度
在启动任务的任务函数中我们创建3个具体业务处理的任务,这三个任务创建后,启动任务将自己删除掉。
1 static void AppTaskStart (void *p_arg) 2 { 3 OS_ERR err; 4 5 (void)p_arg; 6 7 CPU_Init(); 8 9 #if OS_CFG_STAT_TASK_EN > 0u 10 OSStatTaskCPUUsageInit(&err); 11 #endif 12 13 #ifdef CPU_CFG_INT_DIS_MEAS_EN 14 CPU_IntDisMeasMaxCurReset(); 15 #endif 16 17 OSTaskCreate((OS_TCB *)&AppTaskUpdateTCB, 18 (CPU_CHAR *)"App Task Update", 19 (OS_TASK_PTR )AppTaskGUIUpdate, 20 (void *)0, 21 (OS_PRIO )APP_CFG_TASK_UPDATE_PRIO, 22 (CPU_STK *)&AppTaskUpdateStk[0], 23 (CPU_STK_SIZE )APP_CFG_TASK_UPDATE_STK_SIZE / 10, 24 (CPU_STK_SIZE )APP_CFG_TASK_UPDATE_STK_SIZE, 25 (OS_MSG_QTY )1, 26 (OS_TICK )0, 27 (void *)0, 28 (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 29 (OS_ERR *)&err); 30 31 OSTaskCreate((OS_TCB *)&AppTaskCOMTCB, 32 (CPU_CHAR *)"App Task COM", 33 (OS_TASK_PTR )AppTaskCOM, 34 (void *)0, 35 (OS_PRIO )APP_CFG_TASK_COM_PRIO, 36 (CPU_STK *)&AppTaskCOMStk[0], 37 (CPU_STK_SIZE )APP_CFG_TASK_COM_STK_SIZE / 10, 38 (CPU_STK_SIZE )APP_CFG_TASK_COM_STK_SIZE, 39 (OS_MSG_QTY )2, 40 (OS_TICK )0, 41 (void *)0, 42 (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 43 (OS_ERR *)&err); 44 45 OSTaskCreate((OS_TCB *)&AppTaskUserIFTCB, 46 (CPU_CHAR *)"App Task UserIF", 47 (OS_TASK_PTR )AppTaskUserIF, 48 (void *)0, 49 (OS_PRIO )APP_CFG_TASK_USER_IF_PRIO, 50 (CPU_STK *)&AppTaskUserIFStk[0], 51 (CPU_STK_SIZE )APP_CFG_TASK_USER_IF_STK_SIZE / 10, 52 (CPU_STK_SIZE )APP_CFG_TASK_USER_IF_STK_SIZE, 53 (OS_MSG_QTY )0, 54 (OS_TICK )0, 55 (void *)0, 56 (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 57 (OS_ERR *)&err); 58 59 OSTaskDel(&AppTaskStartTCB,&err); 60 while (1) 61 { 62 OSTimeDly(100, OS_OPT_TIME_DLY, &err); 63 } 64 }
编译运行没有错误,至此我们将uCOS-III移植到目标MCU平台的工作就完成了。
4、小结
本篇中我们简单的介绍了uCOS-III移植到目标MCU平台的过程,并对移植后的系统进行了简单的测试。系统的运行与我们预期的一致,移植本身没有问题。
在本篇中我们对PendSV和SysTick中断处理,采用的是修改启动文件startup_stm32f407xx.s来实现的。事实上我们觉得更好的方式是编写一段汇编程序,在PendSV_Handler和SysTick_Handler中断处理函数中调用OS_CPU_PendSVHandler和OS_CPU_SysTickHandler,这样就不用修改startup_stm32f407xx.s和os_cpu_a.asm这两个文件了。当然之所以能如此操作,是因为在startup_stm32f407xx.s文件中PendSV_Handler和SysTick_Handler函数是弱定义。