Особенности работы с дисплеем lph9157-2

Разбираемся с LCD экраном LPH9157-2 от Siemens C75/ME75

Особенности работы с дисплеем lph9157-2

Внятной документации на этот экран я не нашел поэтому пришлось разбираться с тем что есть и экспериментировать. В качестве управляющего устройства я использовал Raspberry PI. Так-же была написана программа позволяющая превратить этот экран в мини-монитор.

Описание

Данный дисплей имеет разрешение 132 x 176 пикселей и даёт возможность работать с тремя цветовыми палитрами 16(5-6-5), 12(4-4-4) и 8(3-3-2) бит.

Распиновка и подключение

Тут всё просто, экран питается напряжением 2,9 вольт, подсветка (LED±) запитывается отдельно напряжением примерно 12 вольт(я использовал батарею аккумуляторов соединённую с подсветкой через резистор на 510 Ом).

Pin description
# Name Function
1 RS Low=CMD, High=DATA
2 ~RST Reset input, active low
3 ~CS Chip select, active low
4 SYNC External frame synchorization input, unused by default
5 CLK SPI Clock-in signal (High-to-Low)
6 DATA SPI Data-in signal (MSB first)
7 VCC Power supply, normally 2.9V (I tested with 3.3V)
8 GND Ground
9 LED+ Backlight voltage, approx. 12V (depends on required current)
10 LED- Backlight common pin

Как можно заметить экран управляется через интерфейс SPI (контакты CS/CLK/DAT(MOSI)), предположительно это лишь половина интерфейса так как отсутствует контакт MISO, следовательно писать данные в экран мы можем, а вот читать — нет(здесь следует упомянуть что SPI может работать в двунаправленном режиме с использованием одного провода (MIMO) но так как отсутствует какие либо команды чтения данных из экрана будем считать что этот режим экраном не используется).

И перед тем как переходить непосредственно к управлению экраном надо бы этот экран к чему-нибудь подключить. В моём случае это будет Raspberry Pi. Контакты SPI экрана подключены к соответствующим им контактам SPI «малины», RS и RST к GPIO_17 и GPIO_27 соответственно. Данное подключение актуально для RPI Revision-2, если у вас иная модель то названия и номера контактов GPIO могут отличаться.

Заморачиваться с разъёмом подключения экрана я не стал и просто подпаялся к выводам проводом МГТФ. Экран в данном подключении питается от 3.3В, а не от 2.9 как в описании. Вот так выглядит вся схема в сборе

Команды управления экраном

Экран управляется достаточно просто — путём посылки по SPI команд и данных. Отличить одни от других экрану помогает состояние пина RS где высокий уровень(лог. 1) означает передачу данных, а низкий(лог. 0) передачу команд. При передаче используется тупоконечный(big-ending) порядок байт.

Список команд:

  • CMD_RESET 0x01 — программный сброс
  • CMD_MEMORY_ACCESS_CONTROL 0x36 — установка направления заполнения области дисплея, имеет однобайтовый аргумент 0bVHRXXXXX, где V — заполнение по вертикали (0 — сверху-вниз, 1 — снизу-вверх), H — заполнение по горизонтали (0 — слева-направо, 1 — справа-налево),R — меняются местами строки и столбцы (при этом заполнение остается сверху-вниз, слева-направо))
  • CMD_WAKEUP 0x11 — выход из спящего режима
  • CMD_PALETTE 0x3A — установка цветовой палитры 8(0x02), 12(0x03) и 16(0x05) бит
  • CMD_ENABLE 0x29 — включение дисплея
  • CMD_SET_X 0x2A — задаем область рисования по X
  • CMD_SET_Y 0x2B — задаем область рисования по Y
  • CMD_START_WRITE 0x2C — начало записи в видеопамять

Код

Работа экрана была проверена во всех 3х цветовых режимах но, дабы не захламлять исходник, далее я буду рассматривать только 16-битный.

Во всех остальных режимах работа экрана не отличается, за исключением, разве что 12-битного — в нём на 2 пикселя приходится 3 байта, а если нужно вывести лишь одну точку то посылается 2 байта(4 младших бита последнего экраном игнорируются).

Для доступа к GPIO «малины» была использована библиотека bcm2835.

Начальная инициализация GPIO

int init_gpio()
{ if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // CPOL = 0, CPHA = 0, Clock idle low, data is clocked in on rising edge, output data (change) on falling edge bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // в телефоне экран работает на частоте SPI в 13 МГц
// поэтому небольшое превышение по частоте ему не повредит
// хотя у меня он продолжал работать и при вдвое большей частоте (30 МГц) bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // настроим порты на запись bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1; }

Несколько вспомогательных функций

void send_cmd(char cmd)
{ bcm2835_gpio_write(LCD_RS, RS_CMD); // следующий байт – команда bcm2835_spi_transfer(cmd);
} void send_data(char data)
{ bcm2835_gpio_write(LCD_RS, RS_DATA); // следующий байт – данные bcm2835_spi_transfer(data);
}

Задание области рисования

void set_draw_area(char x1, char y1, char x2, char y2)
{ send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2);
}В результате экспериментов с экраном выяснилось что необязательно задавать область перед выводом каждого кадра, достаточно задать её лишь единожды и послав команду начала записи просто гнать по SPI последовательность кадров непрерывным потоком.

Подготовка к выводу и передача данных

void draw_start()
{ send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA);
} void send_draw_data(char* data, int size)
{ bcm2835_spi_transfern(data, size);
}В процессе инициализации экрана обнаружилась досадная вещь — для запуска необходимо передёрнуть RESET экрана, а вот последующие манипуляции данным выводом приводят экран в ступор и он перестаёт реагировать на внешние воздействия. Приходится сбрасывать его по питанию. Это необходимо учесть при разработке программы дабы процедура аппаратного сброса выполнялась лишь единожды.

Инициализация экрана

void reset_LCD()
{ // аппаратный сброс bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); // программный сброс send_cmd(CMD_RESET);
} void init_LCD()
{ reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE);
}Это была подготовка, а теперь самое вкусное — попробуем вывести на экран 16-битную картинку. Первый блин как известно — комом, после запуска программы я получил довольно странное изображение, оказалось — неверный порядок байт, после исправления всё заработало.

