Консоль FinSH Печать
Добавил(а) microsin   

На заре компьютерных технологий, до появления систем графики никто знать не знал про мышь или даже про клавиатуру. Как же люди в то время взаимодействовали с компьютерами? Ранние компьютеры принимали перфокарты для ввода команд и написания программ. Позже, по мере дальнейшей разработки компьютеров, мониторы и клавиатуры стали стандартом, однако операционные системы пока еще не поддерживали графический интерфейс. Компьютеры принимали текстовые команды, введенные пользователем в терминале, передавали их операционной системе, и та в ответ печатала на экране результат выполнения команды. Такая программа терминала работала как обертка вокруг операционной системы, отсюда и пошло её название - шелл (shell, в переводе с английского "оболочка", или "скорлупа").

Встраиваемые устройства обычно требуют соединения платы разработчика с хостом разработки (компьютер PC) для программирования и обмена данными в процессе разработки и отладки программ. Обычно для этой цели используют популярные интерфейсы: последовательный порт (UART), USB, Ethernet, Wi-Fi, и т. д. Гибкий шелл должен также поддерживать несколько методов соединения, чтобы разработчик мог легко запустить свой код и управлять системой с помощью команд. Особенно важно на этапе разработки, чтобы пользователь мог также использовать шелл для вызова тестовой функции, изменения рабочих параметров, быстро обнаружить причину проблемы с помощью вывода отладочных сообщений и тем самым сократить количество загрузок кода и сократить время разработки проекта.

FinSH это компонент для реализации командной строки (shell) операционной системы реального времени RT-Thread. FinSH произносится как [ˈfɪnʃ]. В этом описании (передвод документации [1]) представлена информация о принципах работы FinSH, как экспортировать в консоль FinSH свои собственные команды.

FinSH в системе RT-Thread используется для отладки или просмотра системной информации. Обмен с хостом разработчика происходит через интерфейсы serial/Ethernet/USB, и т. д.

FinSH hardware

Пользователь вводит команду в терминале управления, и эта команда обрабатывается кодом FinSH в устройстве. FinSH считывает введенную команду, обрабатывает её путем автоматического сканирования внутренней таблицы функций, находит соответствующее команде имя функции и выполняет эту функцию. В функцию могут быть также переданы дополнительные параметры, которые пользователь ввел вместе с командой функции. В ответ на терминале управления будет отображена информация о результатах выполнения функции.

При использовании последовательного порта (serial, обычно это UART) для подключения устройства к терминалу управления весь процесс обработки команды в FinSH выглядит следующим образом:

FinSH principle

FinSH поддерживает функционал проверки корректных прав доступа (rights verification). После запуска системы выполняется соответствующая проверка, и только после успешного прохождения этой проверки разрешается работа FinSH. Это повышает безопасность системы.

В текстовой консоли FinSH работает автозавершение команд (auto-completion) и просмотр истории команд примерно так, как это реализовано в терминалах Linux. В интерфейсе терминала поддерживается работа следующих клавиш:

Клавиши Функциональное описание
Tab Нажатие клавиши Tab, когда не введен ни один символ, приведет к выводу всех команд, поддерживаемых текущей системой. Если перед нажатием Tab было введено несколько символов, то произойдет вывод списка подходящих для этих символов команд или будет подставлена команда, если подходит только она одна (функция auto-completion). Также может быть после команды подставлено имя файла в соответствии с текущей директорией файловой системы. После этого можно дальше производить ввод и дальше использовать функции автозавершения имен файлов.
↑ ↓ Прокрутка вверх и вниз истории ранее введенных и сохраненных команд.
Backspace Удалить символ.
← → Переместить курсор влево или вправо.

FinSH поддерживает два режима ввода - режим традиционной командной строки и режим интерпретатора языка C.

Режим обычной командной строки. Этот режим также известен как msh (module shell). В msh-режиме FinSH реализован по принципу традиционного шелла (как он работает в dos/bash). Например, вы можете поменять текущую директорию на корневую командой "cd /".

MSH распознает параметры команд, которые отделены от команды и друг от друга пробелами. Формат выполнения команды следующий:

command [arg1] [arg2] [...]

Команда command может быть либо встроенной командой RT-Thread, либо исполняемым файлом.

Режим интерпретатора языка C. Этот режим также известен как "C-Style mode". В режиме интерпретатора языка C компонент FinSH может решать и анализировать большинство выражений языка C и использовать C-подобные функции и глобальные переменные в системе. Дополнительно в командной строке можно создавать переменные. В этом режиме команда должна быть введена подобно вызову функции в языке C, т. е. нужно ввести имя функции и круглые скобки (). Например, для вывода всех текущих потоков в системе и их состояния введите list_thread(), и FinSH напечатает соответствующую информацию. Вывод команды FinSH это значение, возвращаемое этой функцией. Для некоторых функций, у которых нет возвращаемого значения (возвращаемое значение void), может быть ничего не выведено.

Изначально компонент FinSH поддерживал только C-Style mode. Позже, по мере разработки RT-Thread, режим C-Style стал менее удобен для запуска скриптов или программ. Традиционная консоль шелла для ввода команд оказалась более подходящей для работы. Кроме того, поддержка C-Style mode в компоненте FinSH требует довольно много места под код. По этим причинам в RT-Thread был добавлен режим msh. Этот режим занимает немного ресурсов и прост в использовании.

Если в RT-Thread разрешены оба этих режима, то их можно динамически переключать. В режиме msh введите exit и нажмите Enter, чтобы переключиться в C-Style mode. В режиме C-Style введите msh() и нажмите Enter, чтобы войти в режим msh. Команды в обоих режимах не общие, и команда режима msh не может использоваться в режиме C-Style, и наоборот.

[Встроенные команды FinSH]

Некоторые команды FinSH встроены в RT-Thread по умолчанию. Вы можете вывести все поддерживаемые команды в текущей системе, если просто нажмете клавишу Tab, или введете команду help и нажмете Enter. Встроенные команды в режимах C-Style и msh в основном выполняют одни и те же функции, но вводятся они по-разному.

В режиме msh вы можете получить весь список команд нажатием клавиши Tab. Количество доступных по умолчанию команд не фиксированное, и различные компоненты RT-Thread будут выводить свои команды в FinSH. Наример, когда открыт компонент DFS, добавляются такие команды, как ls, cp и cd.

Ниже показан неполный пример вывода списка команд которые могут быть доступны по умолчанию. В этом списке присутствуют команды, которые показывают статус RT-Thread kernel. Имя команды в списке находится слева, и краткое описание команды справа:

RT-Thread shell commands:
version         - show RT-Thread version information
list_thread     - list thread
list_sem        - list semaphore in system
list_event      - list event in system
list_mutex      - list mutex in system
list_mailbox    - list mail box in system
list_msgqueue   - list message queue in system
list_timer      - list timer in system
list_device     - list device in system
exit            - return to RT-Thread shell mode
help            - RT-Thread shell help
ps              - List threads in the system
time            - Execute command with time
free            - Show the memory usage in the system

Состояние потоков. Используйте команду ps или list_thread, чтобы вывести информацию о потоках в системе, включая приоритет потока, состояние, максимальное использование стека потока, и другую информацию о каждом потоке.

msh />list_thread
thread   pri  status   sp_addr       sp     stack size  max used left tick  error
-------- ---  ------- ---------- ---------- ----------  -------- ---------- -----
ble        4  suspend 0x0041ed44 0x0041fc78 0x00001000    08%    0x00000005 000
core_thr   2  suspend 0x0041d724 0x0041de48 0x00000800    10%    0x00000004 000
wpas_thr   5  suspend 0x0041bc90 0x0041cb98 0x00001000    06%    0x00000005 000
kmsgbk     3  suspend 0x00419bfc 0x0041ab70 0x00001000    03%    0x00000005 000
tshell    20  ready   0x00417abc 0x00419950 0x00002000    04%    0x00000001 000
ntp_sync  26  suspend 0x00416da4 0x00417320 0x00000600    27%    0x00000001 000
tcpip      4  suspend 0x00415698 0x00415db8 0x00000800    10%    0x00000014 000
temp_det   5  suspend 0x00414f48 0x00415270 0x00000400    29%    0x00000003 000
tidle     31  ready   0x003fad7c 0x003faf00 0x00000200    24%    0x0000001f 000
timer      6  suspend 0x003fb160 0x003fc0e0 0x00001000    05%    0x0000000a 000
mythread   2  suspend 0x00413bb8 0x00414b30 0x00001000    03%    0x00000010 000

