Menghubungkan Layar 10 "ER-TFT101-1 ke STM32F429 via FMC

Hari baik untuk semua Pada artikel ini kita akan menganalisis koneksi layar TFT ER-TFT101-1 (10 inci, driver RA8876) ke papan STM32F429L Discovery melalui antarmuka paralel 1680 bit 8080 menggunakan modul FMC (pengendali memori fleksibel).




Tentang unit display


ER-TFT101-1 dari EastRising adalah rakitan matriks 10-inci TFT dengan resolusi 1024x600 dan papan dengan driver RA8876. Pada motherboard dengan driver, semua daya yang diperlukan dialihkan, ada memori SD-RAM 16 megabyte (bus 16-bit, frekuensi maksimum 166 MHz, kapasitas maksimum 64 MB), ada slot kartu microSD standar. Ada jejak kaki kosong di bawah EEPROM dengan font eksternal dan di bawah memori flash untuk gambar dengan konektor output untuk pemrograman mereka. Juga, panel sentuh resistif atau kapasitif secara opsional dapat dipasang pada unit.

On the board adalah driver RAiO RA8876 top-end dengan frekuensi operasi maksimum 120 MHz, yang, jika diinginkan, dapat dengan sendirinya berfungsi sebagai kontrol mikrokontroler. Anda dapat menulis sebuah program kecil (hanya 12 instruksi) dan memasukkannya ke dalam memori flash eksternal. Ketika tampilan dimulai, program ini akan mulai berjalan di tempat pertama, pada dasarnya menduplikasi semua opsi kontrol melalui antarmuka eksternal.



RA8876 tidak memiliki RAM sendiri dan karenanya menggunakan memori SD-RAM eksternal. Ia dapat membaca gambar dari memori flash menggunakan DMA dan memuatnya ke dalam frame buffer-nya dan memiliki konfigurasi yang sangat fleksibel. Driver terhubung ke matriks itu sendiri menggunakan antarmuka RGB lebar standar 18-bit. Hanya 6 bit per saluran merah, 6 bit per hijau dan 6 bit saluran biru yang digunakan. Dua bit yang lebih rendah dari masing-masing saluran tidak digunakan, yang secara teori memberikan 262.144 warna.

Modul DMA RA8876 sangat mirip dengan DMA2D STM - modul ini dapat menyalin bagian memori persegi panjang dari satu tempat ke tempat lain, dengan konversi warna, transparansi, dan chip lainnya.

RA8876 juga memiliki font Inggris dan Cina bawaan (8x16,12x24,16x32 piksel) dengan pengaturan fleksibel untuk tampilan (rotasi, skala, dll.) Dan antarmuka matriks terpisah (5 x 5) untuk tombol perangkat keras (untuk perangkat yang berdiri sendiri dan tidak hanya digunakan) dengan banyak pengaturan, seperti tekan lama dan pendek, bangun layar dengan menekan tombol dan menekan beberapa tombol secara bersamaan.

Ada fungsi gambar-dalam-gambar (tanpa dukungan transparansi) untuk menampilkan sembulan jendela dan menu.

Pengemudi itu sendiri dapat menggambar grafik primitif, seperti kotak, lingkaran, kurva, oval, segitiga, kotak bulat, dengan dan tanpa isian. By the way, di RA8875 dan di RA8876 ada bug kecil untuk mengisi segitiga, dan masing-masing driver memiliki sendiri. Tetapi RAiO tidak peduli tentang itu dari menara lonceng yang tinggi ... entah bagaimana saya mencoba menulis surat kepada mereka, sehingga mereka bahkan tidak menjawab. Menggambar primitif seperti itu memungkinkan Anda untuk membuat grafik yang indah bahkan dengan mikrokontroler yang lambat.

Dengan dunia luar, RA8876 berkomunikasi melalui antarmuka 8080/6800 8/16 bit, 3/4 kawat SPI dan I2C. Selain itu, chip driver itu sendiri dapat bertindak sebagai master SPI dan I2C. Dalam RA8876, ada dua output PWM yang dapat digunakan untuk kontrol lampu latar yang fleksibel. Frekuensi SPI CLK maksimum dinyatakan pada 66 MHz dengan frekuensi driver 120 MHz, yang secara teori memberikan 6 frame per detik dari pembaruan layar penuh (pada 1024 x 600 x 16 bit). Koneksi ini diuji oleh saya dan menunjukkan bahwa ia memiliki hak untuk hidup jika kami tidak menampilkan video di layar.