Код

int main(int argc, char **argv)
{ if (!init_gpio()) return 1; init_LCD(); set_draw_area(0, 0, SCREEN_WIDTH – 1, SCREEN_HEIGHT – 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; FILE* f_scr = fopen(“test.

bmp”, “r”); fseek(f_scr, 0x42, SEEK_SET); // skip bmp header fread(&screen, 1, SCREEN_HEIGHT * SCREEN_WIDTH * 2/*16bit*/, f_scr); fclose(f_scr); // change byte order for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) screen[y][x] = (screen[y][x] >> 8) | (screen[y][x] 176×132 малоинформативен, для фреймбуфера было установлено разрешение 320×240, это можно сделать правкой config.txt на FAT разделе «малиновой» флешки.framebuffer_width=320
framebuffer_height=240После этого изображение всё равно жмётся с использованием примитивной интерполяции, но результат уже можно назвать приемлемым. Так-же был добавлен пропуск одинаковых кадров для экономии CPU.Исходник LPH9157-2_RPI.c#include
#include
#include
#include #include
#include
#include // соответствия контактов GPIO и LCD
#define LCD_RS RPI_V2_GPIO_P1_11
#define LCD_RESET RPI_V2_GPIO_P1_13 #define RS_CMD 0
#define RS_DATA 1 #define CMD_RESET 0x01
#define CMD_MEMORY_ACCESS_CONTROL 0x36 // Memory Access Control
#define CMD_WAKEUP 0x11 // Выход из спящего режима
#define CMD_PALETTE 0x3A // Установка цветовой палитры
#define CMD_ENABLE 0x29 //Включение дисплея #define CMD_SET_X 0x2A // задаем область по X
#define CMD_SET_Y 0x2B // задаем область по Y
#define CMD_START_WRITE 0x2C // начало записи в память #define _8_BIT_COLOR 0x02
#define _12_BIT_COLOR 0x03
#define _16_BIT_COLOR 0x05 #define SCREEN_WIDTH 132
#define SCREEN_HEIGHT 176 int init_gpio()
{ if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); /// CPOL = 0, CPHA = 0, Clock idle low, /// data is clocked in on rising edge, /// output data (change) on falling edge bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // настроим порты на запись bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1; } int close_gpio() { bcm2835_spi_end(); bcm2835_close(); } void send_cmd(char cmd) { bcm2835_gpio_write(LCD_RS, RS_CMD); bcm2835_spi_transfer(cmd); } void send_data(char data) { bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfer(data); } void send_data_array(char* data, int size) { bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfern(data, size); } void set_draw_area(char x1, char y1, char x2, char y2) { send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2); } void draw_start() { send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA); } void send_draw_data(char* data, int size) { bcm2835_spi_transfern(data, size); } void reset_LCD() { bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); send_cmd(CMD_RESET); } void init_LCD() { reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE); } #define FB_WIDTH 320// 176 #define FB_HEIGHT 240// 144 int main(int argc, char **argv) { if (!init_gpio()) return 1; int smooth = 0; int dynamic_fps = 0; int argn = 1; while(argn < argc) { if(argv[argn][0] == '-') switch(argv[argn][1]) { case 'i': init_LCD(); close_gpio(); printf("lcd initialized "); return 0; break; case 's': smooth = 1; break; case 'd': dynamic_fps = 1; break; default: printf("Usage: lcd [options] "); printf("Options: "); printf(" -i initialize lcd (hardware reset) "); printf(" -d dynamic FPS (skip same frames) "); printf(" -s smooth image (enable basic intrpolation) "); return 0; break; } argn++; } ///------------------------------------------------ /// draw screen set_draw_area(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; uint16_t old_fb[FB_HEIGHT * FB_WIDTH]; int fd_scr = open("/dev/fb0", O_RDONLY); int scr_sz = FB_HEIGHT * FB_WIDTH * 2/*16bit*/; uint16_t* fb_screenshot = mmap(0, scr_sz, PROT_READ, MAP_PRIVATE, fd_scr, 0); // scaling float scale_X = FB_HEIGHT / (float)SCREEN_WIDTH; float scale_Y = FB_WIDTH / (float)SCREEN_HEIGHT; int frame_cnt = 0; struct timespec ts1, ts2; clock_gettime(CLOCK_MONOTONIC, &ts1); for(;;) // forever { if(dynamic_fps) if(memcmp(&old_fb, fb_screenshot, sizeof(old_fb)) == 0) { usleep(10000); continue; } else { memcpy(&old_fb, fb_screenshot, sizeof(old_fb)); } for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) { int fb_x = y * scale_X; int fb_y = x * scale_Y; uint16_t px = fb_screenshot[fb_x + fb_y * FB_WIDTH]; if(smooth) { // look around if((fb_x - 1 >= 0) && (fb_x + 1 < FB_WIDTH) && (fb_y - 1 >= 0) && (fb_y + 1 < FB_HEIGHT)) { for(int dx = -1; dx 1) + ((add_px & 0xf7de) >> 1); /// ^thank you habr => http://habr.ru/p/128773/ } } } screen[y][SCREEN_WIDTH – 1 – x] = (px > 8); } send_draw_data((char*)&screen[0][0], sizeof(screen)); /// calc fps frame_cnt++; if(frame_cnt >= 100) { clock_gettime(CLOCK_MONOTONIC, &ts2); float allsec = (ts2.tv_sec – ts1.tv_sec) + (ts2.tv_nsec – ts1.tv_nsec) / 1000000000.0; float fps = frame_cnt / allsec; printf(“%f FPS
“, fps); frame_cnt = 0; clock_gettime(CLOCK_MONOTONIC, &ts1); } usleep(1000); } munmap(fb_screenshot, scr_sz); close(fd_scr); close_gpio(); printf(“fin
“); return 0;
}Сборка:pi@raspberrypi ~ $ gcc -o lcd LPH9157-2_RPI.c -lbcm2835 -lrt -std=gnu99Запуск:pi@raspberrypi ~ $ sudo ./lcd -i
pi@raspberrypi ~ $ sudo ./lcd -d -s
параметры: -i — первичная инициализация (дёргаем хардварный reset) -s — включить сглаживание -d — динамический fps (одинаковые кадры пропускаются — экономит CPU)

