LPH9157-2

Štai čia yra Siemens C75 mobilusis telefonas:

Tačiau kur jo ekranėlis? Jį išlupo kažkoks piktas zuikis. Jis prie ekranėlio prilitavo laidus ir bandys sujungti su pcDuino.

Pasiknaisioję internete pastebėsime, kad ne jis vienas šitaip daro:

Pasirodo nėra viskas čia jau taip ir sudėtinga. Su ekranėliu reikia bendrauti SPI protokolu ir dar keliais papildomais GPIO. Yra komandos inicializavimui, piešimui, piešimo vietos nustatymui. Tereikia prijungti ir nusiųsti tinkamus duomenis. Kiek kėbliau su apšvietumu, nes reikia 12V, kuriuos reikės kažkaip pasigaminti iš 5V ar 3.3V, bet pradžiai galima tuos 12 pasiskolinti iš PC maitinimo šaltinio.

Kontaktų išdėstymas
# Pavadinimas Funkcija
1 RS 0=CMD, 1=DATA
2 ~RST Perktovimo įvestis, aktyvus žemas
3 ~CS SPI SS (pasirinkimo signalas - “slave select” / “chip select”), aktyvus žemas
4 SYNC Išorinis kadro sinchronizavimas. Nenaudojamas
5 CLK SPI SCK (sinchronizavimo signalas aka “clock”), High-to-Low
6 DATA SPI MOSI (duomenų kanalas), MSB first
7 VCC Maitinimas: 3.3V
8 GND Žemė
9 LED+ Apšvietimas: 12V
10 LED- Apšvietimo žemė
Komandos
Komanda Kodas Paaiškinimas
CMD_RESET 0x01 Perkrovimas / inicializavimas
CMD_WAKEUP 0x11 Pabudimo iš miego režimo komanda
CMD_ENABLE 0x29 Ekranėlio įjungimas
CMD_PALETTE 0x3A Spalvų paletė. Kitas baitas turi būti duomenys:
0x02 - 8bit spalvos
0x03 - 12bit spalvos
0x05 - 16bit spalvos
CMD_SET_X 0x2A Piešimo lango koordinatės x. Kiti du baitai turi būti koordinatės.
CMD_SET_Y 0x2B Piešimo lango koordinatės y. Kiti du baitai turi būti koordinatės.
CMD_START_WRITE 0x2C Pradėti rašyti grafinius duomenis. Kiti baitai turi būti grafiniai duomenys. Duomenų kiekis = lango plotis x lango aukštis x paletės dydis. Jeigu duomenų bus siunčiama per daug, vėl pradės piešti nuo pradžių.
CMD_MEMORY_ACCESS_CONTROL 0x36 Grafinės informacijos atvaizdavimo nustatymas. Kitas baitas turi būti duomenys 0bVHRxxxxx, kur:
V - vertikalaus užpildymo kryptis (0 - nuo viršaus apačion, 1 - nuo apačios viršun), kitaip sakant veidrodinis apsukimas pagal verikalią ašį.
H - horizontalaus užpildymo kryptis (0 - į dešinę, 1 - į kairę), kitaip sakant veidrodinis apsukimas pagal horizontalią ašį.
R - Pasukimas. Beprasmė funkcija, nes pasukus viršutinės eilutės liks nepasiekiamos, o šonas nukąstas.

Kodas

Išsiaiškinus komandas ir kontaktų išdėstymus galime ekranėlį jungti prie pcDuino ir bandyti ką nors parodyti. Kaip matome mums reikės SPI ir GPIO modulių. Su GPIO viskas paprasta - jie pasiekiami per /sys/ interfeisą, tačiau SPI numatytoje pcDuino3b konfigūracijoje yra išjungtas.
SPI įjungiame pridėję spidev į pcDuino3b device-tree:

linux-stable/arch/arm/boot/dts/sun7i-a20-pcduino3b.dts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@@ -240,10 +240,16 @@
&spi0 {
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins_a>,
<&spi0_cs0_pins_a>;
status = "okay";
+
+ spidev@0x00 {
+ compatible = "spidev";
+ spi-max-frequency = <120000000>;
+ reg = <0>;
+ };
};
};

Įrašius device-tree ir perkrovus kompiuteriuką pastebėsime, kad atsirado naujas įrenginys - /dev/spidev0.0. Per jį ir bendrausime su ekranėliu.

