硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4775|回复: 6
收起左侧

[FreeRTOS教程] 第28章 FreeRTOS动态内存管理

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-9-9 16:00:07 | 显示全部楼层 |阅读模式



第28章      FreeRTOS动态内存管理



      本章节为大家讲解FreeRTOS动态内存管理,动态内存管理是FreeRTOS非常重要的一项功能,前面章节讲解的任务创建、信号量、消息队列、事件标志组、互斥信号量、软件定时器组等需要的RAM空间都是通过动态内存管理从FreeRTOSConfig.h文件定义的heap空间中申请的。
      本章节整理的部分内容整理自官网地址:http://www.freertos.org/a00111.html
      本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
28.1 动态内存管理介绍
28.2 动态内存和静态内存比较
28.3 动态内存API函数
28.4 实验例程说明
28.5      总结



28.1  动态内存管理介绍


       FreeRTOS支持5种动态内存管理方案,分别通过文件heap_1,heap_2,heap_3,heap_4和heap_5实现,这5个文件在FreeRTOS软件包中的路径是:FreeRTOS\Source\portable\MemMang用户创建的FreeRTOS工程项目仅需要5种方式中的一种。
       下面将这5种动态内存管理方式分别进行讲解。

28.1.1 动态内存管理方式一heap_1


      heap_1动态内存管理方式是五种动态内存管理方式中最简单的,这种方式的动态内存管理一旦申请了相应内存后,是不允许被释放的。尽管如此,这种方式的动态内存管理还是满足大部分嵌入式应用的,因为这种嵌入式应用在系统启动阶段就完成了任务创建、事件标志组、信号量、消息队列等资源的创建,而且这些资源是整个嵌入式应用过程中一直要使用的,所以也就不需要删除,即释放内存。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
            #define configTOTAL_HEAP_SIZE        (( size_t ) ( 17 * 1024 ) )  //单位字节
用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,进而可以根据剩余情况优化动态内存的大小。heap_1方式的动态内存管理有以下特点:
    (1)项目应用不需要删除任务、信号量、消息队列等已经创建的资源。
    (2)具有时间确定性,即申请动态内存的时间是固定的并且不会产生内存碎片。
    (3)确切的说这是一种静态内存分配,因为申请的内存是不允许被释放掉的。

28.1.2 动态内存管理方式二heap_2


      与heap_1动态内存管理方式不同,heap_2动态内存管理利用了最适应算法,并且支持内存释放。但是heap_2不支持内存碎片整理,动态内存管理方式四heap_4支持内存碎片整理。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
           #define configTOTAL_HEAP_SIZE        (( size_t ) ( 17 * 1024 ) )  //单位字节
       用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。另外,就是用户可以根据剩余情况优化动态内存的大小。heap_2方式的动态内存管理有以下特点:
1、不考虑内存碎片的情况下,这种方式支持重复的任务、信号量、事件标志组、软件定时器等内部资源的创建和删除。
2、如果用户申请和释放的动态内存大小是随机的,不建议采用这种动态内存管理方式,比如:
(1)项目应用中需要重复的创建和删除任务,如果每次创建需要动态内存大小相同,那么heap_2比较适合,但每次创建需要动态内存大小不同,那么方式heap_2就不合适了,因为容易产生内存碎片,内存碎片过多的话会导致无法申请出一个大的内存块出来,这种情况使用heap_4比较合适。
    (2)项目应用中需要重复的创建和删除消息队列,也会出现类似上面的情况,这种情况下使用heap_4比较合适。
    (3)直接的调用函数pvPortMalloc() vPortFree()也容易出现内存碎片。如果用户按一定顺序成对的申请和释放,基本没有内存碎片的,而不按顺序的随机申请和释放容易产生内存碎片。
3、如果用户随机的创建和删除任务、消息队列、事件标志组、信号量等内部资源也容易出现内存碎片。
4、heap_2方式实现的动态内存申请不具有时间确定性,但是比C库中的malloc函数效率要高。
      大部分需要动态内存申请和释放的小型实时系统项目可以使用heap_2。如果需要内存碎片的回收机制可以使用heap_4。

28.1.3 动态内存管理方式三heap_3


      这种方式实现的动态内存管理是对编译器提供的malloc和free函数进行了封装,保证是线程安全的。