Читайте также:  Подключение ноутбучного сканера отпечатка пальца к компьютеру

Результат

Источник: https://habr.com/post/251629/

1Спам44 Андрей   (24.12.2014 22:26)lph9157-2сегодня смакетировал только мегу с дисплеем – не работает.

1Спам42 Андрей   (24.12.2014 09:42)Обидно, не нашёл я этой надписи…

1Спам43 Artur21   (24.12.2014 17:37)fчто написано на обороте?  LPH88?

0Спам40 Андрей   (23.12.2014 17:35)Подскажите, плз. Есть Siemens ME75. C его дисплее работать будит, если ему 2,9в подать вместо 5?Или это в прошивке надо всё переделывать?

41 Адвансед   (24.12.2014 06:30)работать будет, только смотрите чтобыдисплей был с маркировкой LS020 – другие не работают

0Спам39 Artur21   (17.12.2014 15:34)Собирал по первой печатке, все работает! Жаль что второй вариант платы добавили позже…

0Спам38 sergej   (16.12.2014 22:43)огромное спасибо за конструкцию, пытаюсь повторить в реалии.

37 Адвансед6476   (27.10.2014 22:06)Есть две собранные платы осциллографа, полностью рабочие, как на фотографиях, собраны по новой версии печаткию отправлю по почте, 500 рублей за плату + ваша пересылка.полный комплект, аккумулятор, дисплей, все все… кроме корпуса

36 Адвансед6476   (27.10.2014 17:04)Внимание! Добавил новую версию печатной платы, подробности в конце статьи. Фотографии там же.

35 Cazykeril   (25.10.2014 07:24)был в отпуске, фотографии постараюсь как можно быстрее выложить

0Спам33 Cazykeril   (02.10.2014 20:57)Есть ли возможность получить исходник программы? Не хватает шкалы для понимания измерений) и вопрос при включении режима ас/dc шкала улетает в неизвестность )))

34 Адвансед   (02.10.2014 22:11)исходника к сожалению нет, данный проект был скачен и сохранен на ПК лет 5 назад на каком то ресурсе, который уже не существует…

1Спам30 Artur21   (27.09.2014 20:34)Кварц на 16 МГц, если контроллер не читается при правильно зашитых фузах, значит кондеры неверные… Или кварц проблемный.

0Спам31 Cazykeril   (27.09.2014 21:28)Спасибо) уже нашел причину, да кондеры не те) без операционника проверить полную работоспособность нет возможности, а и не могу понять как по этой осциллограмме без шкалы определить величину измеряемого параметра)

Источник: http://cxema21.ru/publ/mikrokontrollery/izmeritelnye_ustrojstva/oscillograf_na_atmega8_i_lcd_ls020_ot_siemens/14-2-0-222

Работа с графическим дисплеем на базе PCD8544 — DRIVE2

Популярные в своё время сотовые телефоны фирмы Nokia, такие модели, как 3210 3310 3315 3330 3350 3410 5110 5120 5130 5160 6150 6210, обладали монохромными графическими дисплеями, с экраном 84×48 точек, на базе контроллера PCD8544.

Времена этих телефонов прошли, но дисплеи не канули в Лету, они оказались чрезвычайно удобным и дешёвым вариантом для вывода графической и текстовой информации, при использовании в сочетании с микроконтроллерами.

Сейчас на ebay можно за порядка 100 рублей (вместе с доставкой)купить такой дисплей, установленный на печатную плату 45х45мм, которая также обеспечивает подсветку.

Модуль с дисплеем от Nokia 5110 на базе чипа PCD8544

О подключении таких дисплеев, на базе контроллера PCD8544, и пойдёт речь ниже.

О контроллере

Контроллер PCD8544 разработан фирмой PHILIPS. Документацию на него можно скачать здесь

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

В частности, это накладывает два ограничения:— Нет возможности узнать состояние дисплея, в т.ч. и вообще факт его присутствия программными средствами. Это требует точного соблюдения таймингов при выводе информации.

К счастью контроллер может работать на достаточно большой скорости и это не является проблемой.

— Невозможно работать с изображением в режиме «чтение-изменение-запись». Вывод сложного изображения, где потребуется совмещение разной графической информации, может быть осуществлён только с использованием внеэкранного буфера, или предварительного полного расчёта совмещения изображений.

В то же время, контроллер обеспечивает крайне низкое энергопотребление: менее 300мкА (с типичными дисплеями Nokia) в рабочем режиме, и около 1.5мкА в режиме отключения.

Контроллер имеет встроенный генератор повышенного напряжения, поэтому отдельно подводить напряжение для питания дисплея не требуется.

Сам же контроллер работает при напряжении 2,7 – 3,3 Вольта.

Подключение

Подключение модуля дисплея на базе чипа PCD8544 к МК ATmega8A

Как упоминалось выше, контроллер работает на напряжении 2,7 – 3,3 Вольта. Это значит что сопряжённый микроконтроллер должен либо работать на том же напряжении, либо же как-то согласовать уровни на выводах, например, при помощи делителей.

Кроме «земли» и «питания», к контроллеру дисплея должны идти 5 линий:
* SCLK — тактовый импульс для передачи данных.
* SDIN — передаваемые данные.

* D/C̅ — выбор типа передаваемых данных: высокий уровень — графические данные, низкий — инструкция.
* S̅C̅E̅ — выбор чипа. Передача данных осуществляется при низком уровне на этой линии. При высоком уровне данные игнорируются.

* R̅E̅S̅ — сброс. При низком уровне происходит аппаратный сброс контроллера.

Поскольку, согласно спецификации (см п.8.1) аппаратный сброс является необходимым для инициализации дисплея, линия R̅E̅S̅ также должна быть подключена.

В принципе, линия S̅C̅E̅ может быть притянута постоянно к земле, но в этом случае, если по какой либо причине контроллер дисплея потерял синхронизацию с МК, это никак не удастся обнаружить. При подключении к МК, рекомендую притягивать эту линию к высокому уровню подтягивающим резистором 100-500кОм, чтобы исключить реакцию контроллера на помехи, пока МК находится в состоянии сброса.

