首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

RTOS之UCOS(七)---UCOS系统移植

  • 24-03-03 03:23
  • 3446
  • 8469
blog.csdn.net

文章目录

  • 1. UCOS的底层支持要素
  • 2. UCOSⅡ的移植
  • 3. UCOSⅢ的移植
  • 更多文章:

经过前一篇文章 固件库的移植,我们的处理器就能完成从上电复位到进入main函数的过程,在main函数中初始化我们要用到的外设,并完成外设的中断处理函数。如果我们需要处理器完成的任务比较单一,可以不使用操作系统,比如BLE协议栈应用开发就是用了状态机模型,但如果需要处理器完成的任务比较多,为了便于管理可以引入轻巧的实时操作系统,下面以UCOS-II为例介绍下实时操作系统的移植。

1. UCOS的底层支持要素

前面有一篇文章介绍任务调度时谈到任务调度的核心是通过触发PendSV中断,在其中断处理函数中执行任务切换工作,所以PendSV应该算系统移植的一个重点。任务周期性切换的驱动源是SYSTICK系统滴答时钟,这个号称操作系统心跳的滴答定时器SYSTICK的配置和中断处理同样应该是系统移植的一个重点。对照前面提到的系统启动三要素:时钟系统、中断系统、存储系统,SYSTICK与PendSV应该算时钟与中断部分的底层支持要素。

操作系统一般涉及到多任务间的同步,虽然有互斥量与信号量这类偏上层的事件控制块保证任务间同步,也有临界区这种偏底层的工具保证任务内的原子操作,既然是偏底层的,临界区应该也是移植的一个关注点,而且临界区涉及到开关中断,也属于时钟与中断部分的底层支持要素。

接下来看看存储方面有哪些跟处理器直接相关的要素,我们学习单片机或编程语言时应该了解过相同的数据类型在不同的处理器平台或编译器上可能会表现出不同的位宽,例如整型int可能在16位单片机上占16位宽度也即2字节而在32位单片机上占32位宽度也即4字节,所以根据实际的处理器型号重新定义每种数据类型的位宽算是操作系统移植的一个重点。对堆栈的操作一般涉及寄存器的功能配置(比如xPSR状态寄存器),所以堆栈初始化、堆栈生长方向等也应该是操作系统移植的一个重点。数据类型位宽重定义与堆栈初始化算是存储部分的底层支持要素。

2. UCOSⅡ的移植

先下载UCOS源码,UCOS开发者Micrium除了提供单纯的UCOS源码,也针对很多平台提供了适配或移植后的源码,本文基于STM32F10X硬件平台,针对该平台适配后的源码下载网址如下:
https://www.micrium.com/downloadcenter/download-results/?searchterm=mi-stm32f107&supported=true
UCOSII STM32F107下载界面
下载Micrium_uC-Eval-STM32F107_uCOS-II系统源码,解压后的文件结构如下:
UCOSⅡ源码目录结构

  • EvalBoards:支持评估(开发)板STM32F107的相关文件(可以理解为工程模板示例文件),包含了BSP与UCOSⅡ两个文件夹,BSP目录保存底层板级启动相关的文件,这里的启动文件较旧,我们使用前面介绍的更新的ST V3.5版固件库文件作为启动文件;UCOSⅡ目录保存系统配置文件,我们移植时会借用部分配置文件;
  • uC-CPU:跟CPU架构相关的文件,我们依然使用ST标准外设固件库提供相关的功能,不使用该文件夹下的文件;
  • uC-LIB:这个是Micrium官方的库,可以部分替代C-LIB,KEIL MDK还提供了Micro-LIB微库,我们可以选择使用C-LIB或Micro-LIB,当然也可以选择这个uC-LIB,如果不想使用uC-LIB,可以忽略这个文件夹;
  • uCOS-II:这个文件夹是我们移植的关键,其中Source保存了UCOSⅡ的源码,移植时不需要更改;Port保存了与处理器平台相关的接口文件,是移植时的重点,需要根据处理器型号做出部分更改。

下面用一个简图展示下不同目录模块在移植过程中的关系:
UCOSⅡ源码目录关系
其中BSP由上一节介绍的STM32F10x_StdPeriph_Lib_V3.5.0提供板级支持,如果前面ST固件库已移植完成,可以忽略BSP这部分。然后就是UCOSⅡ Port与Source目录下文件的移植。最后是应用层的配置文件来自…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-II目录,这个等下层移植完成后再介绍。下面先看下UCOSⅡ目录下的文件结构:
UCOSⅡ目录文件结构
其中Source目录下的源码不需要任何更改,在前面的文章中也介绍过,下面再简单总结下该目录下各文件功能:

文件名功能描述
os_core.c内核数据结构管理,ucos-ii的核心,涵盖内核的初始化、任务切换、事件块管理、事件标志组管理等功能
os_flag.c事件标志组的管理代码
os_mbox.c消息邮箱的管理代码
os_mem.c存储分区的管理代码
os_mutex.c互斥量的管理代码
os_q.c消息队列的管理代码
os_sem.c信号量的管理代码
os_task.c任务的管理代码
os_time.c时间管理,主要实现任务延时
os_tmr.c定时器管理,设置定时时间,超时则调用超时函数
ucos_ii.h定义了全局变量、内核数据结构。函数声明等,也是ucos-ii的核心

下面重点看下Port目录下各文件的功能:

文件名功能描述
oc_cpu_a.asm与处理器相关的汇编代码,主要是与任务切换相关,比如状态寄存器保存恢复、SYSTICK与PendSV配置与中断处理
os_cpu_c.c定义用户钩子函数,提供扩充软件功能的的接口,任务堆栈初始化函数也在这里定义
os_cpu.h定义数据类型、处理器相关代码、声明函数原型
os_dbg.c内核调试相关数据和相关函数

先看os_cpu.h,里面数据类型位宽及堆栈生长方向已经针对STM32F107做了调整,所以不需要变更,代码如下:

// uCOS-II\Ports\os_cpu.h
/*
*********************************************************************************************************
*                                              DATA TYPES
*                                         (Compiler Specific)
*********************************************************************************************************
*/

typedef unsigned char  BOOLEAN;
typedef unsigned char  INT8U;                    /* Unsigned  8 bit quantity                           */
typedef signed   char  INT8S;                    /* Signed    8 bit quantity                           */
typedef unsigned short INT16U;                   /* Unsigned 16 bit quantity                           */
typedef signed   short INT16S;                   /* Signed   16 bit quantity                           */
typedef unsigned int   INT32U;                   /* Unsigned 32 bit quantity                           */
typedef signed   int   INT32S;                   /* Signed   32 bit quantity                           */
typedef float          FP32;                     /* Single precision floating point                    */
typedef double         FP64;                     /* Double precision floating point                    */

typedef unsigned int   OS_STK;                   /* Each stack entry is 32-bit wide                    */
typedef unsigned int   OS_CPU_SR;                /* Define size of CPU status register (PSR = 32 bits) */

#define  OS_STK_GROWTH        1u                  /* Stack grows from HIGH to LOW memory on ARM        */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

涉及到的函数如下:

// uCOS-II\Ports\os_cpu.h
/*
*********************************************************************************************************
*                                              PROTOTYPES
*********************************************************************************************************
*/

#if OS_CRITICAL_METHOD == 3u                      /* See OS_CPU_A.ASM                                  */
OS_CPU_SR  OS_CPU_SR_Save(void);
void       OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif

void       OSCtxSw(void);
void       OSIntCtxSw(void);
void       OSStartHighRdy(void);

void       OS_CPU_PendSVHandler(void);

                                                  /* See OS_CPU_C.C                                    */
void       OS_CPU_SysTickHandler(void);
void       OS_CPU_SysTickInit(INT32U  cnts);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

上面的函数中,涉及到底层SYSTICK与PendSV的函数需要调整,由于底层BSP部分不使用UCOS代码直接使用STM32F10x固件库的代码,所以二者需要做好匹配。在STM32F10x固件库的中断向量表(见startup_stm32f10x_hd.s文件)中SysTick与PendSV的异常处理函数名分别为SysTick_Handler与PendSV_Handler,跟UCOS源码中的不一致,这里把startup_stm32f10x_hd.s文件中的SysTick_Handler与PendSV_Handler分别全部替换为UCOS源码中(见os_cpu.h文件)的函数名OS_CPU_SysTickHandler与OS_CPU_PendSVHandler(当然也可以反过来把UCOS源码中的处理函数名全部替换为ST固件库中的处理函数名),SysTick与PendSV异常触发后就可以通过中断向量表调用真正的异常处理函数了。既然这里选择了修改ST固件库中断向量表中的文件名,UCOS中的oc_cpu_a.asm文件(里面的代码在前篇任务调度器中介绍过)就不需要变更了。

下面再看下os_cpu_c.c中的代码,主要是一些钩子函数定义,下面只展示部分重要的代码(堆栈初始化、SYSTICK初始化及异常处理函数):

// uCOS-II\Ports\os_cpu_c.c
/*
*********************************************************************************************************
*                                          SYS TICK DEFINES
*********************************************************************************************************
*/

