Проекты GitHub SourceForge |
Статьи /
USBLCDГрафический LCD индикатор, подключаемый к USB.- cоздан на основе AVR контроллера Atmega8 и драйвера avrusb (V-USB). Причины создания.На самом деле причины три:
На самом деле, в Интернете можно найти множество схем подобных самоделок, однако, большенство из них имеют символьные индикаторы и заточены под работу под конкретные приложения. Писать под них приложения не очень удобно, а об использование в Perl'е речь вообще не идет. Да и гораздо прикольнее придумать что-нибудь самому, чем тупо копировать. Цели.И так, чего именно я хотел добиться:
Немного подумав, я решил отказаться от последнего пункта, так как ни оконный менеджер, ни какое другое более-менее серьезное приложение на монохромном индикаторе с разрешением 122x32 запустить не получится. Хотя, в качестве эксперемента, я зупустил его на Xynth. Еще, было-бы неплохо сделать интерфейс для Python'а, тогда можно было бы писать плагины для Gnome'а и Rhythmbox'а. Однако, Питона я не знаю. Железо.Конструктивно, утройство представляет собой USB гаджет с графическим ЖК индикатором и тремя разноцветными светодиодами. Все это управляется микроконтролеером AVR atMega8. Со схемой устройства можно ознакомиться здесь. USB часть взята целиком из проекта V-USB: та схема, которая со стабилитронами. Из изменений, я, разве что, убрал мегаомный резистор и добавил самовосстанавливающийся предохранитель на 100mA, так как не хотелось спалить компьютер во время монтажа и отладки. Светодиоды подключены к выходам ШИМ, чтобы можно было регулировать их яркость. Так как подсветка ЖК индикаторая состоит из нескольких светодиодов я не рискнул подключить ее напрямую к проту микроконтроллера и добавил транзистор. Вполне возможно, что это является лишиним и порт способен справиться с такой нагрузкой. Регулировать якрость подсветки нельзя. Впрочем, она сама по себе не особенно и яркая. К оставшимся портам ввода-вывода я подключил сам ЖКИ модуль: МТ12232 отечественного производителя МЭЛТ. В общем, схема довольно простая. Все это было смонтированно на небольшой макетной плате. На отдельный DIP разъем были выведены сигналы для программирования микроконтроллера (на схеме не указан). Конденсаторы C1 и C2 нужно установить поближе к выводам микроконтроллера 7 (VCC), 8 (GND) и 20 (AVCC), 22 (AGND) соответственно. Фотографии собранной платы можно посмотреть: здесь, здесь, здесь и здесь. Прошивка.Прошику написать оказалось еще проще, чем сделать железку: благо создатели V-USB сделали за нас всю самую сложную работу. Правда, к сожалению, это драйвер не совсем свободный, однако лицензия разрешает его использование бесплатно в некоммерческих целях. На моей совести остались: протокол взаимодействия высокого уровня, драйвер ЖКИ модуля и светодиодов. Исходный текст прошики можно взять здесь. Я писал это все дело с помощью IDE Code::Blocks и WinAVR, поэтому Make-файлы отсутствуют. Если вы захотите повторить проект один-в-один, то здесь можно взять уже готовый к прошивке бинарник. Исходные коды прошивки, драйвера и примеров можно скачать отсюда. Повозиться пришлось с драйвером ЖК дисплея. Дело в том, что внутренняя видео-память там организованно достаточно запутанно: модуль состоит из двух чипов, каждый из которых управляет половинкой экрана; один байт описывает не строчку из востми пикселей, а столбец и т.д. Однако, я расчитывал сделать устройство с плоской моделью памяти, в котором сканирование происхолит слева-направо - сверху-вниз и при однобитном разрешении один байт соотвтетсвует строчке из восьми пикселей. Поэтому пришлось написать несколько строчек кода для преобразования адресов и повернуть дисплей на 90 градусов: сделать из 122x32 - 32x122. Подробнее об организации видеопамяти будет раcсказано в следующем разделе. Протокол взаимодействия.Устройство имеет два endpoint'а:
Помимо стандартных команд для подключения и настройки, CONTROL endpoint поддерживает следующие Vendor Specific команды:
Рассмотрим их поподробнее. GETINFO
С помощью этого запроса мы узнаем основную информацию об устройстве. Запрос возвращает шестибайтовую структуру t_usblcd_info, которая имеет слудующее описание на языке C: typedef struct { uint32_t type; uint8_t num_modes; uint8_t num_leds; } __attribute__ ((packed)) t_usblcd_info;
Как видно из стуктуры, количество светодиодов и ведиорежимов не может превышать 255. Поле type может отличаться от единицы, если конкретная реализация дисплея отличается от той, что описана в данной статье. Его использует драйвер для того, чтобы определить как именно нужно общаться с устройством. GETMODE
Запрос для получения информации об видеорежиме по его номеру. Нумерация ведется с нуля. В результете возвращается следующая структура: typedef struct { uint16_t width; uint16_t height; uint8_t bpp; uint16_t stride; } __attribute__ ((packed)) t_usblcd_mode;
Поле stride показывает, сколько байт нужно прибавить к текущему адресу видеопамяти, чтобы перейти на следующую строку. В идеале, оно вычисляется по формуле: ` stride = |_ (width * bpp + 7 ) / 8 _| ` Однако, в некоторых устройствах оно может иметь большее значение. Например, в дисплее с разрешением 122x122x8, разработчик хочет, чтобы адрес видеопамяти зависил от координаты пикселя таким образом: YYXXh, то есть, старшый байт слова являлся Y-координатой, а младший - X. В этом случае, значение stride будет равняться 256, несмотря на то, что при данном разрешении строка занимает только 122 байта. "Хвост" строки от 122-го пикселя на экране не отображается. SETMODE
Здесь все относительно ясно: установить текущий видеорежим. Если значение mode number выходит за диапазон допустимых видеорежимов, то ничего не произойдет. Режим по умолчанию (устанавливается по включении питания): 0. Возможность определения текущего видеорежима не предусмотрена. SETPOWER
Управление питанием устройста. Значение mode может быть следующим:
После выключения устройства и последующего включения его состояние не определено. Не гарантируется, что оно будет соответствовать состоянию до выключения или состоянию по умолчанию. Необходимо заного установить видеорежим, яркость светодиодов и загрузить содержимое видеопамяти. GETLED
Получить информацию о светодиооде по его номеру. Возвращает следующую структуру: typedef struct { uint8_t type; uint8_t color; uint8_t max; } __attribute__ ((packed)) t_usblcd_led;
Для некоторых приложений важно зажечь именно тот светодиод, который сигнализирует об ошибке. Как правило, он имеет красный цвет. Но, на устройстве может быть установленно два красных светодиода: один с ошибкой, а другой - еще для чего-нибудь. Именно для их различия и было добавлено поле type. SETLED
Установить значение яркости соответствующего светодиода. Если номер светодиода превышает их общее окличество, то ничего не произойдет. Если передаваемой значение больше максимального для данного светодиода, то будет установленно максимальное значение яркости. 0 - соответствует состоянию "выключено". Яркость растет пропорционально значению. Загрузка данных в видеопамять.Общая структура видеопамяти устройства показана на рисунке. Адрес видеопамяти имеет длину 32 бита, начинается с нуля и вычисляется по формуле: ` addr = y * stride + (x * bpp + 7) / 8 ` В случае, если количество бит на пиксель меньше восьми, то левее отображаются пиксели, закодированные старшими битами. Если количество бит на пиксель больше восьми (16, 24, 32), то значение цвета кодируется в форммате little endian - так, как это удобно x86 процессору. Цвет кодируется в формате RGB: красный цвет - в старших битах, голубой - в младших. Загрузка данных в видеопамять происходит через Output Bulk Endpoint. Для этого массив данных делится на куски длиною не более 255 байт и из них формируются некие команды, которые отправляются на этот Endpoint. Кроме этого, существует особая команда, с помощью которой можно установить определенное значение цвета для целой строки. Формат этих команд описан ниже. HLINE
Рисует горизонтальную линию длиной line length в байтах, начиная с адреса starting addres цветом color. Начальный адрес и длина должны быть выровнены по формату пикселя. Формат поля color зависит от установленного видеорежима: если цветовое разрешение меньше 32-х бит, то старшие биты этого поля не используются. Основное назначение команды: быстрая очистка экрана и рисование прямоугольников. RAW
Копирует байты из image data длиной data size в видеопамять устройства, начиная с адреса starting address. Из таблицы видно, что максимальный блок данных, который мы можем скопировать одной командой не может превышать 255 байт. Адрес и длину выравнивать по цветовому разрешению текущего видеорежима не нужно: мы работаем с байтами, а не пикселями. Формат данных зависит от установленного видеорежима. Драйвер.Драйвер представлчяет собой динамически подключаемую библиотеку, которая содержит функции для работы с дисплеем. Для работы с USB драйверу нужна библиотека libusb. Для того, чтобы собрать драйвер нужно, чтобы была установленная не только сама библиотека но и ее заголовочные файлы. Для Ubuntu или Debian необходимо установить пакеты: libusb и libusb-dev. Во FreeBSD заголовочные файлы устанавливаются вместе с соответствующим пакетом, однако, в директорию: /usr/local/include. Учтите это, запуская скрипт ./configure. Программы, написанные с использованием этого драйвера должны запускаться с привилегиями суперпользователя: иначе доступа в USB не получить. Теоретически, драйвер может быть скомпилирован для Windows, однако в релиз данная возможность не вошла. В драйвер входит тестовая программа, по которой можно изучить API - он сам по себе довольно простой. Для примера, программа, рисующая прямоугольник может выглядеть так: #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <usblcddrv.h> int main(int argc, char *argv[]) { t_usblcd *dev; int err; /* подключаем usblcd устройство */ dev = usblcd_claim(); if(dev == NULL) { printf(stderr, "can't get usblcd device\n"); return -1; } /* установить видеорежим по-умолчанию */ if(err = usblcd_setmode(dev, 0)) { fprintf(stderr, "usblcd_setmode() return: %d\n", err); return -1; } /* очищаем дисплей */ if(err = usblcd_fillrect(dev, 0, 0, dev->mode[0].width, dev->mode[0].height, 0)) { fprintf(stderr, "usblcd_fillrect() return: %d\n", err); return -1; } /* включаем дисплей и подсветку */ if(err = usblcd_setpower(dev, USBLCD_POWER_ON)) { fprintf(stderr, "usblcd_setpower() return: %d\n", err); return -1; } /* рисуем прямоугольник */ if(err = usblcd_fillrect(dev, 8, 8, 16, 8, 1)) { fprintf(stderr, "usblcd_fillrect() return: %d\n", err); return -1; } /* освобождаем usblcd устройство */ usblcd_unclaim(dev); return 0; } Подробней стоит рассказать о координатах и размерах, которые задаются в функциях usblcd_fillrect() и usblcd_bitblt(). Поскольку обе функции, фактически, занимаются пересылкой байтов в видеопамять, то при цветовом разрешении меньше 8-ми бит на пиксель указывать координаты и размеры с точностью до пикселя не представляется возможным. Таким образом, в текущей реализации, где один пиксель занимает один бит, координаты должны быть кратными 8-ми. Perl-модуль.В дистрибьютив USBLCD входит Perl модуль, который служит интерфейсом между драйвером и языком Perl. Думаю, многие со мной согласятся, что в некоторых случаях на Perl'е писать проще, чем на C. Главной "фишкой" данного модуля является то, что в качестве буффера с картинкой методу bitbltgd() передается объект GD::Image, в котором, предварительно, можно нарисовать все, что угодно. Интерфейс модуля во многом похож и интерфесом C-шного драйвера и легко изучается по тестовой программе test.pl. Например, рисование прямоугольника может быть выполнено так: #!/usr/bin/perl use strict; use GD; use USBLCD; # создаем GD::Image объект и рисуем в нем прямоугольник. my $im = new GD::Image(16, 8); my $black = $im->colorAllocate(0,0,0); my $white = $im->colorAllocate(255, 255, 255); $im->rectangle(0, 0, 15, 7, $white); # создаем объект для работы с USBLCD my $usblcd = new USBLCD; die "Can't claim the USB LCD device.\n" unless defined $usblcd; # получаем текущий видеорежим my $mode_ref = $usblcd->getmode; # очищаем экран $usblcd->fillrect(0, 0, $mode_ref->{'width'}, $mode_ref->{'height'}, 0); # поворачиваем экран на 90 градусов (ландшафтная ориентация) $usblcd->setrotate(USBLCD_ROTATE90); # включаем дисплей и подсветку $usblcd->setpower(USBLCD_POWER_ON); # выводим картинку $usblcd->bitbltgd(24, 8, $im); В архиве с исходными кодами вы, также, найдете полноценную программу на Perl: sysinfo.pl, которая выводит информацию о загруженности процессора и оперативной памяти, и рисует график. Файлы.
|