跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)

     有了第一段的基础,接下来第二段的实战应该上手就会比较的快。第一段的讲解是不依赖于任何实际的硬件板子的,具体的实验现象都通过 K e i l M D K Keil\quad MDK KeilMDK自带的仿真器来仿真实现来看结果,既然是实战那么从第二段开始就要依赖于实际的硬件板子,我这里使用的是正点原子的 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6的精英开发板,如图1所示。 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6这一款芯片是基于 A R M − C o r t e x − M 3 ARM-Cortex-M3 ARMCortexM3内核的。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第1张图片
图1.

     有了实际的硬件板子,我们再来看看 F r e e R T O S FreeRTOS FreeRTOS的源码,源码直接去官网:https://www.freertos.org/下载就好,点击图2中的 D o w n l o a d F r e e R T O S Download\quad FreeRTOS DownloadFreeRTOS就会出现源码的下载页面,如图3所示。从跟着野火学FreeRTOS:第一段(基础介绍)这里的介绍我们可以了解到 F r e e R T O S FreeRTOS FreeRTOS除了最基本的内核,其它对于像 T C P / I P TCP/IP TCP/IP协议栈和文件系统等额外功能都是以库的形式存在,如图4所示。所以我们在下载的时候,不要下载错了,我们这里只需要 F r e e R T O S FreeRTOS FreeRTOS的内核源码就可以了,至于其它功能协议的代码,有需要或者有时间的时候再去研究。

 
图2.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第2张图片
图3.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第3张图片
图4.

     点击图3中的 D o w n l o a d Download Download之后解压我们会得到如图5所示的文件,这里我们只需要图5中红圈中的相应文件就可以了,这个文件夹里面就是 F r e e R T O S FreeRTOS FreeRTOS的内核的源码,如图6所示。图5里面 F r e e R T O S − P l u s FreeRTOS-Plus FreeRTOSPlus这个文件夹里面也是前面提到的用于某种特定应用的库。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第4张图片
图5.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第5张图片
图6.

     图6里面 D e m o Demo Demo这个文件夹里面放的是针对特定的处理器架构的 F r e e R T O S FreeRTOS FreeRTOS应用的代码的例子,可以方便我们参考以便快速的将 F r e e R T O S FreeRTOS FreeRTOS应用在我们的产品上,如图7所示。图6里面 L i c e n s e License License这个文件夹里面放的是许可说明,商业使用 F r e e R T O S FreeRTOS FreeRTOS的时候需要重点阅读一下这个文件,我们这里可以略过了。我们重点需要的是图6里面 S o u r c e Source Source这个文件夹里面的源代码。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第6张图片
图7.

     图8里面 i n c l u d e include include这个文件夹里面的所有头文件,我们这里都需要,还有 S o u r c e Source Source文件夹里面的 x x x . c xxx.c xxx.c文件我们这里也需要。图8里面 p o r t a b l e portable portable这个文件夹里面的文件需要根据自己所使用的芯片的架构来选择。如图9所示。我们这里使用的是 k e i l keil keil进行开发的,因此看看图9里面的 k e i l keil keil文件夹。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第7张图片
图8.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第8张图片
图9.

      k e i l keil keil文件夹里面的内容,如图10所示,显示让我们看看图9里面的 R V D S RVDS RVDS文件夹。 R V D S RVDS RVDS文件夹里面的内容如图11所示,因为 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6这一款芯片是基于 A R M − C o r t e x − M 3 ARM-Cortex-M3 ARMCortexM3内核的,因此我们需要文件夹 A R M _ C M 3 ARM\_CM3 ARM_CM3里面的文件,如图12所示。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第9张图片
