Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Принцип построения консоли управления устройством
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
mempfis_
Стоит задача разаработать устройство которое должно иметь возможность управляться через UART.
Думаю встроить отладочную консоль чтобы можно было посылать в устройство комманды и получать ответы.
Комманды в виде одиночных символов реализовал. Теперь хочу расширить функциональность консоли и реализовать длинные команды
(наподобие АТ-комманд). Причём хочется сделать аппаратно-независимую консоль чтобы можно было переносить из проекта в проект меняя только низкоуровневые функции приёма/отправки символовов через UART.

Подскажите кто как делает подобные консоли. Интересует прежде всего как осуществляется поиск комманд в потоке принимаемых символов (выделение слов длиной 2 и более символа)? Какие стандартные библиотеки можно использовать?

Сейчас реализована входная FIFO-UART которая заполняется в прерываниях и функция считывания символа из FIFO которая возвращает -1 если нет данных или принятый символ. Одиночные символы отлавливать легко (считал - обработал), два - уже сложнее но пока реализую устанавливая флаги.
Как отловить три и более символа - вот тут нужны идеи smile.gif
ukpyr
нужен приемный буфер на макс.количество символов в команде и обнулением счетчика по тайм-ауту.
признак завершения команды - символ CR.
для парсера команд как минимум нужна функция парсера DEC/HEX чисел.
парсер HEX легко реализуется путем преобразования Char HEX->BIN и сдвига на 4 бита влево.
DEC - нужно дополнительное преобразование BCD->BIN (поразрядным умножением на степени 10 или сдвигом+BCD коррекцией).
_Pasha
Собираете словарь команд (ASCIIZ), например
Код
const char cmd0[] ="READ";
const char cmd1[] ="ERASE";
const char cmd2[] ="WRITE";
const char *command_set[3] = {&cmd0,&cmd1,&cmd2};

Затем, поочередно сравниваете принятый токен со строками словаря, до тех пор, пока следующий символ из словаря не будет равен \0.
Дальше - все зависит от синтаксиса - допускаете ли Вы у себя разделители токенов или нет... Если разделители необязательны - возвращаете индекс, соответствующий command_set[j] указателю на распознанную команду, и текущую позицию в буфере, подразумевающую дальнейший разбор аргументов. Далее - switch() и тд произвольной сложности.

Игнорирование регистра символа делаете?
zltigo
Цитата(_Pasha @ Aug 12 2009, 13:26) *
Собираете словарь команд (ASCIIZ), например

