FreeRTOS学习笔记(创建精简FreeRTOS)

1、去FreeRTOS官网下载第一个选项的项目(里面带有demo)

2、删除多余的文件

        

3、按照目录格式进行删减

其他的项目均可以删除掉,只保留一个f103的文件

可以注意下面的目录,可以参照下面的连接。(12条消息) 韦东山freeRTOS系列教程之【第一章】FreeRTOS概述与体验_韦东山的博客-CSDN博客_freertos教程

按照下面的文件形式进行精简就可以。

下图中的文件时FreeRTOS的核心文件

 下图中的RVDS中的文件支持很多的架构,可以根据不同的芯片架构进行选择。

 4、打开demo并将项目从keil4更新到keil5

打开之后点击下图所示的部分就可以更新到keil5 ,然后将项目关闭之后,在从新打开。

下面的图时补充上面文件中的common中的文件可以删除精简的部分。

 

然后打开项目进行编译之后,就会编译通过没有错误。

5、source insight的使用

 首先是破解版的软件的安装,可以参考下面的连接中的内容:(12条消息) source insight4.0破解版下载及使用方法_ych9527的博客-CSDN博客

 安装好软件之后,就可以在需要阅读的代码文件夹中创建一个相应的项目,然后添加对应的工程就可以了,操作如下:

首先添加项目

给对应的项目起名并且设置路径地址:

将所有的项目全部添加到项目中,点击“Add Tree”:

 

打开FreeRTOS的demo编译之后,是没有错误的,但是项目中有很多的文件是使用不到的,所以需要进行删除,来精简代码,删除之后的项目文件如下图所示:

 

删除不使用的代码之后,在进行编译就会产生很多的错,这时候只需要找到对用的错误的位置,将对应行的代码进行删除就可以了。

注意:使用source insight的时候,不能出现中文的路径,不然会出现不能查找相应的代码文件的问题(比如查找*.s 文件的时候)

在source insight中修改代码之后,保存之后,在进入keil就可以看到keil中相应的代码已经进行修改。选中代码之后点击“F8”之后,就可以对相应的部分同时进行高亮,就可以对比着看。

学习的时候可以使用下面网站中的学习手册:1.1 FreeRTOS目录结构 — 韦东山百问网freeRTOS教程 文档 (100ask.net)

6、静态任务的创建和动态任务的创建

        动态任务的创建,使用xTaskCreate()函数进行创建,他的栈内存都是动态分配的,不需要给他分配静态的内存。

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )

        静态任务的创建,使用xTaskCreateStatic()函数进行创建的时候,就需要分配静态的内存,可以定义一个全局变量的数组。

    TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                    const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                    const uint32_t ulStackDepth,
                                    void * const pvParameters,
                                    UBaseType_t uxPriority,
                                    StackType_t * const puxStackBuffer,
                                    StaticTask_t * const pxTaskBuffer )

对比可以发现多了:StackType_t * const puxStackBuffer这个静态栈的入口参数。

StackType_t test2_satck[100];
StackType_t xidetest2_satck[100];
TaskHandle_t test_hander;
StaticTask_t test2_hander;
StaticTask_t xidetest2_hander;




void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
    *ppxIdleTaskTCBBuffer = &xidetest2_hander;
    *ppxIdleTaskStackBuffer = xidetest2_satck;
    *pulIdleTaskStackSize = 100;
}
static void vCheckTask( void *pvParameters );
static void test( void *pvParameters )
{
	while(1)
	{
	   printf("1");
	}
}
static void test2( void *pvParameters )
{
	while(1)
	{
	   printf("2");
	}
}

static void vCheckTask( void *pvParameters )
{
extern unsigned short usMaxJitter;

  for( ;; )
	{
		printf("3");
	}
}


