ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • FreeRTOS Porting Guide(2/4)
    IT 개발/Cortex 2015.10.08 15:16

    FreeRTOS

    Download FreeRTOS

    FreeRTOS사이트에 접속하면, FreeRTOS에 관한 정보와 문서, 소스 코드를 다운 받을 수 있다.

    Folder Structure

    FreeRTOS를 다운로드 받으면, 크게 Free-RTOS-Plus 폴더와 FreeRTOS 폴더를 확인 할 수 있다.
    자세한 내용은 http://www.freertos.org/a00017.html를 참고 하기 바란다.

    20150922_193631

    먼저 FreeRTOS 폴더를 확인해 보면 Demo 폴더와 Source 폴더로 구성되어 있으며, Demo 폴더에는 FreeRTOS 커널 기반의 예제 데모 프로젝트가 존재 한다.

    20150922_193652

    Source 폴더에는 FreeRTOS Kenel 구현 파일들이 존재하며, Portable 폴더에는 프로세스별, IDE별 FreeRTOS Port를 정의한 파일이 존재 한다.

    20150922_193732

    본 장에서는 Cortex M0계열의 W7500 프로세서를 이용하여 FreeRTOS를 구동하므로, portable/RVDS/ARM_CM0 폴더에 있는 port.c와 portmacro.h를 사용하려고 한다.
    추가로 FreeRTOS의 Memory Management를 위해 portable/MemMang 폴더에 있는 파일들을 프로젝트에 추가 해야 한다. Memory Management에 대한 자세한 설명은 http://www.freertos.org/a00111.html를 참고 하기 바란다.

    WIZwiki-W7500ECO FreeRTOS Porting

    본 장에서는 WIZwiki-W7500ECO Board에 FreeRTOS를 Porting 하는 방법에 대해 설명한다. 본 장에서 구동하는 예제는 WIZwiki-W7500ECO Board의 LED1을 20ms 마다 Blink 하는 프로그램이다.

    Step 1 : Download W7500 Library and Example

    https://github.com/Wiznet/W7500에 접속한 후, W7500 Library Code를 다운로드 한다.

    20150922_201106

    Step 2 : Create Keil Project for W7500

    W7500-master\W7500x_Library_Examples\Projects\Peripheral_Examples 에 프로젝트를 위한 폴더를 생성한 후, 적당한 프로젝트 파일 하나는 복사한다. 나는 OS\FreeRTOS 폴더를 생성 한 후, GPIO\Blink_LED 안에 있는 파일들을 OS\FreeRTOS 폴더에 복사해서 사용하였다. 원래는 새로운 프로젝트를 만들어서 사용하는 것이 정석이나, WIZnet에서 이미 만들어 놓은 프로젝트의 설정 값들을 그대로 사용하기 위해 위와 같은 방법으로 프로젝트를 생성 하였다.

    OS\FreeRTOS\MDK 폴더의 W7500x_GPIO_Blink_LED.uvproj를 열면 아래 그림과 같이 W7500의 GPIO를 On/Off 하는 예제를 확인 할 수 있다.

    Include Path 설정법

    본 예제는 GPIO\Blink_LED와 폴더 Depth가 동일하기 때문에 Keil 프로젝트의 설정 변경 없이 그대로 사용할 수 있다. 하지만 폴더 Depth를 기존 프로젝트들과 다르게 만들었을 경우, 컴파일이 안되는 문제가 발생 할 수 있다. 이런 경우, 아래 방법을 이용하여 Include Path를 설정하면 문제를 해결 할 수 있다.

    [Project]-[Options for Target] 선택 후, [C/C++]탭의 Include Paths에 아래 폴더들의 경로를 지정한다.

    • \Libraries\CMSIS\Device\WIZnet\W7500\Include
    • \Libraries\W7500x_stdPeriph_Driver\inc
    • \Libraries\CMSIS\Include
    • ..\

    20150922_202318

    20150922_202539

    Step 3 : Porting FreeRTOS

    Step1에서 다운로드 받은 FreeRTOS 파일을 Step 2의 OS\FreeRTOS\ 폴더에 아래 그림과 같이 복사한다.
    FreeRTOS 폴더에는 아래 그림에 있는 내용만 남겨 놓고 다른 폴더들은 다 삭제 하기 바란다.

    20150922_203751

    위 과정을 완료 후, Keil 프로젝트에 FreeRTOS 그룹을 만들고 아래 파일들은 추가 한다.

    • OS\FreeRTOS\FreeRTOS\croutine.c
    • OS\FreeRTOS\FreeRTOS\list.c
    • OS\FreeRTOS\FreeRTOS\queue.c
    • OS\FreeRTOS\FreeRTOS\tasks.c
    • OS\FreeRTOS\FreeRTOS\timers.c
    • OS\FreeRTOS\FreeRTOS\portable\MemMang\heap_2.c
    • OS\FreeRTOS\FreeRTOS\portable\RVDS\ARM_CM0\port.c

    20150922_204021

    Create FreeRTOSConfig.h

    #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.
     *----------------------------------------------------------*/
    
    /* 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                0
    #define configCPU_CLOCK_HZ                ( SystemCoreClock )
    #define configTICK_RATE_HZ                ( ( TickType_t ) 1000 )
    #define configMAX_PRIORITIES            ( 5 )
    #define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 128 )
    #define configTOTAL_HEAP_SIZE            ( ( size_t ) ( 10 * 1024 ) )
    #define configMAX_TASK_NAME_LEN            ( 16 )
    
    #define configUSE_TRACE_FACILITY    0
    #define configUSE_16_BIT_TICKS        0
    #define configIDLE_SHOULD_YIELD        1
    #define configUSE_MUTEXES                1
    
    /* Co-routine definitions. */
    #define configUSE_CO_ROUTINES             0
    #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
    
    /* Software timer definitions. */
    #define configUSE_TIMERS                1
    #define configTIMER_TASK_PRIORITY        ( 2 )
    #define configTIMER_QUEUE_LENGTH        5
    #define configTIMER_TASK_STACK_DEPTH    ( 80 )
    
    /* 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 xPortPendSVHandler PendSV_Handler
    #define xPortSysTickHandler SysTick_Handler
    
    /* Bump up the priority of recmuCONTROLLING_TASK_PRIORITY to prevent false
    positive errors being reported considering the priority of other tasks in the
    system. */
    #define recmuCONTROLLING_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
    
    #endif
    
    /* Kernel includes. */
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"
    
    #include "W7500x_gpio.h"
    
    
    /* Priorities at which the tasks are created. */
    #define mainQUEUE_RECEIVE_TASK_PRIORITY    ( tskIDLE_PRIORITY + 2 )
    #define mainQUEUE_SEND_TASK_PRIORITY        ( tskIDLE_PRIORITY + 1 )
    
    /* The rate at which data is sent to the queue.  The 200ms value is converted
    to ticks using the portTICK_PERIOD_MS constant. */
    #define mainQUEUE_SEND_FREQUENCY_MS            ( 200 / portTICK_PERIOD_MS )
    
    /* The number of items the queue can hold.  This is 1 as the receive task
    will remove items as they are added, meaning the send task should always find
    the queue empty. */
    #define mainQUEUE_LENGTH                    ( 1 )
    
    /* Values passed to the two tasks just to check the task parameter
    functionality. */
    #define mainQUEUE_SEND_PARAMETER            ( 0x1111UL )
    #define mainQUEUE_RECEIVE_PARAMETER            ( 0x22UL )
    
    /* The number of the LED that is toggled. */
    #define mainLED_TO_TOGGLE                    ( 0 )
    
    /*
     * The tasks as described in the comments at the top of this file.
     */
    static void prvQueueReceiveTask( void *pvParameters );
    static void prvQueueSendTask( void *pvParameters );
    
    /*
     * Called by main() to create the simply blinky style application if
     * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1.
     */
    void main_blinky( void );
    
    void vParTestToggleLED( unsigned long ulLED );
    
    /*
     * The hardware only has a single LED.  Simply toggle it.
     */
    extern void vMainToggleLED( void );
    
    /* The queue used by both tasks. */
    static QueueHandle_t xQueue = NULL;
    
    
    void main_blinky( void )
    {
        /* Create the queue. */
        xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) );
    
        if( xQueue != NULL )
        {
            /* Start the two tasks as described in the comments at the top of this
            file. */
            xTaskCreate( prvQueueReceiveTask,                    /* The function that implements the task. */
                        "Rx",                                     /* The text name assigned to the task - for debug only as it is not used by the kernel. */
                        configMINIMAL_STACK_SIZE,                 /* The size of the stack to allocate to the task. */
                        ( void * ) mainQUEUE_RECEIVE_PARAMETER, /* The parameter passed to the task - just to check the functionality. */
                        mainQUEUE_RECEIVE_TASK_PRIORITY,         /* The priority assigned to the task. */
                        NULL );                                    /* The task handle is not required, so NULL is passed. */
    
            xTaskCreate( prvQueueSendTask, "TX", configMINIMAL_STACK_SIZE, ( void * ) mainQUEUE_SEND_PARAMETER, mainQUEUE_SEND_TASK_PRIORITY, NULL );
    
            /* Start the tasks and timer running. */
            vTaskStartScheduler();
        }
    
        /* If all is well, the scheduler will now be running, and the following
        line will never be reached.  If the following line does execute, then
        there was insufficient FreeRTOS heap memory available for the idle and/or
        timer tasks    to be created.  See the memory management section on the
        FreeRTOS web site for more details. */
        for( ;; );
    }
    
    static void prvQueueSendTask( void *pvParameters )
    {
        TickType_t xNextWakeTime;
        const unsigned long ulValueToSend = 100UL;
    
        /* Check the task parameter is as expected. */
        configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_SEND_PARAMETER );
    
        /* Initialise xNextWakeTime - this only needs to be done once. */
        xNextWakeTime = xTaskGetTickCount();
    
        for( ;; )
        {
            /* Place this task in the blocked state until it is time to run again.
            The block time is specified in ticks, the constant used converts ticks
            to ms.  While in the Blocked state this task will not consume any CPU
            time. */
            vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );
    
            /* Send to the queue - causing the queue receive task to unblock and
            toggle the LED.  0 is used as the block time so the sending operation
            will not block - it shouldn't need to block as the queue should always
            be empty at this point in the code. */
            xQueueSend( xQueue, &ulValueToSend, 0U );
        }
    }
    
    static void prvQueueReceiveTask( void *pvParameters )
    {
        unsigned long ulReceivedValue;
    
        /* Check the task parameter is as expected. */
        configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_RECEIVE_PARAMETER );
    
        for( ;; )
        {
            /* Wait until something arrives in the queue - this task will block
            indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
            FreeRTOSConfig.h. */
            xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );
    
            /*  To get here something must have been received from the queue, but
            is it the expected value?  If it is, toggle the LED. */
            if( ulReceivedValue == 100UL )
            {
                vParTestToggleLED( mainLED_TO_TOGGLE );
                ulReceivedValue = 0U;
            }
        }
    }
    
    void vParTestToggleLED( unsigned long ulLED )
    {
        if( GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == Bit_SET )
            GPIO_ResetBits(GPIOA, GPIO_Pin_1);
        else
            GPIO_SetBits(GPIOA, GPIO_Pin_1);
    }
    

    Modified main.c

    /**
      ******************************************************************************
      * @file    Uart/Printf/main.c 
      * @author  IOP Team
      * @version V1.0.0
      * @date    01-May-2015
      * @brief   Main program body
      ******************************************************************************
      * @attention
      *
      * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
      * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
      * TIME. AS A RESULT, WIZnet SHALL NOT BE HELD LIABLE FOR ANY
      * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
      * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
      * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
      *
      * <h2><center>&copy; COPYRIGHT 2015 WIZnet Co.,Ltd.</center></h2>
      ******************************************************************************
      */ 
    
    /* Includes ------------------------------------------------------------------*/
    #include <stdio.h>
    #include "W7500x_uart.h"
    #include "W7500x_gpio.h"
    
    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"
    #include "queue.h"
    
    extern void main_blinky( void );
    /*
     * Initialise the LED ports, and create a timer that periodically toggles an LED
     * just to provide a visual indication that the program is running.
     */
    
    
    int main()
    {
        GPIO_InitTypeDef GPIO_InitDef;
        SystemInit();
        SystemCoreClockUpdate();
    
        GPIO_InitDef.GPIO_Pin = ( GPIO_Pin_1 | GPIO_Pin_2) ; // Set to Pin_1 (LED1) & Pin_2 (LED2)
        GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT; // Set to Mode Output
        GPIO_Init(GPIOA, &GPIO_InitDef);
        PAD_AFConfig(PAD_PA,(GPIO_Pin_1|GPIO_Pin_2), PAD_AF1); // PAD Config - LED used 2nd Function    
    
        main_blinky();
    
        for(;;);
    }
    
    
    void vApplicationMallocFailedHook( void )
    {
        /* vApplicationMallocFailedHook() will only be called if
        configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
        function that will get called if a call to pvPortMalloc() fails.
        pvPortMalloc() is called internally by the kernel whenever a task, queue,
        timer or semaphore is created.  It is also called by various parts of the
        demo application.  If heap_1.c or heap_2.c are used, then the size of the
        heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
        FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
        to query the size of free heap space that remains (although it does not
        provide information on how the remaining heap might be fragmented). */
        taskDISABLE_INTERRUPTS();
        for( ;; );
    }
    /*-----------------------------------------------------------*/
    
    void vApplicationIdleHook( void )
    {
        /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
        to 1 in FreeRTOSConfig.h.  It will be called on each iteration of the idle
        task.  It is essential that code added to this hook function never attempts
        to block in any way (for example, call xQueueReceive() with a block time
        specified, or call vTaskDelay()).  If the application makes use of the
        vTaskDelete() API function (as this demo application does) then it is also
        important that vApplicationIdleHook() is permitted to return to its calling
        function, because it is the responsibility of the idle task to clean up
        memory allocated by the kernel to any task that has since been deleted. */
    }
    /*-----------------------------------------------------------*/
    
    void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
    {
        ( void ) pcTaskName;
        ( void ) pxTask;
    
        /* Run time stack overflow checking is performed if
        configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
        function is called if a stack overflow is detected. */
        taskDISABLE_INTERRUPTS();
        for( ;; );
    }
    /*-----------------------------------------------------------*/
    
    void vApplicationTickHook( void )
    {
        /* This function will be called by each tick interrupt if
        configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h.  User code can be
        added here, but the tick hook is called from an interrupt context, so
        code must not attempt to block, and only the interrupt safe FreeRTOS API
        functions can be used (those that end in FromISR()).  The code in this
        tick hook implementation is for demonstration only - it has no real
        purpose.  It just gives a semaphore every 50ms.  The semaphore unblocks a
        task that then toggles an LED.  Additionally, the call to
        vQueueSetAccessQueueSetFromISR() is part of the "standard demo tasks"
        functionality. */
    
        /* The semaphore and associated task are not created when the simple blinky
        demo is used. */
    
    }
    /*-----------------------------------------------------------*/
    

    Fixed Error

    Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and w7500x_it.o).
    Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and w7500x_it.o).
    

    위 Error는 PendSV_Handler와 SysTick_Handler 함수가 중복되어 있어서 발생한 Error이다. 이를 해결하기 위해서는 W7500x_it.c를 아래와 같이 수정하면 된다.

    __weak void PendSV_Handler(void)
    {}
    __weak void SysTick_Handler(void)
    {}
    

    Step 4 : Execute Example

    https://youtu.be/aXhDnW_doLs

    댓글 0

Designed by Tistory.