Описание полей таблицы информации о потоках:

Поле Описание
thread Имя потока.
pri Приоритет потока.
status Текущее состояние потока.
sp_addr Начало стека потока.
sp Текущая позиция стека потока.
stack size Размер стека потока.
max used Максимальное использование стека потоком, зарегистрированное в истории потоков.
left tick Количество оставшихся тиков потока.
error Код ошибки потока.

Состояние семафоров. Используйте команду list_sem, чтобы показать информацию по всем семафорам в системе, включая имя семафора и количество потоков, которое заблокировано в ожидании на каждом семафоре.

msh />list_sem
semaphore v   suspend thread
-------- --- --------------
rtos_sem 001 0
rtos_sem 000 1:kmsgbk
shrx     000 0
psem     001 0
pmq      001 0
scan_don 000 0
ap       000 0
scan_don 000 0
w0       000 0
TCM      001 0

Описание полей выведенной таблицы:

Поле Описание
semaphore Имя семафора.
v Текущее значение семафора.
suspend thread Количество потоков, которое заблокировано на этом семафоре и ожидает его публикации.

Статус событий. Используйте команду list_event, чтобы отобразить информацию о событиях в системе, включая имя события, значение события и количество потоков, ожидающих это событие.

msh />list_event
event      set    suspend thread
-----  ---------- --------------

Пример вывода этой команды показывает отсутствие в настоящий момент событий в системе. Описание полей выведенной таблицы:

Поле Описание
event Имя события.
set Текущее событие в наборе событий.
suspend thread Количество потоков, ожидающих событие в этом наборе событий.

Статус мьютексов. Используйте команду list_mutex для отображения информации мьютексов в системе, включающую имя мьютекса, владельца мьютекса и количество вложений захватов мьютекса владельцем.

msh />list_mutex
mutex    owner  hold suspend thread
------ -------- ---- --------------
flash  (NULL)   0000 0
mu0    (NULL)   0000 0
fslock (NULL)   0000 0
spi0   (NULL)   0000 0

Назначение полей выведенной таблицы:

Поле Описание
mutex Имя мьютекса.
owner Поток, который в настоящий момент захватил мьютекс.
hold Сколько раз владелец захватил этот мьютекс.
suspend thread Количество потоков, ожидающих освобождения этого мьютекса.

Состояние Mailbox. Используйте команду list_mailbox, чтобы отобразить информацию по всем mailbox-ам в системе, включая имя mailbox, количество в нем сообщений и максимальное количество сообщений, которое может содержать этот mailbox.

msh />list_mailbox
mailbox  entry size suspend thread
-------- ----  ---- --------------
rtos_que 0000  0020 1:ble
rtos_que 0000  0020 0
rtos_que 0000  0064 1:core_thr
rtos_que 0000  0064 1:wpas_thr
mbox0    0000  0008 1:tcpip
rtos_que 0000  0005 1:temp_det

Описание полей выведенной таблицы:

Поле Описание
mailbox Имя mailbox.
entry Количество сообщений в этом mailbox.
size Максимальное количество сообщений, которое может хранить в себе этот mailbox.
suspend thread Количество потоков, которое ожидает появления сообщений на этом mailbox.

Состояние очередей сообщений. Используйте команду list_msgqueue для отображения информации обо всех очередях в системе, включая имя очереди сообщений, количество хранящихся в этой очереди и количество потоков, ожидающих появления сообщений в этой очереди.

msh />list_msgqueue
msgqueue entry suspend thread
-------- ----  --------------

Описание полей выведенной таблицы:

Поле Описание
msgqueue Имя очереди сообщений.
entry Количество сообщений, которое в настоящее время хранится в этой очереди.
suspend thread Количество потоков, ожидающих появления сообщений на этой очереди.

Состояние пулов памяти. Используйте команду list_mempool, чтобы отобразить информацию о всех пулах памяти (memory pool) в системе, включая имя пула, его размер и максимальное использование памяти в нем.

msh />list_mempool
mempool block total free suspend thread
------- ----  ----  ---- --------------
signal  0012  0032  0032 0

Описание полей выведенной таблицы:

Поле Описание
mempool Имя пула памяти.
block Размер блока памяти.
total Общее задействованное пространство.
free Свободное пространство.
suspend thread Количество потоков, ожидающих этот memory pool.

Состояние таймера. Используйте команду list_timer для отображения информации о всех таймерах в системе, включая имя таймера, периодический это таймер или однократный, а также количество отсчетов таймера до момента его таймаута. Кроме того, выводится текущее значение счетчика тиков системы (current tick).

msh />list_timer
timer     periodic   timeout       flag
-------- ---------- ---------- -----------
rtos_per 0x00003a98 0x002eb3db activated
ble      0x00000000 0x00000000 deactivated
core_thr 0x00000000 0x00000000 deactivated
wpas_thr 0x00000000 0x00000000 deactivated
kmsgbk   0x00000000 0x00000000 deactivated
tshell   0x00000000 0x00000000 deactivated
ntp_sync 0x0036ee80 0x00376605 activated
tcpip    0x00000064 0x002e9d07 activated
temp_det 0x00000000 0x00000000 deactivated
tidle    0x00000000 0x00000000 deactivated
timer    0x00003a98 0x002eb3db activated
mythread 0x0000000a 0x002e9cda activated
current tick:0x002e9cd4

Описание полей выведенной таблицы:

Поле Описание
timer Имя таймера.
periodic Является ли таймер периодическим.
timeout Количество тиков таймера, когда истек его таймаут.
flag Состояние таймера, активен он или нет.

Состояние устройств. Используйте команду list_device для отображения информации о всех устройствах в системе, включающую имя устройства, тип устройства и количество открытий устройства.

msh />list_device
device           type         ref count
-------- -------------------- ----------
rtc      RTC                  0
wdt      Miscellaneous Device 0
sys_ctrl Miscellaneous Device 0
gpio     Miscellaneous Device 0
disk0    Block Device         0
ap       Network Interface    0
w0       Network Interface    0
gspi     SPI Device           0
spi0     SPI Bus              0
uart2    Character Device     2
uart1    Character Device     0

Описание полей выведенной таблицы:

Поле Описание
device Имя устройства.
type Тип устройства.
ref count Сколько раз это устройство было открыто.

Состояние динамической памяти. Используйте команду free для отображения информации о памяти кучи в системе (heap).

msh />free
memheap  pool size  max used size available size
------- ---------- ------------- --------------
TCM     121040     54788         74900

Примечание: куч в системе может быть несколько, но обычно куча одна. В этом примере TCM расшифровывается как Thread Control Memory, т. е. это память, откуда динамически выделяется память для системных данных потоков и других объектов. В других системах RTOS память для информации о потоке может называться TCB (Thread Control Block).

Описание полей выведенной таблицы:

Поле Описание
memheap Имя кучи.
pool size Размер кучи.
max used size Максимальное использование кучи, сохраненное в истории.
available size Текущее свободное пространство в куче.

[Добавление пользовательских команд для режима msh]

В режиме msh может быть запущена команда, которую определил пользователь. Для экспорта (добавления) своей команды в режим msh вы можете использовать следующий макрос:

MSH_CMD_EXPORT(name, desc);

Параметры макроса:

Параметр Описание
name Добавляемая команда.
desc Описание команды.

Команда может экспортироваться как с параметрами, так и без параметров. Когда экспортируется команда без параметров, функция команды имеет тип void. Например:

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}
 
MSH_CMD_EXPORT(hello, say hello to RT-Thread);

Когда экспортируется команда с параметрами, то функция команды должна быть определена с параметрами int argc и char** argv. По традиции argc представляет количество аргументов, и argv это указатель на массив строк, где находятся параметры. Пример определения функции, для команда которой экспортируется с поддержкой параметров:

static void atcmd(int argc, char**argv)
{
   ...
}
 
MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd < server|client >);

[Команды и переменные режима C-Style]

Экспорт пользовательских команд для режима C-Style может использовать следующий интерфейс:

FINSH_FUNCTION_EXPORT(name, desc); 

Параметры макроса:

Параметр Описание
name Команда для экспорта.
desc Описание команды.

Следующий пример определяет функцию hello и экспортирует её как команду в режиме C-Style:

void hello(void)
{
   rt_kprintf("hello RT-Thread!\n");
}
 
FINSH_FUNCTION_EXPORT(hello, say hello to RT-Thread);

