Proyek DMA2D dalam Grafis Tertanam

Daftar Isi

Graphics Accelerator Chrom-ART™ (DMA2D)

Apa itu DMA2D?

Seiring kemajuan teknologi grafis tertanam, mikrokontroler kini menangani tugas-tugas komputasi dan tampilan grafis yang semakin kompleks. Namun, daya pemrosesan CPU mungkin tidak cukup untuk menangani grafis beresolusi tinggi dan berwarna cerah. Untungnya, mulai dari STM32F429, periferal eksternal yang mirip dengan GPU telah diperkenalkan ke dalam mikrokontroler STM32 oleh ST, yang dikenal sebagai Chrom-ART Accelerator atau DMA2D. DMA2D memberikan akselerasi dalam banyak skenario grafis 2D dan secara efektif mengintegrasikan fungsi-fungsi yang mirip dengan "GPU" yang ditemukan pada kartu grafis modern.

Meskipun DMA2D hanya menawarkan akselerasi 2D dan kemampuannya relatif dasar dibandingkan dengan GPU pada PC, DMA2D dapat memenuhi sebagian besar kebutuhan akselerasi tampilan grafis dalam pengembangan tertanam. Dengan memanfaatkan DMA2D secara efektif, kita dapat mencapai efek UI yang halus dan memukau pada mikrokontroler.

Fungsi DMA2D

  1. Pengisian Warna (Area Persegi Panjang)
  2. Penyalinan Gambar (Memori)
  3. Konversi Format Warna (misalnya, YCbCr ke RGB atau RGB888 ke RGB565)
  4. Pencampuran Transparansi (Alpha Blend)

Dua yang pertama adalah operasi berbasis memori, sedangkan dua yang terakhir melibatkan akselerasi komputasi. Pencampuran transparansi dan konversi format warna dapat digabungkan dengan penyalinan gambar, sehingga memberikan fleksibilitas yang signifikan.

Dalam pengembangan praktis, penggunaan DMA2D mirip dengan pengontrol DMA tradisional. Dalam skenario non-grafis tertentu, DMA2D bahkan dapat menggantikan DMA konvensional untuk tugas-tugas tertentu.

Penting untuk dicatat bahwa akselerator DMA2D pada lini produk ST yang berbeda mungkin memiliki perbedaan kecil. Misalnya, DMA2D pada MCU seri STM32F4 tidak memiliki kemampuan untuk mengonversi antara format warna ARGB dan AGBR. Oleh karena itu, ketika membutuhkan fungsi tertentu, disarankan untuk memeriksa manual pemrograman untuk memastikan dukungannya.

Mode Operasi DMA2D

Sama seperti DMA tradisional yang memiliki mode periferal-ke-periferal, periferal-ke-memori, dan memori-ke-periferal, DMA2D, sebagai komponen DMA, juga memiliki empat mode operasi:

  1. Register ke Memori
  2. Memori ke Memori
  3. Memori ke Memori dengan Konversi Format Warna Piksel
  4. Memori ke Memori dengan Konversi Format Warna Piksel dan Pencampuran Transparansi

Dua mode pertama melibatkan operasi memori yang sederhana, sedangkan dua mode terakhir melakukan penyalinan memori sambil secara bersamaan menangani konversi format warna dan/atau pencampuran transparansi sesuai kebutuhan.

DMA2D dan Perpustakaan HAL

Dalam banyak kasus, penggunaan pustaka HAL mempermudah penulisan kode dan meningkatkan portabilitas. Namun, ada pengecualian dalam hal penggunaan DMA2D. Masalah utama pada HAL terletak pada penumpukan struktur yang berlebihan dan pemeriksaan keamanan, yang mengurangi efisiensi. Meskipun penurunan efisiensi saat menangani periferal lain mungkin tidak terlalu signifikan, bagi DMA2D—sebuah akselerator yang berfokus pada komputasi dan kecepatan—penggunaan pustaka HAL dapat secara signifikan mengurangi efisiensi akselerasinya.

Akibatnya, kami sering menghindari penggunaan fungsi HAL yang relevan untuk operasi DMA2D. Demi efisiensi, manipulasi register langsung digunakan, memastikan manfaat akselerasi yang maksimal.

Karena sebagian besar kasus penggunaan DMA2D melibatkan perubahan mode operasi yang sering, konfigurasi grafis DMA2D di CubeMX menjadi kurang praktis.

Penerapan DMA2D dalam Pengembangan Grafis Tertanam

Peralatan yang Diperlukan

  • Papan Pengembangan STM32 dengan Periferal DMA2D x1
  • Layar TFT Berwarna x1

Dalam contoh ini, kami menggunakan papan pengembangan ART-Pi dari RT-Thread, yang dilengkapi dengan STM32H750XB dengan frekuensi clock hingga 480MHz dan SDRAM sebesar 32MB. Papan ini juga dilengkapi dengan debugger (ST-Link V2.1). Dan kami menggunakan layar LCD TFT 3,5 inci dengan antarmuka RGB666 dan resolusi 320×240 (QVGA).

ART-Pi Development Board-Produced by RT-Thread (with TFT LCD Display)
ART-Pi Development Board-Produced by RT-Thread (with TFT LCD Display)

Lingkungan Pengembangan

Konten dan kode yang disajikan dalam artikel ini dapat digunakan di berbagai lingkungan pengembangan seperti RT-Thread Studio, MDK, IAR, dan lain-lain.

Sebelum memulai eksperimen dalam artikel ini, Anda memerlukan proyek dasar yang mengendalikan layar LCD menggunakan teknologi framebuffer. Mengaktifkan DMA2D diperlukan sebelum menjalankan kode apa pun yang disediakan.

DMA2D dapat diaktifkan menggunakan makro ini:

				
					__HAL_RCC_DMA2D_CLK_ENABLE();
				
			

Proyek Aplikasi: Mengisi Persegi Panjang

Grafik tertanam mencakup berbagai jenis operasi, termasuk pengisian persegi panjang, penyalinan memori, pencampuran transparansi, dan sebagainya. Kita akan menggunakan pengisian persegi panjang sebagai contoh. Tujuannya adalah membuat diagram batang sederhana menggunakan DMA2D untuk pengisian persegi panjang:

A Simple Histogram
A Simple Histogram

Pertama-tama, kita perlu mengisi layar dengan warna putih, yang akan berfungsi sebagai latar belakang untuk pola tersebut. Langkah ini sangat penting, karena pola yang sudah ada di layar dapat mengganggu desain yang kita inginkan. Selanjutnya, diagram batang dibuat menggunakan empat balok persegi panjang berwarna biru dan sebuah segmen garis, yang dapat dianggap sebagai balok persegi panjang khusus dengan tinggi 1. Oleh karena itu, menggambar grafik ini melibatkan serangkaian operasi "pengisian persegi panjang":

  • Isi persegi panjang dengan warna putih, menutupi seluruh layar.
  • Isi empat batang data dengan warna biru.
  • Isi segmen garis dengan warna hitam, dengan tinggi 1.

Pada dasarnya, menggambar persegi panjang dengan ukuran apa pun di posisi mana pun pada kanvas melibatkan pengaturan data piksel di lokasi memori yang sesuai ke warna yang diinginkan. Namun, karena penyimpanan framebuffer di memori bersifat linier, kecuali lebar persegi panjang tepat sejajar dengan lebar layar, area persegi panjang yang tampak kontinu memiliki alamat memori yang tidak berurutan.

Diagram di bawah ini menggambarkan tata letak memori yang umum, di mana angka-angka menunjukkan alamat memori setiap piksel dalam framebuffer (offset relatif terhadap alamat dasar, tanpa mempertimbangkan piksel multi-byte). Area biru mewakili persegi panjang yang akan diisi. Jelas terlihat bahwa alamat memori di dalam persegi panjang tersebut tidak berurutan.