图10.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第10张图片
图11.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第11张图片
图12.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第12张图片
图13.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第13张图片
图14.

     同时我们还需要用到图8里面 p o r t a b l e portable portable这个文件夹里面的 M e m M a n a g MemManag MemManag文件夹里面的内容,这个文件夹里面有5个类似的 x x x . c xxx.c xxx.c文件,如图13所示,这些文件里面的接口是用来做堆内存管理的,详细请看图14中的介绍。至于选择那一个文件需要根据自己的需求来定,野火这里选择的是 h e a p 4 . c heap_4.c heap4.c文件。
     到此我们使用 F r e e R T O S FreeRTOS FreeRTOS内核所需要的源码包括图8里面 i n c l u d e include include这个文件夹里面的所有头文件, S o u r c e Source Source文件夹里面的 x x x . c xxx.c xxx.c文件,图12里面的两个 x x x . c xxx.c xxx.c x x x . h xxx.h xxx.h文件以及图13里面的 h e a p 4 . c heap_4.c heap4.c文件。有了这些源文件之后我们最后还需要一个 x x x . h xxx.h xxx.h文件, F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h,这个文件在 S o u r c e Source Source文件夹里面是没有的,在图6里面 D e m o Demo Demo这个文件夹里面针对某种架构的示例工程中可以找到这个文件,如图15所示。图15中的 F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h这个文件的部分内容如图16所示,从这里可以看出来,这个文件 S o u r c e Source Source文件夹里面没有的原因是这个文件是针对特定硬件和应用的,需要根据自己的需求来生成这个头文件,这个头文件中相当于是定义了 F r e e R T O S FreeRTOS FreeRTOS内核源码的一些参数,这些参数的具体值需要根据自己的应用需求以及所使用的芯片来决定,定义这些参数的主要是一些宏定义,至于这些宏定义的具体含义我这里就不讲解了,具体可以看看野火的说明,我这里为了方便直接使用野火的这个头文件。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第14张图片
图15.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第15张图片
图16.

     有了以上这些源文件我们新建一个 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6的基本工程并将这些 F r e e R T O S FreeRTOS FreeRTOS的源文件包含进去就算完了代码移植的基本工作了,工程新建好并移植完成的工程视图如图17所示。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第16张图片
图17.

     接下来我们将实际的创建任务并跑跑看看,但是首先我们需要先将 s t m 32 f 10 x _ i t . c stm32f10x\_it.c stm32f10x_it.c这个文件里面的 S V C a l l , P e n d S V SVCall,PendSV SVCallPendSV S y s T i c k SysTick SysTick的中断处理函数的定义给删除了,因为它们已经在 p o r t . c port.c port.c这个 F r e e R T O S FreeRTOS FreeRTOS的源文件中定义且实现功能了,只是名字和 s t a r t u p _ s t m 32 f 10 x _ h d . s startup\_stm32f10x\_hd.s startup_stm32f10x_hd.s里面的定义不一样而已,我们用宏定义在 F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h重新定义一下就可以了,如下所示。但是野火的代码只删除了 s t m 32 f 10 x _ i t . c stm32f10x\_it.c stm32f10x_it.c这个文件里面的 S V C a l l SVCall SVCall P e n d S V PendSV PendSV的中断处理函数,而没有删除 S y s T i c k SysTick SysTick的中断处理函数,而是对 S y s T i c k SysTick SysTick的中断处理函数做了一定外包处理,在 F r e e R T O S FreeRTOS FreeRTOS调度器启动之后才调用 F r e e R T O S FreeRTOS FreeRTOS的源文件 p o r t . c port.c port.c中有关 S y s T i c k SysTick SysTick的中断处理相关的操作,有兴趣的可以去看一看野火的代码。我觉得这里没有必要,就直接把 s t m 32 f 10 x _ i t . c stm32f10x\_it.c stm32f10x_it.c这个文件里面的 S y s T i c k SysTick SysTick的中断处理函数也删除了并在 F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h里面做了重定义,如果后面确实有需要的话可以再改回来。

/*
   SVCall handler,PendSV handler and SysTick handler redefinition
*/
#define xPortPendSVHandler 	   PendSV_Handler
#define vPortSVCHandler 	     SVC_Handler
#define xPortSysTickHandler 	 SysTick_Handler

     接下来我们就开始创建任务了,一个任务相关的主体主要有任务函数,永不返回且为无限循环,任务控制块和任务堆栈。任务控制块和任务堆栈都是需要内存存储空间的,因此任务创建的接口主要分为静态的和动态的创建接口。对于静态的创建接口,任务的任务控制块和堆栈空间使用的存储空间是由程序员自己分配好的。对于动态的创建接口,任务的任务控制块和堆栈空间使用的存储空间是自动分配并释放的。对于接口的具体细节我这里就不细讲了,大家可以看一下 F r e e R T O S FreeRTOS FreeRTOS的源代码中的注释以及 《 F r e e R T O S R e f e r e n c e M a n u a l 》 《FreeRTOS\quad Reference\quad Manual》 FreeRTOSReferenceManual,如图18,图19和图20所示。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第17张图片
