移植的技术路线是
- 获取FreeRTOS源码
- 将必要的文件复制到工程中
- 创建FreeRTOSConfig.h并进行配置
- 测试
2.1 获取FreeRTOS源码
这一步非常简单,就是从FreeRTOS官网下载源码包。得到的源码文件里面是这个样子。
2.2 将必要的文件复制到工程中
2.2.1 移植C文件
参考《Mastering the FreeRTOS™ Real Time Kernel》第13页的文件结构。
我们把除了croutine.c的其他的文件放到项目文件夹下新建的FreeRTOS/目录下。
2.2.2 移植portable文件
一共有2个文件要用。在FreeRTOS/Source/portable/GCC/ARM-CM4F下面把cortex-m4的编译器支持包port.c和portmacro.h考过去。
记得在Project->Option->Preprocessor的User Include Directory中加入我们新建的FreeRTOS/文件夹。
因为我没有使用动态内存,所以不需要添加heap_x.c的那些文件。那些文件在FreeRTOS/Source/portable/MemMang/下面
2.2.3 移植头文件
除了那个要自己创建的FreeRTOSConfig.h,其他的在FreeRTOSv202212.01\FreeRTOS\Source\include\下面的头文件可以都考过来。虽然有那么零星的几个其实是用不上的。
2.3 创建FreeRTOSConfig.h并进行配置
这个文件必须要自己创建的。虽然官方给了很多的模板。笔者比较推荐[官方网页的模板]。(https://www.freertos.org/a00110.html)
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* Here is a good place to include header files that are required across
your application. */
#include "something.h"
#define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE 0
#define configCPU_CLOCK_HZ 60000000
#define configSYSTICK_CLOCK_HZ 1000000
#define configTICK_RATE_HZ 250
#define configMAX_PRIORITIES 5
#define configMINIMAL_STACK_SIZE 128
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_16_BITS
#define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 0
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
#define configUSE_MINI_LIST_ITEM 1
#define configSTACK_DEPTH_TYPE uint32_t
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
#define configHEAP_CLEAR_MEMORY_ON_FREE 1
#define configUSE_APPLICATION_TASK_TAG 0
#define configSTATS_BUFFER_MAX_LENGTH 0xFFFF
/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configKERNEL_PROVIDED_STATIC_MEMORY 1
#define configTOTAL_HEAP_SIZE 10240
#define configAPPLICATION_ALLOCATED_HEAP 1
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 1
#define configENABLE_HEAP_PROTECTOR 1
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
#define configUSE_SB_COMPLETED_CALLBACK 0
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 1
/* Software timer related definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 3
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
/* Define to trap errors during development. */
#define configASSERT( ( x ) ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
/* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
#define configTOTAL_MPU_REGIONS 8 /* Default value. */
#define configTEX_S_C_B_FLASH 0x07UL /* Default value. */
#define configTEX_S_C_B_SRAM 0x07UL /* Default value. */
#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
#define configENABLE_ERRATA_837070_WORKAROUND 1
#define configUSE_MPU_WRAPPERS_V1 0
#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 10
#define configSYSTEM_CALL_STACK_SIZE 128
#define configENABLE_ACCESS_CONTROL_LIST 1
/* ARMv8-M secure side port related definitions. */
#define secureconfigMAX_SECURE_CONTEXTS 5
/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_uxTaskGetStackHighWaterMark2 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 0
#define INCLUDE_xTaskAbortDelay 0
#define INCLUDE_xTaskGetHandle 0
#define INCLUDE_xTaskResumeFromISR 1
/* A header file that defines trace macro can be included here. */
#endif /* FREERTOS_CONFIG_H */
当然这个只是个模板。比较好的原因是这里面的每一个定义下面都有说明。我们稍加修改就可以了。
这里,笔者删掉了#include "something.h"
。
2.3.1 处理中断优先级
下面这三句
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
根据stm32f407改成了
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY ( 15 << 4 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << 4 )
#define configMAX_API_CALL_INTERRUPT_PRIORITY
因为STM32F4的中断优先级是0 - 15,所以我设置成上面的参数。当然如果觉得不保险的话还可以参考例程。
2.3.2 configASSERT( x )
的处理
这个宏的处理方法非常多。但是如果并不真的用它,可以像我这样处理。
/* Define to trap errors during development. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
2.3.3 关于系统时钟、configCPU_CLOCK_HZ
和configSYSTICK_CLOCK_HZ
的一点说明
为了测试方便,笔者这里只是使用了芯片内部的RC震荡时钟。所以要把configCPU_CLOCK_HZ
设置成16MHz。
关于configCPU_CLOCK_HZ
和configSYSTICK_CLOCK_HZ
这个的设计的初衷我不清楚。但是根据调试的效果来看,如果定义了configSYSTICK_CLOCK_HZ
,系统的sysTick会使能8分频。
2.3.4 动态内存和vApplicationGetIdleTaskMemory
符号
把configSUPPORT_DYNAMIC_ALLOCATION
关掉。但是编译后会出现要vApplicationGetIdleTaskMemory
这个符号的问题。
参考官网上FreeRTOSConfig.h的模板的阐述,点一下configSUPPORT_STATIC_ALLOCATION的蓝字,就可以找到解决方案。就是把后面的代码加上去。
笔者的做法是在工程中建立一个新的文件,起名叫user_port.c,把这些代码加进去就好了。
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an
implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
used by the Idle task. */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
configSTACK_DEPTH_TYPE *puxIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
function then they must be declared static - otherwise they will be allocated on
the stack and so not exists after this function exits. */
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
state will be stored. */
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task's stack. */
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configMINIMAL_STACK_SIZE is specified in words, not bytes. */
*puxIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/*-----------------------------------------------------------*/
/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
application must provide an implementation of vApplicationGetTimerTaskMemory()
to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE *puxTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
function then they must be declared static - otherwise they will be allocated on
the stack and so not exists after this function exits. */
static StaticTask_t xTimerTaskTCB;
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
/* Pass out a pointer to the StaticTask_t structure in which the Timer
task's state will be stored. */
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task's stack. */
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
*puxTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
2.3.4 操作系统那三个系统调用
我们都知道,RTOS的运行离不开SVC_Handler、PendSV_Handler和SysTick_Handler。但是系统已经给实现了vPortSVCHandler、xPortPendSVHandler和xPortSysTickHandler。这里只要用#define把名称对应一下就可以了。
/* A header file that defines trace macro can be included here. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
2.3.5 FreeRTOSConfig.h
的代码
根据上面的操作,笔者最后的FreeRTOSConfig.h
的代码如下所示
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* Here is a good place to include header files that are required across
your application. */
#define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE 0
#define configCPU_CLOCK_HZ 16000000
//#define configSYSTICK_CLOCK_HZ 16000000
#define configTICK_RATE_HZ 1000
#define configMAX_PRIORITIES 255
#define configMINIMAL_STACK_SIZE 128
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_16_BITS
#define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 0
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
#define configUSE_MINI_LIST_ITEM 1
#define configSTACK_DEPTH_TYPE uint32_t
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
#define configHEAP_CLEAR_MEMORY_ON_FREE 1
#define configUSE_APPLICATION_TASK_TAG 0
#define configSTATS_BUFFER_MAX_LENGTH 0xFFFF
/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configKERNEL_PROVIDED_STATIC_MEMORY 1
#define configTOTAL_HEAP_SIZE 1024
#define configAPPLICATION_ALLOCATED_HEAP 1
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 1
#define configENABLE_HEAP_PROTECTOR 1
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
#define configUSE_SB_COMPLETED_CALLBACK 0
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 1
/* Software timer related definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 3
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY ( 15 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 )
#define configMAX_API_CALL_INTERRUPT_PRIORITY
/* Define to trap errors during development. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
#define configTOTAL_MPU_REGIONS 8 /* Default value. */
#define configTEX_S_C_B_FLASH 0x07UL /* Default value. */
#define configTEX_S_C_B_SRAM 0x07UL /* Default value. */
#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
#define configENABLE_ERRATA_837070_WORKAROUND 1
#define configUSE_MPU_WRAPPERS_V1 0
#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 10
#define configSYSTEM_CALL_STACK_SIZE 128
#define configENABLE_ACCESS_CONTROL_LIST 1
/* ARMv8-M secure side port related definitions. */
#define secureconfigMAX_SECURE_CONTEXTS 5
/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_uxTaskGetStackHighWaterMark2 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 0
#define INCLUDE_xTaskAbortDelay 0
#define INCLUDE_xTaskGetHandle 0
#define INCLUDE_xTaskResumeFromISR 1
/* A header file that defines trace macro can be included here. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
2.4 测试
参考手册,做了下面的这样一个测试用例。
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : main.c
Purpose : Generic application start
*/
#include "stm32f407xx.h"
#include <stdio.h>
#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#include "hardware.h"
/*********************************************************************
*
* main()
*
* Function description
* Application entry point.
*
**********************************************************************/
#define STACK_SIZE 200
static void vTaskMain(void *p);
static StaticTask_t xTaskBuffer;
static StackType_t xStack[ STACK_SIZE ];
int main(void) {
int i;
SystemCoreClockUpdate();
prvSetupHardware();
TaskHandle_t xHandle = NULL;
/* Create the task without using any dynamic memory allocation. */
xHandle = xTaskCreateStatic(
vTaskMain, /* Function that implements the task. */
"NAME", /* Text name for the task. */
STACK_SIZE, /* The number of indexes in the xStack array. */
( void * ) 1, /* Parameter passed into the task. */
155, /* Priority at which the task is created. */
xStack, /* Array to use as the task's stack. */
&xTaskBuffer ); /* Variable to hold the task's data structure. */
vTaskStartScheduler();
while(1);
}
void vTaskMain(void *p){
while(1){
GPIOA->BSRR = 1<<7;
vTaskDelay(pdMS_TO_TICKS(10));
GPIOA->BSRR = 1<<7<<16;
vTaskDelay(pdMS_TO_TICKS(20));
}
}
/*************************** End of file ****************************/
运行,用示波器测PA7的输出。