Пытаюсь реализовать полность асинхронную обработку I2C без выделения памяти. Нюанс этой шины в том, что чтобы что-то прочитать, нужно сперва записать адрес регистра, из которого нужно выполнить чтение. И с точки зрения машины состояний, эти два шага — независимые. Если бы сиутация была как с UART, то достаточно было бы завести кольцевой буфер и все запросы на чтение и запись сваливать туда. А тут требуется разбивать запросы логически на блоки, иначе как понять, что после записи очередной порции данных, теперь нужно считывать?
Я подумал, что задачу можно решить следующим образом. Пусть инициаторы запросов сами формируют структуру сообщения, а драйвер I2C будет лишь связывать структуры в список и исполнять машину состояний для очередного сообщения. После очередного достижения терминального состония, текущее сообщение удаляется из списка (для связного списка это просто переприсваивание указателей), и в обработку берётся следующее, если оно есть.
Например, структура сообщения может иметь вид:
struct msg {
int type; // 0 - READ, 1 - WRITEint size;
char *data;
msg *prev;
msg *next;
void (*callback)(); // вызывается драйвером из прерывания, когда данные прочитаны/записаны
};
Тогда инициатор запроса будет заполнят нужные поля сам и отправлять сообщение драйверу. Драйвер добавляет сообщение в начало списка, а обрабатывает список с конца (или наоборот, это неважно). Весь код является последовательным, и только прерывания добавляют асинхронности, поэтому каждому пользователю шины достаточно хранить одну структуру сообщения в статической памяти. Таким образом достигается и потокобезопасность, и отсутствие необходимости в динамической мапяти.
Как идея — имеет право на жизнь?
Re: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, cppguard, Вы писали:
C>Как идея — имеет право на жизнь?
Не очень имхо. Я бы в интерфейсе драйвера функцию request(int rw, data, size, callback) сделал. Остальное — детали реализации драйвера, нечего им наружу просачиваться. Любой клиент, криво владея твоей структурой, может порушить драйвер.
Re[2]: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А я бы еще машину состояний делал на плюсовом объекте с методами, а не на сишной структуре с функциями.
да там непонятно, может на С все пишется. А так, я бы еще из request хотя бы номер запроса, присвоенный драйвером, возвращал, а когда драйвер callback дергает, совал бы его туда в качестве аргумента. Так пользовательский код сможет отследить выполнение конкретных своих запросов. Оно, конечно, и так можно жить, подсовывая разные коллбэки разным запросам, но с номерами мне больше нравится.
Re[2]: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, andyp, Вы писали:
A>Не очень имхо. Я бы в интерфейсе драйвера функцию request(int rw, data, size, callback) сделал. Остальное — детали реализации драйвера, нечего им наружу просачиваться. Любой клиент, криво владея твоей структурой, может порушить драйвер.
Суть проблемы в том, что шина очень медленная, а драйвер работает через прерывание, которое срабатывает, когда состояние шины меняется. И хочется клиентам дать неблокирующийся доступ к функциям драйвера. Самый простой вариат — односвязный список, где каждый элемент хранит буфер для приёма или отправки данных. Но руками оперировать памятью не хочется, потому что у нас даже кучи нормальной нет — микроконтроллер. Проблемы с владением могут быть, но такой пример проще будет написать искуственно, чем он всплывёт в реальной программе.
Re[3]: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, cppguard, Вы писали:
C>односвязный список, где каждый элемент хранит буфер для приёма или отправки данных.
Сколько таких элементов может быть в одном списке?
C>Проблемы с владением могут быть, но такой пример проще будет написать искуственно
Да понятно, что при аккуратном подходе риск минимальный. Но, если уж такое делать, то добавить в отладочную сборку какие-нибудь проверочные поля, и менять/проверять и в клиенте, и в драйвере.
Re[4]: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, cppguard, Вы писали:
C>Суть проблемы в том, что шина очень медленная, а драйвер работает через прерывание, которое срабатывает, когда состояние шины меняется. И хочется клиентам дать неблокирующийся доступ к функциям драйвера. Самый простой вариат — односвязный список, где каждый элемент хранит буфер для приёма или отправки данных. Но руками оперировать памятью не хочется, потому что у нас даже кучи нормальной нет — микроконтроллер. Проблемы с владением могут быть, но такой пример проще будет написать искуственно, чем он всплывёт в реальной программе.
Блокировка там очень короткая будет — пока драйвер добавляет твою структуру в список запросов на обработку. Там всего-то несколько указателей заполнить имхо. Количество структур можно фиксированным сделать и держать пул свободных. Когда пул пустой, драйвер даёт отлуп на выполнение запроса. Памятью (той, на что указывает data в твоей структуре) могут и клиенты владеть, на контроллере проблем не будет, MMU-то нету. Ну я бы как-то так пил это всё.
Re[5]: Хранение управляющей структуры в вызывающем объекте
Здравствуйте, cppguard, Вы писали:
C>По одной записи на каждое устройство. Например, висит на шине I2C дисплей, компас, акселерометр и датчик влажности. Вот будет 4 узла — максимум.
Я о том, сколько машине состояний драйвера реально необходимо отдельных записей для обработки запросов клиентов. Состояние самой машины достаточно хранить в объекте, представляющем устройство/соединение — его создает клиент. Если в запросе на чтение/запись только один участок (буфер) данных, то адрес, размер и указатель логично положить туда же. Список из неопределенного количества описателей имеет смысл, когда клиент в одном запросе может указать несколько участков/буферов, разные виды операций и т.п., но у Вас такого, как я понял, не предусмотрено.