본문 바로가기
Embedded SW/Embedded SW Introduction

Shell이란? UART를 이용한 Shell 기능 구현 1편 (TC275 인피니언 MCU)

by 방구석 임베디드 2022. 9. 18.
반응형

안녕하세요.

오늘은 좀 재미있는 기능을 한번 구현해 보려고 합니다.

우리는 MCU안에 있는 UART라는 주변기기를 이용하여 컴퓨터와 라인을 연결하여

통신을 수행할수 있습니다.

이때 Shell기능도 UART를 이용하여 구현할 수 있는데요.

 

혹시 Shell이라고 들어 보셨나요?

터미널? Shell?

Shell을 위키에서 검색해 보면 아래와 같습니다.

Shell은 운연체제에서 커널과 이용자 사이에서

이용자가 cmd로 명령을 주면 그 명령을 해석하여 

커널의 처리결과를 뿌려주는 프로그램입니다.

 

쉽게 의미해서 저는 아래와 같은 shell을 만들수 있습니다.

만일 uart를 통해

a라고 컴퓨터로 입력을 보내면

MCU에서는 ADC Data를 취득해서 저에게 그 값을 보내줄수 있습니다.

물론 이것은 제가 하나의 시나리오로 만든 것입니다.

a를 보내면 특정라인의 adc값을 취득해서 보내줘!

무언가 값을 계속 보내는 것이 아니라

event 개념으로 동작해서 저에게 뿌려줍니다.

즉, Uart를 이용하여 우리가 원하는 정보를 요청하면

그것에 대한 결과를 뿌려주는 것입니다.

 

이렇게 임베디드 시스템에 직접 Shell을 이용하면

정말 좋은 디버깅을 수행할수 있습니다.

 

디버거가 연결되어 있는데 이런한 작업이 필요할까요?

사실 요즘 디버거가 좋아서 이러한 작업은 필요하지 않습니다.

 

하지만 집에서 개발하는 디버거가 좀 많이 불편하기 때문에

이렇게 shell을 이용하여 쉽게 디버깅을 하실수 있습니다.

또한 이렇게 uart로 명령을 주어서

다양한 SW 모드 전환도 할 수 있습니다.

그리고 shell은 회사에서도 가끔씩 사용합니다.

개발과정에서 디버거가 좋을 지라도,

챔버나, 고온, 고압에서 동작을 할경우

디버거 연결을 하지 못할 경우도 많이 있습니다.

이럴때 uart 라인만 잘 빼서,

개발을 수행하면 손쉬운 디버깅이 가능하게 됩니다.​

 

shell을 잘 만들어서 사용하는것은 여로모로 필요한 일이기 때문에

이 글에서 한번 다루어 보도록 하겠습니다.

저는 인피니언 MCU TC275를 이용하여 Shell 기능을 구현할 것입니다.

먼저 인피니언에서 제공해주는 예제 데모 코드를 포팅해서 동작시켜 보도록 하겠습니다.

그리고 Infienon에서 제공해주는 Shell을 포팅해서 사용하면서

무언가 Shell에 대한  Insight를 얻어 보도록 하겠습니다.

 

우선 예제코드는 아래와 같습니다.

Shell을 포팅해 보겠습니다.

드디어 포팅이 완료되었습니다.

Shell 포팅은 조금 어렵네요 ㅎㅎ

그러면 한번 코드를 살펴 보도록 하겠습니다.

초기화 코드부터 보도록 하겠습니다.

void AsclinShellInterface_init(void)
{
    /** - Initialise the time constants */
    initTime();

    /** - Initialise the serial interface and the console */
    initSerialInterface();

    g_AsclinShellInterface.info.srcRev      = SW_REVISION;
    g_AsclinShellInterface.info.srcRevDate  = SW_REVISION_DATE;
#if defined(__TASKING__)
    g_AsclinShellInterface.info.compilerVer = 0;// SW_COMPILER_VERSION;
#else
    g_AsclinShellInterface.info.compilerVer = 0;
#endif

    /** - Enable the global interrupts of this CPU */
    restoreInterrupts(TRUE);

    Ifx_SizeT count = 52;

    Ifx_Console_print(ENDL);

    /** - Simple print using IfxAsclin_Asc_write API */
    IfxAsclin_Asc_write(&g_AsclinShellInterface.drivers.asc, "Hello world!  => print using IfxAsclin_Asc_write()\n", &count, TIME_INFINITE);

    /** - Simple print using Ifx_Console_print API */
    Ifx_Console_print(ENDL "Hello world!  => print using Ifx_Console_print()"ENDL);

    /** - Simple print using IfxStdIf_DPipe_print API */
    IfxStdIf_DPipe_print(&g_AsclinShellInterface.stdIf.asc, ENDL "Hello world!  => print using IfxStdIf_DPipe_print()"ENDL);

    /** - Show the welcome screen using the standard DPipe interface */
    welcomeScreen(&g_AsclinShellInterface, &g_AsclinShellInterface.stdIf.asc);

    /** - Initialise the shell interface  */
    {
        Ifx_Shell_Config config;
        Ifx_Shell_initConfig(&config);
        config.standardIo     = &g_AsclinShellInterface.stdIf.asc;
        config.commandList[0] = &AppShell_commands[0];

        Ifx_Shell_init(&g_AsclinShellInterface.shell, &config);
    }

    Ifx_Console_print(ENDL "Enter 'help' to see the available commands"ENDL);
}