Обратная сторона модуля

Передача осуществляется по протоколу SPI, но только в одном направлении. При работе с микроконтроллерами AVR, удобно использовать USART в режиме SPI-master, когда включен только передатчик. Режим SPI-3 (CPHA=1, CPOL=1).

Это значит, что пока обмен отсутствует, на линии SCLK должен присутствовать высокий уровень, а чтение данных с линии SDIN контроллер осуществляет по нарастающему фронту на линии SCLK в течение 100нс.

При этом они должны быть выставлены минимум за 100 нс до нарастания фронта. Передача осуществляется по 8 бит, сначала старший.

Уровень на линии D/C̅ определяет, как трактовать полученные данные. Высокий уровень означает, что переданные данные должны быть выведены на дисплей, низкий уровень – что передана команда.

Контроллер читает значение на этой линии вместе с последним (младшим) битом каждого переданного байта данных. При использовании асинхронной аппаратной передачи с этим могут возникнуть трудности.

Перед установкой уровня необходимо дождаться завершения передачи предыдущего байта.

Максимальная частота, на которой может осуществляться обмен с PCD8544 – 4 МГц. При программном формировании импульсов это, обычно не является проблемой, но при использовании аппаратной передачи, в случае если частота работы МК превышает 8МГц, следует ограничить максимальную скорость передачи.

Кроме линий передачи данных, на модуле с дисплеем обычно присутствует вход управления подсветкой, соединённый с катодом светодиодов. Аноды же через ограничивающие резисторы подключены к линии питания.

Для постоянно работающей подсветки можно данный вход подключить напрямую, или через резистор к «земле». Для управления от МК подсветка может быть подключена через транзистор.

Поскольку подсветка потребляет менее 20мА, при подключении к МК AVR возможно также подключать её напрямую к выводу МК.

Команды контроллера

См. раздел 8 спецификации

Команды передаются контроллеру дисплея, когда на линии D/C̅ низкий уровень.
Контроллер работает с двумя наборами команд: обычным и расширенным.

Команда 00100PVH присутствует в обоих наборах команд. Значение бита H определяет какой набор команд будет использоваться: 0 – обычный, 1 – расширенный. Установленный бит P означает режим отключения (power-down). V – выбирает режим адресации: 1 – вертикальная, 0 – горизонтальная.

Обычный набор команд

Обычный набор команд выбирается после передачи 00100PV0 (см. выше)
В нём присутствуют команды:

Команда 00001D0E — выбирает режим работы дисплея. E – признак инверсии изображения. D – признак вывода изображения. Если D = 0, дисплей либо полностью очищен (E = 0), либо полностью чёрный (E = 1)

Команда 01000yyy, или 0x40 + y — выбор номера строки (страницы) на которую выводится изображение. Где y = 0 – самая верхняя строка, 5 – самая нижняя. Строка имеет высоту 8 пикслей.

Команда 1xxxxxxx, или 0x80 + x — выбор горизонтальной позиции в текущей строке, куда будет выводиться изображение. Где x = 0 – самая левая позиции, 83 – самая правая.

Расширенный набор команд

Расширенный набор команд выбирается после передачи 00100PV1 (см. выше)
В нём присутствуют команды:

Команда 000001tt, или 0x04 + t — выбор одного из четырёх режимов температурной коррекции. В зависимости от режима будет по-разному изменяться напряжение дисплея в зависимости от температуры.

Команда 00010bbb, или 0x10 + b — выбор одного из восьми режимов расчёта смещения уровней для управления LCD. Для обычных дисплеев от Nokia рекомендуется режим 0001011, или 0x13

Команда 1vvvvvvv, или 0x80 + v — выбор напряжения на генераторе повышенного напряжения для LCD. При v = 0 генератор отключен. Выходное напряжение рассчитывается по формуле (см. спецификацию п.8.9): Vlcd = 3.06 В + v * 0.06 В.

В зависимости от выбора способа коррекции напряжения, это значение изменяется в зависимости от температуры. Чтобы не повредить дисплей при низких температурах, рекомендуется чтобы это значение не превышало 8.5 Вольт, т.е. v<\p>

Источник: https://www.drive2.ru/b/1268120/

Разбираемся с LCD экраном LPH9157-2 от Siemens C75/ME75

Особенности работы с дисплеем lph9157-2Внятной документации на этот экран я не нашел поэтому пришлось разбираться с тем что есть и экспериментировать. В качестве управляющего устройства я использовал Raspberry PI. Так-же была написана программа позволяющая превратить этот экран в мини-монитор.

Данный дисплей имеет разрешение 135 x 176 пикселей и даёт возможность работать с тремя цветовыми палитрами 16(6-5-6), 12(4-4-4) и 8(3-3-2) бит.Тут всё просто, экран питается напряжением 2,9 вольт, подсветка (LED±) запитывается отдельно напряжением примерно 12 вольт(я использовал батарею аккумуляторов соединённую с подсветкой через резистор на 510 Ом).

Читайте также:  Идентификация систем впрыска различных производителей
Pin description

# Name Function
1 RS Low=CMD, High=DATA
2 ~RST Reset input, active low
3 ~CS Chip select, active low
4 SYNC External frame synchorization input, unused by default
5 CLK SPI Clock-in signal (High-to-Low)
6 DATA SPI Data-in signal (MSB first)
7 VCC Power supply, normally 2.9V (I tested with 3.3V)
8 GND Ground
9 LED+ Backlight voltage, approx. 12V (depends on required current)
10 LED- Backlight common pin

Как можно заметить экран управляется через интерфейс SPI (контакты CS/CLK/DAT(MOSI)), предположительно это лишь половина интерфейса так как отсутствует контакт MISO, следовательно писать данные в экран мы можем, а вот читать — нет(здесь следует упомянуть что SPI может работать в двунаправленном режиме с использованием одного провода (MIMO) но так как отсутствует какие либо команды чтения данных из экрана будем считать что этот режим экраном не используется).

