使用FreeRTOS - 图1

前言

FreeRTOS是一个开源的实时操作系统,广泛应用于嵌入式领域。相较于裸机系统,采用嵌入式实时操作系统(RTOS)可以更合理、更有效地利用 CPU 的资源,简化应用软件的设计,缩短系统开发时间,更好地保证系统的实时性和可靠性。FreeRTOS 作为一个轻量级的操作系统,功能包括:任务管理时间管理信号量消息队列、内存管理、记录功能、软件定时器协程等,可基本满足较小系统的需要。
FreeRTOS 由美国的Richard Barry于2003年发布,Richard Barry是FreeRTOS的拥有者和维护者,在过去的十多年 中 FreeRTOS 历经了9个版本,与众多半导体厂商合作密切,累计开发者数百万,是目前市场占有率最高的RTOS。 FreeRTOS于2018年被亚马逊收购,改名为AWS FreeRTOS,版本号升级为V10,且开源协议也由原来的GPLv2+修改为 MIT,与GPLv2+相比,MIT更加开放,你完全可以理解为是为所欲为的免费。V9以前的版本还是维持原样,V10版本相 比于V9就是加入了一些物联网相关的组件,内核基本不变。

为什么要使用 FreeRTOS

从技术上讲,并非在所有情况下都需要 RTOS。一些嵌入式应用可能非常简单,例如从传感器读取值并将这些值发送到输出。在这种情况下,不需要 RTOS。然而,随着 MCU 的功能越来越强大,嵌入式应用变得越来越复杂,使用 RTOS 的好处也越来越大。 多任务处理是使用 RTOS 的重要原因之一,也是 FreeRTOS 的一项关键功能。 什么是多任务处理?它是一种在嵌入式应用程序中运行多个任务的方法,以便这些任务可以彼此独立执行,同时仍共享同一处理器。任务执行由所谓的调度程序管理。对于单核微控制器,调度程序的工作是根据指定的优先级和其他因素为任务提供执行时间。这可能会给人一种同时执行多个任务的感觉,即使调度程序实际上是通过在毫秒级任务之间快速连续切换来管理执行。

安装配置 FreeRTOS

树莓派 C/C++ SDK 中并未包含 FreeRTOS,所以你需要将 FreeRTOS 先安装到 Mac 上,并且配置环境变量。
  1. cd ~
  2. # 使用 git clone 命令下载 FreeRTOS 内核
  3. git clone -b smp https://github.com/FreeRTOS/FreeRTOS-Kernel --recurse-submodules
  4. # 配置环境变量
  5. vim .zprofile
  6. export FREERTOS_KERNEL_PATH=$HOME/FreeRTOS-Kernel
  7. source .zprofile

在 Pico 上使用 FreeRTOS

下面我们通过一个简单的示例程序来学习如何使用 FreeRTOS。

拷贝cmake 文件和配置文件

创建项目文件目录,并且将FreeRTOS_Kernel_import.cmake 和 pico_sdk_import.cmake 文件拷贝到项目根目录下,并且创建项目所需文件。
  1. mkdir blink_freertos
  2. cd blink_freertos
  3. touch blink_freertos.c CMakeLists.txt FreeRTOSConfig.h
  4. cp $PICO_SDK_PATH/external/pico_sdk_import.cmake .
  5. cp $FREERTOS_KERNEL_PATH/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake .
我们还需要一个“FreeRTOSConfig.h”文件来为我们的项目配置 FreeRTOS。FreeRTOSConfig 文件内容如下:
  1. /*
  2. * FreeRTOS V202111.00
  3. * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy of
  6. * this software and associated documentation files (the "Software"), to deal in
  7. * the Software without restriction, including without limitation the rights to
  8. * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  9. * the Software, and to permit persons to whom the Software is furnished to do so,
  10. * subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  17. * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  18. * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19. * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. *
  22. * http://www.FreeRTOS.org
  23. * http://aws.amazon.com/freertos
  24. *
  25. * 1 tab == 4 spaces!
  26. */
  27. #ifndef FREERTOS_CONFIG_H
  28. #define FREERTOS_CONFIG_H
  29. /*-----------------------------------------------------------
  30. * Application specific definitions.
  31. *
  32. * These definitions should be adjusted for your particular hardware and
  33. * application requirements.
  34. *
  35. * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
  36. * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
  37. *
  38. * See http://www.freertos.org/a00110.html
  39. *----------------------------------------------------------*/
  40. /* Scheduler Related */
  41. #define configUSE_PREEMPTION 1
  42. #define configUSE_TICKLESS_IDLE 0
  43. #define configUSE_IDLE_HOOK 0
  44. #define configUSE_TICK_HOOK 0
  45. #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
  46. #define configMAX_PRIORITIES 32
  47. #define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 1024
  48. #define configUSE_16_BIT_TICKS 0
  49. #define configIDLE_SHOULD_YIELD 1
  50. /* Synchronization Related */
  51. #define configUSE_MUTEXES 1
  52. #define configUSE_RECURSIVE_MUTEXES 1
  53. #define configUSE_APPLICATION_TASK_TAG 0
  54. #define configUSE_COUNTING_SEMAPHORES 1
  55. #define configQUEUE_REGISTRY_SIZE 8
  56. #define configUSE_QUEUE_SETS 1
  57. #define configUSE_TIME_SLICING 1
  58. #define configUSE_NEWLIB_REENTRANT 0
  59. // todo need this for lwip FreeRTOS sys_arch to compile
  60. #define configENABLE_BACKWARD_COMPATIBILITY 1
  61. #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
  62. /* System */
  63. #define configSTACK_DEPTH_TYPE uint32_t
  64. #define configMESSAGE_BUFFER_LENGTH_TYPE size_t
  65. /* Memory allocation related definitions. */
  66. #define configSUPPORT_STATIC_ALLOCATION 0
  67. #define configSUPPORT_DYNAMIC_ALLOCATION 1
  68. #define configTOTAL_HEAP_SIZE (128*1024)
  69. #define configAPPLICATION_ALLOCATED_HEAP 0
  70. /* Hook function related definitions. */
  71. #define configCHECK_FOR_STACK_OVERFLOW 0
  72. #define configUSE_MALLOC_FAILED_HOOK 0
  73. #define configUSE_DAEMON_TASK_STARTUP_HOOK 0
  74. /* Run time and task stats gathering related definitions. */
  75. #define configGENERATE_RUN_TIME_STATS 0
  76. #define configUSE_TRACE_FACILITY 1
  77. #define configUSE_STATS_FORMATTING_FUNCTIONS 0
  78. /* Co-routine related definitions. */
  79. #define configUSE_CO_ROUTINES 0
  80. #define configMAX_CO_ROUTINE_PRIORITIES 1
  81. /* Software timer related definitions. */
  82. #define configUSE_TIMERS 1
  83. #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
  84. #define configTIMER_QUEUE_LENGTH 10
  85. #define configTIMER_TASK_STACK_DEPTH 1024
  86. /* Interrupt nesting behaviour configuration. */
  87. /*
  88. #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
  89. #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
  90. #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
  91. */
  92. #if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS
  93. /* SMP port only */
  94. #define configNUM_CORES 2
  95. #define configTICK_CORE 0
  96. #define configRUN_MULTIPLE_PRIORITIES 1
  97. #define configUSE_CORE_AFFINITY 1
  98. #endif
  99. /* RP2040 specific */
  100. #define configSUPPORT_PICO_SYNC_INTEROP 1
  101. #define configSUPPORT_PICO_TIME_INTEROP 1
  102. #include <assert.h>
  103. /* Define to trap errors during development. */
  104. #define configASSERT(x) assert(x)
  105. /* Set the following definitions to 1 to include the API function, or zero
  106. to exclude the API function. */
  107. #define INCLUDE_vTaskPrioritySet 1
  108. #define INCLUDE_uxTaskPriorityGet 1
  109. #define INCLUDE_vTaskDelete 1
  110. #define INCLUDE_vTaskSuspend 1
  111. #define INCLUDE_vTaskDelayUntil 1
  112. #define INCLUDE_vTaskDelay 1
  113. #define INCLUDE_xTaskGetSchedulerState 1
  114. #define INCLUDE_xTaskGetCurrentTaskHandle 1
  115. #define INCLUDE_uxTaskGetStackHighWaterMark 1
  116. #define INCLUDE_xTaskGetIdleTaskHandle 1
  117. #define INCLUDE_eTaskGetState 1
  118. #define INCLUDE_xTimerPendFunctionCall 1
  119. #define INCLUDE_xTaskAbortDelay 1
  120. #define INCLUDE_xTaskGetHandle 1
  121. #define INCLUDE_xTaskResumeFromISR 1
  122. #define INCLUDE_xQueueGetMutexHolder 1
  123. /* A header file that defines trace macro can be included here. */
  124. #endif /* FREERTOS_CONFIG_H */

配置 CMamkeLists.txt

