티스토리 뷰

심화

User Interface - CLI2

Just4Fun 2017. 7. 9. 15:00

앞의 글에서 설명한 CLI 구현 방식에는 조금 불편한 내용이 있다.

새로운 명령을 추가하거나 삭제하기 위해서는 cli_command[] 배열을 수정해야 된다는 것이다.  이것이 크게 문제 되지 않기는 하겠지만 때로는 상당히 번거로운 일이 되기도 한다.

이번 글에서는 좀 더 쉬운 방법을 통해서 CLI 처리 부분에 대한 소스 코드 수정없이 새로운 명령을 추가 하는 방법에 대해서 설명 하도록 하겠다.

이 방법은 링커 프로그램을 이용하는 방법이다.

따라서 이 기능을 위해서 링커 스크립트 파일을 수정하도록 하겠다.

1
2
3
4
5
6
7
8
9
10
.text :
{
    KEEP(*(.vectors.table))
    KEEP(*(.vectors.code))
    *(.text .text.*)
    __cli_start = .;
    KEEP(*(SORT(.cli.*)))
    __cli_end = .;
    *(.rodata .rodata.*)
} > rom

6~8번 줄에 .cli.*에 해당되는 섹션으로 선언된 것들을 .text 섹션에 포함되도록 한다.

그리고 command.h 파일에 다음과 같은 내용을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
struct cli_cmd
{
    char    *cmd_name;
    char    *cmd_help;
    void    (*cli_func)(int , char **);
};
typedef struct cli_cmd  cli_cmd_t;
 
#define CLI_CMD(name, help, cmd)    \
    cli_cmd_t __cli_cmd_##name __attribute__ ((section (".cli."#name))) = \
    {#name, help, cmd}

9~11번 줄은 CLI_CMD라는 매크로를 만들어 주는데 주의해서 보아야 할 내용은 CLI_CMD 매크로를 사용하면 .cli. 섹션으로 선언된다는 것이다.

예를 들어,

1
2
3
4
5
static void do_help(int argc, char **argv)
{
}
 
CLI_CMD(help, "Display CLI command usage", do_help);

위와 같이 만들어 프로젝트를 빌드하고 난 후, map 파일을 열어보면 다음과 같은 결과를 볼 수 있다.

1
2
3
4
5
               0x08002744                __cli_start = .
*(SORT(.cli.*))
.cli.help      0x08002744        0xc cli.o
               0x08002744                __cli_cmd_help
               0x08002750                __cli_end = .

0x08002744 번지에 __cli_cmd_help라는 이름의 배열이 하나 생성되어 있고 .cli.help라는 섹션안에 포함되어 있는 것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
extern cli_cmd_t __cli_start, __cli_end;
 
void run_cli(void)
{
    cli_cmd_t   *cli_cmd = &__cli_start;
    int         num_command = &__cli_end - &__cli_start;
 
    int     argc;
    char    **argv;
    int     i;
 
    printf("[%s]$ ", BD_PROMPT);
    parse_command(wait_command(), &argc, (void*)&argv);
 
    if (!argc)
        return;
 
    for (i=0; i<num_command; i++, cli_cmd++)
    {
        if (strncmp(cli_cmd->cmd_name, argv[0], strlen(argv[0])) == 0)
        {
            cli_cmd->cli_func(argc, argv);
            break;
        }
    }
 
    if (num_command <= i)
    {
        printf("You entered wrong command: \"%s\"\n", argv[0]);
        do_help(0, 0);
    }
}

1번 줄은 링커에서 생성한 심볼을 C 코드에서 참조하기 위해 선언된 코드이다.

6번 줄은 .cli 섹션안에 몇개의 테이블이 있는지 계산하는 코드이다.

프로그램을 빌드한 후 보드에서 실행하면 다음과 같은 결과를 볼 수 있다.

현재까지 등록된 명령어는 help 하나 밖에 없기 때문에 help 명령만 보여 진다.

여기까지만 코드를 만들어 놓으면 더이상 CLI를 처리하기 위한 코드는 수정할 필요가 없다.  새로운 명령을 추가하기 위해서는 CLI_CMD 매크로만 이용하면 된다.

toys.c 파일과 media.c 파일에 각각 다음과 같은 코드를 추가한 후 결과가 어떻게 나오는지 확인해 본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
toys.c
 
static void do_toys(int argc, char **argv)
{
    puts("Toys & Hobbies\n");
}
 
CLI_CMD(toys, "Toys & Hobbies", do_toys);
 
 
 
media.c
 
static void do_medias(int argc, char **argv)
{
    puts("DVDs & Movies\n");
}
 
CLI_CMD(medias, "DVDs & Movies", do_medias);

단지 CLI_CMD 매크로만 이용해서 새로운 명령어가 추가 되었고, 각 명령어를 실행하였을때 정상적으로 명령이 실행되는것을 볼 수 있다.

map 파일을 열어 보면 새로운 명령어가 추가 되었음을 볼 수 있다.

1
2
3
4
5
6
7
8
9
               0x0800275c                __cli_start = .
*(SORT(.cli.*))
.cli.help      0x0800275c        0xc cli.o
               0x0800275c                __cli_cmd_help
.cli.medias    0x08002768        0xc media.o
               0x08002768                __cli_cmd_medias
.cli.toys      0x08002774        0xc toys.o
               0x08002774                __cli_cmd_toys
               0x08002780                __cli_end = .


이제 마지막으로 books 명령어를 추가해 보도록 하겠다.  books.c 파일에 다음과 같은 코드를 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
struct cli_cmd book_cmd[] =
{
    {"art", "Arts & Photography", do_art},
    {"comic", "Comics & Graphic Novels", do_comics},
    {"history", "History", do_history},
    {"sf", "Science Fiction", do_sf},
};
static int books_items = array_num(book_cmd);
 
static void do_art(int argc, char **argv)
{
    puts("Arts & Photography\n");
}
static void do_comics(int argc, char **argv)
{
    puts("Comics & Graphic Novels\n");
}
static void do_history(int argc, char **argv)
{
    puts("History\n");
}
static void do_sf(int argc, char **argv)
{
    puts("Science Fiction\n");
}
static void book_help(void)
{
    int     i;
 
    printf("You can select one of below item\n");
    for (i=0; i<books_items; i++)
        printf("%-10s : %s\n", book_cmd[i].cmd_name, book_cmd[i].cmd_help);
}
 
static void do_books(int argc, char **argv)
{
    int     i;
 
    if (argc == 1)
    {
        book_help();
        return;
    }
 
    for (i=0; i<books_items; i++)
    {
        if (strncmp(book_cmd[i].cmd_name, argv[1], strlen(argv[1])) == 0)
        {
            book_cmd[i].cli_func(argc, argv);
            break;
        }
    }
}
 
CLI_CMD(books, "Books", do_books);

앞의 글에서 사용하였던 코드이다.

프로젝트를 다시 빌드하여 실행해 본다.

의도했던대로 명령어가 제대로 실행되는 것을 확인할 수 있다.


ui_cli2.zip


'심화' 카테고리의 다른 글

User Interface - Command Line Interface(CLI)  (0) 2017.07.09
User Interface - 메뉴 방식  (0) 2017.07.08
User Interface - argc, argv  (0) 2017.07.07
User Interface - UART 문자열 입력  (0) 2017.06.29
User Interface - UART 특수키 처리  (0) 2017.06.24
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함