Dalam kasus kami, kami akan menghubungkan layar menggunakan protokol 8080 dengan lebar 16 bit ke STM32F429ZIT6 melalui modul FMC (pengontrol memori fleksibel), yang akan memungkinkan kami mendapatkan lebih banyak kecepatan pengisian layar dan lebih sedikit beban pada mikrokontroler.

Konfigurasi pin 8080 dan FMC


Kami akan melihat diagram koneksi untuk 8080 di lembar data pada layar:



Kami melihat pin yang diperlukan untuk menghubungkan ke STM32 di CubeMX. Kami tertarik dengan bank # 1 (NOR Flash / PSRAM / SRAM / ROM / LDC 1).



Mengenai XnWAIT di lembar data, Anda bisa membaca yang berikut:
Kecepatan menulis data terus menerus menentukan kecepatan pembaruan tampilan. Interval siklus-ke-siklus harus lebih besar dari 5 periode jam sistem jika pengguna tanpa mengadopsi XnWait untuk memasukkan status tunggu. Over spesifikasi dapat menyebabkan kehilangan data atau fungsi gagal jika mekanisme xnwait tidak digunakan.
Secara harfiah, di antara siklus operasi protokol 8080, penundaan 5 sistem cabikan RA8876 harus dimasukkan jika pengguna tidak menggunakan mekanisme XnWAIT untuk menunggu rilis RA8876. Kami akan menggunakan pin ini sekali lagi, sebagai dalam praktiknya, saya mencoba memasukkan penundaan lima siklus, dan itu tidak berhasil.

Alih-alih bus alamat lengkap untuk unit FMC, kami hanya menggunakan satu pin A16.

  1. Kami mengonfigurasikan pin data (D0 - D15) sebagai fungsi alternatif # 12, seperti push pool, kecepatan maksimum, dan tanpa suspender.
  2. Pin XnWAIT, XnWR, XnRD, XA0 dan XnCS dikonfigurasikan sebagai fungsi alternatif # 12, seperti push-pull dengan lift ke plus (PULL UP).
  3. Kami mengonfigurasi XnRST sebagai GPIO biasa tanpa suspender (ada di papan itu sendiri).
  4. XnINTR dapat dikonfigurasi sebagai GPIO ke input dengan lift ke plus.

Saya juga menghubungkan lampu latar ke 100% tanpa mengendalikannya melalui PWM. Untuk melakukan ini, pin # 14 pada konektor unit display terhubung ke VDD.

Saya tidak memberikan kode konfigurasi untuk pin, karena Saya menggunakan pustaka konfigurasi saya sendiri, dan konfigurasi GPIO sendiri sudah dikunyah ratusan kali pada hub dan pada sumber lain.

Pustaka inisialisasi ada di sini .

Pengaturan FMC


Tiga bank untuk masing-masing bank bertanggung jawab untuk menyiapkan bank-bank dari modul FMC (NOR Flash / PSRAM / SRAM / ROM / LDC 1). Ini adalah FMC_BCRx, FMC_BTRx dan FMC_BWTRx. Dalam definisi STM32F429 MK, register FMC_BCRx dan FMC_BTRx digabungkan menjadi satu array umum yang disebut FMC_BTCR dengan delapan elemen, di mana elemen nol adalah FMC_BCR1, elemen pertama adalah FMC_BTR1, elemen kedua adalah FMC_BCR2, dan seterusnya. FMC_BWTRx digabungkan menjadi array FMC_BWTR dengan tujuh elemen, meskipun harus ada empat. Jangan tanya kenapa ...

FMC_BCRx berisi pengaturan dasar, FMC_BTRx berisi pengaturan waktu umum, dan FMC_BWTRx berisi pengaturan waktu terpisah untuk membaca, jika perangkat mengharuskannya.

Diagram waktu dan pengaturan waktu untuk interaksi STM32F429 dan RA8876.



Untuk kemudahan konfigurasi, kami akan memasukkan timing protokol 8080 ke dalam konstanta. Saya memilih timing sendiri secara empiris, mengurangi sedikit nilai, karena Tabel waktu dengan lembar data lebih seperti kuda bulat dalam ruang hampa.

