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

2015. 10. 8. 15:192018년 이전 관심사/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


반응형