Подобным образом вы можете также экспортировать переменную, которая будет доступна в интерфейсе режима C-Style:

FINSH_VAR_EXPORT(name, type, desc); 

Параметры макроса:

Параметр Описание
name Переменная для экспорта.
type Тип переменной.
desc Описание экспортируемой переменной.

Следующий пример определит переменную dummy и экспортирует её в переменные режима команд C-Style:

static int dummy = 0;
FINSH_VAR_EXPORT(dummy, finsh_type_int, dummy variable for finsh);

Переименование пользовательской команды. Длина имени функции в FinSH ограничена. Это управляется значением в макросе FINSH_NAME_MAX (находится в заголовочном файле finsh.h). По умолчанию установлена длина имени 16, т. е. команда FinSH по длине не превысит 16 байт. Здесь скрыта потенциальная проблема: когда имя функции длиннее FINSH_NAME_MAX, после использования макроса FINSH_FUNCTION_EXPORT для экспорта функции в таблицу команд в таблице символов FinSH будет видно полное имя, однако выполнение команды приведет к ошибке "null node error". Причина в том, что хотя полное имя и отображается, фактически FinSH сохранит только первые 16 байт в качестве команды. Слишком много введенных символов команды не будут корректно найдены. В этом случае вы можете использовать FINSH_FUNCTION_EXPORT_ALIAS для повторного экспорта имени команды.

FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc); 

Параметры макроса:

Параметр Описание
name Имя команды для экспорта.
alias Имя, которое отображается при экспорте в FinSH.
desc Описание экспортируемой команды.

Команда может быть экспортирована в режим msh добавлением __cmd_ к имени для переименования. Иначе команда будет экспортирована в режим C-Style. Следующий пример определяет функцию hello, переименовывает её в ho и экспортирует в команду режима C-Style.

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}
 
FINSH_FUNCTION_EXPORT_ALIAS(hello, ho, say hello to RT-Thread);

[Конфигурация FinSH]

Функционал FinSH может быть урезан или расширен, для этого макросы опций определены в заголовочном файле rtconfig.h. В следующей таблице приведены макросы конфигурации FinSH. Тип значения "None" обозначает, что если макрос определен (не закомментирован), то соответствующая опция разрешена (on).

Макрос Тип значения Описание По умолчанию
RT_USING_FINSH None Разрешает FinSH on
FINSH_THREAD_NAME Строка Имя для потока FinSH "tshell"
FINSH_USING_HISTORY None Разрешает историю команд on
FINSH_HISTORY_LINES int Максимальное отслеживаемое количество команд в истории 5
FINSH_USING_SYMTAB None Может ли в FinSH быть использована таблица символов on
FINSH_USING_DESCRIPTION None Добавлять описание для каждого символа FinSH on
FINSH_USING_MSH None Разрешить режим msh on
FINSH_USING_MSH_ONLY None Использовать только режим msh on
FINSH_ARG_MAX int Максимальное количество входных параметров 10
FINSH_USING_AUTH None Разрешить проверку прав доступа к FinSH off
FINSH_DEFAULT_PASSWORD Строка Пароль для проверки авторизации off

Ниже приведен пример образца конфигурации в rtconfig.h, и его можно изменить в соответствии с реальными требованиями к необходимому функционалу консоли.

/* Компонент консоли FinSH разрешен: */
#define RT_USING_FINSH
 
/* Имя потока для FinSH определено как tshell: */
#define FINSH_THREAD_NAME "tshell"
 
/* История команд разрешена: */
#define FINSH_USING_HISTORY
 
/* В историю может быть записано до 5 последних команд: */
#define FINSH_HISTORY_LINES 5
 
/* Клавиша Tab разрешена: */
#define FINSH_USING_SYMTAB
 
/* Описание команд включено: */
#define FINSH_USING_DESCRIPTION
 
/* Приоритет потока FinSH: */
#define FINSH_THREAD_PRIORITY 20
 
/* Определение размера стека потока: */
#define FINSH_THREAD_STACK_SIZE 4096
 
/* Длина команды ограничена 80 байтами: */
#define FINSH_CMD_SIZE 80
 
/* Функционирование msh разрешено: */
#define FINSH_USING_MSH
 
/* По умолчанию используется режим msh: */
#define FINSH_USING_MSH_DEFAULT
 
