[PolarSSL+FreeRTOS]"bad message length" & "malloc failed" Error 해결 방법

Posted by 카이젠8501 카이젠8501
2016.12.01 12:57 IT 개발/기타

PolarSSL “bad message length” & “malloc failed” Error 해결 방법

Environment

  • SSL : PolarSSL-1.3.8
  • RTOS : FreeRTOS-8.1.2
  • Network Stack : Lwip-1.4.1
  • MCU : RealTek RTL8195a

문제

Wi-Fi 모듈에서 SKT ThingPlug에 SSL 연결을 위한 기능을 구현 하는 중, SSL 연결이 되지 않는 문제가 발생 하였다.

원인 분석

정확한 원인을 분석하기 위하여 Wi-Fi 모듈과 ThingPlug 간에 주고 받는 패킷을 캡쳐 하였다. 패킷 분석 결과, SSL 서버와 Hand Shake 과정이 정상적으로 완료 되지 않는 것을 확인 하였다.

20161201_103207

SKT ThingPlug Server와 정상적인 통신 패킷을 확인 해 보기 위해, PC에서 SSL 연결을 시도 해 보았다.
연결이 성공 하는 경우, 아래 그림과 같이 Client Hello -> Server Hello 이후 SSL 서버가 Certificate를 송신하는 과정을 볼 수 있다.

20161201_104837

Certificate 과정의 패킷을 보면, 4개의 인증서를 송신하는 것을 볼 수 있다. 인증서는 1개의 sktiot.com 인증서와 sktiot.com의 신뢰성을 보장해 주는 상위 기관 인증서 3개를 송신 한다.

20161201_110219

20161201_105209

위의 4개의 인증서를 수신하기 위해서는 5410bytes 이상의 메모리가 필요 할 것으로 보이며, 위에서 발생한 “bad message length” 에러도 Wi-Fi 모듈에서 SSL 인증서를 처리 하기 위한 메모리가 부족하거나 설정이 작은 값으로 되어 있는 것으로 판단 된다.

문제 해결

“bad message length” Error

Wi-Fi 모듈에서 에러를 출력하는 부분을 확인해 보면, 아래와 같이 수신된 ssl message length가 SSL_MAX_CONTENT_LEN 값 보다 크면 해당 에러를 출력하는 코드를 볼 수 있다. 현재 SSL_MAX_CONTENT_LEN 값이 4096으로 되어 있어서 그보다 더 큰 Contents를 수신하지 못 했던 것으로 판단 되며, 값을 6144( 6Kbytes )로 수정 하였다.

if( ssl->in_msglen > SSL_MAX_CONTENT_LEN )
{
    SSL_DEBUG_MSG( 1, ( "bad message length" ) );
    return( POLARSSL_ERR_SSL_INVALID_RECORD );
}

“malloc failed” Error

SSL_MAX_CONTENT_LEN 값을 수정 하고 테스트 해 보니, 이번에는 malloc failed 에러가 발생 하였다.

위는 Heap 메모리가 부족해서 출력된 에러로 판단 되며, FreeRTOSConfig.h의 configTOTAL_HEAP_SIZE의 값을 (70 1024) —> ( 80 1024 )로 수정 후 정상적으로 SKT ThingPlug에 SSL 연결이 되는 것을 확인 하였다.



저작자 표시
신고
이 댓글을 비밀 댓글로

FreeRTOS기반의 W7500 UART CLI(Command Line Interpreter) 구현하기

Posted by 카이젠8501 카이젠8501
2015.10.08 15:19 IT 개발/Cortex

FreeRTOS기반의 W7500 UART CLI(Command Line Interpreter) 구현하기

CLI(Command Line Interpreter)

CLI, Command line interface) 또는 명령어 인터페이스는 텍스트 터미널을 통해 사용자와 컴퓨터가 상호 작용하는 방식을 뜻한다. 즉, 작업 명령은 사용자가 컴퓨터 키보드 등을 통해 문자열의 형태로 입력하며, 컴퓨터로부터의 출력 역시 문자열의 형태로 주어진다.

참고 : https://ko.wikipedia.org/wiki/%EB%AA%85%EB%A0%B9_%EC%A4%84_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4

Repository of Project

Repository에 접속한 후, Download ZIP혹은 Clone in Desktop 버튼을 클릭하면 해당 프로젝트를 사용 할 수 있다.