#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010uL)) /* SysTick Ctrl & Status Reg. */
#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)0xE000E014uL)) /* SysTick Reload  Value Reg. */
#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018uL)) /* SysTick Current Value Reg. */
#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01CuL)) /* SysTick Cal     Value Reg. */
#define  OS_CPU_CM3_NVIC_PRIO_ST    (*((volatile INT8U  *)0xE000ED23uL)) /* SysTick Handler Prio  Reg. */

#define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    0x00010000uL   /* Count flag.                */
#define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  0x00000004uL   /* Clock Source.              */
#define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    0x00000002uL   /* Interrupt enable.          */
#define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   0x00000001uL   /* Counter mode.              */
#define  OS_CPU_CM3_NVIC_PRIO_MIN                               0xFFu    /* Min handler prio.          */

/*
*********************************************************************************************************
*                                          SYS TICK HANDLER
*
* Description: Handle the system tick (SysTick) interrupt, which is used to generate the uC/OS-II tick
*              interrupt.
*
* Arguments  : none.
*
* Note(s)    : 1) This function MUST be placed on entry 15 of the Cortex-M3 vector table.
*********************************************************************************************************
*/

void  OS_CPU_SysTickHandler (void)
{
    OS_CPU_SR  cpu_sr;


    OS_ENTER_CRITICAL();                         /* Tell uC/OS-II that we are starting an ISR          */
    OSIntNesting++;
    OS_EXIT_CRITICAL();

    OSTimeTick();                                /* Call uC/OS-II's OSTimeTick()                       */

    OSIntExit();                                 /* Tell uC/OS-II that we are leaving the ISR          */
}

/*
*********************************************************************************************************
*                                          INITIALIZE SYS TICK
*
* Description: Initialize the SysTick.
*
* Arguments  : cnts          is the number of SysTick counts between two OS tick interrupts.
*
* Note(s)    : 1) This function MUST be called after OSStart() & after processor initialization.
*********************************************************************************************************
*/

void  OS_CPU_SysTickInit (INT32U  cnts)
{
    OS_CPU_CM3_NVIC_ST_RELOAD = cnts - 1u;
                                                 /* Set prio of SysTick handler to min prio.           */
    OS_CPU_CM3_NVIC_PRIO_ST   = OS_CPU_CM3_NVIC_PRIO_MIN;
                                                 /* Enable timer.                                      */
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;
                                                 /* Enable timer interrupt.                            */
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;
}

/*
*********************************************************************************************************
*                                        INITIALIZE A TASK'S STACK
*
* Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the
*              stack frame of the task being created.  This function is highly processor specific.
*
* Arguments  : task          is a pointer to the task code
*
*              p_arg         is a pointer to a user supplied data area that will be passed to the task
*                            when the task first executes.
*
*              ptos          is a pointer to the top of stack.  It is assumed that 'ptos' points to
*                            a 'free' entry on the task stack.  If OS_STK_GROWTH is set to 1 then
*                            'ptos' will contain the HIGHEST valid address of the stack.  Similarly, if
*                            OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address
*                            of the stack.
*
*              opt           specifies options that can be used to alter the behavior of OSTaskStkInit().
*                            (see uCOS_II.H for OS_TASK_OPT_xxx).
*
* Returns    : Always returns the location of the new top-of-stack once the processor registers have
*              been placed on the stack in the proper order.
*
* Note(s)    : 1) Interrupts are enabled when your task starts executing.
*              2) All tasks run in Thread mode, using process stack.
*********************************************************************************************************
*/

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *stk;


    (void)opt;                                   /* 'opt' is not used, prevent warning                 */
    stk       = ptos;                            /* Load stack pointer                                 */

                                                 /* Registers stacked as if auto-saved on exception    */
    *(stk)    = (INT32U)0x01000000uL;            /* xPSR                                               */
    *(--stk)  = (INT32U)task;                    /* Entry Point                                        */
    *(--stk)  = (INT32U)OS_TaskReturn;           /* R14 (LR)                                           */
    *(--stk)  = (INT32U)0x12121212uL;            /* R12                                                */
    *(--stk)  = (INT32U)0x03030303uL;            /* R3                                                 */
    *(--stk)  = (INT32U)0x02020202uL;            /* R2                                                 */
    *(--stk)  = (INT32U)0x01010101uL;            /* R1                                                 */
    *(--stk)  = (INT32U)p_arg;                   /* R0 : argument                                      */

                                                 /* Remaining registers saved on process stack         */
    *(--stk)  = (INT32U)0x11111111uL;            /* R11                                                */
    *(--stk)  = (INT32U)0x10101010uL;            /* R10                                                */
    *(--stk)  = (INT32U)0x09090909uL;            /* R9                                                 */
    *(--stk)  = (INT32U)0x08080808uL;            /* R8                                                 */
    *(--stk)  = (INT32U)0x07070707uL;            /* R7                                                 */
    *(--stk)  = (INT32U)0x06060606uL;            /* R6                                                 */
    *(--stk)  = (INT32U)0x05050505uL;            /* R5                                                 */
    *(--stk)  = (INT32U)0x04040404uL;            /* R4                                                 */

    return (stk);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

