понедельник, 26 июля 2010 г.

MacrOS: Simply RTOS on Macroses

При всей бесперспективности повторного изобретательства велосипедов порой от этого праздного занятия просто невозможно удержаться. И вот сейчас, зачем нужна ещё одна пародия на RTOS, когда их и так полно, и тут есть несколько соображений. Во-первых, как ни крути, а своя рубашка ближе к телу. И во-вторых, после некоторого опыта писания прошивок под наши приборы я осознал, что для полного счастья мне необходимо совсем не много. Это немного и является отличительной особенностью этого произведения.
1. Таймеры. Программные, с характерной длительностью в 10-тки миллисекунд. Неблокирующие ожидания таймаутов. Wait-ы внутри задачи.
2. Кооперативная многозадачность. В топку приоритеты, вытеснения, жесткое распределение времени. Специфика такая, что прошивка состоит из множества небольших задач, которые не несут сложных расчётов, а большую часть времени ждут срабатывания какого-нить флага или исключения, или запускаются периодически, чтобы собрать данные.
3. Быстрое определение необходимости выполнения задачи, и переход на следующую в противном случае. Переключение контекста - дорогостоящая операция, поэтому хотелось бы понять, чего ждёт задача и проверить это условие не входя в контекст задачи.
Вот, пожалуй, три вещи необходимые для счастья.
Про ОС как таковую тут можно говорить вообще с большой натяжкой. По сути нам необходим лишь планировщик, который бы в цикле перебирал все задачки. Прототипом был планировщик из какого-то примера с www.atmel.com. Уж очень мне нравится фирма, и даташиты толковые, и контроллеры функциональные, производительные и не обременённые ненужными сложностями, и примеры есть, и gcc умеет компилить под avr. Да, изначально MacrOS написана под AVR архитектуру, но для переноса на другой контроллер требуется лишь переписать код для миллисекундного таймера, т.к. всё остальное написано на C. Это в теории. на практике надо переписывать всё, что касается прерываний, а это usart, spi и т.д. Но вернёмся к планировщику. К сожалению, он не умел управлять контекстом, а наличие такой фичи сильно облегчает написание каждой отдельной задачи.
Спасение я нашел в функции setjmp. Но её использование весьма нетривиально, и именно тогда родилась идея, "а почему  бы не обернуть ей вызов в макрос?" Сказано - сделано. Заводим на каждую задачку структуру, типа task, в которой храним контекст и ещё немного инфы, о которой скажу позже. Пишем макрос MOS_Scheduler(), который сохраняет контекст текущей задачи и делает longjmp в контекст планировщика. Всё просто! Планировщик, получив управление, переходит к следующей задаче, сохраняет свой контекст и делает longjmp в следующую task-у, и так по кругу. Но это слишком медленно, учитывая что часто надо проверить лишь состояние флага. Делаем финт ушами, в структуру задачи добавляем ссылку на фнкцию checker и пару переменных, которые будем передавать этой функции как параметры. Одна - указатель, вторая - значение которое надо проверить. После в планировщике вместо перехода в задачу сначала делаем вызов checker-а, а по его результатам определяем, требуется ли переход в задачу. И тут Остапа понесло: можно сделать checker-ы разного типа, которые будут проверять налицие флага, отсутствие, делать anr или or со значением в указателе. В общем, практически всё, что может понадобится для отслеживания нажатия на кнопку или отслаживания аппаратного флага.
Теперь ввести таймеры не составит большого труда. Ставим условие, что проверка на таймаут должна быть быстрой, а старт можно сделать и дольше. Создаём массив с таймерами. Заводим глобальный счётчик, который инктементируется от аппаратного таймера. При старте высчитываем время, когда таймер закончится, и сбрасываем флаг таймаута. В прерывании от аппаратного таймера пробегаемся по всему массиву, и выставляем флаги для тех таймеров, чьи значения меньше текущего. Значения этих флагов позже проверятся в задаче, которая ждёт таймаута.
На этом пока всё, минимально-необходимое окружение есть. Надеюсь, что это окружение будет полезно не только мне, и велосипед приобретёт статус ноу-хау. Исходники имеются и будут выложены, как я приведу их в божеский вид. Нетерпеливым просьба писать на мыло.