20150922_215621

Explain key point function of this project

serial.c

serial.c는 W7500에서 FreeRTOS 기반에서 UART를 사용하기 위한 Driver Code이다.

Serial 초기화(xSerialPortInitMinimal())과정에서 Rx Data를 관리하기 위한 xRxedChars Queue와 Tx Data를 관리하기 위한 xCharsForTx Queue를 생성한다.

UART Rx Interrupt가 발생하면, xRxedChars Queue에 보관하고 xSerialGetChar() 시에 Queue에서 Data를 꺼내서 사용한다.

xComPortHandle xSerialPortInitMinimal( unsigned long ulWantedBaud, unsigned portBASE_TYPE uxQueueLength )
{
xComPortHandle xReturn;
    /* Create the queues used to hold Rx/Tx characters. */
    xRxedChars = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed char ) );
    xCharsForTx = xQueueCreate( uxQueueLength + 1, ( unsigned portBASE_TYPE ) sizeof( signed char ) );

    /* If the queue/semaphore was created correctly then setup the serial port
    hardware. */
    if( ( xRxedChars != serINVALID_QUEUE ) && ( xCharsForTx != serINVALID_QUEUE ) )
    {
        S_UART_Init(115200);

        S_UART_ITConfig(S_UART_CTRL_RXI,ENABLE);
        NVIC_EnableIRQ(UART2_IRQn);

        xReturn = (xComPortHandle)UART2;
    }
    else
    {
        xReturn = ( xComPortHandle ) 0;
    }

    /* This demo file only supports a single port but we have to return
    something to comply with the standard demo header file. */
    return xReturn;
}

void UART2_Handler( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
char cChar;

    if( S_UART_GetITStatus(S_UART_INTSTATUS_RXI) != RESET ) {
        S_UART_ClearITPendingBit(S_UART_INTSTATUS_RXI);
        cChar = S_UART_ReceiveData();
        xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken );
    }    
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
signed portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedChar, TickType_t xBlockTime )
{
    /* The port handle is not required as this driver only supports one port. */
    ( void ) pxPort;

    /* Get the next character from the buffer.  Return false if no characters
    are available, or arrive before xBlockTime expires. */
    if( xQueueReceive( xRxedChars, pcRxedChar, xBlockTime ) )
    {
        return pdTRUE;
    }
    else
    {
        return pdFALSE;
    }
}
/*-----------------------------------------------------------*/

void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength )
{
signed char *pxNext;

    /* A couple of parameters that this port does not use. */
    ( void ) usStringLength;
    ( void ) pxPort;

    /* NOTE: This implementation does not handle the queue being full as no
    block time is used! */

    /* The port handle is not required as this driver only supports UART1. */
    ( void ) pxPort;

    /* Send each character in the string, one at a time. */
    pxNext = ( signed char * ) pcString;
    while( *pxNext )
    {
        xSerialPutChar( pxPort, *pxNext, serNO_BLOCK );
        pxNext++;
    }
}
/*-----------------------------------------------------------*/

signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, TickType_t xBlockTime )
{
signed portBASE_TYPE xReturn;
char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    if( xQueueSend( xCharsForTx, &cOutChar, xBlockTime ) == pdPASS )
    {
        xReturn = pdPASS;
        if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
        {
            /* A character was retrieved from the queue so can be sent to the
            THR now. */
            S_UART_SendData( cChar );
        }        
    }
    else
    {
        xReturn = pdFAIL;
    }

    return xReturn;
}

UARTCommandConsole.c, FreeRTOS_CLI.c

UART로 입력된 명령을 Parsing 해서 지정된 Task를 호출하는 기능을 수행한다.
prvUARTCommandConsoleTask() 함수는 Uart Data가 “\r\n” 개행 문자가 나올 때 까지 문자열을 보관한 후, FreeRTOS_CLIProcessCommand()함수를 실행시켜 등록되어 있는 Command인지 확인하고 해당 Task를 호출하는 기능을 수행한다.

CLI-commands.c

Command 이름과 수행해야 하는 Task, Parameter 수를 CLI_Command_definition_t 타입에 저장 한후, FreeRTOS_CLIRegisterCommand()를 이용하여 Command들을 등록한다.
본 예제에서는 SetNetConfig와 GetNetConfig를 등록해서 사용한다.