전형적인 인피니언 초기화 코드이네요!

구조체에 우리가 원하는 설정을 집어넣고

레지스터를 초기화 합니다.

하지만 결국 UART 기반입니다.

 

먼저 UART를 컴퓨터에 연결시키고 테라텀을 한번 연결하여 동작시켜 보도록 하겠습니다.

혹시 테라텀에 대해서 궁금하신 분들은 아래 링크을 따라가시면

테라텀에 대해서 잘 아실 수 있으실 것입니다. (예전에 쓴 글입니다 ^^)

Teraterm 시리얼 통신 터미널 프로그램 사용법, 다운로드 방법 (feat 허큘리스)

 

실행시키면 아래와 같은 창이 뜨는것을 확인 할 수 있습니다.

무언가 창에 많은것이 떳네요 ㅎㅎ

여기서 창에 help를 기입하니

위와 같이 다양한 shell 명령어가 나오고 있는것을 알 수 있습니다.

즉, 이 shell이라는 데모코드는

shell 명령어를 입력하고

이 명령어에 따른 collback 함수를 등록해 주면

그 함수가 불려서 다시 uart로 뿌려주는 구조로 되어 있는것 같습니다.

 

그리고, 아래와 같이 처음에 창에 뿌려주는 데이터가 아래

welcomeScreen함수로 정이되어 있는것을 볼수 있습니다.

void welcomeScreen(App_AsclinShellInterface *app, IfxStdIf_DPipe *io)
{
    IfxStdIf_DPipe_print(io, ENDL ""ENDL);
    IfxStdIf_DPipe_print(io, "******************************************************************************"ENDL);
    IfxStdIf_DPipe_print(io, "*******                                                                *******"ENDL);
    IfxStdIf_DPipe_print(io, "*******  Infineon "IFXGLOBAL_DERIVATIVE_NAME " uC                                             *******"ENDL);
    IfxStdIf_DPipe_print(io, "******************************************************************************"ENDL);
    IfxStdIf_DPipe_print(io, "******************************************************************************"ENDL);
    IfxStdIf_DPipe_print(io, "******* Copyright (c) 2013 Infineon Technologies AG.                   *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* All rights reserved.                                           *******"ENDL);
    IfxStdIf_DPipe_print(io, "*******                                                                *******"ENDL);
    IfxStdIf_DPipe_print(io, "*******                        IMPORTANT NOTICE                        *******"ENDL);
    IfxStdIf_DPipe_print(io, "*******                                                                *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* Infineon Technologies AG (Infineon) is supplying this file for *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* use exclusively with Infineon's microcontroller products. This *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* file can be freely distributed within development tools that   *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* are supporting such microcontroller products.                  *******"ENDL);
    IfxStdIf_DPipe_print(io, "*******                                                                *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* THIS SOFTWARE IS PROVIDED 'AS IS'.  NO WARRANTIES, WHETHER     *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO,  *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A        *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. INFINEON SHALL NOT, *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR    *******"ENDL);
    IfxStdIf_DPipe_print(io, "******* CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.              *******"ENDL);
    IfxStdIf_DPipe_print(io, "******************************************************************************"ENDL);
    IfxStdIf_DPipe_print(io, "*******  Software Version Date : %4X/%2X/%2X                            *******"ENDL, app->info.srcRevDate >> 16, (app->info.srcRevDate >> 8) & 0xFF, (app->info.srcRevDate & 0xFF));
    IfxStdIf_DPipe_print(io, "*******  Software Version      : %2d.%02d                                 *******"ENDL, (app->info.srcRev >> 8) & 0xFF, (app->info.srcRev >> 0) & 0xFF);
    IfxStdIf_DPipe_print(io, "*******  iLLD version          : %d.%d.%d.%d.%d                             *******"ENDL, IFX_LLD_VERSION_GENERATION, IFX_LLD_VERSION_MAJOR, IFX_LLD_VERSION_MAJOR_UPDATE, IFX_LLD_VERSION_MINOR, IFX_LLD_VERSION_REVISION);
    IfxStdIf_DPipe_print(io, "*******  "COMPILER_NAME " Compiler      : %ld.%1dr%1d                                 *******"ENDL, (app->info.compilerVer >> 16) & 0xFF, (app->info.compilerVer >> 8) & 0xFF, (app->info.compilerVer >> 0) & 0xFF);
    IfxStdIf_DPipe_print(io, "*******  Author                : Alann Denais (ATV SYS SE)             *******"ENDL);
    IfxStdIf_DPipe_print(io, "******************************************************************************"ENDL);
}

지금, 초기화 코드에 미리 설정한 printf 함수를 통해

내용을 uart를 이용하여 terminal로 보내고 있습니다.

결국 uart 기반입니다.

 

그리고나서,

help를 입력하니

다양한 shell 명령어가 나오고 있는것을 볼 수 있습니다.

status를 한번 처볼게요.

status를 치니까

아래와 같이 현재 시간을 응답하고 있는것을 볼 수 있습니다.

Real-time: 00:00:00

위의 데이터는

MCU가 터미널로 뱉어주고 있는 데이터를 의미합니다.

조금더 코드를 살펴 보겠습니다.

const Ifx_Shell_Command AppShell_commands[] = {

{"status", " : Show the application status", &g_AsclinShellInterface, &AppShell_status, },

{"info", " : Show the welcome screen", &g_AsclinShellInterface, &AppShell_info, },

{"help", SHELL_HELP_DESCRIPTION_TEXT, &g_AsclinShellInterface.shell, &Ifx_Shell_showHelp, },

IFX_SHELL_COMMAND_LIST_END

};

위와 같이 shell에 대한 명령어가 정의되어 있는것을 볼 수 있습니다.

만일 status를 쳤다고 가정한다면

아래 함수가 동작을 하게 되는데

아까 보았던 Real-time에 대한 응답을 주고 있는것을 볼수 있습니다.
boolean AppShell_status(pchar args, void *data, IfxStdIf_DPipe *io)
{
    if (Ifx_Shell_matchToken(&args, "?") != FALSE)
    {
        IfxStdIf_DPipe_print(io, "Syntax     : status"ENDL);
        IfxStdIf_DPipe_print(io, "           > Show the application internal state"ENDL);
    }
    else
    {
        Ifx_DateTime rt;
        DateTime_get(&rt);
        IfxStdIf_DPipe_print(io, "Real-time: %02d:%02d:%02d"ENDL, rt.hours, rt.minutes, rt.seconds);
        //IfxStdIf_DPipe_print(io, "CPU Frequency: %ld Hz"ENDL, (sint32)g_AppCpu0.info.cpuFreq);
        //IfxStdIf_DPipe_print(io, "SYS Frequency: %ld Hz"ENDL, (sint32)g_AppCpu0.info.sysFreq);
        //IfxStdIf_DPipe_print(io, "STM Frequency: %ld Hz"ENDL, (sint32)g_AppCpu0.info.stmFreq);
    }

    return TRUE;
}

 

결국, uart 기반으로

언하는 문장을 uart로 받으면

그것에 대한 응답을 다시 uart로 전달해 주는 

본연의 기능을 수행하고 있는 것입니다.

 

단지 조금더 멋지게 만들었을 뿐입니다.

 

우리는 이러한 인피니언에서 제공하는 shell을 잘 갔다가 쓰고

위에 함수를 정의하여 등록하고

우리가 원하는 값을 피드백 받으면 됩니다.

하지만 나중에 포팅해서 사용하시면 아시겠지만

사용하는데 조금 불편함이 있습니다.

우선 포팅한 프로젝트는 아래 링크에서 다운로드 가능합니다.

https://cafe.naver.com/binaryembedded/155

 

저는 지금부터 최대한 직접 다시 Shell 기능을 만들어서 동작 시켜 보도록 하겠습니다.

다음 글부터 다시 시작할께요!!

반응형

댓글