воскресенье, 6 июня 2010 г.

О значении текущего момента

Рассуждая как-то о важности и значении поступка, вот этого, конкретного, который надо сделать здесь и сейчас, и который изменит всю мою дальнейшую жизнь, я вдруг набрёл на интересную аналогию, суть которой и хочу раскрыть далее.
Для начала опишу ситуацию более определённо. И так, основной вопрос состоит в том, насколько наше текущее положение здесь и сейчас определено всей предыдущей цепочкой наших решений и зависит ли это положение от каждого из этих решений в отдельности. Или, что то же самое, насколько поступок здесь и сейчас определяет то, куда мы попадём в будущем. Попутно также намечаются небольшие рассуждения о судьбе, силе воли, предназначении... А аналогия, от которой я буду отталкиваться, навеяна одной из лабораторных работ по моделированию хаоса в курсе MATLAB, преподаваемом по-моему на втором курсе ФФ. Суть работы в том, что мы берём небольшой ящик, помещаем туда несколько шаров, даём им какие-то начальные скорости и, запустив время, рисуем за каждым шариком его след. Если теперь через некоторое небольшое время мы остановим шарики, и заменим все скорости на противоположные (обратим время), то они пойдут в точности по своим следам, и, в конце концов, вернуться на свои исходные позиции. Соль заключается в том, что если подождать немного подольше, то шарики на свои позиции уже не вернуться, т.е. они фактически "забудут" то место, откуда они сюда попали. Потом мы говорим, что, начиная с этого времени, мы уже наблюдаем хаотичное движение шаров, которое невозможно рассчитать. В модели это происходит из-за ошибок округления при взаимодействиях шаров на малые углы. В реальности всегда есть трение, шероховатости, квантовая неопределённость, в конце-концов, да и количество шаров измеряется в ~10^23 шт.
А теперь представим, что мы, каждый человек в отдельности, представляет из себя такой шар, родившийся когда-то в каких-то начальных условиях, и взаимодействующий с огромным количеством других людей на протяжении всей жизни, с какими-то только вскользь, а другие накладывают существенный отпечаток на дальнейшую судьбу. А раз так, раз мы - это те же шары, то память наших взаимодействий также не безгранична, и чем дальше наши поступки в прошлое, тем менее они значимы для нашего положения здесь и сейчас. Или по другому, что бы мы сейчас не совершили, никому не известно, как обернётся для нас будущее.
Это следствие в своей непосредственной интерпретации оказывается весьма не радужным. В самом деле, это своего рода заявление на то, что самые отважные, смелые поступки, спустя время, не будут иметь никакого значения, а значит, и совершать их не было никакого смысла. И ничего не остаётся, как плыть по течению и ждать, пока кривая куда-нить не занесёт. Однако, мы ведь не бездумные шары, которые просто подчиняются законам. Здесь я хотел бы сделать акцент на том, что из шаров можно взять только механизм взаимодействия и его свойства. Я вовсе не утверждаю, что всё вокруг хаос без цели и смысла. Уж слишком такие утверждения противоречат самому факту нашего существования (жизни на земле). И в тоже время заострю внимание на том, что этот механизм закрывает для нас столь праздную возможность рассуждать в духе, что "вот я поступлю так-то, и жизнь изменится, изменится непременно, именно в этот понедельник"... А также менее безобидные рассуждения типа того, что "в 5 лет я встретил того-то, и эта встреча предопределила всю мою жизнь".
Можно предположить наличие некоторого течения в этом "хаосе", слабой и незаметной силы, которая становится заметной лишь в спорных ситуациях или на большом протяжении времени, и назовём это течение судьбой. Также важен тот факт, что "каждый шарик имеет своё течение". В этом случае получается, что хоть и от текущего решения практически ничего не зависит, но, в конечном счёте, это течение приведёт нас туда, куда нам было предопределено судьбой. В этом смысле, если человеку было суждено работать в науке, то всё сложится именно так, если же суждено быть строителем, то любые его действия в конечном счёте приведут его к этому. И кирпичи людям на головы также просто так не падают. Однако, всё равно грустно, надо добавить ещё один пункт.
Предположим наличие силы воли. Априори известно, что это величина, которая может изменяться в очень широких пределах. Если люди со слабой волей, тогда основную роль будет играть то незаметное течение, которое мы назвали судьбой. Но где же тогда окажется человек с сильной волей, если справедлив тезис о том, что его текущие старания ничего не значат. Ответ прост - воля есть по сути такое же течение, что и судьба, только в том направлении, в котором нам угодно. Но как же быть с решающими поступками, необходимость которых очевидна. Ведь нельзя искать только лёгкие пути, тогда это будет сравни безучастному следованию по течению. Делать смелые и отважные поступки необходимо, но на такой поступок необходимо ещё а) осознанно выйти, и б) осознанно принять решение. В случае утвердительного ответа на оба положения имеем дело с силой воли, в случае отрицательного - с действием судьбы.
И последнее, сухой остаток, что же надо делать, чтобы добиваться целей? Во-первых, не стоит слишком расстраиваться, если текущая ситуация несёт в другом направлении, здесь мало что можно предпринять. Но ветер изменится, и когда он изменится, важно вновь вспомнить о своей цели, и правильно сориентироваться в новых условиях. Поэтому, что необходимо во-вторых, так это думать о цели. Думать постоянно, подсознательно создавая тем самым то течение, которое приведёт нас туда, куда нам надо. Думать и совершать поступки, здесь и сейчас, каждый день. 