Memory Distribution in Frame Buffer
Memory Distribution in Frame Buffer

Sifat framebuffer ini menghalangi kita untuk menggunakan operasi yang efisien seperti `memset` guna mengisi area persegi panjang. Biasanya, kita akan menggunakan pendekatan loop bersarang seperti yang ditunjukkan di bawah ini untuk mengisi persegi panjang apa pun. Di sini, `xs` dan `ys` adalah koordinat sudut kiri atas persegi panjang di layar, `width` dan `height` menentukan dimensi persegi panjang, sedangkan `color` menentukan warna pengisian:

				
					for (int y = ys; y < ys + height; y++) {
    for (int x = xs; x < xs + width; x++) {
        framebuffer[y][x] = color;
    }
}

				
			

Meskipun kode tersebut tampak sederhana, selama proses eksekusi, sejumlah besar siklus CPU terbuang percuma untuk operasi seperti pemeriksaan kondisi, perhitungan alamat, dan penambahan, sementara hanya sebagian kecil yang dialokasikan untuk penulisan memori yang sebenarnya. Situasi ini mengakibatkan penurunan efisiensi.

Dalam kasus seperti itu, mode kerja register-ke-memori DMA2D berperan. DMA2D dapat dengan cepat mengisi wilayah memori persegi panjang, bahkan jika area tersebut tidak berurutan dalam memori.

Dengan menggunakan contoh yang digambarkan pada gambar di atas, mari kita pelajari bagaimana hal ini dicapai:

Illustration of DMA2D Filling Memory Area
Illustration of DMA2D Filling Memory Area

Pertama-tama, karena kita hanya berurusan dengan pengisian memori dan bukan penyalinan, kita perlu mengatur DMA2D agar beroperasi dalam mode register-ke-memori. Hal ini dilakukan dengan mengatur bit [17:16] pada register CR DMA2D menjadi '11', seperti yang ditunjukkan dalam potongan kode berikut:

				
					DMA2D->CR = 0x00030000UL;

				
			

Selanjutnya, kita memberitahu DMA2D mengenai atribut persegi panjang yang akan diisi, seperti alamat awal wilayah tersebut, lebarnya dalam piksel, dan tingginya.

Alamat awal wilayah tersebut adalah alamat memori piksel kiri atas persegi panjang (alamat piksel merah pada diagram), yang dikelola oleh register OMAR dari DMA2D. Lebar dan tinggi persegi panjang keduanya dalam piksel dan dikelola oleh 16 bit atas (lebar) dan 16 bit bawah (tinggi) dari register NLR. Kode untuk mengatur nilai-nilai ini adalah sebagai berikut:

				
					DMA2D->OMAR = (uint32_t)(&framebuffer[y][x]); // Set the starting pixel memory address for filling
DMA2D->NLR  = (uint32_t)(width << 16) | (uint16_t)height; // Set the width and height of the rectangle

				
			

Selanjutnya, karena alamat memori persegi panjang tersebut tidak berurutan, kita perlu menginstruksikan DMA2D untuk melewati sejumlah piksel tertentu setelah mengisi satu baris data (yaitu, panjang area kuning pada diagram). Nilai ini diatur oleh register OOR. Ada cara sederhana untuk menghitung jumlah piksel yang harus dilewati: kurangi lebar persegi panjang dari lebar area tampilan. Kode untuk mengimplementasikannya adalah:

				
					DMA2D->OOR = screenWidthPx - width; // Set the row offset, i.e., skip pixels

				
			

Terakhir, kita perlu memberitahukan kepada DMA2D mengenai warna yang akan digunakan untuk pengisian serta format warnanya. Kedua hal ini dikelola masing-masing oleh register OCOLR dan OPFCCR. Format warna ditentukan oleh makro LTDC_PIXEL_FORMAT_XXX. Kode programnya adalah sebagai berikut:

				
					DMA2D->OCOLR   = color; // Set the color for filling
