Знакомы с ЖК-экраном LPH9157-2 Siemens C75/ME75

Знакомы с ЖК-экраном LPH9157-2 Siemens C75/ME75

1068
ПОДЕЛИТЬСЯ

Ясно документации на этот экран я не отыскал, так что я имел дело с тем, что там и опыта. В качестве управляющего устройства , я употреблял Raspberry PI. Потому была написана программа для поворота экрана в мини-монитор.
Описание
Этот экран имеет разрешение 135 х 176 пикселей и дает возможность работать с 3-мя цветовыми палитрами 16(6-5-6), 12(4-4-4) и 8(3-3-2) бит.
Pin описание

#
Имя
Функция

1
RS
Low=CMD, High=данные

2
~RST
Вход сброса, в активный уровень-маленький

3
~CS
Чип выберите, активный уровень-маленький

4
SYNC
Наружная рама synchorization вход, неиспользованные по умолчанию

5
CLK
SPI Clock-сигнала (высочайший-маленький)

6
Данные
SPI данных в сигнал (MSB first)

7
VCC
Питание, как правило, 2.9 V (я тестировал с 3.3 V)

8
GND
Землю

9
LED+
Подсветка напряжение, прибл. 12V (зависит от нужного тока)

10
LED-
Подсветка общий контакт

Как вы сможете созидать экран управляется через SPI интерфейс (контакты CS/CLK/DAT(MOSI)), предположительно это лишь половина интерфейса, поэтому что нет контакта МИСО потому писать данные в экран, мы можем, но прочесть его и тут следует отметить, что SPI может работать в двунаправленном режиме с помощью 1-го провода (MIMO), но поэтому, что нет никакой команды для считывания данных с экрана, мы можем представить, что этот режим экрана не употребляется). Цоколевку и подключение
Все просто, экран питается от напряжения 2,9 вольт, подсветка (LED±) поставляется раздельно с напряжением около 12 вольт(я употреблял батарея подключен к подсветке через резистор 510 Ом).
Заморачиваться с разъемом экрана я не стал и просто popalsya к выводам провода МГТФ. Связаться экран SPI подсоединены к подходящим контактам SPI «малина», RS и RST GPIO_17 и GPIO_27 соответственно. Вот вся схема сборки В моем случае это будет Raspberry Pi. И до этого чем перейти конкретно к управлению экран должен быть экран, чтоб что-то подключить. Экран для этого соединения питается от 3.3 Вольт, а не от 2.9 как в описание. Эта связь принципиальна для RPI Revision-2, Раз у вас иная модель, то наименование и количество контактов GPIO могут различаться.

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

CMD_RESET 0x01 — soft reset
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);

// В то время как IE = 0, sfr разрешения прерывания = 0, часы ожидания низкими, данные вкалывал по переднему фронту выходных данных (поменять) on falling edge
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);

// в экран телефона работает на частоте SPI в 13 МГц
// так что маленький излишек в частоте будет не больно
// хотя я продолжал работать в два раза больше частоты (30 МГц)
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625 МГц
bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// избираем наше устройство
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, маленький);

// настройка портов на запись
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(данных);
}

Задачка из области рисования
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);
}
Во время инициализации экрана отыскал раздражающая вещь — для начала, для вас необходимо жонглировать сброс экрана, и предстоящее манипулирование данными выходу экран в ступоре и он перестает реагировать на наружные действия.
Инициализация экрана
void reset_LCD()
{
// аппаратного сброса
bcm2835_gpio_write(LCD_RESET, маленький);
bcm2835_delay(50);
bcm2835_gpio_write(LCD_RESET, высочайший);
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-битное изображение. 1-ый блин, как понятно, является флопе, опосля пуска программы, я получил достаточно странноватый образ, и это был неверный порядок байтов, опосля исправления он работал.
Код
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_HEIGHT][SCREEN_WIDTH];

Файл* f_scr = fopen(«test.bmp», «r»);
fseek(f_scr, 0x42, SEEK_SET); // пропустить заголовка bmp

fread(&screen, 1, SCREEN_HEIGHT * SCREEN_WIDTH * 2/*16 бит*/, f_scr);
fclose(f_scr);

// конфигурации порядка байтов
for(int x = 0; x < SCREEN_WIDTH; x++)
for(int y = 0; y < SCREEN_HEIGHT; y++)
экран[y][x] = ([y][x] >> 8) | (screen[y][x] << 8);