И перед тем как переходить непосредственно к управлению экраном надо бы этот экран к чему-нибудь подключить. В моём случае это будет Raspberry Pi. Контакты SPI экрана подключены к соответствующим им контактам SPI «малины», RS и RST к GPIO_17 и GPIO_27 соответственно. Данное подключение актуально для RPI Revision-2, если у вас иная модель то названия и номера контактов GPIO могут отличаться.

Заморачиваться с разъёмом подключения экрана я не стал и просто подпаялся к выводам проводом МГТФ. Экран в данном подключении питается от 3.3В, а не от 2.9 как в описании.Вот так выглядит вся схема в сборе
Экран управляется достаточно просто — путём посылки по SPI команд и данных. Отличить одни от других экрану помогает состояние пина RS где высокий уровень(лог. 1) означает передачу данных, а низкий(лог. 0) передачу команд. При передаче используется тупоконечный(big-ending) порядок байт.

Список команд:

  • CMD_RESET 0x01 — программный сброс
  • CMD_MEMORY_ACCESS_CONTROL 0x36 — установка направления заполнения области дисплея, имеет однобайтовый аргумент 0bVHRXXXXX, гдеV — заполнение по вертикали (0 — сверху-вниз, 1 — снизу-вверх),H — заполнение по горизонтали (0 — слева-направо, 1 — справа-налево),R — меняются местами строки и столбцы (при этом заполнение остается сверху-вниз, слева-направо))
  • CMD_WAKEUP 0x11 — выход из спящего режима
  • CMD_PALETTE 0x3A — установка цветовой палитры 8(0x02), 12(0x03) и 16(0x05) бит
  • CMD_ENABLE 0x29 — включение дисплея
  • CMD_SET_X 0x2A — задаем область рисования по X
  • CMD_SET_Y 0x2B — задаем область рисования по Y
  • CMD_START_WRITE 0x2C — начало записи в видеопамять

Работа экрана была проверена во всех 3х цветовых режимах но, дабы не захламлять исходник, далее я буду рассматривать только 16-битный. Во всех остальных режимах работа экрана не отличается, за исключением, разве что 12-битного — в нём на 2 пикселя приходится 3 байта, а если нужно вывести лишь одну точку то посылается 2 байта(4 младших бита последнего экраном игнорируются).

Для доступа к GPIO «малины» была использована библиотека bcm2835.

int init_gpio(){ if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);// CPOL = 0, CPHA = 0, Clock idle low, data is clocked in on rising edge, output data (change) on falling edge bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);// в телефоне экран работает на частоте SPI в 13 МГц// поэтому небольшое превышение по частоте ему не повредит// хотя у меня он продолжал работать и при вдвое большей частоте (30 МГц) bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // настроим порты на запись bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1;

}

void send_cmd(char cmd){ bcm2835_gpio_write(LCD_RS, RS_CMD); // следующий байт – команда bcm2835_spi_transfer(cmd);}void send_data(char data){ bcm2835_gpio_write(LCD_RS, RS_DATA); // следующий байт – данные bcm2835_spi_transfer(data);}

void set_draw_area(char x1, char y1, char x2, char y2){ send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2);

}

В результате экспериментов с экраном выяснилось что необязательно задавать область перед выводом каждого кадра, достаточно задать её лишь единожды и послав команду начала записи просто гнать по SPI последовательность кадров непрерывным потоком.

void draw_start(){ send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA);}void send_draw_data(char* data, int size){ bcm2835_spi_transfern(data, size);

}

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

void reset_LCD(){ // аппаратный сброс bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); // программный сброс send_cmd(CMD_RESET);}void init_LCD(){ reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE);

}

Это была подготовка, а теперь самое вкусное — попробуем вывести на экран 16-битную картинку. Первый блин как известно — комом, после запуска программы я получил довольно странное изображение, оказалось — неверный порядок байт, после исправления всё заработало.

