LINUX.ORG.RU

Работа с устройствами, низкоуровневые протоколы

 , , ,


0

2

День добрый, недавно столкнулся с проблемой соединения 2х устройств по i2c: slave AT90CAN32, master Linux Based.

Сообствено, как грамотно реализовать обработку комманд со стороны Linux?

Дело в том, что на slave реализовано н-ое кол-во комманд, причем некоторые комманды могут выполнятся на устройстве долго, и необходимо другой коммандой запрашивать статус выполнения.

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

Но так как я сам, не очень силен в программировании протоколов, хочу спросить советов.

Вот пример одной комманды которая уже реализована:

/**
 * @brief количество диспенсеров на инауте.
 *
 * @return QList<int> список из 36 позиций, в котором хранятся количество
 * диспенсеров на каждом инауте.
 */
QList<int> RaspiTWI::dispensersInOuts() {
  QByteArray data(37, 0x00);
  QList<int> result;
  unsigned char checkSumm,
                status;

  this->writeTWI("\x11\x11");

  data = this->readTWI(37);

  checkSumm = data.at(36);

  data = data.mid(0, 36);

  if (!(this->checkCS(&data) == checkSumm)) qDebug() << "Dispensers in inouts is a trap!";

  for(int i=0; i<data.size(); i++){
    result << data.at(i);
  }

  do {
    usleep(STATUS_DELAY);
    status = this->checkStatus();
  }while(status == 0x01);

  //qDebug() << "status is" << status;

  return result;
}

1) Я думаю, что ни есть хорошо после записи данных в шину сразу же их читать 2) Циклический запрос статуса в функции, тоже не очень хорошо

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

1) Я думаю, что ни есть хорошо после записи данных в шину сразу же их читать

предлагаете подождать магическое кол-во миллисекунд? ;) емнип, ничего такого не надо в I2C

2) Циклический запрос статуса в функции, тоже не очень хорошо

судя по прототипу QList<int> RaspiTWI::dispensersInOuts() функция должна быть блокирующей, без цикла не обойдетесь. А вот за отсутствие таймаута (или любого другого «аварийного» выхода из цикла) - надо бить по рукам.


за if (!(==)), кучу магических цифер и отсутствие транспортного уровня протокола вас возненавидят кодеры, за «Dispensers in inouts is a trap!» при расхождении контрольной суммы вас возненавидят админы и прочие пользователи.

dib2 ★★★★★
()

Неизвестно, как внутри класс этот организован, но, в общем случае в i2c:

1. Писать и сразу читать никто не запрещает, обязанность за корректную обработку ситуации лежит на устройстве и его драйвере: чтение может блокироваться до тех пор, пока на шине не появятся 37 байт. Если работает - значит, так и есть. Выглядит тоже нормально.

2. Зависит от реализации checkStatus(). Если устройство само сообщать статус не умеет, то другого способа проверить его нет. Если умеет, лучше переписать в виде получения события и отправки внутреннего сигнала. Это может усложнить устройство класса, палка о двух концах. Опять же, если работает - не трожь, выглядит читабельно и понятно.

Единственные минуса - слишком много пустых строк и for(..size..) надо бы заменить на foreach(char...).

E ★★★
()
Ответ на: комментарий от E

Собственно

/**
 * @brief спросить у платежки статус проведения операции.
 *
 * @return unsigned char - число, которое содержит статус.
 */
unsigned char RaspiTWI::checkStatus() {
  QByteArray data;
  unsigned char result,
                checkSumm;

  this->writeTWI("\x20\x20");
  data = this->readTWI(2);

  checkSumm = data.at(1);
  data = data.mid(0, 1);

  if (!(this->checkCS(&data) == checkSumm)) qDebug() << "Check Status is a trap!";

  result = data.at(0);

  return result;
}

Проблема в том, что этот кодер ушел....и я теперь разгребаю за ним, система на стадии предпроектного тестирования, и есть куча багов и вылетов, я грешу на i2c, но не факт))

Спасибо за: Писать и сразу читать никто не запрещает, об этом не знал.

jeka_sharapov
() автор топика
Ответ на: комментарий от jeka_sharapov

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

Вообще разбираться и отлаживать чужой код - инфернальный труд, сочувствую. ОЧЕНЬ помогут модульные тексты, это вообще базовое правило для работы с унаследованным кодом. Реально сократит время на отладку.

Например, в checkStatus можно оставить запись-чтение, а проверку пришедшего пакета делать в отдельном методе - его проще тестировать, он будет чистым. Как-то так:

unsigned char RaspiTWI::checkStatus() {
  QByteArray data;

  this->writeTWI("\x20\x20");
  data = this->readTWI(2);
  return Protocol::checkStatus(data);
}

...

namespace Protocol {

unsigned char checkStatus(const QByteArray & data)
{
  unsigned char result,
                checkSumm;

  checkSumm = data.at(1);
  data = data.mid(0, 1);

  // Здесь надо как-то обращаться к this->checkCS();
  // хорошо, если он статический, тогда можно просто RaspiTWI::checkCS().
  // Вообще есть в Qt функция qChecksum().
  if (!(this->checkCS(&data) == checkSumm)) qDebug() << "Check Status is a trap!";

  result = data.at(0);

  return result;
}

}

И дальше тестировать:

class ProtocolTest : public QObject {
...
void shouldCheckStatus()
{
   char _data[] = {0x01, 0x02}; // Тут тестовые данные.
   QByteArray data(_data, 2);
   QCOMPARE(Protocol::checkStatus(data), (unsigned char)0x03); // К примеру. Суть понятна.
}
}

Естественно, это нужно для всех методов, где коммункация с устройством проходит. Дальше останется тестировать только writeTWI() и readTWI() и верность ответов устройства отдельно, а это уже намного проще.

E ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.