пятница, 22 января 2010 г.

Khameleon Modular System на пороге качественных перемен или полного фиаско.

Без малого уже чуть более полутора лет прошло со времени появления идеи о создании модульной системы и написания первых пробных строк кода. По сути, это первый и самый большой мой проект такого рода. Многое было сделано поспешно, многое наугад, но так или иначе, это рабочая программа, спрос на которую внутри нашей группы постепенно растёт. Однако сейчас я буду писать не о радужных перспективах, а о проблемах, вся глубина которых осознаётся только сейчас.

И так, в основе проекта лежала концепция конвеерной обработки данных, где каждая цепочка конвеера предоставляет из себя независимый, динамически подгружаемый модуль, а связаны они между собой при помощи компактного (по определению, ограниченного и замкнутого) интерфейса. При реализации первой же конфигурации стало ясно, что одного конвеерного подхода крайне мало для реализации полноценной программы. И рушится всё на простой задаче реализации различных типов интерфейсов, т.е. на задаче ввода/вывода. Эта задача просто напросто подразумевает двухсторонний обмен сообщениями между модулями, который принципиально не уживается с конвеером, двигающимся в одну сторону. Тогда на этот факт небыло обращено должного внимания, и были введены кастыли в виде обратных запросов, что как-то спасло положение и ввело некую долю неразберихи. С ростом количества модулей эта неразбериха росла, как минимум, по квадратичному закону. В итоге, разобраться во всей этой паутени уже невозможно практически никому. Я сам, после перерыва в несколько месяцев, принял одну фичу за баг, и заслуженно, не имея при этом никакой возможности тот баг исправить, ибо рушилось многое. Ситуация усугубилась при попытке сконфигурировать систему для мониторинга и управления лазером, что находится сейчас в разработке. В этой задаче требовалось лишь отправлять и получать данные с прибора, обернув это в дружелюбный графический интерфейс. В интерфейс-то это обернулось, но внутренняя структура модуля ужасна, не очевидна и неэффективна. В результате имеем следующее. На данный момент в системе нет никаких инструментов для эффективной реализации интерфейсов ввода/вывода (com, ethernet and etc), а также протоколов. Модель конвеера для этого оказалась непригодной. Уже есть, однако, новые идеи, но о них в другой раз.

