fk0, легенда (24.10.2011 02:41, просмотров: 2441) ответил или нет? на Правильно, если нет select, значит write блокирующий.
Топик посвящён программированию микроконтроллеров в условиях необходимости экономии электроэнергии и архитектуре ПО в целом. Что касается архитектуры: в википедии (см. ссылку в начале топика) показано несколько вариантов Embedded software architectures. Если исключить вариант использования ОС, то практически часто используется так называемый big loop (Simple control loop). В википедии также отдельно выделяется Interrupt controlled system, но часто эти два метода объединены в один примерно следущим образом: есть "драйвера" различной аппаратуры, время-критичная часть которых исполняется в обработчике прерываний, всё остальное выполняется в основном цикле "big loop". В котором, как сказано в википедии, просто последовательно вызываются различные компоненты программы. Ввиду того, что компоненты могут быть относительно независимыми и при этом не могут блокировать выполнение цикла, например, зацикливанием в какой-либо подпрограмме, потому, что тогда прекратится выполнение других компонентов, то обычно состояние отдельных компонентов хранится в переменных, а сами компоненты реализуются как конечные автоматы, например. Также в главном цикле сбрасывается watchdog таймер например. Причём, возможно, по какому-либо условию, а не в каждом цикле, например, строго 10 раз в секунду, с использованием программного таймера, что гарантирует сброс при случаном запрещении прерываний (таймер остановится). Компоненты взаимодействуют между собой путём непосредственного вызова их функций, путём наблюдения за переменными состояния, путём переменных-флагов и т.п.
Отдельным случаем "big loop" является такая архитектура, когда непосредственно большого цикла, в котором поочерёдно выполняются все компоненты нет. Это хорошо подходит для программ, где есть какой-то основной компонент. В таком случае состояние этого компонента может определяться счётчиком программных инструкций PC. Удобно, например, когда вся работа прибора управляется через GUI-интерфейс на дисплее. В таком случае во всех циклах внутри программы главного компонента (ожидание ввода-вывода, например) вызывается некоторая функция idle(), например, внутри которой находится тот же big loop, описанный в предыдущем абзаце -- что обеспечивает работу других компонентов.
Собственно об экономии энергии. Классическая big loop программа будет на 100% загружать ядро микроконтроллера вычислениями и потреблять энергию. Это принципиальный недостаток big loop. Решением могла бы быть ОС или другая событийно-ориентированная система программирования. Можно ввести в цикл задержку, например, запускать исполнение цикла строго раз в N миллисекунд, например. На время задержки микроконтроллер переводится в SLEEP режим. Это так называемая time-triggered архитектура. Это подходит для каких-то простых задач. Но если компонентов относительно много, они между собой взаимодействуют, и какое-либо событие (смена состояния, установка флага...) в одном компоненте может повлечь изменения в ещё нескольких компонентах, то в time-triggered системе это всё может занять значительное время, по очевидным причинам. Возможен другой способ остановить процессор: в каждом цикле процессор засыпает до возникновения аппаратного прерывания, а после возникновения любого прерывания, наоборот, не засыпает в течении нескольких циклов. Это позволяет обойти ситуацию описанную выше (считается, что все компоненты провзаимодействуют за эти несколько циклов или вызовут аппаратное прерывание). В случае, если какому-либо компоненту требуется производить длительные вычисления, то можно ввести функцию, вызываемую этим компонентом, для запрета засыпания в следующем цикле. В ситуации, когда программа не может положиться только на прерывания и от неё требуется реакция с какой-то определённой задержкой -- используется таймер периодически генеририрующий прерывания с меньшим периодом. Да это всё плохие способы, но практически это работает.
Странно, что это всё не описывается в литературе. Там всё больше биты, регистры и тому подобные вещи, в пересказе с ошибками -- эти сведения можно почерпнуть и из даташита. А на вопрос "а как писать сколько-нибудь большие программы для микроконтроллеров" умные дяди отвечают "ха! это и есть know-how, мы на этом деньги зарабатываем"...
Сразу отвечаю на вопрос, откуда вызвавший нарекания на говнокод write() вообще взялся -- я хотел только сказать, что: во-первых вообще негоже вот-так в SFR-регистры что-то записывать из основного цикла, лучше иметь какой-то драйвер UART, к нему уже функцию write, а для UART и вовсе FIFO с прерываниям. Принцип один -- разделяй и властвуй. Когда в одной функции мешаются SFR и логика программы -- это ни перенести на другой контроллер, ни отладить. Во-вторых сама идея блокирующего цикла, ожидающего весьма заметное время, пока данные будут переданы через медленный UART -- порочна. Если есть FIFO, то ситуацию можно значительно сгладить, но не исключить вовсе. По-уму стоило бы либо довести логику этой функции до конечного автомата с двумя состояниями ("передача", "готов") и исключить блокировку при передаче. Либо предусмотреть вызов в цикле, в котором вызывается write(), некой функции idle()...
Теперь отвечаю непосредственно на счёт select() -- надумано. Никто не мешает открывать файл с O_NONBLOCK и не использовать select(). Но в ОС это приводит к 100% cpu load, в микроконтроллере к потреблению электричества. С чем бороться можно и без select(), чем-то вроде usleep(100), например. Метод имеет очевидный недостаток: время исполнения цикла задерживается. В ОС данный недостаток без помощи select и т.п. функций не обойти. А в микроконтроллере можно по методике описанной выше.
[ZX]