static const CLI_Command_Definition_t xSetNetConfig =
{
    "set-net",
    "set-network <ip-addr> <subnet> <gateway>:\r\n",
    prvSetNetworkConfigCommand, /* The function to run. */
    3 /* Three parameters are expected, which can take any value. */
};

static const CLI_Command_Definition_t xGetNetConfig =
{
    "get-net",
    "get-network\r\n",
    prvGetNetworkConfigCommand, /* The function to run. */
    0
};

void vRegisterCLICommands(void)
{
    /* Register all the command line commands defined immediately above. */
    FreeRTOS_CLIRegisterCommand(&xSetNetConfig);
    FreeRTOS_CLIRegisterCommand(&xGetNetConfig);

}

위에서 등록한 명령에 대한 Task의 구현부분이다.
prvSetNetworkConfigCommand는 set-net 이라는 명령이 들어오면 수행되며, W7500의 IP주소, Subnet Mask, Gateway 주소를 변경하는 기능을 수행한다.
prvGetNetworkConfigCommand는 get-net 이라는 명령이 들어오면 수행되며, W7500의 Network 정보를 Serial로 출력하는 기능을 한다.

static BaseType_t prvSetNetworkConfigCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
const char *pcParameter;
char ip[16]={0};
char subnet[16]={0};
char gateway[16]={0};

BaseType_t xParameterStringLength, xReturn;

    /* Remove compile time warnings about unused parameters, and check the
    write buffer is not NULL.  NOTE - for simplicity, this example assumes the
    write buffer length is adequate, so does not check for buffer overflows. */
    ( void ) pcCommandString;
    ( void ) xWriteBufferLen;
    configASSERT( pcWriteBuffer );

    pcParameter = FreeRTOS_CLIGetParameter(pcCommandString, 1, &xParameterStringLength);
    strncat(ip, pcParameter, xParameterStringLength);
    setSIPR( str_to_ip_array(ip) );

    pcParameter = FreeRTOS_CLIGetParameter(pcCommandString, 2, &xParameterStringLength);
    strncat(subnet, pcParameter, xParameterStringLength);
    setSUBR( str_to_ip_array(subnet) ) ;

    pcParameter = FreeRTOS_CLIGetParameter(pcCommandString, 3, &xParameterStringLength);
    strncat(gateway, pcParameter, xParameterStringLength);
    setGAR( str_to_ip_array(gateway) );

    sprintf(pcWriteBuffer,SUCCESS_MESSAGE); 

    xReturn = pdFALSE;
    return xReturn;
}

static BaseType_t prvGetNetworkConfigCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
BaseType_t xReturn;
uint8_t tmp[8];
static int paramCount = 0;

    /* Remove compile time warnings about unused parameters, and check the
    write buffer is not NULL.  NOTE - for simplicity, this example assumes the
    write buffer length is adequate, so does not check for buffer overflows. */
    ( void ) pcCommandString;
    ( void ) xWriteBufferLen;
    configASSERT( pcWriteBuffer );


    switch(paramCount)
    {
        case 0:
            getSHAR(tmp);
            sprintf(pcWriteBuffer,"MAC ADDRESS : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\r\n",tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]); 
            xReturn = pdPASS;
            paramCount++;
            break;
        case 1:
            getSIPR(tmp);
            sprintf(pcWriteBuffer,"IP ADDRESS  : %d.%d.%d.%d\r\n",tmp[0],tmp[1],tmp[2],tmp[3]); 
            xReturn = pdPASS;
            paramCount++;
            break;
        case 2:
            getGAR(tmp);
            sprintf(pcWriteBuffer,"GW ADDRESS  : %d.%d.%d.%d\r\n",tmp[0],tmp[1],tmp[2],tmp[3]); 
            xReturn = pdPASS;
            paramCount++;
            break;
        case 3:
            getSUBR(tmp);
            sprintf(pcWriteBuffer,"SN MASK    : %d.%d.%d.%d\r\n",tmp[0],tmp[1],tmp[2],tmp[3]); 
            xReturn = pdPASS;
            paramCount++;
            break;
        default:
            paramCount=0;
            xReturn = pdFALSE;
            break;
    }

    return xReturn;
}

Demo