上面的代码也不需要修改,其中初始化堆栈函数OSTaskStkInit中对寄存器R1-R12所赋的值没什么意义,取上面的值主要是为了方便调试,但xPSR第24位(即T位)需要置1,否则第一次执行任务时Fault。SYSTICK初始化函数用的寄存器操作,这里不依赖UCOS源码中的BSP部分,所以也不需要修改代码,但也可以使用库函数的形式实现该函数,实现代码如下:

// uCOS-II\Ports\os_cpu_c.c

void  OS_CPU_SysTickInit (void)
{
	RCC_ClocksTypeDef rcc_clocks;
	RCC_GetClocksFreq(&rcc_clocks);		//获取系统时钟
	SysTick_Config(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC);	//初始化并使能SysTick定时器
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

OS_CPU_SysTickInit函数可以使用原代码,也可以使用上面修改后的代码,需要注意两个函数的参数不一致,如果使用UCOS原代码,在调用该函数时需要传参,比如像下面这样:

RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
OS_CPU_SysTickInit(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC);
  • 1
  • 2
  • 3

到这里移植基本结束,实际上主要修改了两个异常处理函数名,使UCOS源码中的SysTick与PendSV异常处理函数名与ST固件库中断向量表中的函数名一致。接下来再看…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-II目录下的应用层配置文件:
UCOSⅡ配置文件
由于我们没有使用uC-CPU与uC-LIB,所以自然也不需要cpu_cfg.h与lib_cfg.h,在ST固件库移植时已经有一个stm32f10x_conf.h文件,这里不再使用该文件。其余文件功能介绍如下:

文件名功能描述
app_cfg.h主要定义任务优先级、任务堆栈等,由于应用不同,需要重写该文件
app_hooks.c定义一些钩子函数
app.c定义任务函数及main函数等,需要重写该文件
includes.h包含头文件,禁用LIBRARIES与APP / BSP部分,将ST包含头文件替换为stm32f10x.h
os_cfg.h条件编译选项,可以通过关闭某些编译选项裁剪内核

app_hooks.c与os_cfg.h可以不进行任何修改,includes.h则禁用LIBRARIES与APP / BSP部分的头文件且替换ST部分的头文件,剩下的就是重写app.c与app_cfg.h的代码了。我们继续使用上面验证ST固件库时的LED示例,创建两个任务分别用于管理LED0与LED1的亮灭,使用一个信号量事件来控制两个任务间的同步,示例代码如下:

// uCOS-II\Config\app_cfg.h

#ifndef  __APP_CFG_H__
#define  __APP_CFG_H__

/*
*********************************************************************************************************
*                                            TASK PRIORITIES
*********************************************************************************************************
*/

#define  APP_TASK_START_PRIO                        4

#define  OS_TASK_TMR_PRIO                   (OS_LOWEST_PRIO - 2)

#define	 LED0_TASK_PRIO       						7 
#define  LED1_TASK_PRIO       						6 
/*
*********************************************************************************************************
*                                            TASK STACK SIZES
*                             Size of the task stacks (# of OS_STK entries)
*********************************************************************************************************
*/

#define  APP_TASK_START_STK_SIZE                    128
#define  APP_CFG_TASK_LED_STK_SIZE                  128

#define  BUFF_SIZE                                  1000

#define LED0_STK_SIZE  		    					64
#define LED1_STK_SIZE  								64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
// uCOS-II\Config\app.c

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include 
#include 
/*
*********************************************************************************************************
*                                       LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/

static   OS_STK      AppTaskStartStk[APP_TASK_START_STK_SIZE];

OS_EVENT *led_sem;

OS_STK 				LED0_TASK_STK[LED0_STK_SIZE];
OS_STK 				LED1_TASK_STK[LED1_STK_SIZE];
/*
*********************************************************************************************************
*                                         FUNCTION PROTOTYPES
*********************************************************************************************************
*/
void LED_Init(void);

static  void  AppTaskCreate  (void);
static	void	AppEventCreate (void);

static  void  AppTaskStart   (void *p_arg);

void led0_task(void *pdata);
void led1_task(void *pdata);
/*
*********************************************************************************************************
*                                                main()
*
* Description : This is the standard entry point for C code.  It is assumed that your code will call
*               main() once you have performed all necessary initialization.
*
* Arguments   : none
*
* Returns     : none
*********************************************************************************************************
*/

int  main (void)
{
    LED_Init();

    OSInit();                                                   /* Initialize "uC/OS-II, The Real-Time Kernel"              */

	OSTaskCreate(AppTaskStart,(void *)0,(OS_STK *)&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],APP_TASK_START_PRIO );	/* Create the start task                                    */

    OSStart();                                                  /* Start multitasking (i.e. give control to uC/OS-II)       */
}


