topor_topor 0 23 апреля, 2019 Опубликовано 23 апреля, 2019 · Жалоба Доброго времени суток. Решил стать программистом :) и заодно изучить связку STM32\FreeRTOS\C++. Хочу запустить драйвер (LCD с I2C напр.) в отдельном таске FreeRTOS, потому что очень медленная комуникация. Драйвер написан как С++ класс. Доступ к методам объекта драйвера (разны команды к LCD) соответственно с другого (MAIN) таска. Просто очередь для обмена командами не подходит, ибо это не поток данных а разнообразные (разно форматные, с разным набором параметров) команды. ++++++++++++ Подскажите пожалуйста есть ли какие стандарты (или красивые способы) для организации доступа к методам такого объекта-драйвера с других тасков? Может есть стандартный интерфейс драйверов для таких случаев? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 55 24 апреля, 2019 Опубликовано 24 апреля, 2019 · Жалоба 4 hours ago, topor_topor said: Подскажите пожалуйста есть ли какие стандарты (или красивые способы) Скорее эта красота относится к программированию на целевом языке программирования (Си, Си++). Операционка лишь предоставляет способ передать абстрактный объект из одного места к другому. Может быть вам поглядеть на юнионы в Си? Как раз для разнородных сообщений подходит) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 9 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 9 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 55 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 9 25 апреля, 2019 Опубликовано 25 апреля, 2019 · Жалоба 1 минуту назад, haker_fox сказал: Т.е. если один процесс отправил команду, он ждёт ответ. Другие в это время, да, курят бамбук Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? Из любого потока вызывай драйвер LCD::ReadID() и жди ответа. На вскидку: TRetVal MeasCore::sendMsg( Msg &request, Msg &replay, TickType_t _tout ) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 55 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 9 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 2topor_topor Цитата 21 час назад, juvf сказал: Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? LCD драйвер реализует программный I2C. Соответственно если он идёт в MAIN то забивает надолго любую другую активность. А в отдельном потоке он работает паралельно и не видимо. Вы читайте полностью пост. Цитата 22 часа назад, haker_fox сказал: Т.е. если один процесс отправил команду, он ждёт ответ. Другие в это время, да, курят бамбук Так а зачем тогда вообще очереди и драйвер выводить в отдельный поток? Вам же пишет haker_fox - " Другие в это время, да, курят бамбук", а вам нужно "работает паралельно и не видимо". Вы уже определитесь - вам шашечки или ехать? Вот я и спрашиваю, если писать код, что другие в это время курят бамбук - то зачем тогда вообще LCD в другой поток и очереди, если всё равно при таком решении MAIN забивает надолго любую другую активность? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 55 26 апреля, 2019 Опубликовано 26 апреля, 2019 · Жалоба 8 minutes ago, juvf said: то зачем тогда вообще LCD в другой поток и очереди, если всё равно при таком решении MAIN забивает надолго любую другую активность? Ну мой случай - рудимент, когда я ещё думал, что все задачи могут одновременно давать команды. Потом переделывать просто не стал. Да, неоптимально. Но работает) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 9 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 55 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 липнуть? Например можно продолжать реагировать на кнопки, усреднять измерения АЦП итд. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться