Драйвер для lcd матриц



Написание драйвера для LCD дисплея под embedded linux

В данной статье хочу поделиться своим опытом написания linux драйвера для цветного дисплея 320х240 от производителя Newhavendisplays, а именно NHD-5.7-320240WFB-CTXI-T1 под embedded linux. Идея написать статью созрела именно по причине того, что ресурсов по написанию framebufer(FB) драйверов не так уж и много, тем более, на русском языке. Модуль был написан далеко не под самое новое ядро(2.6.30), поэтому допускаю, что в интерфейсах FB много чего поменялось с тех пор. Но, тем не менее, надеюсь, статья будет интересна интересующимся разработкой уровня ядра linux. Не исключаю, что реализацию можно было бы сделать проще и изящней, поэтому комментарии и замечания приветствуются.

Изначально стояла задача написать драйвер, к которому можно было бы обращаться с помощью стандартных средств типа QT embedded, чтобы в конечном итоге соорудить простую менюшку с иконками и текстом для взаимодействия с пользователем. Платформой служила платка на AT91SAM9G45, a точнее www.armdevs.com/IPC-SAM9G45.html
Стримить видео не планировалось. AT91SAM9G45 содержит вполне себе работоспособный встроенный LCD контроллер с поддержкой DMA и довольно скоростной шиной, с которыми потенциально можно было бы добиться приличной скорости и для видео, но увы, хардварно он не совместим с SSD1963. Поэтому было принято решение заюзать для этой цели обычный GPIO интерфейс, как единственную доступную альтернативу.

Интерфейс контроллера SSD1963

Интерфейс контроллера проще всего представить в виде рисунка из даташита дисплея:

С точки зрения разработчика драйвера нас интересуют пины DB0 – DB7. Это 8-битная шина данных, и пины DC, RD, WR, CS, RES которые используются для управления процессом передачи данных на SSD1963.
Что касается формата передаваемых данных, данный дисплей использует формат 888. Что значит: 8 байт – Red, 8 байт – Green, 8 байт – Blue. Еще довольно часто в дисплеях такого типа можно встретить варианты 555, 565, и т.д., но это не наш случай. Формат передаваемых данных изображен на рисунке.

Перед тем, как первый байт данных будет выставлен на шину, должно последовать переключения пинов CS и WR из 1 в 0. А после того как байт данных будет установлен, следует переключение CS и WR из 0 в 1, что, собственно и осуществляет передачу байта данных в контроллер SSD1963. Более детально осциллограммы сигналов можно посмотреть в даташите на контроллер. www.newhavendisplay.com/app_notes/SSD1963.pdf

В исходном коде интерфейс опишем массивами GPIO пинов:

Функция передачи байтов по этому интерфейсу имеет вид:

Как видим, с помощью такой функции можно отправлять на LCD контроллер как комманды (например, для конфигурации дисплея), так и данные в виде пикселей.

Фреймбуфер модель ядра

Как известно, linux ядро предоставляет интерфейсы для разных типов драйверов устройств – char drivers, block drivers, usb drivers и т. д. Framebuffer driver также являет собой отдельную подсистему в линуксовой модели драйверов. Основной структурой, которая используется для репрезентации FB драйвера является struct fb_info в linux/fb.h. Кстати, этот хедер файл также будет интересен любителям юмора в коде linux ядра, так как содержит интересный дефайн —
#define STUPID_ACCELF_TEXT_SHIT. Думаю, название говорит само за себя. Но, вернемся к структуре fb_info. Нас будут интересовать две структуры, которые она содержит – fb_var_screeninfo и fb_fix_screeninfo. Инициализируем их параметрами нашего дисплея.

В нашем случае под пиксель будет выделено 4 байта: 8-Red, 8-Green, 8-Blue, 8-Transparent
Поясню некоторые из полей структур:

.type – способ размещения битов, описывающих пиксели в памяти. Packed pixels означает, что байты (в нашем случае 8888 будут размещены последовательно один за другим).

.visual – глубина цвета дисплея. В нашем случае это truecolor – глубина цвета 24bit

.accel – хардварная акселерация

.transp, red, green, blue – как раз и задают наш 8,8,8,8 формат в виде трех полей – offset, length и msb_right.

Также, для того, чтобы зарегистрировать наш драйвер в ядре, необходимо описать еще две сущности – устройство(device) и драйвер(driver). Опишем FB устройство(struct ssd1963), которое будет содержать страницы нашей видео памяти (struct ss1963_page):

Как и для любого другого модуля ядра линукс, опишем пару функций init/remove. Начнем с init. Framebuffer драйвера, как правило регистрируются в системе как platform_driver:

Platform driver в свою очередь вызывает функцию probe для конкретного драйвера, которая и выполняет все необходимые операции – аллокацию памяти, резервирование ресурсов, инициализацию структур и т.д. Приведем пример функции ssd1963_probe:

Несколько комментариев к функции. Здесь мы последовательно:
— Выделяем память под наше устройство ssd1963
— Выделяем память и инициализируем струкруру fb_info, сначала значениями по умолчанию(framebuffer_alloc), так как многие параметры нам изменять не нужно, а затем конкретными значениями для нашего драйвера, как fb_var_screeninfo, fb_fix_screeninfo и fb_ops, которую мы рассмотрим немного позже.
— Выделяет память под непрерывный буфер пикселей в виртуальной памяти, которая будет использоваться для записи user-space процессами.
— Выделяем ssd1963_page для каждой страницы в виртуальной памяти фреймбуфера. Каждая ssd1963_page будет содержать адрес начала буфера страницы по отношению к общему буферу FB, сдвиг по х, сдвиг по y, и длину буфера страницы. В нашем случае емкость фреймбуфера = line_length*height = 320*4*240 = 307200 байт. Для такой емкости буфера нам потребуется line_length*height/PAGE_SIZE = 307200/4096 = 75 страниц. Отметим, как они будут располагаться в памяти FB. Понимание этого расположения страниц пригодится нам при рассмотрении функции ssd1963_copy немного позже:

— Регистрируем наш FB в системе(register_framebuffer) и инициализируем процедуру отложенного обновления данных (fb_deferred_io_init), детальней об этом в разделе “операции с фреймбуфером”.
ssd1963_setup конфигурирует необходимые GPIO на AT91SAM9G45 CPU и выполняет начальную настройку LCD контроллера. Алгоритм начальной конфигурации в виде отправки набора загадочных байт в хексе взят из документации на SSD1963, поэтому приведу здесь только часть функции:

ssd1963_update_all устанавливает флаг must_update=1 для всех страниц и инициирует механизм обновления дисплея в отложенном контексте с помощью вызова schedule_delayed_work(&item->info->deferred_work, fbdefio->delay);

Итак, с init разобрались, с функцией remove все куда проще, освобождаем выделенную память, и возвращаем FB структуры ядру:

Операции с фреймбуфером

Итак, пришло время рассмотреть структуру fb_ops:

Я не привожу здесь все методы структуры, любопытный читатель сможет найти их в исходном коде модуля либо в любом другом драйвере в коде ядра в каталоге drivers/video. Как вы уже догадались, структура fb_ops описывает действия, которые может осуществлять наш драйвер. К счастью, разработчики ядра частично облегчили нам работу, предоставив стандартные функции для работы с FB, имеющие суфикс sys_ или fb_sys, например fb_sys_read. Нам нужно лишь добавить в нашу имплементацию функций из fb_ops (ssd1963_read, ssd1963_write и др.) функционал, позволяющий выполнять обновление данных в нашей импровизированной видео памяти, когда в этом возникнет необходимость.

Например, функция ssd1963_fillrect будет выглядеть так:

Очевидно, что системный вызов fb_fillrect обновит видео данные в определенной прямоугольной области экрана, поэтому нам нужно указать, какие именно страницы нам нужно обновить, пометив их флажком must_update, и затем вызвав вручную процедуру обновления видеопамяти:

Обновление данных в видеопамяти происходит в виде отложенном контексте(deferred context). User-space приложение, работающее с графикой, не будет ожидать завершения записи каждого кадра в видеопамять, что вполне логично. Отложенная обработка в fb_info определяется в виде структуры fb_deferred_io:

Функция ssd1963_update c прототипом
void ssd1963_update(struct fb_info *info, struct list_head *pagelist);
не обновляет все страницы, а только страницы, которые были изменены в результате перезаписи user-space процессом, или в результате системного вызова, типа fb_fillrect и компании. Соответственно функция имеет вид:

На данном этапе, вы наверняка задались вопросом, что делает функция ssd1963_copy. Она как-раз-таки делает всю “грязную” работу по передаче данных из страниц видеопамяти на искусственно созданную, 8-битную шину на базе GPIO.

Здесь необходимо вспомнить рисунок, на котором изображено как соотносятся наши страницы в памяти с пикселями дисплея. Видим, например, что в page[0] хранится информация для трех верхних линий дисплея по 320 пикселей, и 64 пикселя для 4-й линии. Таких страниц у нас 75, и картинка с рисунка, и как не сложно заметить, page[5] будет выглядеть так же – 3 линии по 320 и одна по 64. Соответственно, функция, принимающая индекс страницы как параметр будет содержать switch(index%5) и в зависимости от офсетов для каждой конкретной страницы отправлять данные в выделенное ей “окно” в памяти дисплея. Функция довольно длинная, поэтому приведу лишь ее часть:

Здесь функция nhd_set_window конфигурирует с помощью уже известных нам nhd_write_data(NHD_COMMAND, …); область дисплея, в которую будет производится запись данных(пикселей).
nhd_write_data(NHD_COMMAND, 0x2c); — команда LCD контроллеру о том, что сейчас последует поток данных.

Ну и напоследок, скриншот работы программы ts_calibrate из пакета tslib на устройстве с дисплеем.
Кому интересно — могу выслать полный код модуля:

Источник

Управление матрицей LCD

Матрица имеет структуру похожую на сетку или матрицу..

Вертикально идут шины данных либо еще их называют столбцы. Они представляют из себя прозрачные проводники (прозрачные электроды). Они подключены сверху к столбцовому драйверу (дешифратору). По вертикальным шинам данных передается напряжение для открывания пикселя (тоесть яркость).
Горизонтальные линии — это строки (выборочные шины) в местах пересечения с вертикальными шинами данных они изолированы от них. Управляет этими шинами строчный дешифратор. Второе название счетчик-адресатор, либо просто драйвер горизонтальных шин. Горизонтальный драйвер подает импульс на ту строку в которую надо записать яркость.

Формирование изображения
Каждое перекрестие это один субпиксель. Запись яркости для матрицы FullHD можно представить как работу цикла. Сперва для 1920*3 транзисторов субпикселей вертикальным драйвером формируютсянапряжения, а затем проходит первый импульс от горизонтального драйвера и происходит запись этих напряжений в каждый субпиксель (напряжением заряжаются 1980*3 конденсаторов) «выполняется первая итерация цикла» и так опереация формирования напряжений и «записи» значений этих напряжений идет по циклу 1080 раз. Таким образом формируется полный кадр. Конденсаторы запоминают заряд пока не прорисуется вся матрица и не начнется новый кадр.

Строчные драйвера.
Строчные драйвера чаще располагаются на «ушках», либо располагаются прямо на стекле. И прикладывают напряжение на затворы транзисторов.
Сигналы драйвера:
1. STVI — импульс с частотой следования кадров или с частотой смены полей. Например у матрицы с частотой 100Гц это 100 полей в секунду.
2. STVO — выход сигнала для перехода на следующий драйвер.
3. CPV — сигнал строчной синхронизации. Идет на все боковые драйвера параллельно.
4. OE — Идет на все боковые драйвера параллельно.
5. VGH (Voltage Gate Hight)(Von) — Напряжение высокого уровня для открытия транзисторов. (18В — 28В)
6. VGL (Voltage Gate Low)(Voff) — Напряжение низкого уровня для закрытия транзисторов. (обычно -6, но бывает -8В или -9В) Если напряжение меньше -4, -3, или выше, то надо смотреть T-CON который формирует эти напряжения.
7. Vdd — Напряжение питания драйвера 3,3В

На ушах драйверов имеются пятачки — контрольные точки.

Столбцовые драйвера.
Они находятся в шлейфах от стекла к планке, либо к блоку T-CON.
Их количество зависит от конструкции самой матрицы. Каждый драйвер работает на определенную часть экрана.
Данные поступают на сдвиговой регистр, затем они сдвигаются и заполняют регистры (ячейки строки), далее данные постпают в ЦАП и затем в усиитель.
Отдельно стоит отметить работу ЦАП. Для свое работы ему необходимо опорное напряжение. Для формирования цветовой гаммы к нему подходит 14 напряжений (GAMMA, GMA1. GMA14). ЦАП выставляя на шину свои 8 бит смешивает эти напряжения и формирует необходимое для конкретной яркости напряжение. Встречается неисправность, ЦАПа, когда матрицу заливает одним цветом, например всё становится красным или зеленым. Эта неисправность может указывать на неправильную работу ЦАП.
На столбцовый драйвер приходят сигналы данных ODATA и EDATA они идут 24-битными и поступают на все столбцовые драйвера.
Для синхронизации есть импульсы:
SP — это стартовый импульс загрузки. Когда первый драйвер отработал SP поступает на второй драйвер.
CLK — частота с которой происходит запись в пиксель.

Синхроимпульсы, питание, ODATA, EDATA, GAMMA — все это формируется на модуле T-CON.

Автономный режим матрицы.
Этот режим нужен для проверки работоспособности матрицы и сокращения времени на диагностику. В автономном режиме матрица переходит в режим «самотестирования» показывает нам разноцветные поля, шахматное поле, серое поле, белое поле и.т.п.
Например если на экране после включения отображаются полосы, то подозрение может упасть как на матрицу, так и на Main Board и на T-CON.
Если в автономном режиме матрица нормально работает, то это говорит о том, что у нас нормально работают: матрица, драйвера, синхронизация, T-CON и в этом случае стоит искать неисправность например в Main Board.
Не все матрицы имеют автономный режим. Часто не бывает автономки у матриц samsunga.
Включение автономных режимов:
При включении автономных режимов во всех приведенных ниже случаях не забываем про подсветку она должна быть включена.
LG — Отключаем LVDS подаем питание на T-CON и матрица переходит в режим тестирования.
— Для включения автономного режима надо узнать какое напряжение идет на T-CON. Обычно это 12В, но бывает и 5В. Далее необходимо включить тестовый режим, для этого на T-CON обычно имеется контрольная точка, которая обычно обозначается (AGM, AGMODE, TEST). Эту контрольную точку можно попробовать замкнуть на корпус через резистор 1кОм, если режим не включился, то пробуем подать на эту точку 3,3 через 1кОм.
— Многие T-CON не имеют на борту кварцевого резонатора, потому для работы им всё же нужна шина LVDS, тогда мы её подключаем, питание в этом случае у нас идет через неё, а мы через резистор 1кОм проделываем описанную операцию с вышеуказанной контрольной точкой.

Неисправности драйверов.
1.
С этими сигналами бывают самые серьезные неисправности. Эти сигналы и напряжения, а точнее проводники подводящие сигналы и напряжения к драйверам — обрываются. Обрываются они под стеклом матрицы. Они проходят под стеклом от столбцового драйвера к строчному. В этом случае необходимо продублировать оборвавшийся сигнал проводком, припаяв его на соответствующие пятаки. Также могут оюорваться проводники идущие от драйвера к драйверу от STI к STV. OE и CPV тоже могут отвалиться на пути к драйверу.

Источник

You may also like...