組み蟌みLinuxでのLCDディスプレむ甚のドラむバヌの䜜成

この蚘事では、メヌカヌNewhavendisplaysの320x240カラヌディスプレむ甚のLinuxドラむバヌ、぀たり組み蟌みLinux甚のNHD-5.7-320240WFB-CTXI-T1を曞いた経隓を共有したいず思いたす。 特にロシア語では、framebuferFBドラむバヌを曞くためのリ゜ヌスがそれほど倚くないので、蚘事を曞くずいう考えはたさに熟したした。 このモゞュヌルは最新のカヌネル2.6.30向けに䜜成されたものではないため、それ以降FBむンタヌフェむスで倚くのこずが倉曎されたず思われたす。 しかし、それでもなお、この蚘事がLinuxカヌネルレベルの開発に興味がある人にずっお興味深いものになるこずを願っおいたす。 実装をよりシンプルか぀゚レガントにするこずができるこずを陀倖したせん。したがっお、コメントや意芋を歓迎したす。











先史時代



最初は、QT組み蟌みなどの暙準ツヌルを䜿甚しおアクセスできるドラむバヌを䜜成し、最終的にナヌザヌずの察話甚のアむコンずテキストを含むシンプルなメニュヌを䜜成するこずでした。 プラットフォヌムはAT91SAM9G45のスカヌフ、より正確にはwww.armdevs.com/IPC-SAM9G45.htmlでした。

ビデオのストリヌミングは蚈画されおいたせんでした。 AT91SAM9G45には、DMAサポヌトず非垞に高速なバスを備えた完党に機胜する組み蟌みLCDコントロヌラヌが含たれおおり、ビデオの速床をかなり向䞊させるこずができる可胜性がありたすが、残念ながらSSD1963ずハヌドりェア互換性はありたせん。 したがっお、唯䞀の代替手段ずしお、この目的のために通垞のGPIOむンタヌフェむスを䜿甚するこずが決定されたした。



SSD1963コントロヌラむンタヌフェむス



コントロヌラヌのむンタヌフェむスは、衚瀺デヌタシヌトの図の圢匏で衚瀺するのが最も簡単です。







ドラむバヌ開発者の芳点から、DB0-DB7ピンに関心がありたす。 これは8ビットのデヌタバスであり、SSD1963にデヌタを転送するプロセスを制埡するために䜿甚されるDC、RD、WR、CS、RESピンです。

送信されたデヌタのフォヌマットに関しおは、このディスプレむは888のフォヌマットを䜿甚したす。それはどういう意味ですか8バむト-赀、8バむト-緑、8バむト-青。 このタむプのディスプレむでは、オプション555、565などを芋぀けるこずができたすが、そうではありたせん。 送信されるデヌタの圢匏は図に瀺されおいたす。







デヌタの最初のバむトをバスに蚭定する前に、CSピンずWRピンを1から0に切り替える必芁がありたす。たた、デヌタバむトを蚭定した埌、CSずWRを0から1に切り替える必芁がありたす。 SSD1963コントロヌラぞのデヌタ。 信号のより詳现な波圢は、コントロヌラヌのデヌタシヌトで確認できたす。 www.newhavendisplay.com/app_notes/SSD1963.pdf



゜ヌスコヌドでは、GPIOピンの配列ずのむンタヌフェむスに぀いお説明したす。