unsigned long ADDSET = 0; unsigned long ADDHLD = 1; unsigned long DATAST = 5; unsigned long BUSTURN = 0; unsigned long CLKDIV = 1; unsigned long DATLAT = 0; unsigned long ACCMOD = 0; RCC->AHB3ENR |= RCC_AHB3ENR_FMCEN; //  FMC FMC_Bank1->BTCR[0] = 0; //    FMC_Bank1->BTCR[0] |= FMC_BCR1_MWID_0; //    16  FMC_Bank1->BTCR[0] |= FMC_BCR1_WREN; //    FMC_Bank1->BTCR[0] |= FMC_BCR1_WAITEN; //   XnWAIT FMC_Bank1->BTCR[0] |= FMC_BCR1_ASYNCWAIT; // XnWAIT    FMC_Bank1->BTCR[0] |= FMC_BCR1_WAITCFG; //  XnWAIT FMC_Bank1->BTCR[1] = 0; //    FMC_Bank1->BTCR[1] |= (ADDSET << 0) | (ADDHLD << 4) | (DATAST << 8) | (BUSTURN << 16) | (CLKDIV << 20) | (DATLAT << 24) | (ACCMOD << 28); //   FMC_Bank1->BTCR[0] |= 1; //    

Nilai register FMC_BTCRx setelah reset adalah 0x0FFF FFFF, mis. pengaturan waktu maksimum ditetapkan. Jika Anda memiliki tampilan atau memori baru, turunkan pengaturan waktu dan coba jalankan.

Inisialisasi tampilan


Bekerja dengan tampilan adalah membaca atau menulis ke area memori tertentu. FMC menangani sisa pekerjaan. Untuk mempermudah pekerjaan, kami mendefinisikan dua definisi:

 #define LCD_DATA 0x60020000 #define LCD_REG 0x60000000 

Dan sekarang kami menggambarkan fungsi tingkat rendah:

 void LCD_CmdWrite (unsigned char cmd) { *(unsigned short *)(LCD_REG) = cmd; }; void LCD_DataWrite (unsigned short data) { *(unsigned short *)(LCD_DATA)= data; }; unsigned char LCD_StatusRead(void) { unsigned short data = *(unsigned short *)(LCD_REG); return data; }; unsigned char LCD_DataRead(void) { unsigned short data = * (unsigned short *)(LCD_DATA); return (unsigned char)data; }; void LCD_RegisterWrite(unsigned char cmd, unsigned char data) { *(unsigned short *)(LCD_REG) = cmd; *(unsigned short *)(LCD_DATA) = data; }; unsigned char LCD_RegisterRead (unsigned char cmd) { volatile unsigned char data = 0; LCD_CmdWrite (cmd); data = LCD_DataRead (); return data; }; 

Selanjutnya, fungsi inisialisasi tampilan itu sendiri. Kode diambil dari penyedia display dan sedikit dirancang ulang agar sesuai dengan kebutuhan Anda. Subfungsi menempati jumlah yang besar dan dalam artikel ini saya tidak akan memberikannya.