int main(int argc, char **argv){ if (!init_gpio()) return 1; init_LCD(); set_draw_area(0, 0, SCREEN_WIDTH – 1, SCREEN_HEIGHT – 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; FILE* f_scr = fopen(“test.bmp”, “r”); fseek(f_scr, 0x42, SEEK_SET); // skip bmp header fread(&screen, 1, SCREEN_HEIGHT * SCREEN_WIDTH * 2/*16bit*/, f_scr); fclose(f_scr); // change byte order for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) screen[y][x] = (screen[y][x] >> 8) | (screen[y][x] 176×132 малоинформативен, для фреймбуфера было установлено разрешение 320×240, это можно сделать правкой config.txt на FAT разделе «малиновой» флешки.
framebuffer_width=320
framebuffer_height=240
После этого изображение всё равно жмётся с использованием примитивной интерполяции, но результат уже можно назвать приемлемым. Так-же был добавлен пропуск одинаковых кадров для экономии CPU.Исходник LPH9157-2_RPI.c

#include #include #include #include #include #include #include // соответствия контактов GPIO и LCD#define LCD_RS RPI_V2_GPIO_P1_11#define LCD_RESET RPI_V2_GPIO_P1_13#define RS_CMD 0#define RS_DATA 1#define CMD_RESET 0x01#define CMD_MEMORY_ACCESS_CONTROL 0x36 // Memory Access Control#define CMD_WAKEUP 0x11 // Выход из спящего режима#define CMD_PALETTE 0x3A // Установка цветовой палитры#define CMD_ENABLE 0x29 //Включение дисплея#define CMD_SET_X 0x2A // задаем область по X#define CMD_SET_Y 0x2B // задаем область по Y#define CMD_START_WRITE 0x2C // начало записи в память#define _8_BIT_COLOR 0x02#define _12_BIT_COLOR 0x03#define _16_BIT_COLOR 0x05#define SCREEN_WIDTH 132#define SCREEN_HEIGHT 176int init_gpio(){ if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); /// CPOL = 0, CPHA = 0, Clock idle low, /// data is clocked in on rising edge, /// output data (change) on falling edge bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // настроим порты на запись bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1;}int close_gpio(){ bcm2835_spi_end(); bcm2835_close();}void send_cmd(char cmd){ bcm2835_gpio_write(LCD_RS, RS_CMD); bcm2835_spi_transfer(cmd);}void send_data(char data){ bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfer(data);}void send_data_array(char* data, int size){ bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfern(data, size);}void set_draw_area(char x1, char y1, char x2, char y2){ send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2);}void draw_start(){ send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA);}void send_draw_data(char* data, int size){ bcm2835_spi_transfern(data, size);}void reset_LCD(){ bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); send_cmd(CMD_RESET);}void init_LCD(){ reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE);}#define FB_WIDTH 320// 176#define FB_HEIGHT 240// 144int main(int argc, char **argv){ if (!init_gpio()) return 1; int smooth = 0; int dynamic_fps = 0; int argn = 1; while(argn < argc) { if(argv[argn][0] == '-') switch(argv[argn][1]) { case 'i': init_LCD(); close_gpio(); printf("lcd initialized "); return 0; break; case 's': smooth = 1; break; case 'd': dynamic_fps = 1; break; default: printf("Usage: lcd [options] "); printf("Options: "); printf(" -i initialize lcd (hardware reset) "); printf(" -d dynamic FPS (skip same frames) "); printf(" -s smooth image (enable basic intrpolation) "); return 0; break; } argn++; } ///------------------------------------------------ /// draw screen set_draw_area(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; uint16_t old_fb[FB_HEIGHT * FB_WIDTH]; int fd_scr = open("/dev/fb0", O_RDONLY); int scr_sz = FB_HEIGHT * FB_WIDTH * 2/*16bit*/; uint16_t* fb_screenshot = mmap(0, scr_sz, PROT_READ, MAP_PRIVATE, fd_scr, 0); // scaling float scale_X = FB_HEIGHT / (float)SCREEN_WIDTH; float scale_Y = FB_WIDTH / (float)SCREEN_HEIGHT; int frame_cnt = 0; struct timespec ts1, ts2; clock_gettime(CLOCK_MONOTONIC, &ts1); for(;;) // forever { if(dynamic_fps) if(memcmp(&old_fb, fb_screenshot, sizeof(old_fb)) == 0) { usleep(10000); continue; } else { memcpy(&old_fb, fb_screenshot, sizeof(old_fb)); } for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) { int fb_x = y * scale_X; int fb_y = x * scale_Y; uint16_t px = fb_screenshot[fb_x + fb_y * FB_WIDTH]; if(smooth) { // look around if((fb_x - 1 >= 0) && (fb_x + 1 < FB_WIDTH) && (fb_y - 1 >= 0) && (fb_y + 1 < FB_HEIGHT)) { for(int dx = -1; dx 1) + ((add_px & 0xf7de) >> 1); /// ^thank you habr => http://ift.tt/1vIjXyi } } } screen[y][SCREEN_WIDTH – 1 – x] = (px > 8); } send_draw_data((char*)&screen[0][0], sizeof(screen)); /// calc fps frame_cnt++; if(frame_cnt >= 100) { clock_gettime(CLOCK_MONOTONIC, &ts2); float allsec = (ts2.tv_sec – ts1.tv_sec) + (ts2.tv_nsec – ts1.tv_nsec) / 1000000000.0; float fps = frame_cnt / allsec; printf(“%f FPS
“, fps); frame_cnt = 0; clock_gettime(CLOCK_MONOTONIC, &ts1); } usleep(1000); } munmap(fb_screenshot, scr_sz); close(fd_scr); close_gpio(); printf(“fin
“); return 0;

}

Сборка:
pi@raspberrypi ~ $ gcc -o lcd LPH9157-2_RPI.c -lbcm2835 -lrt -std=gnu99
Запуск:
pi@raspberrypi ~ $ sudo ./lcd -ipi@raspberrypi ~ $ sudo ./lcd -d -s
параметры:-i — первичная инициализация (дёргаем хардварный reset)-s — включить сглаживание-d — динамический fps (одинаковые кадры пропускаются — экономит CPU)

This entry passed through the Full-Text RSS service – if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Источник: http://habrparser.blogspot.com/2015/02/lcd-lph9157-2-siemens-c75me75.html

Ардуино: вывод текста на ЖК дисплей 1602

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

Скорее всего, вашему очередному устройству тоже не помешает какой-нибудь небольшой дисплейчик

Источник: http://robotclass.ru/tutorials/arduino-lcd1602/

Розбираємося з LCD екраном LPH9157-2 від Siemens C75/ME75

Виразної документації на цей екран я не знайшов тому довелося розбиратися з тим що є і експериментувати. В якості керуючого пристрою я використовував Raspberry PI. Так-же була написана програма, що дозволяє перетворити цей екран міні-монітор.

Читайте также:  Ремонт и регулировка телевизоров "витязь 37/51/54 ctv 6612/6622"

Опис

Цей дисплей має дозвіл 135 x 176 пікселів і дає можливість працювати з трьома колірними палітрами 16(6-5-6), 12(4-4-4) та 8(3-3-2) біт.

Терморегулятори і підключення

Тут все просто, екран живиться напругою 2,9 вольт, підсвічування (LED±) записується окремо напругою приблизно 12 вольт(я використовував батарею акумуляторів з'єднану з підсвічуванням через резистор на 510 Ом).

Pin description

# Name Function 1 RS Low=CMD, High=DATA 2 ~RST Reset input, active low 3 ~CS Chip select, active low 4 SYNC External frame synchorization input, unused by default 5 CLK SPI Clock-in signal (High-to-Low) 6 DATA SPI Data-in signal (MSB first) 7 VCC Power supply, normally 2.9 V (I tested with 3.3 V) 8 GND Ground 9 LED+ Backlight voltage, approx. 12V (depends on required current) 10 LED- Backlight common pin

Як можна помітити екран управляється через інтерфейс SPI (контакти CS/CLK/DAT(MOSI)), імовірно це лише половина інтерфейсу так як відсутній контакт MISO, отже писати дані в екран ми можемо, а от читати — ні(тут слід згадати що SPI може працювати в двонаправленому режимі з використанням одного проводу (MIMO) але так як відсутня якісь команди читання даних з екрану будемо вважати, що цей режим екраном не використовується).

І перед тим як переходити безпосередньо до управління екраном треба б цей екран до чого-небудь підключити. У моєму випадку це буде Raspberry Pi. Контакти SPI екрану підключені до відповідних їм контактам SPI «малини», RS і RST до GPIO_17 і GPIO_27 відповідно. Дане підключення актуально для RPI Revision-2, якщо у вас інша модель назви і номери контактів GPIO можуть відрізнятися.

Морочитися з роз'ємом підключення екрана я не став і просто подпаялся до висновків проводом МГТФ. Екран в даному підключенні живиться від 3.3 В, а не від 2.9 в описі. Ось так виглядає вся схема в зборі

Команди управління екраном

Екран управляється досить просто — шляхом посилки SPI команд і даних. Відрізнити одні від інших екрану допомагає стан піна RS де високий рівень(лог. 1) означає передачу даних, а низький(лог. 0) передачу команд. При передачі використовується тупоконечный(big-ending) порядок байт.

Список команд:

  • CMD_RESET 0x01 — програмний скидання
  • CMD_MEMORY_ACCESS_CONTROL 0x36 — установка напряму заповнення області дисплея, має однобайтний аргумент 0bVHRXXXXX, де V — заповнення по вертикалі (0 — зверху-вниз, 1 — знизу-вгору), H — заповнення по горизонталі (0 — зліва-направо, 1 — праворуч-ліворуч),

    R — міняються місцями рядки і стовпці (при цьому заповнення залишається зверху-вниз, зліва-направо))

  • CMD_WAKEUP 0x11 — вихід із сплячого режиму
  • CMD_PALETTE 0x3A — установка колірної палітри 8(0x02), 12(0x03) і 16(0x05) біт
  • CMD_ENABLE 0x29 — включення дисплея
  • CMD_SET_X 0x2A — задаємо область малювання по X
  • CMD_SET_Y 0x2B — задаємо область малювання по Y
  • CMD_START_WRITE 0x2C — початок запису в відеопам'ять

Код

Робота екрану була перевірена у всіх 3х колірних режимах але, щоб не захаращувати вихідний, далі я буду розглядати тільки 16-бітний.

У всіх інших режимах робота екрану не відрізняється, за винятком, хіба що 12-бітного — в ньому на 2 пікселя припадає 3 байти, а якщо потрібно вивести лише одну точку то надсилається 2 байти(4 молодших біта останнього екраном ігноруються).
Для доступу до GPIO «малини» була використана бібліотека bcm2835.

Початкова ініціалізація GPIO

int init_gpio() { if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // CPOL = 0, CPHA = 0, Clock idle low, data is clocked in on rising edge, output data (change) falling on edge bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // в телефоні екран працює на частоті SPI в 13 МГц // тому невелике перевищення по частоті йому не зашкодить // хоча у мене він продовжував працювати і при вдвічі більшій частоті (30 МГц) bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625 MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // налаштуємо порти на запис bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1; }

Декілька допоміжних функцій

void send_cmd(char cmd) { bcm2835_gpio_write(LCD_RS, RS_CMD); // наступний байт – команда bcm2835_spi_transfer(cmd); } void send_data(char data) { bcm2835_gpio_write(LCD_RS, RS_DATA); // наступний байт – дані bcm2835_spi_transfer(data); }

Завдання області малювання

void set_draw_area(char x1, char y1, char x2, char y2) { send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2); }В результаті експериментів з екраном з'ясувалося що необов'язково ставити область перед виведенням кожного кадру, достатньо задати її лише один раз і пославши команду початку запису просто гнати по SPI послідовність кадрів безперервним потоком.

Підготовка до виведення та передача даних

void draw_start() { send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA); } void send_draw_data(char* data, int size) { bcm2835_spi_transfern(data, size); }В процесі ініціалізації екрану виявилася прикра річ — для запуску необхідно пересмикнути RESET екрану, а от подальші маніпуляції даними висновком призводять екран в ступор і він перестає реагувати на зовнішні впливи. Доводиться скидати його з харчування. Це необхідно врахувати при розробці програми щоб процедура апаратного скидання виконувалася лише один раз.

Ініціалізація екрану

