Интерпретатор последовательных команд микроконтроллера в C / C ++; Способы сделать это; - PullRequest
4 голосов
/ 23 августа 2010

Я хотел бы интерпретировать командную строку, полученную микроконтроллером (PIC16f877A, если это имеет какое-либо значение) через последовательный порт.

Строки имеют довольно простое и простое форматирование: $ AABBCCDDEE (5«блоки» из 2 символов + «$» для 11 символов в общей сложности) где: $ AA = фактическое имя команды (могут быть буквы, цифры, оба; обязательно);BB-EE = параметры (числа; необязательно);

Я хотел бы написать код на C / C ++.

Я полагаю, я мог бы просто получить строку через последовательный порт, взломать еев блоки, switch () {case} и memcmp командный блок ($ AA).Тогда у меня могло бы быть двоичное дерево решений, чтобы использовать блоки DD CC и EE BB CC.

Я хотел бы знать, является ли это правильным способом (мне это кажется некрасивым, конечно, есть)должно быть менее утомительным способом сделать это!).

Ответы [ 4 ]

5 голосов
/ 23 августа 2010

Не переоценивайте это! Это не означает, что нужно слепо программировать, но как только вы разработали что-то, что выглядит так, как будто оно может выполнять свою работу, вы можете приступить к его реализации. Реализация даст вам отзыв о вашей архитектуре.

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

Не стремитесь к лучшему коду с первой попытки. Цель

  • легко читается
  • легко отлаживать

Сделай несколько шагов. Вам не нужно реализовывать все это за один раз.

  • Захватите строку из последовательного порта. Выглядит просто, правда? Что ж, давайте сделаем это сначала, просто распечатав команды.
  • Отделить команду от параметров.
  • Извлечь параметры. Будет ли извлечение одинаковым для каждой команды? Можете ли вы разработать структуру данных, допустимую для каждой команды?

Как только вы все сделали правильно, вы можете начать думать о лучшем решении.

4 голосов
/ 25 августа 2010

Интерфейсы ASCII некрасивы по определению. В идеале у вас есть какая-то структура фрейма, которая, возможно, у вас есть, $ указывает на разделение между фреймами, и вы говорите, что они имеют длину 11 символов. Если всегда 11, то это хорошо, если только иногда это сложнее, мы надеемся, что $ в начале и 0x0A и или 0x0D / 0x0A в конце (CR / LF). Обычно у меня есть один модуль кода, который просто извлекает байты из последовательного порта и помещает их в (циклический) буфер. Буферизация датируется тем временем, когда последовательные порты практически не имели буфера на борту, но даже сегодня, особенно с микроконтроллерами, это все еще так. Затем еще один модуль кода, который следит за буфером поиска кадров. В идеале этот буфер должен быть достаточно большим, чтобы оставить там кадр и иметь место для следующего кадра, и не требовать другого буфера для хранения копий полученных кадров. используя циклический буфер, этот второй модуль может перемещать (при необходимости, отбрасывая) указатель заголовка на маркер начала кадра и ожидает полного объема данных. Как только появляется полный кадр, он вызывает другую функцию, которая обрабатывает этот кадр. Эта функция может быть той, о которой вы спрашиваете. И «просто закодируй это» может быть ответом, ты в микроконтроллере, поэтому ты не можешь использовать ленивое высокоуровневое настольное приложение в решениях операционной системы. Вам понадобится какая-то функция strcmp, если она была создана вами или доступна вам через библиотеку, или не зависит от вашего решения. Грубая сила if (strncmp (& frame [1], "bob", 3) == 0) тогда, иначе if (strncmp (& frame [1], "ted", 3) тогда, иначе if ... Конечно работает, но вы можете жевать свой диск такими вещами, или нет. И буферизация, необходимая для такого подхода, может сжечь много оперативной памяти. Этот подход очень читабелен и удобен в обслуживании, хотя и переносим. Может не быть быстрым (поддерживаемым) обычно конфликтует с надежностью и / или производительностью), но это может не беспокоить, если только вы можете обработать его до того, как появится следующий, или до того, как необработанные данные выпадут из циклического буфера. В зависимости от задачи подпрограмма проверки кадра может просто проверить, что кадр хорош, я обычно ставлю начальные и конечные маркеры, длину и некоторую арифметическую контрольную сумму, и если это плохой кадр, он отбрасывается, это экономит много кода, проверяя на плохой / поврежденный данные. Когда подпрограмма обработки кадра возвращается к поиску подпрограммы кадра, она перемещает указатель заголовка, чтобы очистить кадр, поскольку он больше не n хороший, хороший кадр или плохой. Средство проверки кадров может только проверить кадр и передать его еще одной функции, которая выполняет синтаксический анализ. Каждый блок lego в этом расположении имеет очень простую задачу и работает в предположении, что блок lego под ним выполнил свою задачу должным образом. Модульный, объектно-ориентированный, любой термин, который вы хотите использовать, значительно упрощает проектирование, кодирование, обслуживание, отладку. (за счет производительности и ресурсов). Этот подход хорошо работает для любого потока последовательного типа, будь то последовательный порт в микроконтроллере (с достаточным количеством ресурсов), а также для приложений на настольном компьютере, которые просматривают последовательные данные из последовательного порта или данные TCP, которые также являются последовательными и НЕ ориентированы на фреймы.