图18.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第18张图片
图19.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第19张图片
图20.

     这里首先使用下面的静态的任务创建接口,创建了一个闪灯的简单任务,然后开启调度器(调用接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler里)让这个任务去运行。在使用下面这个静态任务创建接口的时候必须在 F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h中定义宏 c o n f i g S U P P O R T _ S T A T I C _ A L L O C A T I O N configSUPPORT\_STATIC\_ALLOCATION configSUPPORT_STATIC_ALLOCATION的值为1,正因为如此,如图21所示,我们必须自己实现接口 v A p p l i c a t i o n G e t I d l e T a s k M e m o r y vApplicationGetIdleTaskMemory vApplicationGetIdleTaskMemory(在接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler里面会调用),不然编译的时候会报错,这个接口比较简单,也就是返回空闲任务的任务控制块地址,任务堆栈地址以及任务堆栈的大小。宏 c o n f i g S U P P O R T _ S T A T I C _ A L L O C A T I O N configSUPPORT\_STATIC\_ALLOCATION configSUPPORT_STATIC_ALLOCATION的值为1的时候,空闲任务的创建,在接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler里面创建,也是采用的静态创建接口,因此我们也必须要自己先定义空闲任务的任务控制块,任务堆栈以及大小。同理如果在 F r e e R T O S C o n f i g . h FreeRTOSConfig.h FreeRTOSConfig.h中定义宏 c o n f i g U S E _ T I M E R S configUSE\_TIMERS configUSE_TIMERS的值为1,那么我们也必须自己实现接口 v A p p l i c a t i o n G e t T i m e r T a s k M e m o r y vApplicationGetTimerTaskMemory vApplicationGetTimerTaskMemory以及定义相关的任务控制块和堆栈空间,如图22所示。野火这里有实现接口 v A p p l i c a t i o n G e t T i m e r T a s k M e m o r y vApplicationGetTimerTaskMemory vApplicationGetTimerTaskMemory以及定义相关的任务控制块和堆栈空间,但是我的代码没有实现,因为暂时还没有用到相关的接口,等后面用到的时候再说。

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                              const char *pcName,
                              uint32_t ulStackDepth,
                              void *pvParameters,
                              UBaseType_t uxPriority,
                              StackType_t *puxStackBuffer,
                              StaticTask_t *pxTaskBuffer );

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第20张图片
图21.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第21张图片
图22.

     本来这里直接调用接口 x T a s k C r e a t e S t a t i c xTaskCreateStatic xTaskCreateStatic创建 L E D LED LED闪烁的任务然后调用接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler启动调度器就可以了,但是野火的代码专门创建了一个任务来创建 L E D LED LED闪烁的任务, L E D LED LED闪烁的任务创建完毕之后它又把这个创建 L E D LED LED闪烁的任务的任务给删除了,不知这里是多此一举还是为了方便什么,后面再看。这一部分的工程代码在这里。
     接下来我们将使用下面的动态创建任务的接口来创建任务,这里必须将宏 c o n f i g S U P P O R T _ D Y N A M I C _ A L L O C A T I O N configSUPPORT\_DYNAMIC\_ALLOCATION configSUPPORT_DYNAMIC_ALLOCATION定义为1。同时这里为了简便我们将宏 c o n f i g S U P P O R T _ S T A T I C _ A L L O C A T I O N configSUPPORT\_STATIC\_ALLOCATION configSUPPORT_STATIC_ALLOCATION的值为0,这样我们就不用去实现接口 v A p p l i c a t i o n G e t I d l e T a s k M e m o r y vApplicationGetIdleTaskMemory vApplicationGetIdleTaskMemory了。动态创建接口和静态创建接口的最大区别是不需要程序员自己去给任务的任务控制块和堆栈提前分配空间(通常是全局变量的形式),任务的任务控制块和堆栈所占用的空间是自动分配和释放的。使用动态创建任务的接口的时候自动分配和释放的空间来自于 F r e e R T O S FreeRTOS FreeRTOS系统中叫做堆的空间,这部分空间的大小由宏 c o n f i g T O T A L _ H E A P _ S I Z E configTOTAL\_HEAP\_SIZE configTOTAL_HEAP_SIZE定义,在代码中就是一个字节数组,如图23所示。这部分空间在分配和释放的时候分别使用接口 p v P o r t M a l l o c pvPortMalloc pvPortMalloc v P o r t F r e e vPortFree vPortFree,这两个接口应该和 C C C语言里面的接口 m a l l o c malloc malloc f r e e free free类似,还有就是在系统中第一次调用接口 p v P o r t M a l l o c pvPortMalloc pvPortMalloc的时候会调用接口 p r v H e a p I n i t prvHeapInit prvHeapInit对分配的这部分堆空间进行初始化。

    BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, 
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第22张图片
图23.

     这里我们利用接口 x T a s k C r e a t e xTaskCreate xTaskCreate创建了两个任务分别对硬件板子上面的两个 L E D LED LED进行闪灯操作,一个任务对一个 L E D LED LED灯进行每 500 m s 500ms 500ms闪烁一次的操作,另一个任务对另一个 L E D LED LED灯进行每 2 s 2s 2s闪烁一次的操作,这一部分的工程代码在这里。
     接下来野火讲解了目前 F r e e R T O S FreeRTOS FreeRTOS常见的两种启动流程,分别如下面的伪代码所示。 / ∗ S t a r t P r o c e d u r e 1 ∗ / /* Start \quad Procedure\quad 1*/ /StartProcedure1/先对系统运行所需的所有硬件进行初始化,然后初始化 F r e e R T O S FreeRTOS FreeRTOS系统,接着一个接一个的创建所需的所有任务最后启动调度器开始各个任务的运行。 / ∗ S t a r t P r o c e d u r e 2 ∗ / /* Start \quad Procedure\quad 2*/ /StartProcedure2/先对系统运行所需的所有硬件进行初始化,然后初始化 F r e e R T O S FreeRTOS FreeRTOS系统,接着运行创建任务的任务,这个任务是专门用来创建系统中所需的所有任务的,这个任务运行结束之后,系统所需的所有任务也就是创建完毕了,因此此时这个创建任务的任务也就不需要了,直接删除,在前面的例子中我们使用的就是这种启动流程。 / ∗ S t a r t P r o c e d u r e 1 ∗ / /* Start \quad Procedure\quad 1*/ /StartProcedure1/是把所有的任务创建完毕之后才启动调度器开始系统的运行, / ∗ S t a r t P r o c e d u r e 2 ∗ / /* Start \quad Procedure\quad 2*/ /StartProcedure2/创建了任务创建任务之后就开启调度器开启系统的运行了,也就是说此时有些系统所需的任务还没有创建,系统就已经开始运行了。

