|
|
  |
Принцип построения консоли управления устройством, Поделитесь опытом кто как делает :) |
|
|
|
Aug 14 2009, 06:54
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(rezident @ Aug 13 2009, 22:03)  В дополнение ко всему хотел бы заметить, что конфигурирование устройства и его отладка это немного разные вещи. Конфигуратор на основе текстового меню легко делается. И совершенно без разницы куда выводится это меню: на экран устройства или в UART и соответственно откуда управляется: с клавы устройства или опять же из UART. +1 Кроме того, я бы не вставлял в рабочую прогу отладочные режимы. Отладка предполагает свободный доступ к железу. Для рабочего режима это, на мой взгляд, недопустимо. Фактически - это даст возможность пользователю убить работоспособность изделия. Делали один проект, и программист, который писал головную прогу (на IBM) требовал, чтобы я предоставил прямой доступ к памяти устройства (под MODBUS). Я наотрез отказался. Их мотивация - что будет всё отлажено и PC прога не будет вредить устр-ву. За то это даст более широкие возможности, в том числе и по отладке. Моя мотивация - кто будет нести ответственность за работоспособность моего железа?
|
|
|
|
|
Aug 14 2009, 17:02
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Обычно использую бинарный протокол для отладки, но если надо текстовую консоль, скопировал бы схему работы с юниксовых шелл+терминал, что то лучше придумать сложно. Код 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); Конечно тут мало проверок, и можно удалять только последний символ, это упрощенный пример.
|
|
|
|
|
Aug 15 2009, 15:52
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата Принцип построения консоли управления устройством 1. Байт парсер, выделяющий строки команд и управляющие символы из потока. Вызывается или непосредственно из RX INT или из обработчика "OnRead" TCP сокета. 2. Задача поиска обработчика для выделенной из потока строки. (по табличке, в точности так как привел AHTOXA). 3. Обработчик каждой команды. 4. Общий парсер параметров, который пытается интерпретировать строку параметров всеми известными ему форматами: как целое число, как float-point число, как IP addres, mac-address, дата, время, строка и т.д и т.п.. 5. Ну и контекстный Help. (вызываемый в обработчике каждой команды, если команда вызвана с параметром "?" или "--help").
|
|
|
|
|
Aug 20 2009, 18:52
|
Местный
  
Группа: Свой
Сообщений: 262
Регистрация: 18-02-05
Из: SPb
Пользователь №: 2 743