С размахом smile.gif
Код
int command( struct Cmd *cmd )
{
//---------------------------------------------------------------------------
case 'sst ':
...
        break;

case 'psst':
....
        break;

//--- Dummy arguments print -----------
case 'aa  ':
        printf( "\nargc=%i ls='%s'\r", cmd->argc, cmd->command_line );
        for( int i=0; i<= cmd->argc; i++ )
            printf( "argn[%i]=%08lX\r", i, cmd->argn[i] );
        break;

Команды - по первым четырем (для 32битника) символам. Стандартная разборка на аргументы делается заранее.
_Pasha
Цитата(zltigo @ Aug 12 2009, 14:24) *
Стандартная разборка на аргументы делается заранее.

Дык это разбиение на токены заранее можно сделать... а разбор аргументов команды - личное дело самой команды smile.gif Или Вы оговорились?
DpInRock
Длинные команды - бессмысленное пижонство.
Goodefine
Зато обеспечивают детерминированное поведение системы, при значительном потоке символов...
Dog Pawlowa
Цитата(Goodefine @ Aug 12 2009, 17:31) *
Зато обеспечивают детерминированное поведение системы, при значительном потоке символов...

Сказано мощно.
Это что, длинные команды как средство нейтрализации ошибок реализации протокола? smile.gif

Автору.
Передерите какой-нить стандартный протокол, слишком много аспектов существуют в протоколах.
Goodefine
Цитата(Dog Pawlowa @ Aug 12 2009, 18:44) *
Это что, длинные команды как средство нейтрализации ошибок реализации протокола? smile.gif

Не совсем smile.gif
В качестве контрпримера можно рассмотреть поведение управляемого объекта, с односимвольным набором команд, на вход которого постоянно что-то приходит. Без ошибок. При прочих равных, разумеется...
Dog Pawlowa
Цитата(Goodefine @ Aug 12 2009, 18:50) *
В качестве контрпримера можно рассмотреть поведение управляемого объекта, с односимвольным набором команд, на вход которого постоянно что-то приходит. Без ошибок. При прочих равных, разумеется...

И что, как контрпример себя ведет?
Использую односимвольные наборы команд в обе стороны. Никаких проблем.
Очень удобно - в одну сторону идут коды нажатых клавиш, а в другую - коды символов, подлежащих отображению. Полный дуплекс, физический и логический. Если русский язык не нужен, коды управления дисплеем помещаются в правую сторону ASCII без проблем.
Тут главное - не пытаться придумывать что-то посредине между "простым" протоколом и "нормальным" протоколом, в результате получится зависающая хрень.
AHTOXA
Цитата(Dog Pawlowa @ Aug 12 2009, 23:21) *
Очень удобно - в одну сторону идут коды нажатых клавиш, а в другую - коды символов, подлежащих отображению. Полный дуплекс, физический и логический.


Но это вроде немного не то, что хотел топикстартер?

Я лично применяю более-менее полноценную консоль, типа такого:

CODE
// обработчик команды.
typedef int (* CmdHandler)(char * args);

// структура с информацией о команде
typedef struct
{
char * command_name; // имя
CmdHandler handler; // обработчик
}
Command;

const Command CommandTable[] =
{
{"GET", GetCommandHandler},
{"SET", SetCommandHandler},
{"TERM", TermHandler},
...
};

int parse_command_line(char * buf)
{
char *lt;
char *token;
int i;

token = GetToken(buf, &lt);
if (!token) return FALSE;

for (i=0; i<sizeof(CommandTable) / sizeof(CommandTable[0]); i++)
if (!strcmp(CommandTable[i].command_name, token))
{
if (!CommandTable[i].handler)
return FALSE;
return CommandTable[i].handler(lt);
}

return FALSE;
}

void interpreter(void)
{
char buf[81];
for (;;)
{
rs_puts("\r\n=>");
rs_gets(buf, 80);
if (!parse_command_line(buf))
rs_puts("ERR");
}
}


Гораздо приятнее писать "SET PARAM1=23.4", чем "sA34", имея заодно возможность отредактировать строку при неверном вводе.
Да и не накладно это особо.
Dog Pawlowa
Цитата(AHTOXA @ Aug 12 2009, 20:49) *
Гораздо приятнее писать "SET PARAM1=23.4"...


Если уж пошла речь о том, что приятнее, то я пришел к выводу, что лучше ПиСишное приложение, одно на все устройства, а из устройства может быть прочитано, какой физический смысл имеет PARAM1. Типа SNMP.

Хотя протокол именно текстовый - чтобы было "приятнее писать".
AHTOXA
Цитата(Dog Pawlowa @ Aug 13 2009, 00:27) *
Если уж пошла речь о том, что приятнее, то я пришел к выводу, что лучше ПиСишное приложение, одно на все устройства, а из устройства может быть прочитано, какой физический смысл имеет PARAM1. Типа SNMP.


Согласен, так тоже неплохо. Но есть, как мне кажется, пара аргументов против. Первый - универсальность. Линукс/виндовз/что-то там ещё, разные версии... Ну и можно банально забыть писишную программуsmile.gif Терминалка же есть везде.
И второй аргумент - в терминале можно не только конфигурировать устройство (SET/GET), но и выполнять что-то специфическое. Например, непрерывный вывод значения АЦП до нажатия любой кнопки, дрыгание заданной ножкой с заданной частотой. Интерактив, короче. Для таких применений сложно придумать универсальную программу для ПиСи, имхо.
SasaVitebsk
Читал протокол Пирамида. Правда это надпротокольный уровень. Основная идея состоит в том, что терминальное (писишное) приложение прочитает инфу с любого прибора и правильно её отобразит. То есть прибор(изделие) выдаёт по спец запросу - свою структуру, список полей и их тип, а приложение универсальное. Я предпочитаю INI файлы текстовые.
_Pasha
Цитата(Dog Pawlowa @ Aug 12 2009, 21:27) *
из устройства может быть прочитано, какой физический смысл имеет PARAM1.

+1 Все хинты и информация о параметре (разме/знаковость/диапазон) хранятся в приборе. Пустая консоль только спрашивает номер параметра в списке.
Goodefine
Цитата(Dog Pawlowa @ Aug 12 2009, 20:21) *
И что, как контрпример себя ведет?
Использую односимвольные наборы команд в обе стороны. Никаких проблем.
Очень удобно - в одну сторону идут коды нажатых клавиш, а в другую - коды символов, подлежащих отображению...

У Вас все просто: все что отдал - сканкод, все что принял - подлежит отображению...
Я другое имел ввиду. К примеру возьмем дисплей с управляемой внешне подсветкой. Скажем, увеличить на одну градацию - символ "h", уменьшить - "l" . Остальное - отображаем. Как тогда контроллер отреагирует на на сообщение "hello world", если не использовать детерминацию ни по длине строки, ни по времени между поступлениями символов? Если же команды будут длинными, типа "light_background_down" / "light_background_up", то лишними проверками можно себя не утруждать - шанс, что потребуется отобразить именно такую строку, минимален. В этом суть моего первого замечания...
Dog Pawlowa
Цитата(Goodefine @ Aug 12 2009, 22:32) *
В этом суть моего первого замечания...

Понятно.
Пожалуй, мы ушли далеко от обычного определения консоли.
Если устройство выдает "промпт", а человек как-то реагирует в пределах предоставленных ему возможностей, то подобные коллизии исключаются.
_Pasha
Цитата(zltigo @ Aug 12 2009, 14:24) *
Код
....
case 'sst ':
...
case 'psst':
....
case 'aa  ':
....

Команды - по первым четырем (для 32битника) символам.

А все-таки, если у человека тупо буфер, нету манагера кучи для построения деревьев в ОЗУ - АВР средненький, короче, - так построить интерпретатор команд не получится! smile.gif

Цитата(AHTOXA @ Aug 12 2009, 22:06) *
Ну и можно банально забыть писишную программуsmile.gif
Интерактив, короче. Для таких применений сложно придумать универсальную программу для ПиСи, имхо.

Ага, ОРС-серверы всякие не забывают, а тут-досада smile.gif
Ничего сложного в универсальной программе - все действия реализуются через регистровую модель. В одном регистре - временной интервал выборки, в другом - адрес источника, в третьем - размер данных. К терминалке это уже не будет иметь отношение - это личное дело девайса - поддержит он данную функцию или нет.


Цитата(Goodefine @ Aug 12 2009, 22:32) *
Как тогда контроллер отреагирует на на сообщение "hello world"

Это все бессмысленно без
а) строгого разделения команд без аргументов, хотя бы пробелами.
б) минимального синтаксического разбора команд с аргументами.
AHTOXA
Цитата(_Pasha @ Aug 13 2009, 11:02) *
Ничего сложного в универсальной программе - все действия реализуются через регистровую модель. В одном регистре - временной интервал выборки, в другом - адрес источника, в третьем - размер данных. К терминалке это уже не будет иметь отношение - это личное дело девайса - поддержит он данную функцию или нет.