/* Start Procedure 1*/
int main (void)
{
    /* HardWare Initialization*/
    HardWare_Init(); 

    /* RTOS Initialization*/
    RTOS_Init(); 

    /* Create task 1 */ 
    RTOS_TaskCreate(Task1);
    /* Create task 2 */ 
    RTOS_TaskCreate(Task2);
    /* Create other tasks */

    /* Start RTOS,start scheduler */
    RTOS_Start();  
}

void Task1( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}

void Task2( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}

void TaskN( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}
/* Start Procedure 2*/
int main (void)
{
    /* HardWare Initialization*/
    HardWare_Init(); 

    /* RTOS Initialization*/
    RTOS_Init(); 

    /* Create the task creation task for creating all other tasks */ 
    RTOS_TaskCreate(AppTaskCreate);

    /* Start RTOS,start scheduler */
    RTOS_Start();  
}

/* The task creation task for creating all other tasks */
void AppTaskCreate( void *arg )
{
    /* Create task 1 */ 
    RTOS_TaskCreate(Task1);
    /* Create task 2 */ 
    RTOS_TaskCreate(Task2);
    /* Create other tasks */

    /* Delete the task creation task when all other tasks have been created */
    RTOS_TaskDelete(AppTaskCreate);
}



void Task1( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}

void Task2( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}

void TaskN( void *arg ) 
{
    while (1)
    {
        /* ...  */
    }
}

     还有就是 F r e e R T O S FreeRTOS FreeRTOS相对于其它的实时操作系统是比较方便的,不用程序员自己去做大量的初始化工作。这里在调用接口 x T a s k C r e a t e xTaskCreate xTaskCreate创建任务和调用接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler启动调度器的时候就会自己调用相应的接口进行初始化的工作。如图24(前面提到过系统中第一次调用接口 p v P o r t M a l l o c pvPortMalloc pvPortMalloc的时候会调用接口 p r v H e a p I n i t prvHeapInit prvHeapInit对系统的堆空间进行初始化)和图25所示。

  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第23张图片
图24.
  跟着野火学FreeRTOS:第二段(代码移植+任务创建+启动流程)_第24张图片
图25.

你可能感兴趣的:(stm32,单片机,arm)