если у вашего микроресурса нет ресурсов для всего этого, то подход конечного автомата также работает довольно хорошо.Каждый поступающий байт помечает конечный автомат одним состоянием.Начните с ожидания в ожидании первого байта, является ли первый байт $?не выбрасывай его и возвращайся в режим ожидания.если первый байт равен $, тогда переходите к следующему состоянию.Если вы ищете, скажем, команды «and», «add», «or» и «xor», то второе состояние будет сравниваться с «a», «o» и «x», если ни одна из них тогдаидти на холостыеесли a, то перейдите в состояние, которое сравнивается для n и d, если a o, то перейдите в состояние, которое ищет r.Если поиск r in или состояния не видит r, то перейдите в режим ожидания, если это произойдет, обработайте команду и затем перейдите в режим ожидания.Код читабелен в том смысле, что вы можете посмотреть на конечный автомат и увидеть слова a, n, d, a, d, d, o, r, x, o, r, и куда они в конечном итоге ведут, но обычноне считается читаемым кодом.Этот подход использует очень мало оперативной памяти, немного больше опирается на ROM, но в целом может использовать наименьшее количество ROM по сравнению с другими подходами синтаксического анализа.И здесь снова очень переносимо, за пределами микроконтроллеров, но за пределами микроконтроллеров люди могут подумать, что вы безумны с этим видом кода (ну, конечно, если это не verilog или vhdl).Этот подход сложнее поддерживать, труднее читать, но он очень быстрый и надежный и использует наименьшее количество ресурсов.

Чтобы понять, какой подход после интерпретации команды, вы должны убедиться, что вы можете выполнить команду безпотеря любых байтов на последовательном порту, либо из-за детерминированной производительности кода, либо из-за прерываний, либо из-за чего-либо.работа легче, получающиеся инструкции выполняются, уродливы.И один размер не подходит никому по определению.Просто запустите кодирование, попробуйте конечный автомат и попробуйте if-then-else-strncmp и оптимизацию между ними.Вы должны быстро увидеть, какой из них лучше всего подходит как для вашего стиля кодирования, так и для инструментов / процессора и решаемой проблемы.

2 голосов
/ 23 августа 2010

Это зависит от того, насколько вы хотите получить, сколько существует различных команд и вероятность того, что новые команды будут добавляться часто.

Вы можете создать структуру данных, которая связывает каждую действительную командную строку ссоответствующий указатель на функцию - отсортированный список, доступ к которому осуществляется с помощью bsearch(), вероятно, подходит, хотя хэш-таблица является альтернативой, которая может иметь более высокую производительность (поскольку набор допустимых команд известен заранее, вы можете создать идеальный хеш с помощью такого инструмента, какgperf).

Подход bsearch() может выглядеть примерно так:

void func_aa(char args[11]);
void func_cc(char args[11]);
void func_xy(char args[11]);

struct command {
    char *name;
    void (*cmd_func)(char args[11]);
} command_tbl[] = {
    { "AA", func_aa },
    { "CC", func_cc },
    { "XY", func_xy }
};

#define N_CMDS (sizeof command_tbl / sizeof command_tbl[0])

static int comp_cmd(const void *c1, const void *c2)
{
    const struct command *cmd1 = c1, *cmd2 = c2;

    return memcmp(cmd1->name, cmd2->name, 2);
}

static struct command *get_cmd(char *name)
{
    struct command target = { name, NULL };

    return bsearch(&target, command_tbl, N_CMDS, sizeof command_tbl[0], comp_cmd);
}

Тогда, если у вас есть command_str, указывающая на строку изпоследовательный порт, вы должны сделать это для отправки правильной функции:

struct command *cmd = get_cmd(command_str + 1);

if (cmd)
    cmd->cmd_func(command_str);
0 голосов
/ 14 декабря 2015

Не знаю, работаете ли вы еще над этим.Но я работаю над аналогичным проектом и нашел встроенный интерпретатор командной строки http://sourceforge.net/projects/ecli/?source=recommended. Правильно, они имели в виду встроенные приложения.

Функция cli_engine действительно помогает принимать входные данные от вашей командылиния.

Предупреждение: кроме файла readme нет документации.Я все еще работаю над некоторыми ошибками в интеграции фреймворка, но это определенно дало мне преимущество.Вам придется самостоятельно сравнивать строки (например, используя strcmp).

...