Передача сигналов между модулями. По поспешности и неопытности была выбрана обёртка QVariant. Однако лишь недавно я узнал, что QVariant не является по умолчанию зарегистрированным типом. Другими словами, Нельзя обернуть  QVariant в QVariant. А я то думал, отчего это у меня не работают отложенные конекты. Видимо, их реализация принципиально отличается от прямых конектов, которые работали. Следующий на примете тип QVariantList или QByteArray, но для их введения необходимо исправить все модули.

Замкнутые циклы при передаче сообщений. Если мы используем прямые конекты, то сигнал передаётся в сокет немедленно, прерывая выполнения того кода, из которого он был вызван. При этом, часть модулей были написаны так, что результатом их успешной работы была посылка сообщения обратно. Посылка такого сообщения также прерывала работу того модуля, который уже прервал модуль, которому отправляет сообщение.. в результате возникает замкнутый цикл, а в Qt преусмотрен механиз разрыва таких циклов. В результате, возникает потеря сигнала, потеря данных на ровном месте, и давольно сложно было понять, что это потеря внутри программы, а не отсутствие ответа от прибора, тем более когда последнее также имело место.

Распараллеливание на потоки. Идея, которая присутствовала практически с самого начала, но опять же, только сейчас назрела. Во первых, разбиение на потоки практически исключает возникновение замкнутых циклов и непредвиденных разрывов в выполнении модулей. Сейчас удалось лишь на скорую руку таки реализовать отложенные конекты, что как-то сняло проблему образования замкнутых циклов. Также разбиение на потоки даст возможность писать "блокирующие" модули с гораздо более линейным и понятным кодом, и они никак не будут мешать другим частям программы. И в тоже время, введение потоков требует полного пересмотра структуры.

Вот же что представляет из себя сложившаяся ситуация. Развиваться в том же направлении проект не сможет, это тупик. А для изменения этого направления необходимы немалые усилия, и не понятно, хватит ли их... Одно уже произошло определённо, проект вышел из области простых упражнений в программировании и  начинает представлять из себя нечто действительно "живое", существующее по правилам...  

четверг, 21 января 2010 г.

Atmel AVR USI TWI library

Довелось на днях разбираться с тем, что предоставляет нам Atmel для взаимодействия с устройствами по протоколу i2c. Производители решили, что реализовывать раздельно spi и i2c много чести, и предоставили нам набор "рассыпухи", из которой мы сами можем соорудить один из интерфейсов, или придумать свой. Основные составляющие этого набора это сдвиговый регистр для обмена данными, 4-х битный счётчик для подсчёта количества пришедших бит, и различные переключалки, для выбора типа протокола, тактового генератора и пр плюс кое-какие прерывания под это дело. Всего три 8-ми битных регистра.
Первым делом глянул в гугл на предмет готовых реализаций i2c на этом железе. Однако, может я плохо смотрел, но нашел только исходники на аппаратный twi. Про TWI на USI были упоминания рядом с IAR (навороченная среда разработки под МК), но что тянуть палёный и непонятный код. Было решено написать "библиотечку" на основе исходников под аппаратный twi.
Собственно, оттуда я взял только названия функций. Посылка на старт, стоп и клоки реализуется программно, т.о. функции у нас получились блокирующие, и во время пересылки мы не сможем больше ничего сделать. Но поскольку twi мне нужен только для начальной конфигурации пары девайсов, то такая не совсем оптимальная реализация меня устраивает.
Во время внимательного чтения даташита на предмет управления с этими тремя регистрами, и попытки оживить всё это дело, обнаружилась пара совершенно тонких и не очевидных нюансов, ради которых и я пишу эту заметку. В документации о них написано весьма вскользь, и, как мне кажется зря.

1. Во первых, нет чёткого разделения на мастера и слейва. И логика устроена так, что при опознавании на начала передачи выставляется флаг USISIF в регистре USISR и одновременно с этим блокируется линия SCL. Особенность в том, что флаг срабатывает и тогда, когда старт формирует сам девайс, и, т.о. чтобы дальше была возможность контролировать SCL необходимо этот флаг сбросить.