ОРС-серверы - это для устройств, постоянно находящихся на связи. Тут действительно, забыть проблематичноsmile.gif Если же терминал применяется изредка, то шансы возрастают.

Цитата(_Pasha @ Aug 13 2009, 11:02) *
Ничего сложного в универсальной программе - все действия реализуются через регистровую модель. В одном регистре - временной интервал выборки, в другом - адрес источника, в третьем - размер данных. К терминалке это уже не будет иметь отношение - это личное дело девайса - поддержит он данную функцию или нет.


Как реализовать через регистровую модель мини-DOS для флеш-карточки, например?
_Pasha
Цитата(AHTOXA @ Aug 13 2009, 08:45) *
Как реализовать через регистровую модель мини-DOS для флеш-карточки, например?

Легко. Регистр адреса + регистр данных + регистр управления/статуса.
Остальное - епархия девайса.
AHTOXA
Что-то не могу себе представить... Расскажите чуть подробнее, как, например, будет реализована команда "DIR"?
_Pasha
Цитата(AHTOXA @ Aug 13 2009, 09:00) *
как, например, будет реализована команда "DIR"?

Тогда лучше определиться, что мы хотим - получить блочное устройство накопитель данных или девайс, общающийся посредством терминала. А команда DIR - средствами оси.
AHTOXA
Насколько я понял, автор темы хочет консоль управления/отладки.
Если мы реализуем блочное устройство и будем читать карточку средствами оси, то это мало поможет нам отлаживать код работы с файловой системой внутри устройства.
mempfis_
Цитата(AHTOXA @ Aug 13 2009, 09:17) *
Насколько я понял, автор темы хочет консоль управления/отладки...


Именно. Мне нужна консоль в устройстве чтобы посылая определённые команды из терминала можно было изменить настройки, перепрошить устройство, считать текущие параметры и т.д. Ничего сверхумного и сверхсложного не нужно.
Может быть я даже неправильно назвал тему - скорее нужно было назвать "как отловить из потока симмволов команды длиной более 2х символов".
Но пару идей в дискуссии по тому как это сделать (ну и как собственно организовать консоль и систему команд) я уже приметел smile.gif

Все спасибо за дисскуссию. Надеюсь она не остановится и ещё прозвучит ещё много идей smile.gif

По поводу длинных команд - буквы F, E, L не так информативны как WFLASH, EFLASH, WLOCK и т.п.
Сразу видно что я хочу записать флэш, стереть её, записать лок-биты. Вобщем небольшое увеличение длины команды позволяет сразу переходить на необходимую подпрограмму обработки. Будет и самому интуитивно понятнее и система команд значительно расширяется.
_Pasha
Цитата(AHTOXA @ Aug 13 2009, 09:17) *
Насколько я понял, автор темы хочет консоль управления/отладки.

Тогда здесь регистровая модель(у нас с Вами болтовня именно с нее началась) - никаким макаком. Но, вернемся к периодическому опросу АЦП или чего другого. Очевидно, что хотя бы период опроса может быть регулируемым.

Цитата(mempfis_ @ Aug 13 2009, 09:41) *
Именно. Мне нужна консоль ... Будет и самому интуитивно понятнее и система команд значительно расширяется.

И обязательная команда help (лучше "?")для всех приборов.
А еще страшнее - контекстная помощь, например help WFLASH или ?WFLASH - как по мне, то лучше в постфиксной форме
Код
WFLASH ?
. Объясню: "?" может рассматриваться как стандартный аргумент команды, минимум писанины. Дополнительные хинты уже зависят от принятой команды.

И еще раз скажу: просто команды - это уже обговорили. А как же быть с аргументами? wink.gif Они ж тоже переменной длины!
AHTOXA
Цитата(_Pasha @ Aug 13 2009, 13:02) *
Но, вернемся к периодическому опросу АЦП или чего другого. Очевидно, что хотя бы период опроса может быть регулируемым.


Дык, какие проблемы? Аргументы (всё что после команды) передаются обработчику. Там может быть и период:
>POOLADC 0 0 20 - опросить АЦП, число опросов 0 (до нажатия кнопы), канал 0, интервал 20 мс.

Цитата(_Pasha)
И обязательная команда help (лучше "?")для всех приборов.
А еще страшнее - контекстная помощь, например help WFLASH или ?WFLASH - как по мне, то лучше в постфиксной форме
Код
WFLASH ?
.


+1, кроме постфиксной формы. Зачем делать это в каждой команде, проще централизованноsmile.gif
Типа :
Код
typedef struct
{
    char *const    command_name;    // command name
    CmdHandler    handler;            // command handler
    char *const    help_text;        // command description
}Command;

const Command CommandTable[] =
{
     {"GET",                    GetCommandHandler,    "get variable (VARS for list)"},
     {"SET",                    SetCommandHandler,    "set variable (VARS for list)"},
     {"HELP",                HelpHandler,        "display this help"},
...
};
_Pasha
Цитата(AHTOXA @ Aug 13 2009, 10:23) *
проще централизованноsmile.gif