Demo를 위한 절차는 아래와 같다.

  1. get-net 명령을 이용하여 WIZwiki-W7500ECO Board의 Default Network 정보를 출력한다. PC에서 Ping을 보내서 IP 주소가 유효한지 확인한다.
  2. set-net 명령을 이용하여 IP주소를 192.168.77.50으로 변경한다. 변경 후 동일하게 PC에서 Ping을 보내, IP 주소가 유효한지 확인한다.


20150922_223157

Future Work

  1. W7500을 제어하기 위한 Command 보강
  2. FreeRTOS에서 W7500의 Network 기능을 사용하기 위한 Socket Porting


저작자 표시
신고
이 댓글을 비밀 댓글로
    • 2016.08.30 21:04
    비밀댓글입니다
  1. 안녕하세요^^
    메일로 답변 드렸습니다. 진행 하시다가 추가로 궁금하신 내용 있으면 문의 주세요^^

FreeRTOS Porting Guide(2/4)

Posted by 카이젠8501 카이젠8501
2015.10.08 15:16 IT 개발/Cortex

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

저작자 표시
신고
이 댓글을 비밀 댓글로

FreeRTOS Porting Guide (1/4)

Posted by 카이젠8501 카이젠8501
2015.10.08 15:14 IT 개발/Cortex

RTOS

실시간 운영 체제(문화어: 실시간조작체계) 또는 RTOS(←Real Time Operating System) 는 실시간 응용 프로그램을 위해 개발된 운영 체제이다. 운영 체제의 기능 중 CPU 시간 관리 부분에 초점을 맞추어 설계되었다. 실시간 운영 체제는 프로그래머가 프로세스 우선 순위에 더 많은 제어를 할 수 있게 한다. 응용 프로그램의 우선 순위가 시스템 프로그램의 우선 순위를 넘어설 수도 있다. 시스템 코드의 임계 구역을 최소화하였으며, 이를 통하여 응용 프로그램의 처리 요청을 정해진 시간 안에 처리해 줄 수 있다.

실시간 운영 체제의 핵심은 응용 프로그램의 테스크를 처리에 걸리는 시간을 일관되게 유지할 수 있는 정도에 있다. 처리 시간의 변동폭은 지터(jitter)라 부른다. 경성(hard) 실시간 운영 체제와 연성(soft) 실시간 운영 체제로 구분할 수 있으며, 전자가 후자에 비해 지터가 적다.

참고 : https://ko.wikipedia.org/wiki/%EC%8B%A4%EC%8B%9C%EA%B0%84_%EC%9A%B4%EC%98%81_%EC%B2%B4%EC%A0%9C

Ranking of RTOSs

Embedded_OS_List_Android_Linux_RTOS_2013

참고 : http://www.cnx-software.com/2013/05/21/2013-embedded-market-study-software-development-processors/

FreeRTOS

FreeRTOS는 오픈소스 실시간 내장형 운영체제 중 하나로, 단순함, 뛰어난 이식성, 간결함을 목표로 설계되었습니다. 실제로 운영체제 core 소스 코드가 4,000라인을 넘지 않으며, 불가피한 몇몇 코드를 제외한 거의 모든 code가 C 언어로 작성되어 이식성이 뛰어납니다. 현재 8051, AVR, PIC18, H8, MSP430, HCS12, PIC24, ARM Cortex-M3, ARM7, ARM9, AVR32, ColdFire, x86 등의 다양한 8bit, 16 bit, 32 bit 프로세서에 이식되어 있습니다.

FreeRTOS core는 아래와 같은 기능들을 제공합니다.

  • Mutlitasking
    • 선점형, 비선점형 선택 가능
    • Task 수 제한 없음
    • 동적으로 task 생성 가능
    • Priority 수 제한 없음
    • 두개 이상의 task에 같은 priority 할당 가능 (이 경우 라운드 로빈 스케쥴링)
  • Co-routine
  • Message queue
  • Semaphore
  • Memory Management

FreeRTOS는 변형된 GPL license 정책을 사용합니다. GPL license와 주된 차이점은 FreeRTOS와 link되어 실행되는 저작물을 공개할 의무가 없다는 것입니다. 이점은 보안이나 상업적인 목적으로 저작물에 대한 독점적 권리를 확보할 필요가 있을 때 이를 가능하게 합니다.

참고 : http://www.ubinos.org/mediawiki/index.php/FreeRTOS_%EC%86%8C%EA%B0%9C

저작자 표시
신고
Tags
이 댓글을 비밀 댓글로