Salam! Baru-baru ini, sebuah proyek diperlukan untuk meluncurkan tampilan yang memiliki antarmuka LVDS. Untuk mengimplementasikan tugas, controller STM32F746 dipilih, karena Saya sudah banyak bekerja dengannya dan dia memiliki modul LTDC yang memungkinkan Anda untuk bekerja secara langsung dengan layar tanpa pengontrol. Dalam hal ini, pengontrol sudah diterapkan di dalam mikrokontroler. Juga, argumen terakhir adalah bahwa ada debugging STM32F746-Disco pada batu ini, yang saya miliki, yang berarti bahwa saya dapat mulai bekerja pada proyek tanpa menunggu papan, komponen, dll untuk datang kepada saya.
Hari ini saya akan memberi tahu Anda cara menjalankan modul LTDC, bekerja dengan register (CMSIS). HAL dan perpustakaan lain tidak suka dan tidak menggunakan untuk alasan agama, tetapi ini juga menarik. Anda akan melihat bahwa menaikkan peripheral kompleks pada register sesederhana SPI biasa. Menarik? Ayo pergi!

1. Sedikit tentang LTDC
Modul periferal ini pada dasarnya adalah pengontrol, yang biasanya berdiri di sisi tampilan, misalnya, SSD1963 dan sejenisnya. Jika kita melihat struktur LTDC, kita akan melihat bahwa secara fisik ini adalah bus paralel dengan 24 bit + akselerator perangkat keras grafis + sebuah array data dalam RAM, yang sebenarnya merupakan buffer layar (frame buffer).

Pada output, kami memiliki bus paralel biasa, yang berisi 24 bit warna (8 bit per warna model RGB), garis sinkronisasi, garis on / off display dan jam piksel. Yang terakhir, pada kenyataannya, adalah sinyal jam di mana piksel dimuat ke layar, yaitu, jika kita memiliki frekuensi 9,5 MHz, maka dalam 1 detik kita dapat memuat 9,5 juta piksel. Ini dalam teori, tentu saja, dalam praktiknya, jumlahnya agak lebih sederhana karena pengaturan waktu dan hal-hal lain.
Untuk pengantar yang lebih rinci tentang LTDC, saya menyarankan Anda untuk membaca beberapa dokumen:- Ikhtisar kemampuan LTDC di F4, dalam F7 kami semuanya sama
- Catatan Aplikasi 4861. "LCD-TFT display controller (LTDC) pada STM32 MCUs"
2. Apa yang perlu kita lakukan?
ST mikrokontroler telah mendapatkan popularitas karena alasan yang baik, persyaratan paling penting untuk setiap komponen elektronik adalah dokumentasi, dan semuanya baik-baik saja dengan itu. Situs ini tentu saja mengerikan, tetapi saya akan meninggalkan tautan ke semua dokumentasi. Pabrikan menyelamatkan kita dari siksaan dan penemuan sepeda, oleh karena itu, pada halaman 520 dalam
manual rujukan RM0385, langkah-langkah hitam dan putih diberikan, apa yang perlu kita lakukan:

Faktanya, Anda tidak perlu melakukan setengah dari yang dijelaskan: itu tidak diperlukan untuk memulai, atau sudah dikonfigurasi secara default. Untuk permulaan minimum, yang memungkinkan kita menggambar piksel, menampilkan gambar, grafik, teks, dll., Cukup melakukan hal berikut:
- Aktifkan Pencatatan LTDC
- Atur sistem jam dan frekuensi output data (pixel clock)
- Konfigurasikan I / O Ports (GPIO) agar berfungsi dengan LTDC
- Siapkan pengaturan waktu untuk model tampilan kami
Sesuaikan polaritas sinyal. Sudah dilakukan secara defaultTentukan warna latar belakang tampilan. Kami belum akan melihatnya, Anda dapat membiarkannya “nol”- Atur ukuran aktual dari area tampilan yang terlihat untuk lapisan tertentu
- Pilih format warna: ARGB8888, RGB 888, RGB565, dll.
- Tentukan alamat array yang akan bertindak sebagai frame buffer
- Tunjukkan jumlah data dalam satu baris (panjang lebar)
- Tunjukkan jumlah garis (tinggi tampilan)
- Sertakan layer yang sedang kami kerjakan
- Aktifkan modul LTDC
Menakutkan Dan saya takut, tetapi ternyata berhasil selama 20 menit dengan semua proses. Ada tugas, rencana itu direncanakan dan tinggal memenuhi saja.
3. Menyiapkan sistem jam
Item pertama yang perlu kita kirim sinyal clock ke modul LTDC, ini dilakukan dengan menulis ke register RCC:
RCC->APB2ENR |= RCC_APB2ENR_LTDCEN;
Selanjutnya, Anda perlu mengonfigurasi frekuensi jam dari kuarsa eksternal (HSE) ke frekuensi 216 MHz, yaitu maksimum. Langkah pertama adalah menyalakan sumber jam dari resonator kuarsa dan menunggu bendera siap:
RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY));
Sekarang atur penundaan untuk memori flash pengontrol, sebagai dia tidak tahu bagaimana bekerja pada frekuensi inti. Nilainya, seperti data lainnya, diambil dari manual referensi:
FLASH->ACR |= FLASH_ACR_LATENCY_5WS;
Sekarang, untuk mendapatkan frekuensi yang diinginkan, saya akan membagi 25 MHz dari input menjadi 25 dan mendapatkan 1 MHz. Selanjutnya, hanya di PLL saya kalikan dengan 432, karena di masa depan ada pembagi frekuensi dengan nilai minimum / 2 dan Anda harus menerapkan frekuensi dua kali ke sana. Setelah itu, kami menghubungkan input PLL ke resonator kuarsa kami (HSE):
RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4; RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_4 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_8; RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC;
Sekarang aktifkan PLL dan tunggu tanda siap:
RCC->CR |= RCC_CR_PLLON; while((RCC->CR & RCC_CR_PLLRDY) == 0){}
Kami menetapkan output PLL kami sebagai sumber frekuensi sistem dan menunggu tanda siap:
RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {}
Ini mengakhiri pengaturan jam umum dan kami beralih ke pengaturan frekuensi jam (PLLSAI) untuk tampilan kami (jam piksel). Sinyal untuk PLLSAI menurut datasheet diambil setelah pembagi / 25, yaitu, pada input kita memiliki 1 MHz. Kita perlu mendapatkan frekuensi sekitar 9,5 MHz, untuk ini kita kalikan frekuensi 1 MHz dengan 192, dan kemudian menggunakan dua pembagi dengan 5 dan 4 kita mendapatkan nilai yang diinginkan PLLSAI = 1 MHz * 192/5/4 = 9,6 MHz:
RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIN_6 | RCC_PLLSAICFGR_PLLSAIN_7; RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIR_0 | RCC_PLLSAICFGR_PLLSAIR_2; RCC->DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_0; RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_1;
Sebagai langkah terakhir, kami mengaktifkan PLLSAI untuk tampilan dan menunggu bendera siap-bekerja:
RCC->CR |= RCC_CR_PLLSAION; while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0) {}
Ini melengkapi pengaturan dasar sistem jam, agar tidak lupa dan kemudian tidak menderita, mari aktifkan clocking pada semua port input / output (GPIO). Kami tidak memiliki daya baterai, setidaknya untuk debugging, jadi kami tidak menghemat:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOJEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOKEN;
4. Mengkonfigurasi I / O Ports (GPIO)
Menyiapkan gpio sangat sederhana - kami memiliki semua kaki dari LTDC bus untuk dikonfigurasikan sebagai output alternatif dan pada frekuensi tinggi. Untuk melakukan ini, dalam manual referensi pada halaman 201 kami memiliki tip ini:

Tabel menunjukkan bit mana dalam register yang perlu Anda atur untuk mendapatkan pengaturan yang diperlukan. Perlu dicatat bahwa semua kawat gigi dinonaktifkan. Di mana mencari fungsi alternatif apa yang akan dimasukkan? Dan untuk ini, buka halaman 76 di
lembar data pada controller kami dan lihat tabel berikut:

Seperti yang dapat Anda lihat, logika tabelnya adalah yang paling sederhana: kami menemukan fungsi yang kami butuhkan, dalam kasus kami LTDC B0, kemudian kami melihat GPIOnya (PE4, misalnya) dan di bagian atas kami melihat jumlah fungsi alternatif yang akan kami gunakan untuk mengonfigurasi (AF14 bersama kami). Untuk mengkonfigurasi output kami sebagai output push-pull dengan fungsi alternatif, LTDC B0, kita perlu menulis kode berikut:
GPIOE->MODER &= ~GPIO_MODER_MODER4; GPIOE->MODER |= GPIO_MODER_MODER4_1; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4_1; GPIOE->AFR[0] &= ~GPIO_AFRL_AFRL4_0; GPIOE->AFR[0] |= GPIO_AFRL_AFRL4_1 | GPIO_AFRL_AFRL4_2 | GPIO_AFRL_AFRL4_3;
Saya memberi contoh untuk pin PE4, yang sesuai dengan pin B0 pada bus LTDC, yaitu, itu adalah sedikit warna biru nol. Untuk semua kesimpulan lainnya, pengaturannya identik, hanya 2 kesimpulan yang pantas mendapatkan perhatian khusus, salah satu yang siap termasuk tampilan, dan yang lainnya lampu latar. Mereka dikonfigurasikan sebagai output push-pull normal, yang digunakan semua orang untuk mengedipkan LED. Setup terlihat seperti ini:
GPIOK->MODER &= ~GPIO_MODER_MODER3; GPIOK->MODER |= GPIO_MODER_MODER3_0;
Pengaturan ini untuk output PK3, yang menghidupkan dan mematikan lampu latar kami. Omong-omong, Anda juga dapat PUSH untuk menyesuaikan kecerahan dengan lancar. Untuk PI12, yang termasuk display (DISP), semuanya sama. Kecepatan pada 2 pin ini rendah secara default, karena beberapa tindakan frekuensi tinggi tidak diperlukan dari mereka.
Anda dapat melihat semua port I / O lainnya pada papan sirkuit papan debug, atau pada diagram sirkuit perangkat Anda sendiri.
5. Pengaturan waktu dan pengaturannya
Pengaturan waktu dari sudut pandang fisik adalah penundaan biasa. Saya pikir Anda telah berulang kali mengamati berbagai penyimpangan tipe delay (1) ketika Anda melihat contoh kode pada layar dengan pengontrol SPI / I2C mirip dengan ILI9341. Di sana, penundaan diperlukan agar pengontrol, misalnya, memiliki waktu untuk menerima perintah, menjalankannya, dan kemudian melakukan sesuatu dengan data. Dalam kasus LTDC, semuanya hampir sama, hanya saja kami tidak akan membuat kruk dan mengapa tidak - mikrokontroler kami sendiri dapat mengkonfigurasi pengaturan waktu yang diperlukan dalam perangkat keras. Mengapa mereka diperlukan pada layar di mana tidak ada pengontrol? Ya, sangat penting bahwa setelah mengisi garis piksel pertama, buka baris berikutnya dan kembali ke awal. Hal ini disebabkan oleh teknologi produksi layar, dan oleh karena itu setiap model tampilan tertentu memiliki timing sendiri.
Untuk mengetahui nilai apa yang kami butuhkan, kunjungi situs web ST dan lihat
diagram STM32F746-Disco debug board . Di sana kita dapat melihat bahwa tampilannya adalah
RK043FN48H-CT672B dan dokumentasi untuk itu tersedia, misalnya, di
sini . Kami paling tertarik dengan tabel di halaman 13 di bagian 7.3.1:

Ini adalah nilai-nilai kami yang akan kami butuhkan saat menyiapkan. Juga dalam dokumentasi ada banyak lagi yang menarik, misalnya, diagram sinyal pada bus dan sebagainya, yang mungkin Anda perlukan jika, misalnya, Anda ingin menaikkan tampilan ke FPGA atau CPLD.
Buka pengaturan. Pertama-tama, agar tidak menyimpan nilai-nilai ini di kepala saya, saya akan mengaturnya dalam bentuk definisi:
#define DISPLAY_HSYNC ((uint16_t)30) #define DISPLAY_HBP ((uint16_t)13) #define DISPLAY_HFP ((uint16_t)32) #define DISPLAY_VSYNC ((uint16_t)10) #define DISPLAY_VBP ((uint16_t)2) #define DISPLAY_VFP ((uint16_t)2)
Ada fitur yang menarik. Timing
Pulse Width , yang disebut
DISPLAY_HSYNC , memiliki nilai dalam tabel hanya untuk frekuensi clock pixel 5 MHz, tetapi untuk 9 dan 12 MHz tidak. Waktu ini perlu dipilih untuk tampilan Anda, saya mendapat nilai 30 ini, ketika dalam contoh dari ST itu berbeda. Pada permulaan pertama, jika Anda memiliki kesalahan dengan pengaturannya, gambar akan bergeser ke kiri atau ke kanan. Jika ke kanan, kita mengurangi waktunya, jika ke kiri, kita menambahnya. Bahkan, itu mempengaruhi asal zona terlihat, yang akan kita lihat nanti. Perlu diingat, dan gambar berikut dari halaman 24 AN4861 kami akan membantu untuk memahami seluruh paragraf ini:

Sebuah abstraksi kecil nyaman di sini. Kami memiliki 2 zona tampilan: terlihat dan umum. Zona yang terlihat memiliki dimensi dengan resolusi yang dinyatakan 480 x 272 piksel, dan zona total adalah waktu + yang terlihat, yang mana ada 3 di setiap sisi. Perlu juga dipahami (ini bukan lagi abstraksi) bahwa satu sistem centang adalah 1 piksel, sehingga total area adalah 480 piksel + HSYNC + HBP + HFP.
Penting juga untuk menyadari bahwa semakin sedikit timing, semakin baik - tampilan akan diperbarui lebih cepat dan frame rate akan meningkat sedikit. Karena itu, setelah dijalankan pertama kali, bereksperimenlah dengan timing dan kurangi sebanyak mungkin dengan tetap menjaga stabilitas.
Untuk mengatur timing, saya membuat sendiri "lembar contekan" kecil untuk masa depan di dalam proyek, itu juga akan membantu Anda memahami apa yang spesifik angka dan di mana untuk menulisnya:
Dari mana "cheat sheet" ini berasal ... Pertama, Anda melihat "formula" serupa beberapa paragraf sebelumnya. Kedua, buka halaman 56 dari AN4861 kami:

Benar, saya harap Anda mengerti arti fisik dari timing sebelum munculnya lembar contekan ini dan saya yakin Anda sendiri yang bisa menyusunnya. Tidak ada yang rumit di dalamnya, dan gambar dari RM dan AN membantu secara visual memahami efek pengaturan waktu pada proses pembentukan gambar.
Sekarang saatnya menulis kode yang mengatur timing ini. Dalam "lembar contekan" ditunjukkan bit-bit register tempat menulis, misalnya, TOTALH, dan setelah tanda sama dengan rumus yang memberikan output sejumlah tertentu. Oke Lalu kami menulis:
LTDC->SSCR |= ((DISPLAY_HSYNC - 1) << 16 | (DISPLAY_VSYNC - 1)); LTDC->BPCR |= ((DISPLAY_HSYNC+DISPLAY_HBP-1) << 16 | (DISPLAY_VSYNC+DISPLAY_VBP-1)); LTDC->AWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) << 16 | (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1)); LTDC->TWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP -1)<< 16 |(DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1));
Dan itu semua dengan pengaturan waktu! Di bagian ini, Anda hanya dapat mengonfigurasi warna latar belakang. Saya memilikinya hitam secara default, jadi ini ditulis dalam nol. Jika Anda ingin mengubah warna lapisan latar belakang (background), maka Anda dapat menuliskan nilai apa pun, misalnya,
0xFFFFFFFF dan isi semuanya dengan warna putih:
LTDC->BCCR = 0;
Ada ilustrasi yang bagus dalam
manual referensi , yang dengan jelas menunjukkan bahwa kita sebenarnya memiliki 3 lapisan: latar belakang, lapisan 1 dan lapisan 2. Lapisan latar belakang "dikebiri" dan hanya dapat diisi dengan satu warna tertentu, tetapi juga bisa sangat berguna dalam implementasi desain GUI masa depan. Juga, ilustrasi ini dengan jelas menunjukkan prioritas lapisan, yang berarti bahwa kita akan melihat warna isian di latar hanya ketika lapisan yang tersisa kosong atau transparan.
Sebagai contoh, saya akan menunjukkan salah satu halaman proyek di mana, selama implementasi templat, latar belakang diisi dengan satu warna dan pengontrol tidak menggambar ulang seluruh halaman, tetapi hanya sektor individual, yang diperbolehkan menerima sekitar 50-60 fps untuk banyak tugas lain:

6. Bagian terakhir dari pengaturan LTDC
Pengaturan LTDC dibagi menjadi 2 bagian: yang pertama adalah umum untuk seluruh modul LTDC dan terletak di
grup register
LTDC , dan yang kedua dikonfigurasi dalam salah satu dari dua lapisan dan berada dalam kelompok
LTDC_Layer1 dan
LTDC_Layer2 .
Kami membuat pengaturan umum pada paragraf sebelumnya, ini termasuk mengatur timing, layer background. Sekarang kita beralih ke pengaturan layer dan daftar kita membutuhkan ukuran nyata dari zona terlihat dari layer, yang dijelaskan dalam bentuk 4 koordinat (x0, y0, x1, y2), yang memungkinkan kita untuk mendapatkan dimensi persegi panjang. Ukuran lapisan yang terlihat mungkin kurang dari resolusi tampilan, tidak ada yang mengganggu untuk membuat ukuran lapisan 100 per 100 piksel. Untuk menyesuaikan ukuran zona yang terlihat, tulis kode berikut:
LTDC_Layer2->WHPCR |= (((DISPLAY_WIDTH + DISPLAY_HBP + DISPLAY_HSYNC - 1) << 16) | (DISPLAY_HBP + DISPLAY_HSYNC)); LTDC_Layer2->WVPCR |= (((DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) << 16) |(DISPLAY_VSYNC + DISPLAY_VBP));
Seperti yang Anda lihat, semuanya sama seperti timing. Titik awal (x0, y0) dari zona terlihat terdiri dari jumlah dua pengaturan waktu: HSYNC + HBP dan VSYNC + VBP. Untuk menghitung koordinat titik akhir (x1, y1), lebar dan tinggi piksel ditambahkan ke data nilai.
Sekarang Anda perlu mengkonfigurasi format data yang diterima. Kualitas maksimum diperoleh saat menggunakan format ARGB8888, tetapi pada saat yang sama kami mendapatkan jumlah maksimum memori yang digunakan. Satu piksel menempati 32 bit atau 4 byte, yang berarti seluruh layar membutuhkan 4 * 480 * 272 = 522.240 byte, artinya, setengah dari memori flash kami bukan pengontrol terlemah. Jangan takut - menghubungkan SDRAM eksternal dan memori Flash melalui QSPI memecahkan masalah memori dan tidak ada batasan pada format ini, kami bersukacita dalam kualitas yang baik. Jika Anda ingin menghemat ruang atau tampilan Anda tidak mendukung format 24-bit, maka model yang lebih cocok digunakan untuk ini, misalnya, RGB565. Format yang sangat populer untuk tampilan dan kamera, dan yang paling penting saat menggunakannya, 1 piksel hanya membutuhkan 5 + 6 + 5 = 16 bit atau 2 byte. Dengan demikian, jumlah memori yang ditempati oleh lapisan akan 2 kali lebih sedikit. Secara default, controller sudah memiliki format ARGB8888 yang dikonfigurasi dan terlihat seperti ini:
LTDC_Layer2->PFCR = 0;
Jika Anda memerlukan format yang berbeda dari ARGB8888, lalu buka halaman 533 dan 534 dalam
manual referensi dan pilih format yang diinginkan dari daftar di bawah ini:

Sekarang buat sebuah array dan berikan alamatnya ke LTDC, itu akan berubah menjadi frame buffer dan akan menjadi "refleksi" dari layer kita. Misalnya, Anda perlu mengisi piksel ke-1 di baris ke-1 dengan warna putih, untuk ini Anda hanya perlu menuliskan nilai warna (0xFFFFFFFF) ke elemen pertama array ini. Perlu mengisi piksel 1 di baris ke-2? Kemudian kita juga menulis nilai warna pada elemen dengan angka (480 +1). 480 - buat jeda baris, lalu tambahkan angka pada baris yang kita butuhkan.
Pengaturan ini terlihat seperti ini:
#define DISPLAY_WIDTH ((uint16_t)480) #define DISPLAY_HEIGHT ((uint16_t)272) const uint32_t imageLayer2[DISPLAY_WIDTH * DISPLAY_HEIGHT]; LTDC_Layer2->CFBAR = (uint32_t)imageLayer2;
Dalam cara yang baik, setelah mengkonfigurasi LTDC, Anda juga perlu mengkonfigurasi SDRAM untuk menghapus pengubah
konstanta dan mendapatkan buffer bingkai dalam RAM, karena RAM milik MK sendiri tidak cukup bahkan untuk satu layer dengan 4 byte. Meskipun ini tidak ada salahnya untuk menguji konfigurasi periferal yang benar.
Selanjutnya, Anda perlu menentukan nilai lapisan alfa, yaitu transparansi untuk lapisan
Layer2 kami, untuk ini kami menulis nilai dari 0 hingga 255, di mana 0 adalah lapisan yang sepenuhnya transparan, 255 sepenuhnya buram, yang 100% terlihat:
LTDC_Layer2->CACR = 255;
Menurut rencana kami, sekarang perlu untuk mencatat ukuran area tampilan kami yang terlihat dalam byte, untuk ini kami menulis nilai yang sesuai ke dalam register:
LTDC_Layer2->CFBLR |= (((PIXEL_SIZE * DISPLAY_WIDTH) << 16) | (PIXEL_SIZE * DISPLAY_WIDTH + 3)); LTDC_Layer2->CFBLNR |= DISPLAY_HEIGHT;
Dua langkah terakhir tetap, yaitu penyertaan layer 2 dan modul perangkat LTDC itu sendiri. Untuk melakukan ini, tuliskan bit yang sesuai:
LTDC_Layer2->CR |= LTDC_LxCR_LEN; LTDC->GCR |= LTDC_GCR_LTDCEN;
Ini melengkapi konfigurasi modul kami dan Anda dapat bekerja dengan tampilan kami!
7. Sedikit tentang bekerja dengan LTDC
Semua bekerja dengan tampilan sekarang hanya untuk menulis data ke array
imageLayer2 , ia memiliki ukuran 480 oleh 272 elemen, yang sepenuhnya sesuai dengan resolusi kami dan mengisyaratkan kebenaran sederhana -
1 elemen array = 1 piksel pada layar .
Sebagai contoh, saya menulis gambar ke array yang saya konversi di
LCD Image Converter , tetapi dalam kenyataannya tidak mungkin bahwa tugas Anda akan terbatas pada ini. Ada dua cara: menggunakan GUI yang sudah jadi dan menulisnya sendiri. Untuk tugas-tugas yang relatif sederhana seperti output teks, grafik, dan sejenisnya, saya menyarankan Anda untuk menulis GUI Anda sendiri, itu akan memakan waktu sedikit dan memberi Anda pemahaman penuh tentang operasinya. Ketika tugas itu besar dan sulit, dan tidak ada waktu untuk mengembangkan GUI Anda sendiri, saya menyarankan Anda untuk memperhatikan solusi yang sudah jadi, misalnya, uGFX dan sejenisnya.
Simbol teks, garis, dan elemen lain pada dasarnya adalah array piksel, jadi untuk mengimplementasikannya, Anda perlu mengimplementasikan logika sendiri, tetapi Anda harus mulai dengan fungsi paling dasar - "output piksel". Itu harus mengambil 3 argumen: koordinat sepanjang X, koordinat sepanjang Y dan, sesuai, warna di mana piksel yang diberikan dicat. Ini mungkin terlihat seperti ini:
typedef enum ColorDisplay { RED = 0xFFFF0000, GREEN = 0xFF00FF00, BLUE = 0xFF0000FF, BLACK = 0xFF000000, WHITE = 0xFFFFFFFF } Color; void SetPixel (uint16_t setX, uint16_t setY, Color Color) { uint32_t numBuffer = ((setY - 1) * DISPLAY_WIDTH) + setX; imageLayer2[numBuffer] = Color; }
Setelah kami mengambil koordinat menjadi fungsi, kami menghitung ulang mereka ke dalam jumlah array yang sesuai dengan koordinat yang diberikan dan kemudian menulis warna yang diterima ke elemen yang diterima. Berdasarkan fungsi ini, Anda sudah dapat mengimplementasikan fungsi untuk menampilkan geometri, teks, dan "barang" GUI lainnya. Saya pikir ide itu bisa dimengerti, tetapi bagaimana menghidupkannya adalah atas kebijaksanaan Anda.
Ringkasan
Seperti yang Anda lihat, implementasi peripheral on register (CMSIS) yang rumit bukan tugas yang sulit, Anda hanya perlu memahami cara kerjanya di dalam. Tentu saja, sekarang modis untuk mengembangkan firmware tanpa memahami apa yang terjadi, tetapi ini adalah jalan buntu jika Anda berencana untuk menjadi seorang insinyur, dan bukan ...
Jika Anda membandingkan kode yang dihasilkan dengan solusi dalam HAL atau SPL, Anda akan melihat bahwa kode yang ditulis dalam register lebih kompak. Menambahkan beberapa komentar di mana Anda membutuhkannya dan membungkusnya dalam fungsi, kami mendapatkan keterbacaan setidaknya tidak lebih buruk daripada HAL / SPL, dan jika Anda ingat bahwa
manual referensi mendokumentasikan register, maka bekerja dengan CMSIS lebih nyaman.
1) Proyek dengan sumber-sumber di TrueSTUDIO dapat diunduh
di sini2) Bagi mereka yang lebih nyaman melihat
GitHub3) Utilitas pengunduhan untuk mengonversi gambar menjadi kode Pengonversi Gambar LCD
di sini