Ага. Не увидел сразу.
? без параметров вываливает список команд из CommandTable[]
AHTOXA
Цитата(_Pasha @ Aug 13 2009, 13:33) *
Ага. Не увидел сразу.


Ну, сразу и не было, я убрал, чтоб не загромождать идеюsmile.gif

Цитата(_Pasha @ Aug 13 2009, 13:33) *
? без параметров вываливает список команд из CommandTable[]


Ага. Плюс к этому есть такой же массив описаний переменных, с именем перемнной, ф-ями Get/Set, типом переменной границами и описанием (для хелпа). Я таскаю это хозяйство по всем проектам, очень помогает.
SasaVitebsk
Цитата(mempfis_ @ Aug 13 2009, 09:41) *
"как отловить из потока симмволов команды длиной более 2х символов".


Так в принципе, ничего нового не выдумано, насколько я понимаю. Всё тоже как и 30 лет назад.

1) если идёт сплошной поток, то префикс команды (Байтстафинг). Как вы уже отметили в модемах используется. Префикс AT (за малым исключением). То есть "hgjghjhati3" будет квалифицировано как команда "i3" - идентификация устройства. Префикс может быть любым, в том числе и односимвольным. Я обычно использую "Ъ". smile.gif Естественно префикс из простого потока данных изымается (заменяется на комбинацию), либо удваивается. Один из вариантов WAKE.

2) Применяется пауза (в том числе определённой длительности) перед началом команды.

3) Применяется комбинация 1 и 2.

Есть ещё битстафинг (я применял), но он неподходит для UARTа.
rezident
В дополнение ко всему хотел бы заметить, что конфигурирование устройства и его отладка это немного разные вещи. Конфигуратор на основе текстового меню легко делается. И совершенно без разницы куда выводится это меню: на экран устройства или в UART и соответственно откуда управляется: с клавы устройства или опять же из UART.
SasaVitebsk
Цитата(rezident @ Aug 13 2009, 22:03) *
В дополнение ко всему хотел бы заметить, что конфигурирование устройства и его отладка это немного разные вещи. Конфигуратор на основе текстового меню легко делается. И совершенно без разницы куда выводится это меню: на экран устройства или в UART и соответственно откуда управляется: с клавы устройства или опять же из UART.

+1

Кроме того, я бы не вставлял в рабочую прогу отладочные режимы. Отладка предполагает свободный доступ к железу. Для рабочего режима это, на мой взгляд, недопустимо. Фактически - это даст возможность пользователю убить работоспособность изделия.

Делали один проект, и программист, который писал головную прогу (на IBM) требовал, чтобы я предоставил прямой доступ к памяти устройства (под MODBUS). Я наотрез отказался. Их мотивация - что будет всё отлажено и PC прога не будет вредить устр-ву. За то это даст более широкие возможности, в том числе и по отладке. Моя мотивация - кто будет нести ответственность за работоспособность моего железа?
MrYuran
Цитата(SasaVitebsk @ Aug 14 2009, 10:54) *
Кроме того, я бы не вставлял в рабочую прогу отладочные режимы. Отладка предполагает свободный доступ к железу. Для рабочего режима это, на мой взгляд, недопустимо. Фактически - это даст возможность пользователю убить работоспособность изделия.

Ну почему, можно запаролить критические команды. А пароль никому не давать.
amaora
Обычно использую бинарный протокол для отладки, но если надо текстовую консоль, скопировал бы схему работы с юниксовых шелл+терминал, что то лучше придумать сложно.

Код
u8 uart_read();
void uart_write(u8 val);

//...

do {
    c = uart_read();
    buff[i++];
    if (c == CR) {
        uart_write('\n');
        parse_line(buff);
        i = 0;
        continue;
    }
    if (c == BACKSPACE) {
        uart_write('\b');
        --i;
        continue;
    }
    uart_write(c);
}
while(1);


Конечно тут мало проверок, и можно удалять только последний символ, это упрощенный пример.
defunct
Цитата
Принцип построения консоли управления устройством