void reset_LCD() { // апаратний скидання bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); // програмний скидання send_cmd(CMD_RESET); } void init_LCD() { reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE); }Це була підготовка, а тепер найсмачніше — спробуємо вивести на екран 16-бітну картинку. Перший млинець, як відомо, грудкою, після запуску програми я отримав досить дивне зображення, виявилося — неправильний порядок байт, після виправлення все запрацювало.

Код

int main(int argc, char **argv) { if (!init_gpio()) return 1; init_LCD(); set_draw_area(0, 0, SCREEN_WIDTH – 1, SCREEN_HEIGHT – 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; FILE* f_scr = fopen(“test.

bmp”, “r”); fseek(f_scr, 0x42, SEEK_SET); // skip bmp header fread(&screen, 1, SCREEN_HEIGHT * SCREEN_WIDTH * 2/*16bit*/, f_scr); fclose(f_scr); // change byte order for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) screen[y][x] = (screen[y][x] >> 8) | (screen[y][x] 176×132 малоінформативний, для фреймбуфер було встановлено дозвіл 320×240, це можна зробити правкою config.txt на FAT розділі «малинової» флешки.framebuffer_width=320 framebuffer_height=240Після цього зображення все одно тулиться з використанням примітивної інтерполяції, але результат вже можна назвати прийнятним. Так-же був доданий пропуск однакових кадрів для економії CPU.

Ісходник LPH9157-2_RPI.c

#include #include < stdio.h> #include < sys/mman.h> #include #include #include < string.h> #include // відповідності контактів GPIO та LCD #define LCD_RS RPI_V2_GPIO_P1_11 #define LCD_RESET RPI_V2_GPIO_P1_13 #define RS_CMD 0 #define RS_DATA 1 #define CMD_RESET 0x01 #define CMD_MEMORY_ACCESS_CONTROL 0x36 // Memory Access Control #define CMD_WAKEUP 0x11 // Вихід із сплячого режиму #define CMD_PALETTE 0x3A // Установка колірної палітри #define CMD_ENABLE 0x29 //Включення дисплея #define CMD_SET_X 0x2A // задаємо область по X #define CMD_SET_Y 0x2B // задаємо область по Y #define CMD_START_WRITE 0x2C // початок запису в пам'ять #define _8_BIT_COLOR 0x02 #define _12_BIT_COLOR 0x03 #define _16_BIT_COLOR 0x05 #define SCREEN_WIDTH 132 #define SCREEN_HEIGHT 176 int init_gpio() { if (!bcm2835_init()) return 0; bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); /// CPOL = 0, CPHA = 0, Clock idle low, /// data is clocked in on rising edge, /// output data (change) falling on edge bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625 MHz bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// Select Our Device bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // налаштуємо порти на запис bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(LCD_RESET, BCM2835_GPIO_FSEL_OUTP); return 1; } int close_gpio() { bcm2835_spi_end(); bcm2835_close(); } void send_cmd(char cmd) { bcm2835_gpio_write(LCD_RS, RS_CMD); bcm2835_spi_transfer(cmd); } void send_data(char data) { bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfer(data); } void send_data_array(char* data, int size) { bcm2835_gpio_write(LCD_RS, RS_DATA); bcm2835_spi_transfern(data, size); } void set_draw_area(char x1, char y1, char x2, char y2) { send_cmd(CMD_SET_X); send_data(x1); send_data(x2); send_cmd(CMD_SET_Y); send_data(y1); send_data(y2); } void draw_start() { send_cmd(CMD_START_WRITE); bcm2835_gpio_write(LCD_RS, RS_DATA); } void send_draw_data(char* data, int size) { bcm2835_spi_transfern(data, size); } void reset_LCD() { bcm2835_gpio_write(LCD_RESET, LOW); bcm2835_delay(50); bcm2835_gpio_write(LCD_RESET, HIGH); bcm2835_delay(50); send_cmd(CMD_RESET); } void init_LCD() { reset_LCD(); send_cmd(CMD_MEMORY_ACCESS_CONTROL); send_data(0b00000000); send_cmd(CMD_WAKEUP); bcm2835_delay(20); send_cmd(CMD_PALETTE); send_data(_16_BIT_COLOR); bcm2835_delay(20); send_cmd(CMD_ENABLE); } #define FB_WIDTH 320// 176 #define FB_HEIGHT 240// 144 int main(int argc, char **argv) { if (!init_gpio()) return 1; int smooth = 0; int dynamic_fps = 0; int argn = 1; while(argn < argc) { if(argv[argn][0] == '-') switch(argv[argn][1]) { case 'i': init_LCD(); close_gpio(); printf("lcd initialized "); return 0; break; case 's': smooth = 1; break; case 'd': dynamic_fps = 1; break; default: printf("Usage: lcd [options] "); printf("Options: "); printf(" -i initialize lcd (hardware reset) "); printf(" -d dynamic FPS (skip same frames) "); printf(" -s smooth image (enable basic intrpolation) "); return 0; break; } argn++; } ///------------------------------------------------ /// draw screen set_draw_area(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); draw_start(); uint16_t screen[SCREEN_HEIGHT][SCREEN_WIDTH]; uint16_t old_fb[FB_HEIGHT * FB_WIDTH]; int fd_scr = open("/dev/fb0", O_RDONLY); int scr_sz = FB_HEIGHT * FB_WIDTH * 2/*16bit*/; uint16_t* fb_screenshot = mmap(0, scr_sz, PROT_READ, MAP_PRIVATE, fd_scr, 0); // scaling float scale_X = FB_HEIGHT / (float)SCREEN_WIDTH; float scale_Y = FB_WIDTH / (float)SCREEN_HEIGHT; int frame_cnt = 0; struct timespec ts1, ts2; clock_gettime(CLOCK_MONOTONIC, &ts1); for(;;) // forever { if(dynamic_fps) if(memcmp(&old_fb, fb_screenshot, sizeof(old_fb)) == 0) { usleep(10000); continue; } else { memcpy(&old_fb, fb_screenshot, sizeof(old_fb)); } for(int x = 0; x < SCREEN_WIDTH; x++) for(int y = 0; y < SCREEN_HEIGHT; y++) { int fb_x = y * scale_X; int fb_y = x * scale_Y; uint16_t px = fb_screenshot[fb_x + fb_y * FB_WIDTH]; if(smooth) { // look around if((fb_x - 1 >= 0) && (fb_x + 1 < FB_WIDTH) && (fb_y - 1 >= 0) && (fb_y + 1 < FB_HEIGHT)) { for(int dx = -1; dx 1) + ((add_px & 0xf7de) >> 1); /// ^thank you habr => http://habr.ru/p/128773/ } } } screen[y][SCREEN_WIDTH – 1 – x] = (px > 8); } send_draw_data((char*)&screen[0][0], sizeof(screen)); /// calc fps frame_cnt++; if(frame_cnt >= 100) { clock_gettime(CLOCK_MONOTONIC, &ts2); float allsec = (ts2.tv_sec – ts1.tv_sec) + (ts2.tv_nsec – ts1.tv_nsec) / 1000000000.0; float fps = frame_cnt / allsec; printf(“%f FPS
“, fps); frame_cnt = 0; clock_gettime(CLOCK_MONOTONIC, &ts1); } usleep(1000); } munmap(fb_screenshot, scr_sz); close(fd_scr); close_gpio(); printf(“fin
“); return 0; }Збірка:pi@raspberrypi ~ $ gcc-o lcd LPH9157-2_RPI.c-lbcm2835-lrt-std=gnu99Запуск:pi@raspberrypi ~ $ sudo ./lcd-i pi@raspberrypi ~ $ sudo ./lcd-d-s параметри: -i — первинна ініціалізація (смикаємо хардварный reset) -s — увімкнути згладжування -d — динамічний fps (однакові кадри пропускаються — економить CPU)

Результат

Джерело: Хабрахабр

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.

Источник: http://it-ua.info/news/2015/03/07/rozbiramosya-z-lcd-ekranom-lph9157-2-vd-siemens-c75-me75.html

Ссылка на основную публикацию
Adblock
detector