send_draw_data((char*)&screen[0][0], SCREEN_WIDTH*SCREEN_HEIGHT*2/*16 бит*/);

close_gpio();
return 0;
}
изображение до и опосля правки

Мысль проста — изображение берется из /dev/fb0, это просто 16-bit, resisits и выдается на экран. LCD в качестве монитора
С начала тестов я не оставил идею применять экран в качестве монитора для «малины», которую я поторопился воплотить. В итоге сжатия изображения 1024×768 => 176×132 неинформативные, для фреймбуфера было задано разрешение 320х240, это может быть изготовлено методом редактирования config.txt на FAT разделе «crimson» палку.
Также добавлены пас схожих кадров, чтоб сохранить ЦП. framebuffer_width=320
framebuffer_height=240
Опосля этого, изображение по-прежнему цепляясь за помощью примитивной интерполяции, но итог можно именовать применимым.

Источник LPH9157-2_RPI.c#include <bcm2835.h>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>

#include <time.h>
#include <string.h>
#include <unistd.h>

// соответствия выводов 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 // контроль доступа к памяти
#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); /// в то время как IE = 0, sfr разрешения прерывания = 0, часы ожидания низкими,
/// данных работает на частоте в on rising edge,
/// выходных данных (поменять) on falling edge
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); ///< 16 = 64ns = 15.625 МГц
bcm2835_spi_chipSelect(BCM2835_SPI_CS0); /// избираем наше устройство
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, маленький);

// настройка портов на запись
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(данных);
}

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, маленький);
bcm2835_delay(50);
bcm2835_gpio_write(LCD_RESET, высочайший);
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Полностьюswitch(argv[argn][1])
{
case ‘i’:
init_LCD();
close_gpio();
printf(«ЖК-экран инициализированn»);
return 0;
break;

case ‘s’:
smooth = 1;
break;
case ‘d’:
dynamic_fps = 1;
break;
по умолчанию:
printf(«Usage: lcd [характеристики]n»);
printf(«функции:n»);
printf(» -я инициализации lcd (аппаратный сброс)n»);
printf(» -d динамичный FPS (скип же кадры)n»);
printf(» -s сглаживания изображений (enable basic intrpolation)n»);
return 0;
break;
}
argn++;
}

///————————————————
/// привлечь экран

set_draw_area(0, 0, SCREEN_WIDTH — 1, SCREEN_HEIGHT — 1);
draw_start();

uint16_t экран[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/*16 бит*/;
uint16_t* fb_screenshot = mmap(0, scr_sz, PROT_READ, MAP_PRIVATE, fd_scr, 0);

// масштабирование
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(;;) // навсегда
{
раз(dynamic_fps)
if(memcmp(&old_fb, fb_screenshot, sizeof(old_fb)) == 0)
{
usleep(10000);
продолжить;
}
еще
{
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((fb_x — 1 >= 0) && (fb_x + 1 < FB_WIDTH) && (fb_y — 1 >= 0) && (fb_y + 1 < FB_HEIGHT))
{
for(int dx = -1; dx <= 1; dx++)
for(int dy = -1; dy= 1; dy++)
if((dx == 0) ^ (dy == 0))
{
uint16_t add_px = fb_screenshot[(fb_x + dx) + (fb_y + dy) * FB_WIDTH];
px = ((px & 0xf7de) >> 1) + ((add_px & 0xf7de) >> 1);
/// ^спасибо, хабр => http://habr.ru/p/128773/
}
}
}
экран[y][SCREEN_WIDTH — 1 — x] = (px << 8) | (px >> 8);
}

send_draw_data((char*)&screen[0][0], sizeof(screen));

/// fps calc
frame_cnt++;
раз(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 FPSn», fps);

frame_cnt = 0;
clock_gettime(CLOCK_MONOTONIC, &ts1);
}

usleep(1000);
}

случае конфликта(fb_screenshot, scr_sz);
close(fd_scr);
close_gpio();

printf(«finn»);
return 0;
}
Сборка:
pi@raspberrypi ~ $ gcc-o ЖК-экран LPH9157-2_RPI.c-lbcm2835-lrt-std=gnu99
Старт:
pi@raspberrypi ~ $ sudo ./lcd-я
pi@raspberrypi ~ $ sudo ./lcd-d-s

характеристики:
-я — первичной инициализации (тянуть аппаратный сброс)
-s — включить сглаживание
-d — динамичный fps (те же кадры пропускаются — экономит CPU)
Итог
habrahabr.ru