/*
*********************************************************************************************************
*                                          STARTUP TASK
*
* Description : This is an example of a startup task.  As mentioned in the book's text, you MUST
*               initialize the ticker only once multitasking has started.
*
* Arguments   : p_arg   is the argument passed to 'AppTaskStart()' by 'OSTaskCreate()'.
*
* Returns     : none
*
* Notes       : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
*                  used.  The compiler should not generate any code for this statement.
*********************************************************************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
	OS_CPU_SR cpu_sr=0;

  	RCC_ClocksTypeDef rcc_clocks;
	RCC_GetClocksFreq(&rcc_clocks);
	OS_CPU_SysTickInit(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC);

	OS_ENTER_CRITICAL();
#if (OS_TASK_STAT_EN > 0)
    OSStatInit();                                               /* Determine CPU capacity                                   */
#endif

    AppEventCreate();                                           /* Create Application Kernel objects                        */
    AppTaskCreate();                                            /* Create application tasks                                 */

    OSTaskSuspend(APP_TASK_START_PRIO);							/* Suspend the start task                                    */
	OS_EXIT_CRITICAL();
}


/*
*********************************************************************************************************
*                                      CREATE APPLICATION TASKS
*
* Description:  This function creates the application tasks.
*
* Arguments  :  none
*
* Returns    :  none
*********************************************************************************************************
*/

static  void  AppTaskCreate (void)
{
 	OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);						   
 	OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
}



/*
*********************************************************************************************************
*                                          AppEventCreate()
*
* Description : Create application kernel objects tasks.
*
* Argument(s) : none
*
* Return(s)   : none
*
* Caller(s)   : AppTaskStart()
*
* Note(s)     : none.
*********************************************************************************************************
*/

static void AppEventCreate(void)
{
	led_sem = OSSemCreate(0);
}

/*
*********************************************************************************************************
*                                         FUNCTION DEFINITION
*********************************************************************************************************
*/
void led0_task(void *pdata)
{	 	
	while(1)
	{		
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
		OSTimeDly(1000);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
		OSTimeDly(1000);
		
		OSSemPost(led_sem);
	};
}

void led1_task(void *pdata)
{
	INT8U err;
	
	while(1)
	{
		OSSemPend(led_sem,0,&err);
		
		GPIO_ResetBits(GPIOE,GPIO_Pin_5);
		OSTimeDly(500);
		GPIO_SetBits(GPIOE,GPIO_Pin_5);
		OSTimeDly(500);
	};
}