Kadangi tingiu rašyti tiesioginį bendarvima su GPIO ir SPI per /sys/ ir /dev/spidev0.0, pasinaudosiu libsoc biblioteka. Tikriausiai kiekvienas avietės turėtojas turi pasirašęs savo gpio biblioteką, bet kam rašyti kažką, kas jau šimtą kartų prašyta?

Taigi, panagrinėkime kodą: pirma inicializuojame GPIO ir SPI interfeisus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void initializeGlobalVariables()
{
gpioPower = libsoc_gpio_request(233, LS_GREEDY); //GPIO7 - PH9/233
gpioReset = libsoc_gpio_request(229, LS_GREEDY); //GPIO9 - PH5/229
gpioDataOrCommand = libsoc_gpio_request(234, LS_GREEDY); //GPIO8 - PH10/234
libsoc_gpio_set_direction(gpioPower, OUTPUT);
libsoc_gpio_set_direction(gpioReset, OUTPUT);
libsoc_gpio_set_direction(gpioDataOrCommand, OUTPUT);
spiDev = libsoc_spi_init(0, 0); ///dev/spidev0.0
libsoc_spi_set_speed(spiDev, 12000000); //12 MHz
libsoc_spi_set_mode(spiDev, MODE_0);
libsoc_spi_set_bits_per_word(spiDev, BITS_8);
}

Tada reikia iš mirties prikelti ir inicializuoti patį ekranėlį. Kadangi libsoc prieš atlaisvinant atmintį nuleidžia ~RST į 0, programai išsijungus ekranėlis pereina į persikrovimo režimą. Bandant progamą paleisti antrą kartą inicializavimo seka jau nebetinka. Tai pataisome atjungdami ekranėlio VCC pusantros sekundės ir jis pamiršta, kad kažkada iš vis gyveno.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void initializeDevice()
{
//Išjungiame ekranėlį
if(libsoc_gpio_get_direction(gpioPower) == HIGH)
{
libsoc_gpio_set_level(gpioPower, LOW);
libsoc_gpio_set_level(gpioReset, LOW);
libsoc_gpio_set_level(gpioDataOrCommand, LOW);
msleep(1500);
}
//Įjungiame ekranėlį
libsoc_gpio_set_level(gpioPower, HIGH);
//Perkrauname
libsoc_gpio_set_level(gpioReset, LOW);
msleep(2);
libsoc_gpio_set_level(gpioReset, HIGH);
sendCommand(CMD_RESET);
//Inicializuojame atvaizdavimo registrą
sendCommand(CMD_MEMORY_ACCESS_CONTROL);
sendData(0b00000000);
sendCommand(CMD_WAKEUP);
sendCommand(CMD_PALETTE);
sendData(currentPalette);
sendCommand(CMD_ENABLE);
}

Toliau inicializuojame piešimo langą ir siunčiame piešimo duomenis. Jeigu kompiuteriukas pcDuino3b būtų ne Allwinner produktas, grafinius duomenis būtų galima tiesiog imti ir nusiųsti, tačiau ne visiems gyvenime sekasi. Programa pati turi sukramtyti duomenis į 63 bitų gabaliukus, kad pcDuino neužspringtų:

1
2
3
4
5
6
7
8
9
10
11
12
13
void draw(uint8_t *data)
{
sendCommand(CMD_START_WRITE);
libsoc_gpio_set_level(gpioDataOrCommand, HIGH);
//Sukramtome duomenis:
int chunkSize = 63;
for(int i = getDataSize(); i > 0; i -= chunkSize)
{
libsoc_spi_write(spiDev, data, i > chunkSize ? chunkSize : i);
data += chunkSize;
}
}

Kiek įdomesnė bmp paveikslėlio nuskaitymo funkcija. Iš BMP antraštės ištraukiama paletės informacija, taip pat apsukami little-endian baitai į big endian, nes ekranėlis nori MSB first duomenų.

Rezultatai

Paleidžiame programą ir stebime rezultatus:

16 bitų16 bitų

8 bitų8 bitų

Kaip matome 8 bitų paveiksliukas atrodo baisiai, bet taip yra dėl nesuderintų spalvų. Gimpas saugant 256 bitų formatu naudoja indeksuotas spalvas, o ekranėlis tikisi 3R3G2B formato. Kadangi tingėjau pats rašyti konverterį, turime ką matome. Progamą ir instrukcijas kaip ją sukompiliuoti bei paleisti galima rasti githube. Ten pat yra ir du pavyzdiniai paveikslėliai.