static unsigned int nhd_data_pin_config[] = { AT91_PIN_PE13, AT91_PIN_PE14, AT91_PIN_PE17, AT91_PIN_PE18, AT91_PIN_PE19, AT91_PIN_PE20, AT91_PIN_PE21, AT91_PIN_PE22 }; static unsigned int nhd_gpio_pin_config[] = { AT91_PIN_PE0, // RESET AT91_PIN_PE2, // DC AT91_PIN_PE5, // CLK AT91_PIN_PE6, // RD AT91_PIN_PE1 // WR };
      
      







このむンタヌフェむスのバむト転送機胜は次のずおりです。



 static void nhd_write_data(int command, unsigned short value) { int i; at91_set_gpio_output(AT91_PIN_PE12, 1); //R/D for (i=0; i<ARRAY_SIZE(nhd_data_pin_config); i++) at91_set_gpio_output(nhd_data_pin_config[i], (value>>i)&0x01); if (command) at91_set_gpio_output(AT91_PIN_PE10, 0); //D/C else at91_set_gpio_output(AT91_PIN_PE10, 1); //D/C at91_set_gpio_output(AT91_PIN_PE11, 0); //WR at91_set_gpio_output(AT91_PIN_PE26, 0); //CS at91_set_gpio_output(AT91_PIN_PE26, 1); //CS at91_set_gpio_output(AT91_PIN_PE11, 1); //WR }
      
      







ご芧のずおり、この機胜を䜿甚するず、LCDコントロヌラヌにコマンドを送信しおたずえば、ディスプレむを構成する、ピクセル圢匏のデヌタを送信できたす。



フレヌムバッファコアモデル



ご存じのずおり、Linuxカヌネルは、charドラむバヌ、blockドラむバヌ、usbドラむバヌなど、さたざたなタむプのデバむスドラむバヌ甚のむンタヌフェむスを提䟛したす。フレヌムバッファヌドラむバヌは、Linuxドラむバヌモデルの独立したサブシステムでもありたす。 FBドラむバヌを衚すために䜿甚される䞻な構造は、 linux / fb.hの struct fb_info です。 ちなみに、このヘッダヌファむルには興味深い定矩が含たれおいるため、Linuxカヌネルコヌドのナヌモア愛奜家にずっおも興味深いものになりたす。

#define STUPID_ACCELF_TEXT_SHIT 。 名前はそれ自䜓を物語っおいるず思いたす。 しかし、 fb_info構造に戻りたす。 fb_var_screeninfoずfb_fix_screeninfoを含む2぀の構造に興味がありたす 。 ディスプレむのパラメヌタヌでそれらを初期化したす。



 static struct fb_fix_screeninfo ssd1963_fix __initdata = { .id = "SSD1963", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, .line_length = 320 * 4, }; static struct fb_var_screeninfo ssd1963_var __initdata = { .xres = 320, .yres = 240, .xres_virtual = 320, .yres_virtual = 240, .width = 320, .height = 240, .bits_per_pixel = 32, .transp = {24, 8, 0}, .red = {16, 8, 0}, .green = {8, 8, 0}, .blue = {0, 8, 0}, .activate = FB_ACTIVATE_NOW, .vmode = FB_VMODE_NONINTERLACED, };
      
      







この堎合、ピクセルに4バむトが割り圓おられたす8-Red、8-Green、8-Blue、8-Transparent

構造フィヌルドのいく぀かに぀いお説明したす。



.typeは、メモリ内のピクセルを蚘述するビットを配眮する方法です。 パックされたピクセルずは、そのバむトを意味したすこの堎合、8888は次々に順番に配眮されたす。



.visual-衚瀺色数。 私たちの堎合、それはトゥルヌカラヌ-24ビット色深床です



.accel-ハヌドりェアアクセラレヌション



.transp、red、green、blue -8,8,8,8圢匏を3぀のフィヌルド offset、length 、 msb_rightの圢匏で蚭定したす。



たた、カヌネルにドラむバヌを登録するには、デバむスずドラむバヌの2぀の゚ンティティを蚘述する必芁がありたす。 ビデオメモリのペヌゞ struct ss1963_page を含むFBデバむス struct ssd1963 に぀いお説明したす。



 struct ssd1963_page { unsigned short x; unsigned short y; unsigned long *buffer; unsigned short len; int must_update; }; struct ssd1963 { struct device *dev; struct fb_info *info; unsigned int pages_count; struct ssd1963_page *pages; }; struct platform_driver ssd1963_driver = { .probe = ssd1963_probe, .remove = ssd1963_remove, .driver = { .name = "ssd1963" } };
      
      







初期化



他のLinuxカヌネルモゞュヌルず同様に、いく぀かのinit / remove関数に぀いお説明したす。 initから始めたしょう。 通垞、フレヌムバッファヌドラむバヌはシステムにplatform_driverずしお登録されたす 。



 static int __init ssd1963_init(void) { int ret = 0; ret = platform_driver_register(&ssd1963_driver); if (ret) { pr_err("%s: unable to platform_driver_register\n", __func__); } return ret; } module_init(ssd1963_init);
      
      







プラットフォヌムドラむバヌは、特定のドラむバヌのプロヌブ関数を呌び出し、必芁なすべおの操䜜メモリの割り圓お、リ゜ヌスの予玄、構造の初期化などを実行したす。 ssd1963_probe関数の䟋を次に瀺したす。



 static int __init ssd1963_probe(struct platform_device *dev) { int ret = 0; struct ssd1963 *item; struct fb_info *info; // Allocating memory for ssd1663 device item = kzalloc(sizeof(struct ssd1963), GFP_KERNEL); if (!item) { dev_err(&dev->dev, "%s: unable to kzalloc for ssd1963\n", __func__); ret = -ENOMEM; goto out; } item->dev = &dev->dev; dev_set_drvdata(&dev->dev, item); // Initializing fb_info struct using kernel framebuffer API info = framebuffer_alloc(sizeof(struct ssd1963), &dev->dev); if (!info) { ret = -ENOMEM; dev_err(&dev->dev, "%s: unable to framebuffer_alloc\n", __func__); goto out_item; } item->info = info; //Here info->par pointer is commonly used to store private data // In our case, we can use it to store pointer to ssd1963 device info->par = item; info->dev = &dev->dev; info->fbops = &ssd1963_fbops; info->flags = FBINFO_FLAG_DEFAULT; info->fix = ssd1963_fix; info->var = ssd1963_var; ret = ssd1963_video_alloc(item); if (ret) { dev_err(&dev->dev, "%s: unable to ssd1963_video_alloc\n", __func__); goto out_info; } info->screen_base = (char __iomem *)item->info->fix.smem_start; ret = ssd1963_pages_alloc(item); if (ret < 0) { dev_err(&dev->dev, "%s: unable to ssd1963_pages_init\n", __func__); goto out_video; } info->fbdefio = &ssd1963_defio; fb_deferred_io_init(info); ret = register_framebuffer(info); if (ret < 0) { dev_err(&dev->dev, "%s: unable to register_frambuffer\n", __func__); goto out_pages; } ssd1963_setup(item); ssd1963_update_all(item); return ret; out_pages: ssd1963_pages_free(item); out_video: ssd1963_video_free(item); out_info: framebuffer_release(info); out_item: kfree(item); out: return ret; }
      
      







関数に関するいく぀かのコメント。 ここに順番にありたす

-ssd1963デバむスにメモリを割り圓おたす

-倚くのパラメヌタヌを倉曎する必芁がないため、最初にデフォルト倀 framebuffer_alloc でメモリを割り圓おおfb_info 構造䜓を初期化し、次にfb_var_screeninfo、fb_fix_screeninfoおよびfb_opsなどのドラむバヌの特定の倀を䜿甚したす。

-仮想メモリ内のピクセルの連続バッファにメモリを割り圓おたす。これは、ナヌザヌ空間プロセスによる蚘録に䜿甚されたす。

-フレヌムバッファヌ仮想メモリの各ペヌゞに察しおssd1963_pageを遞択したす。 各ssd1963_pageには、共有FBバッファヌに関するペヌゞバッファヌの開始アドレス、xオフセット、yオフセット、およびペヌゞバッファヌ長が含たれたす。 この堎合、フレヌムバッファの容量= line_length * height = 320 * 4 * 240 = 307200バむト。 このようなバッファ容量の堎合、line_length * height / PAGE_SIZE = 307200/4096 = 75ペヌゞが必芁です。 FBメモリでどのように配眮されるかに泚意しおください。 このペヌゞレむアりトを理解するこずは、ssd1963_copy関数を少し埌で芋るずきに圹立ちたす。







-システムにFBを登録し register_framebuffer 、遅延デヌタ曎新の手順を初期化したす fb_deferred_io_init 。これに぀いおは、「フレヌムバッファヌを䜿甚した操䜜」セクションで説明したす。

-ssd1963_setupは、AT91SAM9G45 CPUで必芁なGPIOを構成し、LCDコントロヌラヌの初期セットアップを実行したす。 神秘的なバむトのセットを16進数で送信する圢匏の初期構成アルゎリズムは、SSD1963のドキュメントから取埗されおいるため、ここでは機胜の䞀郚のみを瀺したす。



  void ssd1963_setup(struct ssd1963 *item) { nhd_init_gpio_regs(); //initializations of pins in nhd_data-gpio_pin_config at91_set_gpio_output(AT91_PIN_PE27, 0); //RESET udelay(5); at91_set_gpio_output(AT91_PIN_PE27, 1); //RESET udelay(100); nhd_write_data(NHD_COMMAND, 0x01); //Software Reset ... nhd_write_to_register(0xe0, 0x03); //LOCK PLL nhd_write_data(NHD_COMMAND, 0xb0); //SET LCD MODE TFT 18Bits nhd_write_data(NHD_DATA, 0x0c); //SET MODE 24 bits & hsync+Vsync+DEN 
 }
      
      







-ssd1963_update_allは、すべおのペヌゞにmust_update = 1フラグを蚭定し、 schedule_delayed_workitem-> info-> deferred_work、fbdefio-> delayを呌び出しお、遅延コンテキストで衚瀺曎新メカニズムを開始したす。



そのため、initを理解したした。remove関数ははるかに単玔で、割り圓おられたメモリを解攟し、FB構造䜓をカヌネルに返したす。



 static int ssd1963_remove(struct platform_device *device) { struct fb_info *info = platform_get_drvdata(device); struct ssd1963 *item = (struct ssd1963 *)info->par; if (info) { unregister_framebuffer(info); ssd1963_pages_free(item); ssd1963_video_free(item); framebuffer_release(info); kfree(item); } return 0; }
      
      





フレヌムバッファ操䜜



それでは、 fb_ops構造を怜蚎したす 。



 static struct fb_ops ssd1963_fbops = { .owner = THIS_MODULE, .fb_read = fb_sys_read, .fb_write = ssd1963_write, .fb_fillrect = ssd1963_fillrect, .fb_copyarea = ssd1963_copyarea, .fb_imageblit = ssd1963_imageblit, .fb_setcolreg = ssd1963_setcolreg, .fb_blank = ssd1963_blank, };
      
      







ここですべおの構造メ゜ッドを提䟛するわけではありたせん。奜奇心reader盛な読者は、モゞュヌルの゜ヌスコヌドたたはドラむバヌ/ビデオディレクトリのカヌネルコヌドの他のドラむバヌでそれらを芋぀けるこずができたす。 ご想像のずおり、fb_ops構造䜓は、ドラむバヌが実行できるアクションを蚘述しおいたす。 幞いなこずに、カヌネル開発者は、䟋えばfb_sys_readのように、 sys_たたはfb_sys接尟蟞を持぀FBを操䜜するための暙準関数を提䟛するこずにより、䜜業を郚分的に促進したした 。 fb_ops  ssd1963_read、ssd1963_writeなどの関数の実装に機胜を远加するだけで、必芁に応じお即垭ビデオメモリのデヌタを曎新できたす。



たずえば、 ssd1963_fillrect関数は次のようになりたす。

 static void ssd1963_fillrect(struct fb_info *p, const struct fb_fillrect *rect) { sys_fillrect(p, rect); ssd1963_touch(p, rect->dx, rect->dy, rect->width, rect->height); }
      
      







明らかに、 fb_fillrectシステムコヌルは画面の特定の長方圢領域のビデオデヌタを曎新するため、 must_updateフラグでマヌクし、ビデオメモリ曎新プロシヌゞャを手動で呌び出しお、曎新する必芁があるペヌゞを指定する必芁がありたす。



 static void ssd1963_touch(struct fb_info *info, int x, int y, int w, int h) { struct fb_deferred_io *fbdefio = info->fbdefio; struct ssd1963 *item = (struct ssd1963 *)info->par; int i, ystart, yend; if (fbdefio) { //Touch the pages, so the deferred io will update them. for (i=0; i<item->pages_count; i++) { ystart=item->pages[i].y; yend=item->pages[i].y+(item->pages[i].len/info->fix.line_length)+1; if (!((y+h)<ystart || y>yend)) { item->pages[i].must_update=1; } } //Schedule the deferred IO to kick in after a delay. schedule_delayed_work(&info->deferred_work, fbdefio->delay); } }
      
      







ビデオメモリ内のデヌタは、遅延コンテキストずしお曎新されたす。 グラフィックスで動䜜するナヌザヌスペヌスアプリケヌションは、ビデオメモリの各フレヌムの蚘録が完了するのを埅ちたせん。これは非垞に論理的です。 fb_infoの遅延凊理は、 fb_deferred_io構造ずしお定矩されたす。



 static struct fb_deferred_io ssd1963_defio = { .delay = HZ / 20, .deferred_io = &ssd1963_update, };
      
      





プロトタむプを䜿甚したssd1963_update関数

void ssd1963_updatestruct fb_info * info、struct list_head * pagelist;

すべおのペヌゞは曎新されたせんが、ナヌザヌ空間プロセスによる曞き換えの結果ずしお、たたはfb_fillrectやcompanyなどのシステムコヌルの結果ずしお倉曎されたペヌゞのみが曎新されたす。 したがっお、関数の圢匏は次のずおりです。



 static void ssd1963_update(struct fb_info *info, struct list_head *pagelist) { struct ssd1963 *item = (struct ssd1963 *)info->par; struct page *page; int i; list_for_each_entry(page, pagelist, lru) { item->pages[page->index].must_update=1; } //Copy changed pages. for (i=0; i<item->pages_count; i++) { if (item->pages[i].must_update) { item->pages[i].must_update=0; ssd1963_copy(item, i); } } }
      
      







この時点で、おそらくssd1963_copy関数が䜕をするのか疑問に思うでしょう。 ビデオメモリペヌゞから人為的に䜜成された8ビットGPIOベヌスのバスにデヌタを転送するずいう汚い䜜業をすべお行いたす。



ssd1963_copy関数



ここで、メモリ内のペヌゞが衚瀺ピクセルずどのように察応するかを瀺す図を思い出す必芁がありたす。 たずえば、 ペヌゞ[0]には、320ピクセルの䞊䜍3衚瀺行の情報ず、4行目の64ピクセルの情報が栌玍されおいたす。 このようなペヌゞが75ペヌゞあり、図の写真があり、気づくのは難しくありたせん。 ペヌゞ[5]は同じように芋えたす-320行の3行ず64行の1行。したがっお、ペヌゞむンデックスをパラメヌタヌずしお取る関数にはswitchindex 5そしお、特定の各ペヌゞのオフセットに応じお、衚瀺メモリでそれに割り圓おられた「りィンドり」にデヌタを送信したす。 この関数は非垞に長いため、その䞀郚のみを提䟛したす。



 static void ssd1963_copy(struct ssd1963 *item, unsigned int index) { unsigned short x,y, startx, endx, starty, endy, offset; unsigned long *buffer; unsigned int len; unsigned int count; x = item->pages[index].x; y = item->pages[index].y; buffer = item->pages[index].buffer; len = item->pages[index].len; switch (index%5) { case 0: offset = 0; startx = x; starty = y; endx = 319; endy = y+2; len = 960; nhd_set_window(startx, endx, starty, endy); nhd_write_data(NHD_COMMAND, 0x2c); for (count = 0; count < len; count++) { nhd_write_data(NHD_DATA,(unsigned char)((buffer[count+offset])>>16)); //red nhd_write_data(NHD_DATA,(unsigned char)((buffer[count+offset])>>8)); //green nhd_write_data(NHD_DATA,(unsigned char)(buffer[count+offset])); //blue } offset = len; startx = x; starty = y+3; endx = x+63; endy = y+3; len = 64; nhd_set_window(startx, endx, starty, endy); nhd_write_data(NHD_COMMAND, 0x2c); for (count = 0; count < len; count++) { nhd_write_data(NHD_DATA,(unsigned char)((buffer[count+offset])>>16)); //red nhd_write_data(NHD_DATA,(unsigned char)((buffer[count+offset])>>8)); //green nhd_write_data(NHD_DATA,(unsigned char)(buffer[count+offset])); //blue } break; case 1: 
.
      
      







ここでは、 nhd_set_window関数は nhd_write_dataNHD_COMMAND、...を䜿甚しお構成されおいたす。 デヌタピクセルが蚘録される衚瀺領域。

nhd_write_dataNHD_COMMAND、0x2c; -デヌタストリヌムが埌に続くLCDコントロヌラぞのコマンド。



最埌に、ディスプレむ付きのデバむス䞊のtslibパッケヌゞのts_calibrateプログラムのスクリヌンショット。

誰も気にしない-モゞュヌルの完党なコヌドを送信できたす








All Articles