void LED_Init(void)
{
 
	GPIO_InitTypeDef  GPIO_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	    //使能PB,PE端口时钟
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;			  //LED0-->PB.5 端口配置
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 //推挽输出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //IO口速度为50MHz
  	GPIO_Init(GPIOB, &GPIO_InitStructure);			     //初始化GPIOB.5
  	GPIO_SetBits(GPIOB,GPIO_Pin_5);						//PB.5 输出高
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	        //LED1-->PE.5推挽输出
  	GPIO_Init(GPIOE, &GPIO_InitStructure);	  	       //初始化GPIO
  	GPIO_SetBits(GPIOE,GPIO_Pin_5); 			 		//PE.5 输出高 	  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

到这里就完成了UCOSⅡ的移植,同时也完成了测试例程的编写,编译成功无报错,下面给出PB.5与PE.5两个LED灯电平变化的仿真结果:
UCOS移植编译结果
UCOS移植仿真结果
从仿真结果看,两个任务工作正常,且信号量事件也起作用了,烧录到开发板中LED灯的亮灭效果跟预期一致,到这里UCOSⅡ的移植经验证没有问题。移植后的代码:https://github.com/StreamAI/UCOS_STM32

3. UCOSⅢ的移植

UCOSⅢ的移植比UCOSⅡ复杂些,主要是由于UCOSⅢ Source代码跟底层关系相对密切,移植UCOSⅢ需要将uC-CPU、uC-LIB一起移植,对比下UCOSⅡ与UCOSⅢ Source里面的头文件内容就可以看出来:

// uCOS-II\Config\includes.h
/*
*********************************************************************************************************
*                                       uC/OS-II VERSION NUMBER
*********************************************************************************************************
*/
#define  OS_VERSION                 29207u              /* Version of uC/OS-II (Vx.yy mult. by 10000)  */
/*
*********************************************************************************************************
*                                        INCLUDE HEADER FILES
*********************************************************************************************************
*/
#include 
#include 
#include 

// uCOSIII\Source\os.h
/*
************************************************************************************************************************
*                                               uC/OS-III VERSION NUMBER
************************************************************************************************************************
*/
#define  OS_VERSION  30301u                       /* Version of uC/OS-III (Vx.yy.zz mult. by 10000)                   */

/*
************************************************************************************************************************
*                                                 INCLUDE HEADER FILES
************************************************************************************************************************
*/
#include 
#include 
#include 
#include 
#include 
#include 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

UCOSⅢ下载网址跟UCOSⅡ基本一样,选择下载对象uC/OS-Ⅲ V3.03.01,目录结构跟UCOSⅡ也类似,不再详细解释。但UCOSⅢ支持的功能更丰富些,二者的性能对比如下:
UCOSⅢ与UCOSⅡ性能对比
下面给出Source系统各源文件的主要功能作用如下:
UCOSⅢ各源文件功能描述
打开Ports文件夹os_cpu.h文件查看PendSV与SysTick的异常处理函数名分别为OS_CPU_PendSVHandler与OS_CPU_SysTickHandler,跟前面UCOSⅡ的一致,所以对启动文件startup_stm32f10x_hd.s的修改也跟前面移植UCOSⅡ时一致。下面为了方便起见,使用UCOSⅢ定义的OS_CPU_SysTickInit函数,不再对其进行更改。也就是到目前为止,Source、Ports、uC-CPU、uC-LIB这四个文件夹均未做任何改动。

下面开始看…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III目录下的文件,UCOSⅢ的配置文件比UCOSⅡ也要多一些,目录如下:
UCOSⅢ应用层配置
上图一共10个C语言文件,除了stm32f10x_conf.h外(ST固件库已有该文件),其余的都添加到工程内,但app.c与app_cfg.h需要重写。includes.h文件修改跟前面移植UCOSⅡ时类似,禁用APP / BSP部分的头文件且替换ST部分的头文件,由于UCOSⅢ需要uC-CPU与uC-LIB的支持,所以includes.h内依然包含LIBRARIES部分的头文件。

下面依然使用验证UCOSⅡ移植结果的示例程序,但由于UCOSⅢ的API函数接口与UCOSⅡ存在较大差异,所以下面把重写后的app.c与app_cfg.h代码附上:

// uCOSIII\Config\app_cfg.h

#ifndef  __APP_CFG_H__
#define  __APP_CFG_H__

/*
*********************************************************************************************************
*                                            TASK PRIORITIES
*********************************************************************************************************
*/

#define  APP_TASK_START_PRIO                        2

#define	 LED0_TASK_PRIO       					7 
#define  LED1_TASK_PRIO       					6 
/*
*********************************************************************************************************
*                                            TASK STACK SIZES
*                             Size of the task stacks (# of OS_STK entries)
*********************************************************************************************************
*/

#define  APP_TASK_START_STK_SIZE                    128

#define  LED0_STK_SIZE  		    				64
#define  LED1_STK_SIZE  							64

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
// uCOSIII\Config\app.c

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include 
#include 
/*
*********************************************************************************************************
*                                       LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
OS_SEM led_sem;
/*
*********************************************************************************************************
*                                                 TCB
*********************************************************************************************************
*/

static  OS_TCB   AppTaskStartTCB;

static  OS_TCB   LED0TCB;
static  OS_TCB   LED1TCB;
/*
*********************************************************************************************************
*                                                STACKS
*********************************************************************************************************
*/

static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];

static  CPU_STK  LED0_TASK_STK[LED0_STK_SIZE];
static  CPU_STK  LED1_TASK_STK[LED1_STK_SIZE];
/*
*********************************************************************************************************
*                                         FUNCTION PROTOTYPES
*********************************************************************************************************
*/

static  void  AppTaskCreate (void);
static  void  AppObjCreate  (void);
static  void  AppTaskStart  (void *p_arg);