|
Цитата(zltigo @ Aug 12 2009, 15:24)  С размахом Код int command( struct Cmd *cmd ) { //--------------------------------------------------------------------------- case 'sst ': ... break; Команды - по первым четырем (для 32битника) символам. Стандартная разборка на аргументы делается заранее. А не трудно показать начало ветвления (switch). Что за тип Вы ему даете? У меня как-то не срабатывает подобное, хотя пытался такое изобразить не раз. Компилятор варнингует, что нельзя сравнивать мультичарестерный чар (multi-character character constant). И везде встречал побайтный анализ, что приводит к огромным ветвлениям switch/case.
|
|
|
|
|
Aug 20 2009, 19:23
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(AlexMad @ Aug 20 2009, 20:52)  А не трудно показать начало ветвления (switch). Что за тип Вы ему даете? Код struct Cmd{ // Command unsigned long cmdcode; ....... };
switch( cmd->cmdcode ) {
..... Цитата У меня как-то не срабатывает подобное, хотя пытался такое изобразить не раз. Компилятор варнингует, что нельзя сравнивать мультичарестерный чар (multi-character character constant). Естественно, что это исходник, как уже писал, для 32bit. Для 16bit, соответственно только пара символов, ну а с 8bit  Если какой-то компилятор warning, выдает, что бывает - надо отключить для этого куска через #pragma Предупреждать он, конечно, может, но работать, как приказано, обязан.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Aug 25 2009, 11:56
|

Участник

Группа: Участник
Сообщений: 52
Регистрация: 6-05-09
Из: Москва
Пользователь №: 48 733

|
Цитата(mempfis_ @ Aug 12 2009, 12:43)  Сейчас реализована входная FIFO-UART которая заполняется в прерываниях и функция считывания символа из FIFO которая возвращает -1 если нет данных или принятый символ. Одиночные символы отлавливать легко (считал - обработал), два - уже сложнее но пока реализую устанавливая флаги. Как отловить три и более символа - вот тут нужны идеи  А расскажите пожалуйста теорию про FIFO (я на асме пишу под Мегу8). Не понимаю самого принципа. Форма команд моему девайсу: <префикс начала><команда> [<параметры>]<CRLF> Выделил какой-то буфер, думаю тупо заполнять его поступающими данными из UART, инкрементируя указатель... и не могу сообразить, с какого момента начать его обрабатывать? Вижу варианты: 1. Буфер переполнен - выдаём ошибку 2. Нашли CRLF - парсим буфер, обрабатываем команду 3. Какой-то тайм-аут передачи - буфер отбасывается, ошибка 4. Неверная команда - выдаём ошибку. Обрабатывать думаю - искать старт команды в буфере (три байта), и написать что-то типа strcmp. Мыслю верно? Смущает, как определить то, что передача данных закончилась... Если кому интересно, нашёл "для тупых" тут статейку. Идеи разжёваны идаже примеры есть http://www.atmel.ru/Articles/Atmel12.htm
Причина редактирования: Излишнее цитирование.
|
|
|
|
|
Aug 25 2009, 12:25
|

Участник

Группа: Участник
Сообщений: 52
Регистрация: 6-05-09
Из: Москва
Пользователь №: 48 733

|
2_Pasha Кхм, я путаюсь уровнем ниже. Как принимаемое пихать в буфер и что делать, если будут слать следующую команду,пока обрабатывается текущая. Думаю отключать UART на приём(?). То-есть есть отдельно принималка строки и есть отдельно парсер. Вот с принималкой строки затыки следующие: 0. Есть область памяти и счётчик адреса = буфер. Так? 1.1. По прерыванию пихаем байт, инкрементируем счётчик, проверяем переполнение счётчика, выдаём ошибки, если надо (переполнен буфер). 1.2. После принятия очередного байта проверяем два последних на CRLF. Если равны = отключаем приём, передаём буфер парсеру. 2. Парсер уже сам разбирается - команда/ошибка, выдаёт ответ или выполняет её, в конце разрешая приём.
Есть ли тут какие-то подводные камни? Первый вижу сам - пока обрабатываем что-то, теряем посылаемое в этот момент. Это я закладываю в протокол: если не пришёл ответ от команды, повторить посылку.
И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога? Или вообще не делать, а просто ждать CRLF?
Видел модуль, там типа терминала через RS-232 написано, так, вроде там таймаута нет никакого. Просто строка кончается CRLF.
|
|
|
|
|
Aug 25 2009, 12:58
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(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; } Цитата И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога? Для начала определитесь, для чего именно Вам таймаут. Он может быть как на время между принятыми командами, так и на время между символами. ЗЫ для определения переполнения буфера можно параллельно ввести счетчик принятых байт. При чтении - декрементировать его.
|
|
|
|
|
Aug 25 2009, 13:03
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(C.S. @ Aug 25 2009, 18:25)  Как принимаемое пихать в буфер и что делать, если будут слать следующую команду,пока обрабатывается текущая. Думаю отключать UART на приём(?). Во-первых, кто это будет так плохо себя вести?  Во-вторых, если такое всё же возможно, можно сделать два буфера на приём (и соответственно два счётчика), один принимается (в прерывании), другой обрабатывается (парсером). И флажок, номер_готового_буфера (0/1). Цитата И ещё непонятно с таймаутом - как его посчитать? Выходит что-то типа ватчдога? На да. При каждом принятом символе некий счётчик устанавливается в заданное значение, а в прерывании таймера - декрементируется. Как стал нулём, так тогось, тайм-аут. Только вот что делать по тайм-ауту? Цитата Или вообще не делать, а просто ждать CRLF? Да, так гораздо проще. Единственно, когда тайм-аут нужен, это в том случае, если при входе в консоль устройство перестаёт работать в штатном режиме. Тогда его минут через пять после ухода забывчивого наладчика таки желательно вернуть в рабочее состояние  Цитата Видел модуль, там типа терминала через RS-232 написано, так, вроде там таймаута нет никакого. Просто строка кончается CRLF. И это правильно. Вдруг человек просто задумался?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Aug 25 2009, 13:17
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(C.S. @ Aug 25 2009, 16:02)  А sym_pending у Вас как вызывается? Где-то в цикле или по таймеру? Конечно, где-то в основном процессе. Вначале узнаем, сколько символов принято, а потом можем еще и покапризничать  - надо обрабатывать или нет. Может, есть другие более важные задачи. Затем - читаем посимвольно. Разворачиваем это все в другой буфер (к сожалению). и парсим от замеченного начала и до замеченного ВКПС. ЗЫ FIFO_flush делается как присвоение pos_wr = pos_rd. позаботившись об атомарности . Если надо, конечно.
|
|
|
|
|
Aug 25 2009, 13:26
|

Участник

Группа: Участник
Сообщений: 52
Регистрация: 6-05-09
Из: Москва
Пользователь №: 48 733

|
Цитата Разворачиваем это все в другой буфер (к сожалению). и парсим от замеченного начала и до замеченного ВКПС Аааа! А я-то хотел обрабатывать в том же, куда принял. Тогда можно рыбу накатать и проверить. По таймауту - чистить буфер. Не успели передать строку целиком - значит пришёл мусор. Оставлю как опцию. Насчёт плохо себя ведущих - вообще, потом думаю о каком-то протоколе, где несколько мастеров и несколько подчинённых. Хотел, чтобы они слали друг другу инфу в формате АдресПолучателя АдресОтправителя Инфа. Выходит, что сама магистраль может быть забита чем угодно, и в этом случае просто будет несколько попыток достучаться до девайса (передаёт и принимает всегда одна пара). Но это пока задумки и вопросы для другой темы. Мне б пока с компютера научиться управлять и команды парсить...
|
|
|
|
|
Aug 25 2009, 13:36
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

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