前面的教程我们已经学习了如何配置 CMakeLists.txt,在这里我们只需要多添加几项配置:

  1. 额外添加导入 FreeRTOS cmake 文件。
  2. 在 target_link_libraries 中添加 FreeRTOS-Kernel-Heap4。
  3. 添加 target_include_directories 导入 FreeRTOSConfig.h。
  1. # 设置Cmake 最小依赖版本
  2. cmake_minimum_required(VERSION 3.17)
  3. # 设置c/c++ 编译版本
  4. set(CMAKE_C_STANDARD 11)
  5. set(CMAKE_CXX_STANDARD 17)
  6. set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
  7. # 导入 pico sdk cmake
  8. include(pico_sdk_import.cmake)
  9. include(FreeRTOS_Kernel_import.cmake)
  10. # ====================================================================================
  11. # 设置开发板为 pico w (如果你的开发板是 pico 可以注释这行)
  12. set(PICO_BOARD pico_w CACHE STRING "Board type")
  13. # 设置项目名
  14. project(blink_freertos C CXX ASM)
  15. # 初始化sdk
  16. pico_sdk_init()
  17. # 添加执行文件
  18. add_executable(blink_freertos
  19. blink_freertos.c
  20. )
  21. # 设置项目名称字符串
  22. pico_set_program_name(blink_freertos "hello_usb")
  23. # 设置项目版本号
  24. pico_set_program_version(blink_freertos "0.1")
  25. # Modify the below lines to enable/disable output over UART/USB
  26. # 是否打开 UART 串口
  27. pico_enable_stdio_uart(blink_freertos 0)
  28. # 是否打开USB 串口
  29. pico_enable_stdio_usb(blink_freertos 1)
  30. # 添加依赖库 (pico w 必须加入 pico_cyw43_arch_none )
  31. target_link_libraries(blink_freertos
  32. pico_stdlib # for core functionality
  33. pico_cyw43_arch_none
  34. FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap
  35. )
  36. # Add the standard include files to the build
  37. target_include_directories(blink_freertos PRIVATE
  38. ${CMAKE_CURRENT_LIST_DIR}
  39. ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
  40. )
  41. # 设置输出文件
  42. pico_add_extra_outputs(blink_freertos)

blink_freertos.c

现在我们来使用 C 语言编写一个简单 FreeRTOS 应用程序。它使用FreeRTOS 的 xTaskCreate 函数创建了个两个任务,一个任务是使板载的 LED 闪烁,另外一个任务是打印 “Hello, world!”。最后使用 vTaskStartScheduler 函数来启动这些任务。代码如下所示:
  1. #include <stdio.h>
  2. #include "pico/stdlib.h"
  3. #include "pico/cyw43_arch.h"
  4. #include "FreeRTOS.h"
  5. #include "task.h"
  6. void led_task(pvParameters)
  7. {
  8. // const uint LED_PIN = PICO_DEFAULT_LED_PIN;
  9. // gpio_init(LED_PIN);
  10. // gpio_set_dir(LED_PIN, GPIO_OUT);
  11. while (true) {
  12. // gpio_put(LED_PIN, 1);
  13. cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
  14. vTaskDelay(100);
  15. // gpio_put(LED_PIN, 0);
  16. cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
  17. vTaskDelay(100);
  18. }
  19. }
  20. void sayHelloWorld(pvParameters)
  21. {
  22. while (true) {
  23. printf("Hello, world!\n");
  24. vTaskDelay(1000);
  25. }
  26. }
  27. int main()
  28. {
  29. stdio_init_all();
  30. // Initialise the Wi-Fi chip
  31. if (cyw43_arch_init()) {
  32. printf("Wi-Fi init failed\n");
  33. return -1;
  34. }
  35. xTaskCreate(led_task, "LED_Task", 256, NULL, 1, NULL);
  36. xTaskCreate(sayHelloWorld, "Hello_Task", 256, NULL, 1, NULL);
  37. vTaskStartScheduler();
  38. while(1){};
  39. }
使用之前学习的编译命令,编译 uf2 文件并烧录到 树莓派 Pico 中。
  1. mkdir build && cd build
  2. cmake ..
  3. make -j4
如果你正常烧录成功,你应该可以看到 Raspberry Pi Pico 上的 LED 闪烁,此时,使用 CoolTerm 连接 Pico ,可以同时看到打印的 “Hello, world!” 信息。

总结

在本教程中,我们通过一个简单的 Demo 来讲解如何按照配置 FreeRTOS 以及如何使用 FreeRTOS 来执行多任务。

源代码:https://github.com/xtcel/Raspberry_Pi_Pico_C_Tutorial/tree/master/blink_freertos