void LED_Init(void);
void led0_task(void *pdata);
void led1_task(void *pdata);
/*
*********************************************************************************************************
*                                                main()
*
* Description : This is the standard entry point for C code.  It is assumed that your code will call
*               main() once you have performed all necessary initialization.
*
* Arguments   : none
*
* Returns     : none
*********************************************************************************************************
*/

int  main (void)
{
    OS_ERR  err;

    LED_Init();

    OSInit(&err);                                               /* Init uC/OS-III.                                      */

    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                /* Create the start task                                */
                 (CPU_CHAR   *)"App Task Start",
                 (OS_TASK_PTR ) AppTaskStart,
                 (void       *) 0,
                 (OS_PRIO     ) APP_TASK_START_PRIO,
                 (CPU_STK    *)&AppTaskStartStk[0],
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
                 (OS_MSG_QTY  ) 5u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);

    OSStart(&err);                                              /* Start multitasking (i.e. give control to uC/OS-III). */
}


/*
*********************************************************************************************************
*                                          STARTUP TASK
*
* Description : This is an example of a startup task.  As mentioned in the book's text, you MUST
*               initialize the ticker only once multitasking has started.
*
* Arguments   : p_arg   is the argument passed to 'AppTaskStart()' by 'OSTaskCreate()'.
*
* Returns     : none
*
* Notes       : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
*                  used.  The compiler should not generate any code for this statement.
*********************************************************************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
    CPU_INT32U  cpu_clk_freq;
    CPU_INT32U  cnts;
    OS_ERR      err;
    (void)p_arg;
   
	CPU_SR_ALLOC();
    CPU_Init();
	
	RCC_ClocksTypeDef rcc_clocks;
	RCC_GetClocksFreq(&rcc_clocks);
    cpu_clk_freq = rcc_clocks.HCLK_Frequency;				             /* Determine SysTick reference freq.                    */
    cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;        /* Determine nbr SysTick increments                     */
    OS_CPU_SysTickInit(cnts);                                   /* Init uC/OS periodic time src (SysTick).              */

	OS_CRITICAL_ENTER();
    Mem_Init();                                                 /* Initialize Memory Management Module                  */
		
#if OS_CFG_STAT_TASK_EN > 0u
    OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

    //CPU_IntDisMeasMaxCurReset();
 
    AppObjCreate();												/* Create Application Objects                           */
	
    AppTaskCreate();                                            /* Create Application Tasks                             */
		
	OSTaskSuspend((OS_TCB*)&AppTaskStartTCB,&err);
    OS_CRITICAL_EXIT();
}


/*
*********************************************************************************************************
*                                      CREATE APPLICATION TASKS
*
* Description:  This function creates the application tasks.
*
* Arguments  :  none
*
* Returns    :  none
*********************************************************************************************************
*/

static  void  AppTaskCreate (void)
{
	OS_ERR  err;
	
	OSTaskCreate((OS_TCB     *)&LED0TCB,                /* Create the start task                                */
                 (CPU_CHAR   *)"LED0 Task",
                 (OS_TASK_PTR ) led0_task,
                 (void       *) 0,
                 (OS_PRIO     ) LED0_TASK_PRIO,
                 (CPU_STK    *)&LED0_TASK_STK[0],
                 (CPU_STK_SIZE) LED0_STK_SIZE / 10,
                 (CPU_STK_SIZE) LED0_STK_SIZE,
                 (OS_MSG_QTY  ) 5u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);
								 
	OSTaskCreate((OS_TCB     *)&LED1TCB,                /* Create the start task                                */
                 (CPU_CHAR   *)"LED1 Task",
                 (OS_TASK_PTR ) led1_task,
                 (void       *) 0,
                 (OS_PRIO     ) LED1_TASK_PRIO,
                 (CPU_STK    *)&LED1_TASK_STK[0],
                 (CPU_STK_SIZE) LED1_STK_SIZE / 10,
                 (CPU_STK_SIZE) LED1_STK_SIZE,
                 (OS_MSG_QTY  ) 5u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);
}


/*
*********************************************************************************************************
*                                      CREATE APPLICATION EVENTS
*
* Description:  This function creates the application kernel objects.
*
* Arguments  :  none
*
* Returns    :  none
*********************************************************************************************************
*/

static  void  AppObjCreate (void)
{
	OS_ERR  err;
	
	OSSemCreate((OS_SEM*)&led_sem,
				(CPU_CHAR*)"LED SEM",
				(OS_SEM_CTR)0,
				(OS_ERR*)&err);
}

/*
*********************************************************************************************************
*                                         FUNCTION DEFINITION
*********************************************************************************************************
*/

