topor_topor 0 23 апреля, 2019 Опубликовано 23 апреля, 2019 · Жалоба Доброго времени суток. Решил стать программистом :) и заодно изучить связку STM32\FreeRTOS\C++. Хочу запустить драйвер (LCD с I2C напр.) в отдельном таске FreeRTOS, потому что очень медленная комуникация. Драйвер написан как С++ класс. Доступ к методам объекта драйвера (разны команды к LCD) соответственно с другого (MAIN) таска. Просто очередь для обмена командами не подходит, ибо это не поток данных а разнообразные (разно форматные, с разным набором параметров) команды. ++++++++++++ Подскажите пожалуйста есть ли какие стандарты (или красивые способы) для организации доступа к методам такого объекта-драйвера с других тасков? Может есть стандартный интерфейс драйверов для таких случаев? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 24 апреля, 2019 Опубликовано 24 апреля, 2019 · Жалоба 4 hours ago, topor_topor said: Подскажите пожалуйста есть ли какие стандарты (или красивые способы) Скорее эта красота относится к программированию на целевом языке программирования (Си, Си++). Операционка лишь предоставляет способ передать абстрактный объект из одного места к другому. Может быть вам поглядеть на юнионы в Си? Как раз для разнородных сообщений подходит) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 17 24 апреля, 2019 Опубликовано 24 апреля, 2019 · Жалоба 6 часов назад, topor_topor сказал: Просто очередь для обмена командами не подходит, ибо это не поток данных а разнообразные (разно форматные, с разным набором параметров) команды. Почему не подходит? См паттрен Command. Делайте очередь из Command-ов. Или, на вскидку.... структура struct Command { void (*f)(void *data); //указатель на LCD::print(void *)/rect(void*)/clear(void*) void *data; }; в очередь структуру Command. в lcd-Таске из очереди извлекать структуру и выполнять Command::f(Command::data); в методе аргумент void * приводить к нужному, например void LCD::print(void *data) { char *text = (char*)data; this->print(text); } void LCD::rect(void *data) { Rect *rect = (Rect *)data; this->drawHorizon(rect->x0, rect->x1); ... } ну как-то так... зы ну и ещё нужно быть уверенным, что память Command::data освободиться после выполнения. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Johnny81 0 24 апреля, 2019 Опубликовано 24 апреля, 2019 · Жалоба А вы уверены, что тут вообще нужна очередь? Есть общие данные - содержимое дисплея. С ними работают из разных потоков через локи. Отдельный поток занимается передачей этих данных в дисплей. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
topor_topor 0 24 апреля, 2019 Опубликовано 24 апреля, 2019 · Жалоба 18 hours ago, juvf said: Почему не подходит? См паттрен Command. Делайте очередь из Command-ов. Или, на вскидку.... структура Спасибо за дельный совет. Решение красивое и полностью в концепте ООП. Пробую реализовать. Планирую передать объект Command (объект практически та-же структура). Это прекрасно сработает для передачи команды с параметром (данными) от MAIN в таск драйвера. ++++++++++++++++++++++++++++++++++++++ А что делать для команды чтения (типа ReadID например)? Думаю в обратную сторону можно очередь только для голых прочитанных данных использовать. В неё драйвер вложит результат после приёма объекта Command::ReadID. Соответственно со стороны MAIN нужно выдать в командную очередь объект Command::ReadID и делать периодический поллинг появления данных в приёмной очереди (это и будет ID). После получения данных команду ReadID можно считать завершенной. Подскажите пожалуйста если есть другие варианты. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 17 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба 6 часов назад, topor_topor сказал: Command::ReadID как вариант вторая очередь будет работать. Если ответов мало.... тасков мало.... вполне рабочий лёгкий вариант, без оверинженеринга. НО.... вот допустим, в очереди команд у вас уже 10 команд на выполнение, в обратной очереди 5 ответов (с других тасков были запросы). Ваш текущий таск отправляет Command::ReadID, получаем следуещее состояние: в очереди команд 11 команд в очереди ответов 5 ответов как ваш таск найдет/дождется именно ваш ответ? нужно перебрать всю очередь, в объекты Command/Ответ вводить id по которому таск найдет свой ответ. А если MAIN не дождется ответа и продолжит что-то делать, после появится ответ в очереди. Но этот ответ уже ни кому не нужен. Кто его удалит из очереди ответов? Ваша MAIN должда выдать и дождаться Command::ReadID. Т.е. MAIN заблокируется на команде. Если MAIN блокируется, то зачем Command::ReadID передавать в задачу драйвера LCD? Выполните непосредственно из MAIN медленную LCD::ReadID(). LCD::ReadID() должна быть потокозащищённая (мьютекс или симафор). Ещё вариант.... (если хорошо владеете ООП, а то ногу отстрелите), то создать очередь команд из указателей на Command. В таске драйвера lcd извлекаете из очереди указатель на Command, выполняете команду, И.... если команда с ответом, то в Command помещаете ответ и выставляет в Command флаг "выполененно". Если Command безответная (типа clear), то после выполнения команды удаляете этот объект (Command) по указателю. В MAIN ждите флаг в нужной своей Command. ps Command::флаг можно оформить через фриртосовский эвент, тогда не нужно в MAIN периодически проверять флаг, а заблокироваться на ожидании евента. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба 1 hour ago, juvf said: как ваш таск найдет/дождется именно ваш ответ? Именно по этой причине в своё время, вдоволь наигравшись с глюками индентификации своего ответа, сделал две очереди. Одну для команд, другую для ответов. В каждую очередь влазит по одной команде и по одному ответу. А функцию отправки сообщения защищена мьютексом. Т.е. если один процесс отправил команду, он ждёт ответ. Другие в это время, да, курят бамбук. Но мне показалось, что такаяреализация самая надёжная. Впрочем она и работает в серийном приборе. /*! * Отправить сообщение системе сбора данных. * * @param _msg сообщение, см. структуру Msg в хидере. * @param _tout таймаут, мс. Либо portMAX_DELAY. * * @return см. файл RetVals.hpp. */ TRetVal MeasCore::sendMsg( Msg &_msg, TickType_t _tout ) { configASSERT(m_daqMsgMutex); configASSERT(m_daqMsgRxQueue); configASSERT(m_daqMsgTxQueue); TickType_t timeout = ( _tout == portMAX_DELAY ) ? portMAX_DELAY : MSEC(_tout); BaseType_t qResult = xSemaphoreTake(m_daqMsgMutex, timeout); if (qResult == pdTRUE) { xQueueReset(m_daqMsgTxQueue); qResult = xQueueSend(m_daqMsgRxQueue, &_msg, 0); configASSERT(qResult == pdTRUE); if (qResult == pdTRUE) { Msg rxMsg; qResult = xQueueReceive(m_daqMsgTxQueue, &rxMsg, timeout); if (qResult == pdTRUE) { _msg.outparams = rxMsg.outparams; _msg.retVal = rxMsg.retVal; } } xSemaphoreGive(m_daqMsgMutex); } return ( qResult == pdTRUE ) ? rvOK : rvTIME_OUT; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 17 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба 1 минуту назад, haker_fox сказал: Т.е. если один процесс отправил команду, он ждёт ответ. Другие в это время, да, курят бамбук Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? Из любого потока вызывай драйвер LCD::ReadID() и жди ответа. На вскидку: TRetVal MeasCore::sendMsg( Msg &request, Msg &replay, TickType_t _tout ) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба А у меня так и есть, вот что из себя Msg представляет: /// Структура с сообщением, которое можно отправить системе сбора данных typedef struct _Msg { MsgCode code; /// код команды const void * inparams; /// входные параметры void * outparams; /// выходные параметры (ответ) TRetVal retVal; /// Результат исполнения команды _Msg() : code(MsgCode::NOP), inparams(0), outparams(0), retVal(rvFAILED) {} _Msg( MsgCode _code, const void * _params, void * _outparams ) : code(_code), inparams(_params), outparams(_outparams), retVal(rvFAILED) {} explicit _Msg( MsgCode _code ) : code(_code), inparams(0), outparams(0), retVal(rvFAILED) {} } Msg; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
topor_topor 0 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба 14 hours ago, juvf said: Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? LCD драйвер реализует программный I2C. Соответственно если он идёт в MAIN то забивает надолго любую другую активность. А в отдельном потоке он работает паралельно и не видимо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 17 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 2topor_topor Цитата 21 час назад, juvf сказал: Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? LCD драйвер реализует программный I2C. Соответственно если он идёт в MAIN то забивает надолго любую другую активность. А в отдельном потоке он работает паралельно и не видимо. Вы читайте полностью пост. Цитата 22 часа назад, haker_fox сказал: Т.е. если один процесс отправил команду, он ждёт ответ. Другие в это время, да, курят бамбук Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? Вам же пишет haker_fox - " Другие в это время, да, курят бамбук", а вам нужно "работает паралельно и не видимо". Вы уже определитесь - вам шашечки или ехать? Вот я и спрашиваю, если писать код, что другие в это время курят бамбук - то зачем тогда вообще LCD в другой поток и очереди, если всё равно при таком решении MAIN забивает надолго любую другую активность? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 8 minutes ago, juvf said: то зачем тогда вообще LCD в другой поток и очереди, если всё равно при таком решении MAIN забивает надолго любую другую активность? Ну мой случай - рудимент, когда я ещё думал, что все задачи могут одновременно давать команды. Потом переделывать просто не стал. Да, неоптимально. Но работает) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 17 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 56 минут назад, haker_fox сказал: Ну мой случай - рудимент, когда я ещё думал, что все задачи могут одновременно давать команды. Потом переделывать просто не стал. Да, неоптимально. Но работает) Про рудимент понятно..... но всётаки наверно очередь из указателей - опасно. В вашем коде заряженный пистолет ждёт чью-то ногу. возможно в вашем коде пистолет ни когда не стрельнет, но в другом коде может быть очень больно. допустим вы в майне на стеке создали структуру Msg _msg, передали ссылку в sendMsg(_msg, 100), которая передает указатель в очередь qResult = xQueueSend(m_daqMsgRxQueue, &_msg, 0); Допустим в ОС крутится много задач очень важные задачи (с приоритетом выше LCD), которые заняли МК например на 200 миллисекунд. MeasCore::sendMsg() не дождался ответа и вернул false. MAIN удалил _msg (или вышел из области _msg). Тут вдруг LCD дождался таки процессорного внимания и начал работу. Поток LCD видит в извлекает из очереди команд указатель на _msg и.... упс.... а _msg уже удалена. Обращение к удаленному объекту!!! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 1 hour ago, juvf said: Обращение к удаленному объекту!!! Совершенно верно, я уже тоже с этим столкнулся, когда были указатели на стековые удалённые переменные. Поэтому таймаут везде при вызове portMAX_DELAY. Дикий рудимент Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
topor_topor 0 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 2 hours ago, juvf said: Другие в это время, да, курят бамбук", а вам нужно "работает паралельно и не видимо". Вы уже определитесь - вам шашечки или ехать? Вот я и спрашиваю, если писать код, что другие в это время курят бамбук - то зачем тогда вообще LCD в другой поток и очереди,... Ok. LCD висит на I2C. На нем же висят AUDIO, EEPROM итд. Если один из них занял порт то ви прави - остальние курят.... Но зачем другим полезним функциям MAIN липнуть? Например можно продолжать реагировать на кнопки, усреднять измерения АЦП итд. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться