Консоль 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 в устройстве. FinSH считывает введенную команду, обрабатывает её путем автоматического сканирования внутренней таблицы функций, находит соответствующее команде имя функции и выполняет эту функцию. В функцию могут быть также переданы дополнительные параметры, которые пользователь ввел вместе с командой функции. В ответ на терминале управления будет отображена информация о результатах выполнения функции. При использовании последовательного порта (serial, обычно это UART) для подключения устройства к терминалу управления весь процесс обработки команды в FinSH выглядит следующим образом: FinSH поддерживает функционал проверки корректных прав доступа (rights verification). После запуска системы выполняется соответствующая проверка, и только после успешного прохождения этой проверки разрешается работа FinSH. Это повышает безопасность системы. В текстовой консоли FinSH работает автозавершение команд (auto-completion) и просмотр истории команд примерно так, как это реализовано в терминалах Linux. В интерфейсе терминала поддерживается работа следующих клавиш:
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
Описание полей таблицы информации о потоках:
Состояние семафоров. Используйте команду 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
Описание полей выведенной таблицы:
Статус событий. Используйте команду list_event, чтобы отобразить информацию о событиях в системе, включая имя события, значение события и количество потоков, ожидающих это событие. msh />list_event
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
Назначение полей выведенной таблицы:
Состояние 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
Описание полей выведенной таблицы:
Состояние очередей сообщений. Используйте команду list_msgqueue для отображения информации обо всех очередях в системе, включая имя очереди сообщений, количество хранящихся в этой очереди и количество потоков, ожидающих появления сообщений в этой очереди. msh />list_msgqueue
msgqueue entry suspend thread
-------- ---- --------------
Описание полей выведенной таблицы:
Состояние пулов памяти. Используйте команду list_mempool, чтобы отобразить информацию о всех пулах памяти (memory pool) в системе, включая имя пула, его размер и максимальное использование памяти в нем. msh />list_mempool
mempool block total free suspend thread
------- ---- ---- ---- --------------
signal 0012 0032 0032 0
Описание полей выведенной таблицы:
Состояние таймера. Используйте команду 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
Описание полей выведенной таблицы:
Состояние устройств. Используйте команду 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
Описание полей выведенной таблицы:
Состояние динамической памяти. Используйте команду 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). Описание полей выведенной таблицы:
[Добавление пользовательских команд для режима msh] В режиме msh может быть запущена команда, которую определил пользователь. Для экспорта (добавления) своей команды в режим msh вы можете использовать следующий макрос: MSH_CMD_EXPORT(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); Параметры макроса:
Следующий пример определяет функцию 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); Параметры макроса:
Следующий пример определит переменную 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); Параметры макроса:
Команда может быть экспортирована в режим 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).
Ниже приведен пример образца конфигурации в 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. |