Tautan ke driver di github

 void RA8876_Init(void) { RA8876_PLL_Init (); //   RA8876  120  RA8876_SDRAM_Init (); //  SDRAM  166  TFT_24bit (); //    24   ??? Host_Bus_16bit (); //   16  RGB_16b_16bpp (); //   16  MemWrite_Left_Right_Top_Down (); //       . Graphic_Mode (); //    Memory_Select_SDRAM (); //   SDRAM /*  RGB     RA8876 */ HSCAN_L_to_R (); VSCAN_T_to_B (); PDATA_Set_RGB (); PCLK_Falling (); DE_High_Active (); HSYNC_High_Active (); VSYNC_High_Active (); LCD_HorizontalWidth_VerticalHeight (1024, 600); LCD_Horizontal_Non_Display (160); LCD_HSYNC_Start_Position (160); LCD_HSYNC_Pulse_Width (70); LCD_Vertical_Non_Display (23); LCD_VSYNC_Start_Position (12); LCD_VSYNC_Pulse_Width (10); //   Frame_Buffer_Start_Address (PAGE0_START_ADDR); Frame_Buffer_Width (1024); Frame_Buffer_Start_XY (0, 0); Frame_Buffer_Color_Mode_16bpp (); //   Canvas_Window_Start_Address (PAGE0_START_ADDR); Canvas_Window_Width (1024); Canvas_Window_Start_XY (0, 0); Canvas_Window_WH (1024, 600); Canvas_Memory_XY_Mode (); Canvas_Window_Color_Mode_16bpp (); } 

Tentang framebuffer dan area aktif sedikit lagi. Untuk dua pengaturan ini, kami mendefinisikan definisi berikut:

 #define PAGE0_START_ADDR 0 #define PAGE1_START_ADDR (1024 * 600 * 2 * 1) #define PAGE2_START_ADDR (1024 * 600 * 2 * 2) #define PAGE3_START_ADDR (1024 * 600 * 2 * 3) #define PAGE4_START_ADDR (1024 * 600 * 2 * 4) #define PAGE5_START_ADDR (1024 * 600 * 2 * 5) #define PAGE6_START_ADDR (1024 * 600 * 2 * 6) #define PAGE7_START_ADDR (1024 * 600 * 2 * 7) #define PAGE8_START_ADDR (1024 * 600 * 2 * 8) #define PAGE9_START_ADDR (1024 * 600 * 2 * 9) #define PAGE10_START_ADDR (1024 * 600 * 2 * 10) #define PAGE11_START_ADDR (1024 * 600 * 2 * 11) #define PAGE12_START_ADDR (1024 * 600 * 2 * 12) 

Setiap halaman (PAGEx_START_ADDR) adalah alamat awal di SDRAM. Dalam memori 16 megabyte, kita dapat menempatkan 13 lapisan penuh dalam ukuran 1228800 byte (1024 * 600 * 2).
Fungsi Frame_Buffer_Start_Address menetapkan area memori awal untuk framebuffer (apa yang saat ini sedang ditampilkan).

Fungsi Canvas_Window_Start_Address menetapkan area memori awal untuk kanvas. Selain itu, kanvas mungkin lebih besar dari framebuffer, sehingga Anda dapat menggulir gambar di layar. Misalnya, untuk permainan platform, Anda dapat membuat kanvas panjang berukuran 13312 x 600 piksel dan kemudian menggulirnya secara horizontal, menggerakkan framebuffer secara horizontal di atasnya.

Jika Anda membandingkan output grafis dengan LTDM dari STM32, maka semuanya tidak begitu cerah di sini. Pada saat yang sama, driver itu sendiri hanya dapat menampilkan satu lapisan (buffer) akhir, dan LTDC segera dua, mencampurnya, tanpa memerlukan partisipasi Anda dalam proses ini.

Menggambar primitif


Kode dari penyedia tampilan dengan fitur yang dibangun sebelumnya:

 void Start_Line (void); void Start_Triangle (void); void Start_Triangle_Fill (void); void Line_Start_XY (unsigned short WX, unsigned short HY); void Line_End_XY (unsigned short WX, unsigned short HY); void Triangle_Point1_XY (unsigned short WX, unsigned short HY); void Triangle_Point2_XY (unsigned short WX, unsigned short HY); void Triangle_Point3_XY (unsigned short WX, unsigned short HY); void Square_Start_XY (unsigned short WX, unsigned short HY); void Square_End_XY (unsigned short WX, unsigned short HY); void Start_Circle_or_Ellipse (void); void Start_Circle_or_Ellipse_Fill (void); void Start_Left_Down_Curve (void); void Start_Left_Up_Curve (void); void Start_Right_Up_Curve (void); void Start_Right_Down_Curve (void); void Start_Left_Down_Curve_Fill (void); void Start_Left_Up_Curve_Fill (void); void Start_Right_Up_Curve_Fill (void); void Start_Right_Down_Curve_Fill (void); void Start_Square (void); void Start_Square_Fill (void); void Start_Circle_Square (void); void Start_Circle_Square_Fill (void); void Circle_Center_XY (unsigned short WX, unsigned short HY); void Ellipse_Center_XY (unsigned short WX, unsigned short HY); void Circle_Radius_R (unsigned short WX); void Ellipse_Radius_RxRy (unsigned short WX, unsigned short HY); void Circle_Square_Radius_RxRy (unsigned short WX, unsigned short HY); 

Misalnya, untuk menggambar segitiga yang diisi, kami menetapkan tiga poin: Triangle_Point1_XY, Triangle_Point2_XY, Triangle_Point2_XY dan menjalankan fungsi Start_Triangle_Fill.

Bekerja dengan DMA


Untuk kenyamanan, saya menulis fungsi saya dengan struktur sebagai parameter yang diteruskan:

 struct GFX_BTE_options { unsigned long layer_s0_addr; //     0 unsigned long layer_s1_addr; //     1 unsigned long layer_d_addr; //     unsigned short layer_s0_width; //    0 unsigned short layer_s1_width; //    1 unsigned short layer_d_width; //    unsigned short layer_s0_start_x; //     0 unsigned short layer_s0_start_y; //   Y  0 unsigned short layer_s1_start_x; //   X  1 unsigned short layer_s1_start_y; //   Y  1 unsigned short layer_d_start_x; //   X   unsigned short layer_d_start_y; //   Y   unsigned short window_size_x; //     unsigned short window_size_y; //     unsigned char rop_code; //  ROP unsigned char operation_code; //   DMA };       DMA: void GFX_BTE_operation (struct GFX_BTE_options options) { BTE_S0_Color_16bpp (); BTE_S0_Memory_Start_Address (options.layer_s0_addr); BTE_S0_Image_Width (options.layer_s0_width); BTE_S0_Window_Start_XY (options.layer_s0_start_x, options.layer_s0_start_y); BTE_S1_Color_16bpp (); BTE_S1_Memory_Start_Address (options.layer_s1_addr); BTE_S1_Image_Width (options.layer_s1_width); BTE_S1_Window_Start_XY (options.layer_s1_start_x, options.layer_s1_start_y); BTE_Destination_Color_16bpp (); BTE_Destination_Memory_Start_Address (options.layer_d_addr); BTE_Destination_Image_Width (options.layer_d_width); BTE_Destination_Window_Start_XY (options.layer_d_start_x, options.layer_d_start_y); BTE_Window_Size (options.window_size_x, options.window_size_y); BTE_ROP_Code (options.rop_code); BTE_Operation_Code (options.operation_code); BTE_Enable (); Check_BTE_Busy (); } 

Deskripsi kode operasi (KODE OPERASI):

0000: Menulis ke memori dengan ROP menggunakan MK.
0001: Baca memori tanpa ROP menggunakan MK.
0010: Menyalin blok memori ke arah maju menggunakan ROP.
0011: Menyalin blok memori ke arah sebaliknya menggunakan ROP.
0100: Menulis ke memori (dengan transparansi) tanpa ROP menggunakan MK.
0101: Menyalin (memindahkan) blok memori (dengan transparansi) ke arah depan tanpa ROP.
0110: Isi dengan pola menggunakan ROP.
0111: Isi templat dengan chromakey.
1000: Ekstensi Warna
1001: Warna yang ditingkatkan dengan transparansi
1010: Memajukan blok memori ke depan dengan alpha blending
1011: Menulis ke memori dengan alpha blending menggunakan MK.
1100: Mengisi area memori dengan warna solid.
1101: Dicadangkan
1110: Dicadangkan
1111: Dicadangkan

Deskripsi kode raster (ROP CODE):

0000b: 0 (Hitam)
0001b: ~ S0 ・ ~ S1 atau ~ (S0 + S1)
0010b: ~ S0 ・ S1
0011b: ~ S0
0100b: S0 ・ ~ S1
0101b: ~ S1
0110b: S0 ^ S1
0111b: ~ S0 + ~ S1 atau ~ (S0 ・ S1)
1000b: S0 ・ S1
1001b: ~ (S0 ^ S1)
1010b: S1
1011b: ~ S0 + S1
1100b: S0
1101b: S0 + ~ S1
1110b: S0 + S1
1111b: 1 (Putih)

S0 adalah lapisan nol, S1 adalah lapisan pertama. Interaksi di antara mereka terjadi menggunakan operasi aritmatika dan bit.

Output font sebaris


 void GFX_Show_String_TMODE (short x, short y, char *ptr, unsigned short charColor, unsigned short bkColor) { Foreground_color_65k (charColor); Background_color_65k (bkColor); CGROM_Select_Internal_CGROM (); Font_Select_12x24_24x24 (); Text_Mode (); Goto_Text_XY (x, y); LCD_CmdWrite (0x04); while (*ptr != '\0') { LCD_DataWrite (*ptr); Check_Mem_WR_FIFO_not_Full (); ++ptr; } Check_2D_Busy (); Graphic_Mode (); //back to graphic mode } 

Versi lengkap driver dapat ditemukan di github di tautan

Terima kasih sudah membaca!

Source: https://habr.com/ru/post/id483538/


All Articles