heap_3方式的动态内存管理有以下特点:
1、需要编译器提供mallocfree函数。
2、不具有时间确定性,即申请动态内存的时间不是固定的。
3、增加RTOS内核的代码量。
      另外要特别注意一点,这种方式的动态内存申请和释放不是用的FreeRTOSConfig.h文件中定义的heap空间大小,而是用的编译器设置的heap空间大小或者说STM32启动代码中设置的heap空间大小,比如MDK版本的STM32F103工程中heap大小就是在这里进行的定义:
28.1.jpg


28.1.4 动态内存管理方式四heap_4


      与heap_2动态内存管理方式不同,heap_4动态内存管理利用了最适应算法,且支持内存碎片的回收并将其整理为一个大的内存块。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
      #define configTOTAL_HEAP_SIZE        (( size_t ) ( 17 * 1024 ) )  //单位字节
heap_4同时支持将动态内存设置在指定的RAM空间位置。
      用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。使用函数xPortGetMinimumEverFreeHeapSize能够获取从系统启动到当前时刻的动态内存最小剩余,从而用户就可以根据剩余情况优化动态内存的大小。heap_4方式的动态内存管理有以下特点:
1、可以用于需要重复的创建和删任务、信号量、事件标志组、软件定时器等内部资源的场合。、
2、随机的调用pvPortMalloc() vPortFree(),且每次申请的大小都不同,也不会像heap_2那样产生很多的内存碎片。
3、不具有时间确定性,即申请动态内存的时间不是确定的,但是比C库中的malloc函数要高效。
      heap_4比较实用,本教程配套的所有例子都是用的这种方式的动态内存管理,用户的代码也可以直接调用函数pvPortMalloc() vPortFree()进行动态内存的申请和释放。

28.1.5 动态内存管理方式五heap_5


      有时候我们希望FreeRTOSConfig.h文件中定义的heap空间可以采用不连续的内存区,比如我们希望可以将其定义在内部SRAM一部分,外部SRAM一部分,此时我们就可以采用heap_5动态内存管理方式。另外,heap_5动态内存管理是在heap_4的基础上实现的。
      heap_5动态内存管理是通过函数vPortDefineHeapRegions进行初始化的,也就是说用户在创建任务FreeRTOS的内部资源前要优先级调用这个函数vPortDefineHeapRegions,否则是无法通过函数pvPortMalloc申请到动态内存的。
     函数vPortDefineHeapRegions定义不同段的内存空间采用了下面这种结构体:
  1. typedef struct HeapRegion
  2. {
  3.     /* Start address of a block of memory that will be part of the heap.*/
  4.     uint8_t *pucStartAddress;
  5.     /* Size of the block of memory. */
  6.      size_t xSizeInBytes;
  7. } HeapRegion_t;
复制代码
比如下面定义了两个内存块:
  1. /* Allocate two blocks of RAM for use by the heap.  The first is a block of
  2. 0x10000 bytes starting from address 0x80000000, and the second a block of
  3. 0xa0000 bytes starting from address 0x90000000.  The block starting at
  4. 0x80000000 has the lower start address so appears in the array fist. */
  5. const HeapRegion_t xHeapRegions[] =
  6. {
  7.     { ( uint8_t * ) 0x80000000UL, 0x10000 },
  8.     { ( uint8_t * ) 0x90000000UL, 0xa0000 },
  9.     { NULL, 0 } /* Terminates the array. */
  10. };
  11. /* Pass the array into vPortDefineHeapRegions(). */
  12. vPortDefineHeapRegions( xHeapRegions );
复制代码
定义的时候要注意两个问题,一个是内存段结束时要定义NULL。另一个是内存段的地址是从低地址到高地址排列。
     用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。使用函数xPortGetMinimumEverFreeHeapSize能够获取从系统启动到当前时刻的动态内存最小剩余,从而用户就可以根据剩余情况优化动态内存的大小。

28.1.6 五种动态内存方式总结


     五种动态内存管理方式简单总结如下,实际项目中,用户根据需要选择合适的:
    (1)heap_1:五种方式里面最简单的,但是申请的内存不允许释放。
    (2)heap_2:支持动态内存的申请和释放,但是不支持内存碎片的处理,并将其合并成一个大的内存块。
    (3)heap_3:将编译器自带的malloc和free函数进行简单的封装,以支持线程安全,即支持多任务调用。
    (4)heap_4:支持动态内存的申请和释放,支持内存碎片处理,支持将动态内存设置在个固定的地址。
    (5)heap_5:在heap_4的基础上支持将动态内存设置在不连续的区域上。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:13:05 | 显示全部楼层
28.2 动态内存和静态内存比较


     静态内存方式是从FreeRTOS的V9.0.0版本才开始有的,而我们本次教程使用的版本是V8.2.3。所以静态内存方式我们暂时不做讲解,等FreeRTOS教程版本升级时再做讲解。关于静态内存方式和动态内存方式的优缺点可以看官方的此贴说明:
                  http://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html
         (制作此教程的时候,官方的FreeRTOS V9.0.0正式版本还没有发布,所以采用的是当前最新的V8.2.3)
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:14:05 | 显示全部楼层
28.3   动态内存API函数


      动态内存的API函数在官方的在线版手册上面没有列出,其实使用也比较简单,类似C库的malloc和free函数,具体使用参看下面的实例说明。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:18:33 | 显示全部楼层
28.4 实验例程说明



28.4.1 STM32F103开发板实验


配套例子:
      V4-334_FreeRTOS实验_动态内存管理
实验目的:
      1.     学习FreeRTOS的动态内存管理。
      2.     FreeRTOS提供了5种内存管理方法,板子配套的都是用的方法4,即heap_4文件。
实验内容:
      1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
      2.     K2键按下,向消息队列xQueue1发送数据, 要发送数据的变量空间是通过函数pvPortMalloc和vPortFree实现申请和释放。
      3.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
      FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS        0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
        系统主频72MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
28.2.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR           ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
系统栈大小分配:
28.3.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.      /* 创建任务通信机制 */
  34.      AppObjCreate();
  35.    
  36.     /* 启动调度,开始执行任务 */
  37.     vTaskStartScheduler();
  38.      /*
  39.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  40.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  41.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  42.      */
  43.      while(1);
  44. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
FreeRTOS消息队列创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {   
  11.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  12.      xQueue1 = xQueueCreate(10, sizeof(struct Msg *));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                    /* K2键按下,向xQueue1发送数据 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("=================================================\\r\\n");
  39.                        printf("当前动态内存大小 = %d\\r\\n", xPortGetFreeHeapSize());
  40.                        ptMsg = (MSG_T  *)pvPortMalloc(sizeof(MSG_T));
  41.                        printf("申请动态内存后剩余大小 = %d\\r\\n", xPortGetFreeHeapSize());
  42.                   
  43.                        ptMsg->ucMessageID = ucCount++;
  44.                        ptMsg->ulData[0] = ucCount++;
  45.                        ptMsg->usData[0] = ucCount++;
  46.                   
  47.                        /* 使用消息队列实现指针变量的传递 */
  48.                        if(xQueueSend(xQueue1,                  /* 消息队列句柄 */
  49.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  50.                                       (TickType_t)10) != pdPASS )
  51.                        {
  52.                             /* 发送失败,即使等待了10个时钟节拍 */
  53.                             printf("K2键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  54.                             vPortFree(ptMsg);
  55.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());
  56.                        }
  57.                        else
  58.                        {
  59.                             /* 发送成功 */
  60.                             printf("K2键按下,向xQueue2发送数据成功\\r\\n");
  61.                         /* 由于是低优先级任务向高优先级任务发送消息队列,如果成功的话说明高优先级任务已经执
  62. 行。并获得了消息队列中的数据,所以我们可以在此处释放动态内存,不会出现高优先级任
  63. 务还没有获得消息队列数据,我们就将动态内存释放掉了。
  64.                          */                       
  65.                             vPortFree(ptMsg);
  66.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());                        
  67.                        }
  68.                   
  69.                    /* 其他的键值不处理 */
  70.                    default:                    
  71.                        break;
  72.               }
  73.          }
  74.         
  75.          vTaskDelay(20);
  76.      }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskLED
  81. *    功能说明: KED闪烁
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 2
  85. *********************************************************************************************************
  86. */
  87. static void vTaskLED(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 200;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          bsp_LedToggle(2);
  97.          bsp_LedToggle(3);
  98.         
  99.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  100.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  101.     }
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: vTaskMsgPro
  106. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  107. *    形    参: pvParameters 是在创建该任务时传递的形参
  108. *    返 回 值: 无
  109. *   优 先 级: 3
  110. *********************************************************************************************************
  111. */
  112. static void vTaskMsgPro(void *pvParameters)
  113. {
  114.      MSG_T *ptMsg;
  115.      BaseType_t xResult;
  116.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  117.    
  118.     while(1)
  119.     {
  120.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  121.                                  (void *)&ptMsg,          /* 这里获取的是结构体的地址 */
  122.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  123.         
  124.         
  125.          if(xResult == pdPASS)
  126.          {
  127.               /* 成功接收,并通过串口将数据打印出来 */
  128.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  129.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  130.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  131.          }
  132.          else
  133.          {
  134.               /* 超时 */
  135.               bsp_LedToggle(1);
  136.               bsp_LedToggle(4);
  137.          }
  138.     }
  139. }
  140. /*
  141. *********************************************************************************************************
  142. *    函 数 名: vTaskStart
  143. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  144. *    形    参: pvParameters 是在创建该任务时传递的形参
  145. *    返 回 值: 无
  146. *   优 先 级: 4
  147. *********************************************************************************************************
  148. */
  149. static void vTaskStart(void *pvParameters)
  150. {
  151.     while(1)
  152.     {
  153.          /* 按键扫描 */
  154.          bsp_KeyScan();
  155.         vTaskDelay(10);
  156.     }
  157. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:22:52 | 显示全部楼层
28.4.2 STM32F407开发板实验


配套例子:
      V5-334_FreeRTOS实验_动态内存管理
实验目的:
      1.     学习FreeRTOS的动态内存管理。
      2.     FreeRTOS提供了5种内存管理方法,板子配套的都是用的方法4,即heap_4文件。
实验内容:
      1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
      2.     K2键按下,向消息队列xQueue1发送数据, 要发送数据的变量空间是通过函数pvPortMalloc和vPortFree实现申请和释放。
      3.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. /* Ensure stdint is only used by the compiler, and not the assembler. */
  6. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  7. #include <stdint.h>
  8. extern volatile uint32_t ulHighFrequencyTimerTicks;
  9. #endif
  10. #define configUSE_PREEMPTION         1
  11. #define configUSE_IDLE_HOOK          0
  12. #define configUSE_TICK_HOOK          0
  13. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  14. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  15. #define configMAX_PRIORITIES         ( 5 )
  16. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  17. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  18. #define configMAX_TASK_NAME_LEN      ( 16 )
  19. #define configUSE_TRACE_FACILITY      1
  20. #define configUSE_16_BIT_TICKS       0
  21. #define configIDLE_SHOULD_YIELD       1
  22. /* Run time and task stats gathering related definitions. */
  23. #define configGENERATE_RUN_TIME_STATS                1
  24. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  25. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  26. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  27. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  28. /* Co-routine definitions. */
  29. #define configUSE_CO_ROUTINES             0
  30. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  31. /* Set the following definitions to 1 to include the API function, or zero
  32. to exclude the API function. */
  33. #define INCLUDE_vTaskPrioritySet     1
  34. #define INCLUDE_uxTaskPriorityGet         1
  35. #define INCLUDE_vTaskDelete               1
  36. #define INCLUDE_vTaskCleanUpResources 0
  37. #define INCLUDE_vTaskSuspend              1
  38. #define INCLUDE_vTaskDelayUntil           1
  39. #define INCLUDE_vTaskDelay                1
  40. /* Cortex-M specific definitions. */
  41. #ifdef __NVIC_PRIO_BITS
  42.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  43.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  44. #else
  45.      #define configPRIO_BITS              4        /* 15 priority levels */
  46. #endif
  47. /* The lowest interrupt priority that can be used in a call to a "set priority"
  48. function. */
  49. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  50. /* The highest interrupt priority that can be used by any interrupt service
  51. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  52. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  53. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  54. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
         使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
28.4.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
28.5.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.      /* 创建任务通信机制 */
  35.      AppObjCreate();
  36.     /* 启动调度,开始执行任务 */
  37.     vTaskStartScheduler();
  38.      /*
  39.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  40.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  41.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  42.      */
  43.      while(1);
  44. }
复制代码
硬件外设初始化
      硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
