5일동안 삽질하면서 ARM-M0 보드에 FreeRTOS를 포팅하는 과정과 오류를 정리하겠다.
여기서 Keil 컴파일러를 사용하였지만 Keil Pack Installer에서 CMSIS-FreeRTOS를 설치하는 과정에서 오류가 생겨 직접 포팅하였다.
직접 포팅
1. FreeRTOS-Kernel 다운로드
https://github.com/FreeRTOS/FreeRTOS-Kernel 에서 Kernel 파일을 가져와 프로젝트 파일에 넣어준다.
Keil에서 본인 프로젝트에 Manage Project Itmes로 Kernel 파일 일부를 가져와야 된다.
2. Keil 프로젝트 구성
공통 파일
- .\tasks.c
- .\list.c
- .\queue.c
- .\...(본인 선택)
Keil - ARM_M0
- .\portable\RVDS\ARM_CM0\port.c
- .\portable\RVDS\ARM_CM0\portmacro.h
Keil 파일안에서 Text파일 제목으로 RVDS 파일에서 사용하라고 알려줌.
만약 Keil을 사용하지 않고 다른 IDE나 컴파일러를 사용한다면 본인에게 맞는 파일에 들어가면 된다.
3. Keil Path 설정
위와 같이 가져오고 Options for Target ... -> C/C++에서 Path를 연결해준다.
- .\portable\RVDS\ARM_CM0
- .\include
기본적인 세팅은 다 끝났다.
4. 주요 코드
이제 main.c와 FreeRTOSConfig.h 파일을 만들어서 코딩만 해주면 된다.
Main.c 예제 코드
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
int main(void) {
/* System Setting Code */
/* Task 1 */
if (xTaskCreate(vTask1, "T1", configMINIMAL_STACK_SIZE, NULL, 2, &xTask1Handle) != pdPASS) {
printf("vTask1 생성 실패\n");
}
/* Task 2 */
if (xTaskCreate(vTask2, "T2", configMINIMAL_STACK_SIZE, NULL, 1, &xTask2Handle) != pdPASS) {
printf("vTask2 생성 실패\n");
}
vTaskStartScheduler();
for (;;);
}
void vTask1(void *pvParameters) {
(void)pvParameters;
while (1) {
printf("Task1_실행\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters) {
(void)pvParameters;
while (1) {
printf("Task2_실행\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
FreeRTOSConfig.h 예제 코드
/*
* FreeRTOS V202212.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#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
*----------------------------------------------------------*/
/* Demo related settings. */
/* Set mainCREATE_SIMPLE_BLINKY_DEMO_ONLY to
* 0 -- to run the more comprehensive test and demo application,
* 1 -- to run the simple blinky demo.
*/
#define mainCREATE_SIMPLE_BLINKY_DEMO_ONLY 1
/* When mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 0,
* set mainNO_TASK_NO_CHECK to
* 0 -- to include all predefined test tasks and checks,
* 1 -- to exclude all predefined test tasks and checks.
* When set to 1 (with few tasks in system), user could observe
* fewer tick interrupts thus reduce overall MCU power consumption. */
#define mainNO_TASK_NO_CHECK 0
/* Prevent C code being included by the IAR assembler. */
#ifndef __IASMARM__
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 1
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES 5
#define configMAX_TASK_NAME_LEN 8
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_MALLOC_FAILED_HOOK 1
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1
#define configGENERATE_RUN_TIME_STATS 0
/* Support various memory allocation. */
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
/* Heap and stack.
* The bytes specified in configTOTAL_HEAP_SIZE need to fit in to
* the first memory bank, which is of size 64kB in total. This 64kB
* consists of FreeRTOS heap, linker heap and also .bss etc. Thus
* FreeRTOS heap cannot take the entire 64kB. */
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 20 * 1024 ) )
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 2
#define configTIMER_QUEUE_LENGTH 2
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE )
/* Enabling tickless. */
#define configUSE_TICKLESS_IDLE 1
/* 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 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names - or at least those used in the unmodified vector table. */
#define vPortSVCHandler SVCall_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
main.c와 FreeRTOSConfig.h도 대충 만들어준다.
main.c와 FreeRTOSConfig.h에 보드에 맞는 세팅을 추가해주고 실행하면 Task가 만들어지고 실행이 될 것이다.
5. 오류 해결 과정
전체적인 세팅을 마치고 컴파일을 하고 실행을 시키니 FreeRTOS가 정상 작동하지 않고 우선순위가 높은 Task 하나만 실행되거나 아예 실행되지 않는 오류가 발생하였다.
스케줄러 오류일까, 메모리 오류일까 하나하나 분석하며 찾았지만 해결되지 않았고 ARM_CM0 Demo에 있는 파일에서 Config.h파일을 긁어오며 오류를 해결하였다.
#define vPortSVCHandler SVCall_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
1. `#define vPortSVCHandler SVCall_Handler`
- `vPortSVCHandler` : FreeRTOS 내부에서 SVC 인터럽트를 처리하기 위해 사용하는 핸들러 이름이다.
- `SVCall_Handler` : ARM Cortex-M에서 SVC 인터럽트를 처리하기 위해 제공하는 기본 핸들러 이름이다.
2. `#define xPortPendSVHandler PendSV_Handler`
- `xProtPendSVHandler` : FreeRTOS에서 PendSV(Pending Supervisor Call) 인터럽트를 처리하는 핸들러 이름이다.
- `PendSV_Handler` : ARM Cortex-M에서 PendSV 인터럽트를 처리하기 위해 제공하는 기본 핸들러 이름이다.
- FreeRTOS는 PendSV 인터럽트를 사용하여 태스크 전환(Context Switching)을 수행한다. 이 매크로를 통해 FreeRTOS의 태스크 전환 코드가 `PendSV_Handler`로 연결된다.
3. `#define xPortSysTickHandler SysTick_Handler`
- `xPortSysTickHandler` : FreeRTOS에서 시스템 틱(SysTick) 인터럽트를 처리하기 위해 사용하는 핸들리 이름이다.
- `SysTick_Handler` : ARM Cortex-M에서 SysTick 인터럽트를 처리하기 위해 제공하는 기본 핸들러 이름이다.
- FreeRTOS는 SysTick 인터럽트를 사용하여 주기적인 시스템 틱을 생성하며, 이 틱을 기반으로 태스크 지연 함수나 스케줄러가 작동한다. 이 매크로는 FreeRTOS의 SysTick 처리 코드가 `SysTick_Handler`로 연결되도록 한다.
이렇게 ARM-CM0에 포팅을 진행해보았다.
대부분의 오류는 FreeRTOSConfig.h에서 발생하니 Heap_Size를 바꾼다거나 혹시 부족한 설정이 없는지.. 오류 메시지를 읽어보기 바란다. 좀 더 자세한 설명이나 궁금한 점은 댓글로 질문 남겨주길..
'MCU' 카테고리의 다른 글
[MCU] Startup 코드란? (0) | 2024.12.11 |
---|