2. Передача данных из USIDR на порт. Этот момент менее описан в доках, по этому за правильность не ручаюсь, но когда я сначала выставлял на порт SDA нуль, а после писал данные в USIDR и пытался их оправить, то линия никак на мои действия не реагировала. Однако, когда я сначала выставил на SDA единичку, после чего, чтобы таки выставить его в нуль для посылки start, записал 0x00 в USIRD, и только после этого записал адрес устройства и попытался его отправить, то всё заработало. Эврика, как говорится :) Итого, рецепт такой. Пишем в PORT на пины SDA и SCL по единичке, потом делаем их выходами, а после рулим через USIDR и флаг USITC из USICR.

Исходники можно взять по адресу http://galilley.at.nsu.ru/usitwi.tar.bz2, но есть нюанс, проверена работоспособность только функций на запись, функции на чтение не отлажены, хотя теоретически должны работать.

четверг, 14 января 2010 г.

linux4sam: соображения о работе с TFT LCD

Идея о подключении экранчика к плате на AT91SAM9260 уже давно витает в воздухе. Товарищь alfamayonez.ru, героически повторив подвиг народных умельцев, показал возможность работы по SPI с дисплеем от телефона samsung s65. Однако, такое решение сложно использовать для дальнейших разработок из-за ограниценных размеров самого экрана, а во вторых, чтобы это"счастье" обновлялось с более/менее приемлемой скоростью ему надо отдавать шину SPI полностью, а она нам может ещё понадобится. Вопрос состоит в следующем: можно ли, и если да, то как подключить к нашей плате дисплей размером в несколько дюймов, доступный для покупки, без больших костылей и без большой загрузки ЦП.
После двух дней изучения гугла на предмет того, что у нас вообще бывает, получилась вот какая картина.

1. У нас бывает VGA. Есть чипы по преобразования цифры в VGA, но этот вариант я не рассматриваю т.к.  имеем мы дело не с аналоговым монитором, и как-то странно использовтаь для цифровой панели аналоговый интерфейс. К тому же, большинство панелей с цифровым интерфейсом, а с VGA уже идут мини-мониторы, которые существенно дороже.

2. Extended Bus Interface (EBI). Такая шина присутствует на нашем контроллере и предназначения для подключения переферии типа CompactFlash, SDRAM и много чего ещё (см. datasheet). На некоторых дисплеях, как правило до 3-5 дюймов, есть 16-ти битный параллельный интерфейс. И при беглом взгляде он совместим с EBI. Т.е. в таком случае получается всё просто, берём LCD с таим интерфейсом и напрямую вешаем на EBI. Проблема в том, что такая шина есть только у LCD с малой диагональю. По крайней мере я не видел этой шины у дисплеев с диагональю 7' и выше.

3. Ещё бывает RGB. Цифровой параллельный интерфейс, где используются несколько служебных сигналов, и шина данных, шириной в (разрядность одного цвета)х3, где за один такт передаётся значение R, G и B для одного пикселя. В подробности работы не вникал, но судя по всему, вывод на экран осуществляется просто последовательной передачей данных для каждого пикселя от первого до последнего. Если требуются большие расстояния, то можно постараться и найти чип для преобразования этой шины в LVDS и обратно. Ещё одна главная особенность состоит в том, что мы не можем напрямую подключить эту шину к нашему контроллеру, необходимо нечто вроде "видеокарты", которая и будет заниматься полной перерисовкой, а мы бы только записывали в память картинку, которую хотим отобразить. Первое, что пришло на ум, так это поискать интерфейс EBI<->RGB. Оказалось, что это вещь весьма экзотическая. Из того, что наиболее близко к этой задаче нашел решение на fpga от Altera (Avalon LCD Controller), но сомневаюсь что такую штуку будет легко достать. Т.о. в нашем случае остаётся практически один выход, это использование микроконтроллера со встроенным контроллером LCD. Благо, это уже не редкость, и имеется в МК тойже линейки AT91SAM9263.

Сухой остаток: если достаточно дисплея до 5-ти дюймов, то можно использовать EBI, если же хочется чего-то большего, то надо менять контроллер. Все остальные решения будут слишком хлопотными и дорогими.