• 基于STM32结合CubeMX学习Free-RT-OS的源码之任务创建


    任务创建

            目前free rt os与cube mx 结合地非常好,让开发都变得简单起来,就是因为它实在是太优雅了(总而言之就是太懂程序员了),让不少开发者STM32开发都离不开CUBE MX的自动配置。

            创建任务有两种方式,与RT-Thread一样,动态创建和静态创建两种,它们的区别在于每个任务(线程)所分配的任务栈是系统自动分配还是自己指定一块内存区域(通常以静态数组的形式)。

    这里在配置的时候需要注意的在于

    1.StackSize(任务栈大小),它是以字为单位,所以真正的大小应该为其4倍字节数。

    2.任务栈是从RAM上分配下来的,这个RAM大小有多大看芯片的说明手册。另外CUBEMX在配置的时候回给一个默认的总的堆大小。

    总的堆大小:这个值需要用户自己随着任务分配自己去修改,任务的栈是从这个总的堆上瓜分下来的(在创建任务时标记任务栈顶的地址以及栈大小为一个任务的任务栈)

    关于这里堆栈的称呼实际上是一个意思,这与平时我们所说堆栈不同。在这里它们都是内存RAM的部分,任务栈从系统设定的总的堆大小上分配出来一块区域(一块数组)作为栈。504=4*96+其他属性信息所占内存大小(比如说任务的入口函数地址,任务的名字,任务的优先级大小,任务的任务栈大小等等)

    在FreeRTOSConfig.h里进行配置。裁剪出用户自定义的一些配置(比如OS的心跳(systick的中断函数,idle的钩子函数,定时器的最大深度等等)),其中就包括所定义的总的堆大小。

    创建任务

     使用封装的一个函数 osThreadNew 传入函数入口地址,函数入口参数,配置的属性结构体

    以动态创建函数为例。在各种判断后最终执行动态创建函数 

    在动态创建函数中,初始化堆栈,然后根据该任务的优先级插入到对应的优先级列表中,尾插法插入,

    新加入的同优先级的任务放在尾部。 

    所做的正是插入链表的算法。

    1. 获取当前尾结点(尾结点是一个空的结点),它的前继永远执行真正的最后一个结点。新结点的后继为尾结点。
    2. 新结点(新创建的任务)的前继为尾结点的前继结点(旧链表的最后一个任务)。
    3. 尾结点的前继的后继也就是旧链表的最后一个任务结点的后继为当前新结点。
    4. 更新尾结点的前继为当前任务结点。

     

    最后在main函数里开启调度。

    开启调度时创建一个空闲任务。

    关于空闲任务:

    空闲线程的入口函数

    static portTASK_FUNCTION( prvIdleTask, pvParameters )
    1. static portTASK_FUNCTION( prvIdleTask, pvParameters )
    2. {
    3. /* Stop warnings. */
    4. ( void ) pvParameters;
    5. /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
    6. SCHEDULER IS STARTED. **/
    7. /* In case a task that has a secure context deletes itself, in which case
    8. the idle task is responsible for deleting the task's secure context, if
    9. any. */
    10. portTASK_CALLS_SECURE_FUNCTIONS();
    11. for( ;; )
    12. {
    13. /* See if any tasks have deleted themselves - if so then the idle task
    14. is responsible for freeing the deleted task's TCB and stack. */
    15. prvCheckTasksWaitingTermination();
    16. #if ( configUSE_PREEMPTION == 0 )
    17. {
    18. /* If we are not using preemption we keep forcing a task switch to
    19. see if any other task has become available. If we are using
    20. preemption we don't need to do this as any task becoming available
    21. will automatically get the processor anyway. */
    22. taskYIELD();
    23. }
    24. #endif /* configUSE_PREEMPTION */
    25. #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
    26. {
    27. /* When using preemption tasks of equal priority will be
    28. timesliced. If a task that is sharing the idle priority is ready
    29. to run then the idle task should yield before the end of the
    30. timeslice.
    31. A critical region is not required here as we are just reading from
    32. the list, and an occasional incorrect value will not matter. If
    33. the ready list at the idle priority contains more than one task
    34. then a task other than the idle task is ready to execute. */
    35. if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
    36. {
    37. taskYIELD();
    38. }
    39. else
    40. {
    41. mtCOVERAGE_TEST_MARKER();
    42. }
    43. }
    44. #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */
    45. #if ( configUSE_IDLE_HOOK == 1 )
    46. {
    47. extern void vApplicationIdleHook( void );
    48. /* Call the user defined function from within the idle task. This
    49. allows the application designer to add background functionality
    50. without the overhead of a separate task.
    51. NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
    52. CALL A FUNCTION THAT MIGHT BLOCK. */
    53. vApplicationIdleHook();
    54. }
    55. #endif /* configUSE_IDLE_HOOK */
    56. /* This conditional compilation should use inequality to 0, not equality
    57. to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
    58. user defined low power mode implementations require
    59. configUSE_TICKLESS_IDLE to be set to a value other than 1. */
    60. #if ( configUSE_TICKLESS_IDLE != 0 )
    61. {
    62. TickType_t xExpectedIdleTime;
    63. /* It is not desirable to suspend then resume the scheduler on
    64. each iteration of the idle task. Therefore, a preliminary
    65. test of the expected idle time is performed without the
    66. scheduler suspended. The result here is not necessarily
    67. valid. */
    68. xExpectedIdleTime = prvGetExpectedIdleTime();
    69. if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
    70. {
    71. vTaskSuspendAll();
    72. {
    73. /* Now the scheduler is suspended, the expected idle
    74. time can be sampled again, and this time its value can
    75. be used. */
    76. configASSERT( xNextTaskUnblockTime >= xTickCount );
    77. xExpectedIdleTime = prvGetExpectedIdleTime();
    78. /* Define the following macro to set xExpectedIdleTime to 0
    79. if the application does not want
    80. portSUPPRESS_TICKS_AND_SLEEP() to be called. */
    81. configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );
    82. if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
    83. {
    84. traceLOW_POWER_IDLE_BEGIN();
    85. portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
    86. traceLOW_POWER_IDLE_END();
    87. }
    88. else
    89. {
    90. mtCOVERAGE_TEST_MARKER();
    91. }
    92. }
    93. ( void ) xTaskResumeAll();
    94. }
    95. else
    96. {
    97. mtCOVERAGE_TEST_MARKER();
    98. }
    99. }
    100. #endif /* configUSE_TICKLESS_IDLE */
    101. }
    102. }

    空闲任务的优先级永远是0 (最低优先级,且空闲任务永远礼让给别的优先级任务哪怕在空闲链表里得到执行也会主动礼让。) 

  • 相关阅读:
    [黑马程序员Pandas教程]——Pandas缺失值处理
    Electron App 安装包定制 -- Inno Setup 脚本 Pascal Scripting 初探
    【Java并发入门】03 互斥锁(上):解决原子性问题
    python过滤非法字符
    麒麟信安服务器操作系统V3.5.2重磅发布!
    unplugin-vue-components和unplugin-auto-import插件
    Kafka系列之:APIS
    第十四章大数据和数据科学4分
    DSP生成hex方法
    利用Axios封装及泛型实现定制化HTTP请求处理
  • 原文地址:https://blog.csdn.net/PHILICS7/article/details/127826351