DMA2D->OPFCCR  = pixelFormat; // Set the color format, e.g., use the macro LTDC_PIXEL_FORMAT_RGB565 for RGB565

				
			

Setelah semua pengaturan selesai, DMA2D telah memperoleh semua informasi yang diperlukan untuk mengisi persegi panjang tersebut. Untuk memulai transfer, kita mengatur bit ke-0 pada register CR DMA2D menjadi '1':

				
					DMA2D->CR |= DMA2D_CR_START; // Start DMA2D data transfer, where DMA2D_CR_START is a macro with the value 0x01

				
			

Setelah proses transfer DMA2D dimulai, kita tinggal menunggu hingga selesai. Setelah DMA2D menyelesaikan transfer, bit 0 pada register CR secara otomatis diatur ulang menjadi '0', sehingga kita dapat menunggu hingga proses selesai dengan menggunakan kode berikut:

				
					while (DMA2D->CR & DMA2D_CR_START) {} // Wait for DMA2D transfer completion

				
			

Tips: Jika Anda menggunakan sistem operasi, Anda dapat mengaktifkan interupsi penyelesaian transfer DMA2D. Selanjutnya, Anda dapat membuat semafor, menunggunya setelah memulai transfer, dan melepaskannya dalam rutin layanan interupsi penyelesaian transfer DMA2D.

Demi keumuman fungsi, alamat awal transfer dan offset baris dihitung di luar fungsi dan diteruskan ke dalamnya. Berikut adalah kode fungsi lengkapnya:

				
					static inline void DMA2D_Fill(void * pDst, uint32_t width, uint32_t height, uint32_t lineOff, uint32_t pixelFormat, uint32_t color) {
    
    /* Configure DMA2D */
    DMA2D->CR      = 0x00030000UL;                                  // Configure for register-to-memory mode
    DMA2D->OCOLR   = color;                                         // Set the color for filling (format should match the configured color format)
    DMA2D->OMAR    = (uint32_t)pDst;                                // Starting memory address of the fill region
    DMA2D->OOR     = lineOff;                                       // Row offset, i.e., skip pixels (in pixel units)
    DMA2D->OPFCCR  = pixelFormat;                                   // Set the color format
    DMA2D->NLR     = (uint32_t)(width << 16) | (uint16_t)height;    // Set the width and height of the fill region (in pixel units)

    /* Start transfer */
    DMA2D->CR   |= DMA2D_CR_START;

    /* Wait for DMA2D transfer completion */
    while (DMA2D->CR & DMA2D_CR_START) {}
}

				
			

Demi kemudahan, mari kita kemas ini ke dalam fungsi pengisian persegi panjang berdasarkan sistem koordinat layar Anda:

				
					void FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
    void* pDist = &(((uint16_t*)framebuffer)[y*320 + x]);
    DMA2D_Fill(pDist, w, h, 320 - w, LTDC_PIXEL_FORMAT_RGB565, color);
}

				
			

Terakhir, mari kita gunakan kode tersebut untuk menggambar grafik yang ditampilkan di awal bagian ini:

				
					// Fill background color
FillRect(0,   0,   320, 240,  0xFFFF);
// Draw data bars
FillRect(80,  80,  20,  120,  0x001F);
FillRect(120, 100, 20,  100,  0x001F);
FillRect(160, 40,  20,  160,  0x001F);
FillRect(200, 60,  20,  140,  0x001F);
// Draw X-axis
FillRect(40,  200, 240, 1,    0x0000);

				
			

Efek dari operasi kode tersebut adalah sebagai berikut:

The Final Effect of DMA2D Rectangle Filling
The Final Effect of DMA2D Rectangle Filling

Berlangganan

Daftar ke milis kami untuk mendapatkan pembaruan blog bulanan, berita teknologi, dan studi kasus. Kami tidak akan pernah mengirimkan spam, dan Anda dapat berhenti berlangganan kapan saja.

Scroll to Top

Instant Quote