int main( void )
{
#ifdef DEBUG
  debug();
#endif
	prvSetupHardware();
	xTaskCreate( vCheckTask, "Check", mainCHECK_TASK_STACK_SIZE, NULL, mainCHECK_TASK_PRIORITY, NULL );
	xTaskCreate( test, "test_1", 100, NULL, 3, &test_hander );
	xTaskCreateStatic(test2,"test_2",100,NULL, 3, test2_satck,&test2_hander);
	printf("daoyuzuishuai\r\n");
	vTaskStartScheduler();
	return 0;
}

      上面的代码关于静态创建任务函数,到xTaskCreateStatic()函数定义的地方会发现需要打开宏定义,并且定义vApplicationGetIdleTaskMemory()函数;


#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

    TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                    const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                    const uint32_t ulStackDepth,
                                    void * const pvParameters,
                                    UBaseType_t uxPriority,
                                    StackType_t * const puxStackBuffer,
                                    StaticTask_t * const pxTaskBuffer )
    {
        TCB_t * pxNewTCB;
        TaskHandle_t xReturn;

        configASSERT( puxStackBuffer != NULL );
        configASSERT( pxTaskBuffer != NULL );

        #if ( configASSERT_DEFINED == 1 )
            {
                /* Sanity check that the size of the structure used to declare a
                 * variable of type StaticTask_t equals the size of the real task
                 * structure. */
                volatile size_t xSize = sizeof( StaticTask_t );
                configASSERT( xSize == sizeof( TCB_t ) );
                ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */
            }
        #endif /* configASSERT_DEFINED */

在FreeRTOS.h文件中可以开启对应的宏:


#ifndef configUSE_POSIX_ERRNO
    #define configUSE_POSIX_ERRNO    0
#endif

#ifndef portTICK_TYPE_IS_ATOMIC
    #define portTICK_TYPE_IS_ATOMIC    0
#endif

#ifndef configSUPPORT_STATIC_ALLOCATION
    /* Defaults to 0 for backward compatibility. */
    #define configSUPPORT_STATIC_ALLOCATION    1
#endif

#ifndef configSUPPORT_DYNAMIC_ALLOCATION
    /* Defaults to 1 for backward compatibility. */
    #define configSUPPORT_DYNAMIC_ALLOCATION    1
#endif

7、调度与优先级的关系

        同优先级的任务交叉执行,高优先级的任务先执行,高优先级的任务没有放弃执行的时候,低优先级的任务是不能执行的。

        任务函数可以删除自己vTaskDelet(NULL)就是删除自己,删除其他的任务,只需要传入句柄就可以了。

        使用同一个任务函数是可以创建多个任务的,他们之间互不影响,因为分配的栈是不同的。但是名字不能相同 ,任务创建函数下的双引号的入口参数就是任务的名字。

        在创建函数的时候分配的栈的字节数=分配的入口参数*4(单位byte)(32位的处理器),在任务函数中使用内存的时候,不能超过分配栈的大小,否的会将任务函数的头数据给覆盖掉(栈时向上增长的)。

8、任务的状态和切换时间

        前面以及说过,相同优先级的任务之间的执行是相互切换执行的,任务就切换的时间大概是1ms,这是因为FreeRTOS是靠滴答定时器(或者其他定时器)作为时钟心脏频率的,这个定时器设置的时间大概是1ms,可以在头文件FreeRTOSConfig.h中进行配置,配置的代码如下图所示:

代码中的1000hz就是1ms时钟配置的代码。


#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION		1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )	
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES		( 5 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN		( 16 )
#define configUSE_TRACE_FACILITY	0
#define configUSE_16_BIT_TICKS		0
#define configIDLE_SHOULD_YIELD		1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */


/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

#endif /* FREERTOS_CONFIG_H */

在FreeRTOS中任务有四种状态:running(运行状态)、ready(准备运行状态)、blocked(阻塞状态:等待某事)、suspencled(暂停或者挂起状态:主动或者被动休息)。

        各个任务之间的调用以及状态的设置,使用的是链表的知识,四个状态会有四个对应的链表,将不同状态的任务添加到对应状态的链表中。

9、延时函数

       vTaskDelay(100);是延时函数,延时级别位ms(毫秒)级别。   可以保证每次休眠的时间是相同的。可以理解为绝对延时函数

        xTaskDelayUntil(TickType_t * const pxPreviousWakeTime,
                                const TickType_t xTimeIncrement);这个延时函数是在在pxPreviousWakeTime时间开始延时,延时持续xTimeIncrement长时间,并且在延时之后会自动更新函数的入口参数pxPreviousWakeTime+=xTimeIncrement;这样就可以保证延时的时间包括函数执行的时间和延时的时间,

                xTimeIncrement=函数运行时间+函数不运行的延时时间,可以理解为相对延时函数。                        

10、空闲任务钩子函数

        这里从一个示例引出钩子函数的作用 ,比如使用vTaskDelet(NULL)自杀的时候,任务函数自己是不能自己释放自己的内存的(也就是自己死了之后,自己是不能给自己收尸的),如果不进行释放的话,堆栈的内存就会被浪费占用,那么就需要其他任务函数调用内存释放自杀的任务函数(也就是自己死了之后,需要别人帮忙收尸),这时候可以在空闲任务中释放自杀的任务函数的内存,空闲的任务函数是在vTaskStartScheduler();中进行创建的。

 xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
                                                 configIDLE_TASK_NAME,
                                                 ulIdleTaskStackSize,
                                                 ( void * ) NULL,       /*lint !e961.  The cast is not redundant for all compilers. */
                                                 portPRIVILEGE_BIT,     /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                                 pxIdleTaskStackBuffer,
                                                 pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

        创建的空闲任务函数,static portTASK_FUNCTION( prvIdleTask, pvParameters ),这里面有很多其他重要的任务需要处理,官方为了不让用户随便修改导致出问题,就提供了一个钩子函数,在这里里面进行调用,用户通过编写钩子函数来解决一些用户问题,但是钩子函数中的用户代码要越短,越简洁越好。


        #if ( configUSE_IDLE_HOOK == 1 )
            {
                extern void vApplicationIdleHook( void );

                /* Call the user defined function from within the idle task.  This
                 * allows the application designer to add background functionality
                 * without the overhead of a separate task.
                 * NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
                 * CALL A FUNCTION THAT MIGHT BLOCK. */
                vApplicationIdleHook();
            }
        #endif /* configUSE_IDLE_HOOK */

钩子函数的作用有这些:

              1、  执行一些低优先级的、后台的、需要连续执行的函数
               2、 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
                3、让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式了。


空闲任务的钩子函数的限制:

                1、不能导致空闲任务进入阻塞状态、暂停状态
                2、如果你会使用vTaskDelete()来删除任务,那么钩子函数要非常高效地执行。如果空闲任务移植卡在钩子函数里的话,它就无法释放内存。

11、任务函数的调度算法

        高优先级的任务没有执行完成前,其他低优先级的任务函数是不能执行的,也即是说:高优先级的任务函数中的死循环中如果移植在执行任务,那么其他的任务区函数就不能执行了,这时候只需要在高优先级的任务函数中添加延时函数(添加延时函数进行休眠,如果不休眠的话,其他的任务无法得到执行)。这也叫做“抢占调度模式”

        通过配置文件FreeRTOSConfig.h的两个配置项来配置调度算法:configUSE_PREEMPTION、configUSE_TIME_SLICING。

还有第三个配置项:configUSE_TICKLESS_IDLE,它是一个高级选项,用于关闭Tick中断来实现省电,后续单独讲解。现在我们假设configUSE_TICKLESS_IDLE被设为0,先不使用这个功能。

        下面代码就是FreeRTOSConfig.h头文件中的宏代码:

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION		1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )	
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES		( 5 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN		( 16 )
#define configUSE_TRACE_FACILITY	0
#define configUSE_16_BIT_TICKS		0
#define configIDLE_SHOULD_YIELD		1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */


/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

#endif /* FREERTOS_CONFIG_H */

        当关闭允许抢占的宏的时候,程序开始运行的时候,就会首先执行优先级高的任务函数,执行完之后去执行任务优先级低的函数,如果任务优先级低的任务函数没有添加延时函数进行休眠,那么就不会在回来执行任务优先级高的任务函数了,因为这时候任务优先级高的任务函数并不能抢占任务优先级低的任务函数。

关于这个头文件的配置的解释中可以看到一个网站,进入这个网站(FreeRTOS - The Free RTOS configuration constants and configuration options - FREE Open Source RTOS for small real time embedded systems)之后采用浏览器翻译,就可以看到相应的宏的使用配置文件了。

比如:

configUSE_PREEMPTION

设置为 1 以使用抢占式 RTOS 调度程序,或设置为 0 以使用协作式 RTOS 调度程序。

configUSE_TICKLESS_IDLE

将 configUSE_TICKLESS_IDLE 设置为 1 以使用低功耗无滴答声模式,或将 0 设置为始终保持逐笔报价中断运行。并非所有自由转场端口都提供低功耗无滴答声实现。

configUSE_IDLE_HOOK

如果希望使用空闲挂钩,请设置为 1,如果省略空闲挂钩,则设置为 0。

时间片轮转的宏

        也就是当多个低优先级的任务没有延时函数作为休眠的时候,最高优先级的函数有延时函数作为休眠,最高优先级的函数抢占低优先级的函数之后,在休眠的时候,轮转执行低优先级的任务函数。

configUSE_TIME_SLICING

默认情况下(如果未定义configUSE_TIME_SLICING,或者configUSE_TIME_SLICING定义为 1),FreeRTOS 使用具有时间切片的优先抢占式调度。这意味着 RTOS 调度程序将始终运行处于“就绪”状态的最高优先级任务,并将在每个 RTOS 逐笔报价中断时在优先级相等的任务之间切换。如果configUSE_TIME_SLICING设置为 0,则 RTOS 调度程序仍将运行处于“就绪”状态的最高优先级任务,但不会仅仅因为发生了刻度中断而在优先级相等的任务之间切换。

下面是空闲函数礼让用户函数的配置宏

配置了之后,就不会抢占cpu的运行资源了。

configIDLE_SHOULD_YIELD

此参数控制空闲优先级下的任务的行为。它仅在以下情况下有效:

  1. 正在使用抢占式调度程序。
  2. 应用程序创建以空闲优先级运行的任务。

如果configUSE_TIME_SLICING设置为 1(或未定义),则具有相同优先级的任务将进行时间片。如果没有一个任务被抢占,那么可以假设给定优先级的每个任务将被分配相等的处理时间 - 如果优先级高于空闲优先级,那么情况确实如此。

当任务共享空闲优先级时,行为可能会略有不同。如果configIDLE_SHOULD_YIELD设置为 1,则当任何其他具有空闲优先级的任务已准备好运行时,空闲任务将立即生成。这可确保在应用程序任务可用于调度时,在空闲任务中花费的时间最少。但是,此行为可能会产生不良影响(具体取决于应用程序的需要),如下所示:

上图显示了四个任务的执行模式,这些任务都以空闲优先级运行。任务 A、B 和 C 是应用程序任务。任务 I 是空闲任务。上下文切换在时间 T0、T1、...、T6 的固定时间段内发生。当空闲任务生成任务 A 时,任务 A 开始执行 - 但空闲任务已占用部分当前时间片。这导致任务 I 和任务 A 有效地共享同一时间片。因此,应用程序任务 B 和 C 获得的处理时间比应用程序任务 A 多。

这种情况可以通过以下方式避免:

  • 如果合适,请使用空闲挂钩代替空闲优先级的单独任务。
  • 以大于空闲优先级的优先级创建所有应用程序任务。
  • 将configIDLE_SHOULD_YIELD设置为 0。

将configIDLE_SHOULD_YIELD设置为 0 可防止空闲任务在其时间段结束之前产生处理时间。这可确保为处于空闲优先级的所有任务分配相等的处理时间(如果没有一个任务被抢占),但代价是将总处理时间的更大比例分配给空闲任务。

实时操作系统源代码下载中包含的每个演示应用程序都有自己的免费交易系统配置文件。一些演示已经很旧了,并且不包含所有可用的配置选项。省略的配置选项设置为 RTOS 源文件中的默认值。所有会存在demo中宏不全的情况。

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值