void led0_task(void *pdata)
{
	OS_ERR err;
	
	while(1)
	{		
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
		OSTimeDly(1000,OS_OPT_TIME_DLY,&err);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
		OSTimeDly(1000,OS_OPT_TIME_DLY,&err);
		
		OSSemPost(&led_sem,OS_OPT_POST_1,&err);
	};
}

void led1_task(void *pdata)
{
	OS_ERR err;
	
	while(1)
	{
		OSSemPend(&led_sem,0,OS_OPT_PEND_BLOCKING,(CPU_TS *)0,&err);
		
		GPIO_ResetBits(GPIOE,GPIO_Pin_5);
		OSTimeDly(500,OS_OPT_TIME_DLY,&err);
		GPIO_SetBits(GPIOE,GPIO_Pin_5);
		OSTimeDly(500,OS_OPT_TIME_DLY,&err);
	};
}

void LED_Init(void)
{
 
	GPIO_InitTypeDef  GPIO_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	    //使能PB,PE端口时钟
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;			  //LED0-->PB.5 端口配置
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 //推挽输出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //IO口速度为50MHz
  	GPIO_Init(GPIOB, &GPIO_InitStructure);			     //初始化GPIOB.5
  	GPIO_SetBits(GPIOB,GPIO_Pin_5);						//PB.5 输出高
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	        //LED1-->PE.5推挽输出
  	GPIO_Init(GPIOE, &GPIO_InitStructure);	  	       //初始化GPIO
  	GPIO_SetBits(GPIOE,GPIO_Pin_5); 			 		//PE.5 输出高 	  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257

编译结果如下:
UCOSⅢ移植编译结果
编译报了两个错误,分别是未定义的CPU_TS_TmrRd与CPU_TS_TmrInit符号,这是两个跟时间戳相关的函数名,都定义在bsp.c中。有两种方法解决该问题:一种是移植bsp.c与bsp.h文件,里面的大部分内容要删除,只保留跟CPU_TS_TmrRd与CPU_TS_TmrInit相关的代码,该方案经验证移植后运行正常仿真报错;另一种是在os_cfg.h中关闭跟时间戳相关的编译选项,这就不用再移植bsp.c与bsp.h文件了,这里采用这第二种方案,关闭编译选项的配置如下:

// uCOSIII\Config\os_cfg.h中关闭的编译选项

#define OS_CFG_TS_EN                    0u   /* Enable (1) or Disable (0) time stamping                               */
#define OS_CFG_SCHED_LOCK_TIME_MEAS_EN  0u   /* Include code to measure scheduler lock time                           */

// uCOSIII\Config\cpu_cfg.h中关闭的宏定义
/*
*********************************************************************************************************
*                                     CPU TIMESTAMP CONFIGURATION
*********************************************************************************************************
*/

                                                                /* Configure CPU timestamp features (see Note #1) :     */
#define  CPU_CFG_TS_32_EN                       DEF_DISABLED
#define  CPU_CFG_TS_64_EN                       DEF_DISABLED
                                                                /*   DEF_DISABLED  CPU timestamps DISABLED              */
                                                                /*   DEF_ENABLED   CPU timestamps ENABLED               */
/*
*********************************************************************************************************
*                        CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION
*********************************************************************************************************
*/

#if 0                                                           /* Configure CPU interrupts disabled time ...           */
#define  CPU_CFG_INT_DIS_MEAS_EN                                /* ... measurements feature (see Note #1a).             */
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

两个文件中与时间戳相关的编译选项关闭后,重新编译没有报错:
UCOSⅢ移植编译结果
仿真结果如下图示:
UCOSⅢ移植仿真结果
仿真结果跟预期一致,运行结果也跟预期一致,多任务调度与信号量机制都正常工作了,UCOSⅢ的移植经验证没有问题。移植后的代码:https://github.com/StreamAI/UCOS_STM32

下图中的前两个目录分别是UCOSⅡ与UCOSⅢ针对STM32F107的官方源码,直接从Micrium官网下载后未经过任何更改;中间三个目录分别是STM32、UCOSⅢ与UCOSⅡ移植后并经示例程序验证成功的工程源码;最后一个压缩包是STM32F10x的固件库文件,从ST官网下载后删除了占空间较大的chm帮助文件,其余文件未经任何更改。
STM32_UCOS移植源码目录

更多文章:

  • 《RTOS之UCOS(六)—系统启动与固件移植》
  • 《UCOS_STM32 Porting Source Code from GitHub》
  • 《30天自制操作系统》
  • 《IOT-OS之RT-Thread(一)—系统启动与移植过程》
注:本文转载自blog.csdn.net的流云IoT的文章"https://blog.csdn.net/m0_37621078/article/details/93783606"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top