1. Байт парсер, выделяющий строки команд и управляющие символы из потока. Вызывается или непосредственно из RX INT или из обработчика "OnRead" TCP сокета.
2. Задача поиска обработчика для выделенной из потока строки. (по табличке, в точности так как привел AHTOXA).
3. Обработчик каждой команды.
4. Общий парсер параметров, который пытается интерпретировать строку параметров всеми известными ему форматами: как целое число, как float-point число, как IP addres, mac-address, дата, время, строка и т.д и т.п..
5. Ну и контекстный Help. (вызываемый в обработчике каждой команды, если команда вызвана с параметром "?" или "--help").
AlexMad
Цитата(zltigo @ Aug 12 2009, 15:24) *
С размахом smile.gif
Код
int command( struct Cmd *cmd )
{
//---------------------------------------------------------------------------
case 'sst ':
...
        break;

Команды - по первым четырем (для 32битника) символам. Стандартная разборка на аргументы делается заранее.

А не трудно показать начало ветвления (switch). Что за тип Вы ему даете? У меня как-то не срабатывает подобное, хотя пытался такое изобразить не раз. Компилятор варнингует, что нельзя сравнивать мультичарестерный чар (multi-character character constant).

И везде встречал побайтный анализ, что приводит к огромным ветвлениям switch/case.
zltigo
Цитата(AlexMad @ Aug 20 2009, 20:52) *
А не трудно показать начало ветвления (switch). Что за тип Вы ему даете?

Код
struct Cmd{                 // Command
unsigned long cmdcode;
.......
};

    switch( cmd->cmdcode )
    {

.....

Цитата
У меня как-то не срабатывает подобное, хотя пытался такое изобразить не раз. Компилятор варнингует, что нельзя сравнивать мультичарестерный чар (multi-character character constant).

Естественно, что это исходник, как уже писал, для 32bit. Для 16bit, соответственно только пара символов, ну а с 8bit sad.gif smile.gif
Если какой-то компилятор warning, выдает, что бывает - надо отключить для этого куска через #pragma
Предупреждать он, конечно, может, но работать, как приказано, обязан.
C.S.
Цитата(mempfis_ @ Aug 12 2009, 12:43) *
Сейчас реализована входная FIFO-UART которая заполняется в прерываниях и функция считывания символа из FIFO которая возвращает -1 если нет данных или принятый символ. Одиночные символы отлавливать легко (считал - обработал), два - уже сложнее но пока реализую устанавливая флаги.
Как отловить три и более символа - вот тут нужны идеи smile.gif


А расскажите пожалуйста теорию про FIFO (я на асме пишу под Мегу8). Не понимаю самого принципа.
Форма команд моему девайсу: <префикс начала><команда> [<параметры>]<CRLF>
Выделил какой-то буфер, думаю тупо заполнять его поступающими данными из UART, инкрементируя указатель... и не могу сообразить, с какого момента начать его обрабатывать?
Вижу варианты:
1. Буфер переполнен - выдаём ошибку
2. Нашли CRLF - парсим буфер, обрабатываем команду
3. Какой-то тайм-аут передачи - буфер отбасывается, ошибка
4. Неверная команда - выдаём ошибку.
Обрабатывать думаю - искать старт команды в буфере (три байта), и написать что-то типа strcmp.

Мыслю верно? Смущает, как определить то, что передача данных закончилась...

Если кому интересно, нашёл "для тупых" тут статейку. Идеи разжёваны идаже примеры есть http://www.atmel.ru/Articles/Atmel12.htm
_Pasha
Цитата(C.S. @ Aug 25 2009, 14:56) *
Смущает, как определить то, что передача данных закончилась...

<префикс начала><команда> [<параметры>]<CRLF>
<CRLF> - как раз передача данных закончилась. Про таймаут Вы уже говорили...

<префикс начала> - если будете обрабатывать в прерывании, FIFO Вам не нужен будет совсем.
C.S.
2_Pasha Кхм, я путаюсь уровнем ниже. Как принимаемое пихать в буфер и что делать, если будут слать следующую команду,пока обрабатывается текущая. Думаю отключать UART на приём(?).
То-есть есть отдельно принималка строки и есть отдельно парсер. Вот с принималкой строки затыки следующие:
0. Есть область памяти и счётчик адреса = буфер. Так?
1.1. По прерыванию пихаем байт, инкрементируем счётчик, проверяем переполнение счётчика, выдаём ошибки, если надо (переполнен буфер).
1.2. После принятия очередного байта проверяем два последних на CRLF. Если равны = отключаем приём, передаём буфер парсеру.
2. Парсер уже сам разбирается - команда/ошибка, выдаёт ответ или выполняет её, в конце разрешая приём.

Есть ли тут какие-то подводные камни? Первый вижу сам - пока обрабатываем что-то, теряем посылаемое в этот момент. Это я закладываю в протокол: если не пришёл ответ от команды, повторить посылку.

И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога? Или вообще не делать, а просто ждать CRLF?

Видел модуль, там типа терминала через RS-232 написано, так, вроде там таймаута нет никакого. Просто строка кончается CRLF.
_Pasha
Цитата(C.S. @ Aug 25 2009, 15:25) *
Первый вижу сам - пока обрабатываем что-то, теряем посылаемое в этот момент. Это я закладываю в протокол: если не пришёл ответ от команды, повторить посылку.

Тогда FIFO (пардон за Си):
Код
volatile char buffer[BUFSIZE];
volatile unsigned char pos_rd, pos_wr;

// прерывание UART
{
char tmp=UDR;
buffer[pos_wr++] = tmp;
if(pos_wr >= BUFSIZE ) pos_wr = 0;
}

// опр числа принятых символов
// условие кгда нет символов одно  - это pos_wr == pos_rd
unsigned char sym_pending(void)
{
return (pos_wr >= pos_rd) ? (pos_wr - pos_rd) : ( pos_wr + BUFSIZE - pos_rd);
}
// чтение из буфера
char sym_read(void)
{
char tmp = buffer[pos_rd++];
if(pos_rd >= BUFSIZE) pos_rd = 0;
return tmp;
}


Цитата
И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога?

Для начала определитесь, для чего именно Вам таймаут. Он может быть как на время между принятыми командами, так и на время между символами.
ЗЫ для определения переполнения буфера можно параллельно ввести счетчик принятых байт. При чтении - декрементировать его.
C.S.
На время между символами. Наверное, может и не нужен. Заложу - если команда не выполнилась - послать CRLF (очистка буфера) и повторить команду.
А sym_pending у Вас как вызывается? Где-то в цикле или по таймеру?
AHTOXA
Цитата(C.S. @ Aug 25 2009, 18:25) *
Как принимаемое пихать в буфер и что делать, если будут слать следующую команду,пока обрабатывается текущая. Думаю отключать UART на приём(?).


Во-первых, кто это будет так плохо себя вести? smile.gif Во-вторых, если такое всё же возможно, можно сделать два буфера на приём (и соответственно два счётчика), один принимается (в прерывании), другой обрабатывается (парсером). И флажок, номер_готового_буфера (0/1).
Цитата
И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога?

На да. При каждом принятом символе некий счётчик устанавливается в заданное значение, а в прерывании таймера - декрементируется. Как стал нулём, так тогось, тайм-аут. Только вот что делать по тайм-ауту?
Цитата
Или вообще не делать, а просто ждать CRLF?

Да, так гораздо проще. Единственно, когда тайм-аут нужен, это в том случае, если при входе в консоль устройство перестаёт работать в штатном режиме. Тогда его минут через пять после ухода забывчивого наладчика таки желательно вернуть в рабочее состояниеsmile.gif
Цитата
Видел модуль, там типа терминала через RS-232 написано, так, вроде там таймаута нет никакого. Просто строка кончается CRLF.
И это правильно. Вдруг человек просто задумался? smile.gif
_Pasha
Цитата(C.S. @ Aug 25 2009, 16:02) *
А sym_pending у Вас как вызывается? Где-то в цикле или по таймеру?

Конечно, где-то в основном процессе. Вначале узнаем, сколько символов принято, а потом можем еще и покапризничать smile.gif - надо обрабатывать или нет. Может, есть другие более важные задачи.
Затем - читаем посимвольно. Разворачиваем это все в другой буфер (к сожалению). и парсим от замеченного начала и до замеченного ВКПС.
ЗЫ FIFO_flush делается как присвоение pos_wr = pos_rd. позаботившись об атомарности . Если надо, конечно.
C.S.
Цитата
Разворачиваем это все в другой буфер (к сожалению). и парсим от замеченного начала и до замеченного ВКПС

Аааа! А я-то хотел обрабатывать в том же, куда принял. Тогда можно рыбу накатать и проверить.

По таймауту - чистить буфер. Не успели передать строку целиком - значит пришёл мусор. Оставлю как опцию.

Насчёт плохо себя ведущих - вообще, потом думаю о каком-то протоколе, где несколько мастеров и несколько подчинённых. Хотел, чтобы они слали друг другу инфу в формате АдресПолучателя АдресОтправителя Инфа. Выходит, что сама магистраль может быть забита чем угодно, и в этом случае просто будет несколько попыток достучаться до девайса (передаёт и принимает всегда одна пара).
Но это пока задумки и вопросы для другой темы. Мне б пока с компютера научиться управлять и команды парсить...
_Pasha
Цитата(C.S. @ Aug 25 2009, 16:26) *
Аааа! А я-то хотел обрабатывать в том же, куда принял.

FIFO ведь не самоцель, а средство разгрузки основного процесса от тяжелых прерываний. Оно может быть каких-нить компромиссных размеров, скажем, 8 байт. Учитывая двойную буферизацию UDR, может хватить.
Да и приемный буфер может быть динамическим.
А если "рыбу накатать" - у Вас будет эдакий бегемот в ОЗУ сидеть статически, чтоб не прозевать команду, да еще и там же ее парсить... не думаю, что сие полезно.
ЗЫ: это ж ответ от девайса пойдет только после приема команды? Тогда у Вас большой буфер для работы на RX/TX и ФИФО на прием.
C.S.
Цитата
это ж ответ от девайса пойдет только после приема команды? Тогда у Вас большой буфер для работы на RX/TX и ФИФО на прием

Эммм... А как иначе (про ответ)? Если я девайсу шлю например команду SETBR 1,100 - вот он её должен распарсить, выполнить и ответить OK? Так ведь и выходит - причём, обработка, ответ? Я по 32 байта выделил на буферы...
_Pasha
Цитата(C.S. @ Aug 25 2009, 16:54) *
Так ведь и выходит - причём, обработка, ответ? Я по 32 байта выделил на буферы...

Тогда нафига Вам второй буфер ? Если, конечно, во время ответа хост не пошлет еще чего-нить.
C.S.
Так а если надо будет вернуть чего-то более разумное в ответ, нежели ОК? Тогдабудет ОК <данные>
Вот и думал(ю) сделать буфер на отправку: забиваем его всем, чем надо, вызываем подпрограмму, которая по прерываниям его автономно отсылает.
Можно, конечно и так: отослать ОК, отослать пробел, отослать данные - но логическая целостность как-то теряется.
_Pasha
Цитата(C.S. @ Aug 25 2009, 17:17) *
Так а если надо будет вернуть чего-то более разумное в ответ, нежели ОК? Тогдабудет ОК <данные>

1. Принимаем команду в буфер
2. Парсим ее в буфере
3. Все аргументы - преобразуются и хранятся в локальных переменных.
4. Выполняются действия с полученным набором аргументов.
5. Ответ отправляется в тот же буфер. Ставится счетчик TxCNT = длине отправляемой строки
6. Разрешаем UDRE. (в нем отправляем и как только TxCNT ==0 запрещаем прерывание)
7. Ждем пока UDRE не станет ==0
За это время FIFO - если может заполниться - то оставьте его 32 байта, если низзя принимать команду - оставляйте меньше. Если новая команда не может поступить ранее, чем отдан ответ, этот факт фиксируется с помощью переполнения ФИФО, и далее - ответ, типа "?"
C.S.
Понял. Идиот. Спасибо большое!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.