/* Максимальное количество входных параметров: */
#define FINSH_ARG_MAX 10

[Примеры приложения FinSH]

Команда msh без аргументов. В коде реализована функция hello, и она экспортирована в команды FinSH макросом MSH_CMD_EXPORT.

#include < rtthread.h>
 
void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}
 
MSH_CMD_EXPORT(hello , say hello to RT-Thread);

После того, как система загрузилась и заработала, нажатие клавиши Tab в консоли FinSH выведет команду hello в общем списке команд:

msh />
RT-Thread shell commands:
hello             - say hello to RT-Thread
version           - show RT-Thread version information
list_thread       - list thread

Запуск команды hello:

msh />hello
hello RT_Thread!
msh />

Команда msh с параметрами. В следующем примере кода создана функция atcmd(), и она экспортирована в список команд msh command с помощью макроса MSH_CMD_EXPORT.

#include < rtthread.h>
 
static void atcmd(int argc, char**argv)
{
    if (argc < 2)
    {
        rt_kprintf("Please input'atcmd < server|client >'\n");
        return;
    }
 
    if (!rt_strcmp(argv[1], "server"))
    {
        rt_kprintf("AT server!\n");
    }
    else if (!rt_strcmp(argv[1], "client"))
    {
        rt_kprintf("AT client!\n");
    }
    else
    {
        rt_kprintf("Please input'atcmd < server|client >'\n");
    }
}
 
MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd < server|client >);

После того, как система загрузилась и заработала, нажатие клавиши Tab в консоли FinSH выведет команду atcmd в общем списке команд:

msh />
RT-Thread shell commands:
hello             - say hello to RT-Thread
atcmd             - atcmd sample: atcmd < server|client >
version           - show RT-Thread version information
list_thread       - list thread
...

Запуск команды atcmd без параметров:

msh />atcmd
Please input 'atcmd < server|client >'
msh />

Запуск команды atcmd с параметром server:

msh />atcmd server
AT server!
msh />

Запуск команды atcmd client:

msh />atcmd client
AT client!
msh />

[Портирование FinSH]

Код FinSH написан полностью на ANSI C и обладает отличной портируемостью. Требуемый объем места под код небольшой, и динамически выделяемая память (куча) использоваться не будет, если вы не применяете функции, описанные выше, для динамического добавления символов в FinSH. Исходный код FinSH находится в директории components/finsh. Портирование FinSH требует внимания к следующим аспектам:

Поток FinSH. Каждая выполняемая команда работает в контексте потока FinSH, т. е. в потоке с именем (по умолчанию) tshell. Когда определен макрос RT_USING_FINSH, поток FinSH может быть инициализирован вызовом finsh_system_init() из потока инициализации. В RT-Thread 1.2.0 и более свежей версии не требуется использовать функцию finsh_set_device(const char* device_name) для явного указания используемого устройства. Вместо этого вызывается функция rt_console_get_device() для использования устройства консоли. Функция finsh_set_device(const char* device_name) должна использоваться в FinSH 1.1.x и более старой версии, чтобы указать устройство, которое использует FinSH. Поток FinSH создается в функции finsh_system_init(), которая будет ждать публикации семафора rx_sem.

Вывод FinSH. Вывод из FinSH зависит от вывода системы, и полагается на функцию rt_kprintf(). В startup-функции rt_hw_board_init() функция rt_console_set_device(const char* name) устанавливает устройство печати для FinSH.

Ввод FinSH. После получения семафора rin_sem поток FinSH вызовет функцию rt_device_read(), чтобы взять символ из устройства ввода (в качестве устройства выбран последовательный интерфейс), и затем обрабатывает введенный символ. Таким образом, миграция FinSH требует реализации функции rt_device_read(). Освобождение семафора rx_sem semaphore завершает оповещение ввода в поток FinSH путем вызова функции rx_indicate(). Обычно процесс заключается в том, что при возникновении прерывания последовательного порта (т. е. пришел введенный пользователем символ) обработчик прерывания вызовет функцию rx_indicate(), чтобы оповестить поток FinSH о вводе, и затем поток FinSH берет введенные в последовательный порт данные, и выполняет соответствующую обработку команды.

[Ссылки]

1. FinSH Console site:rt-thread.io.