FreeRTOS消息队列创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {   
  11.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  12.      xQueue1 = xQueueCreate(10, sizeof(struct Msg *));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                    /* K2键按下,向xQueue1发送数据 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("=================================================\\r\\n");
  39.                        printf("当前动态内存大小 = %d\\r\\n", xPortGetFreeHeapSize());
  40.                        ptMsg = (MSG_T  *)pvPortMalloc(sizeof(MSG_T));
  41.                        printf("申请动态内存后剩余大小 = %d\\r\\n", xPortGetFreeHeapSize());
  42.                   
  43.                        ptMsg->ucMessageID = ucCount++;
  44.                        ptMsg->ulData[0] = ucCount++;
  45.                        ptMsg->usData[0] = ucCount++;
  46.                   
  47.                        /* 使用消息队列实现指针变量的传递 */
  48.                        if(xQueueSend(xQueue1,                  /* 消息队列句柄 */
  49.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  50.                                       (TickType_t)10) != pdPASS )
  51.                        {
  52.                             /* 发送失败,即使等待了10个时钟节拍 */
  53.                             printf("K2键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  54.                             vPortFree(ptMsg);
  55.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());
  56.                        }
  57.                        else
  58.                        {
  59.                             /* 发送成功 */
  60.                             printf("K2键按下,向xQueue2发送数据成功\\r\\n");
  61.                         /* 由于是低优先级任务向高优先级任务发送消息队列,如果成功的话说明高优先级任务已经执
  62. 行。并获得了消息队列中的数据,所以我们可以在此处释放动态内存,不会出现高优先级任
  63. 务还没有获得消息队列数据,我们就将动态内存释放掉了。
  64.                          */                       
  65.                             vPortFree(ptMsg);
  66.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());                        
  67.                        }
  68.                   
  69.                    /* 其他的键值不处理 */
  70.                    default:                    
  71.                        break;
  72.               }
  73.          }
  74.         
  75.          vTaskDelay(20);
  76.      }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskLED
  81. *    功能说明: KED闪烁
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 2
  85. *********************************************************************************************************
  86. */
  87. static void vTaskLED(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 200;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          bsp_LedToggle(2);
  97.          bsp_LedToggle(3);
  98.         
  99.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  100.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  101.     }
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: vTaskMsgPro
  106. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  107. *    形    参: pvParameters 是在创建该任务时传递的形参
  108. *    返 回 值: 无
  109. *   优 先 级: 3
  110. *********************************************************************************************************
  111. */
  112. static void vTaskMsgPro(void *pvParameters)
  113. {
  114.      MSG_T *ptMsg;
  115.      BaseType_t xResult;
  116.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  117.    
  118.     while(1)
  119.     {
  120.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  121.                                  (void *)&ptMsg,           /* 这里获取的是结构体的地址 */
  122.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  123.         
  124.         
  125.          if(xResult == pdPASS)
  126.          {
  127.               /* 成功接收,并通过串口将数据打印出来 */
  128.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  129.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  130.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  131.          }
  132.          else
  133.          {
  134.               /* 超时 */
  135.               bsp_LedToggle(1);
  136.               bsp_LedToggle(4);
  137.          }
  138.     }
  139. }
  140. /*
  141. *********************************************************************************************************
  142. *    函 数 名: vTaskStart
  143. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  144. *    形    参: pvParameters 是在创建该任务时传递的形参
  145. *    返 回 值: 无
  146. *   优 先 级: 4
  147. *********************************************************************************************************
  148. */
  149. static void vTaskStart(void *pvParameters)
  150. {
  151.     while(1)
  152.     {
  153.          /* 按键扫描 */
  154.          bsp_KeyScan();
  155.         vTaskDelay(10);
  156.     }
  157. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:28:55 | 显示全部楼层
28.4.3 STM32F429开发板实验


配套例子:
      V6-334_FreeRTOS实验_动态内存管理
实验目的:
      1.     学习FreeRTOS的动态内存管理。
      2.     FreeRTOS提供了5种内存管理方法,板子配套的都是用的方法4,即heap_4文件。
实验内容:
      1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
      2.      K2键按下,向消息队列xQueue1发送数据, 要发送数据的变量空间是通过函数pvPortMalloc和vPortFree实现申请和释放。
      3.      各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描
FreeRTOS的配置:
      FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
28.6.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR           ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
     #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
28.7.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.      /* 创建任务通信机制 */
  35.      AppObjCreate();
  36.     /* 启动调度,开始执行任务 */
  37.     vTaskStartScheduler();
  38.      /*
  39.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  40.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  41.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  42.      */
  43.      while(1);
  44. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
FreeRTOS消息队列创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {   
  11.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  12.      xQueue1 = xQueueCreate(10, sizeof(struct Msg *));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                    /* K2键按下,向xQueue1发送数据 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("=================================================\\r\\n");
  39.                        printf("当前动态内存大小 = %d\\r\\n", xPortGetFreeHeapSize());
  40.                        ptMsg = (MSG_T  *)pvPortMalloc(sizeof(MSG_T));
  41.                        printf("申请动态内存后剩余大小 = %d\\r\\n", xPortGetFreeHeapSize());
  42.                   
  43.                        ptMsg->ucMessageID = ucCount++;
  44.                        ptMsg->ulData[0] = ucCount++;
  45.                        ptMsg->usData[0] = ucCount++;
  46.                   
  47.                        /* 使用消息队列实现指针变量的传递 */
  48.                        if(xQueueSend(xQueue1,                  /* 消息队列句柄 */
  49.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  50.                                       (TickType_t)10) != pdPASS )
  51.                        {
  52.                             /* 发送失败,即使等待了10个时钟节拍 */
  53.                             printf("K2键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  54.                             vPortFree(ptMsg);
  55.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());
  56.                        }
  57.                        else
  58.                        {
  59.                             /* 发送成功 */
  60.                             printf("K2键按下,向xQueue2发送数据成功\\r\\n");
  61.                         /* 由于是低优先级任务向高优先级任务发送消息队列,如果成功的话说明高优先级任务已经执
  62. 行。并获得了消息队列中的数据,所以我们可以在此处释放动态内存,不会出现高优先级任
  63. 务还没有获得消息队列数据,我们就将动态内存释放掉了。
  64.                          */                       
  65.                             vPortFree(ptMsg);
  66.                             printf("释放申请的动态内存后大小 = %d\\r\\n", xPortGetFreeHeapSize());                        
  67.                        }
  68.                   
  69.                    /* 其他的键值不处理 */
  70.                    default:                    
  71.                        break;
  72.               }
  73.          }
  74.         
  75.          vTaskDelay(20);
  76.      }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskLED
  81. *    功能说明: KED闪烁
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 2
  85. *********************************************************************************************************
  86. */
  87. static void vTaskLED(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 200;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          bsp_LedToggle(2);
  97.          bsp_LedToggle(3);
  98.         
  99.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  100.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  101.     }
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: vTaskMsgPro
  106. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  107. *    形    参: pvParameters 是在创建该任务时传递的形参
  108. *    返 回 值: 无
  109. *   优 先 级: 3
  110. *********************************************************************************************************
  111. */
  112. static void vTaskMsgPro(void *pvParameters)
  113. {
  114.      MSG_T *ptMsg;
  115.      BaseType_t xResult;
  116.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  117.    
  118.     while(1)
  119.     {
  120.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  121.                                  (void *)&ptMsg,           /* 这里获取的是结构体的地址 */
  122.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  123.         
  124.         
  125.          if(xResult == pdPASS)
  126.          {
  127.               /* 成功接收,并通过串口将数据打印出来 */
  128.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  129.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  130.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  131.          }
  132.          else
  133.          {
  134.               /* 超时 */
  135.               bsp_LedToggle(1);
  136.               bsp_LedToggle(4);
  137.          }
  138.     }
  139. }
  140. /*
  141. *********************************************************************************************************
  142. *    函 数 名: vTaskStart
  143. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  144. *    形    参: pvParameters 是在创建该任务时传递的形参
  145. *    返 回 值: 无
  146. *   优 先 级: 4
  147. *********************************************************************************************************
  148. */
  149. static void vTaskStart(void *pvParameters)
  150. {
  151.     while(1)
  152.     {
  153.          /* 按键扫描 */
  154.          bsp_KeyScan();
  155.         vTaskDelay(10);
  156.     }
  157. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-9 16:32:13 | 显示全部楼层
28.5  总结

      本章节为大家讲解了FreeRTOS动态内存管理,动态内存管理是FreeRTOS非常重要的一项功能,前面章节讲解的任务创建、信号量、消息队列、事件标志组、互斥信号量、软件定时器组等需要的RAM空间都是通过动态内存管理从FreeRTOSConfig.h文件定义的heap空间中申请的。
      大家在实际的项目中用到动态内存管理的地方也非常多,望初学者将FreeRTOS的这5种方式的区别都掌握了,根据项目需要,选择合适的动态内存管理方案。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-30 20:41 , Processed in 0.285957 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表