S3C2440 LCD驅動(FrameBuffer)實例開發<二>(轉)

来源:http://www.cnblogs.com/chd-zhangbo/archive/2016/02/03/5180252.html
-Advertisement-
Play Games

開發板自帶的LCD驅動是基於platform匯流排寫的,所以如果要使其它的LCD能夠在自己的開發板上跑起來,那麼就先瞭解platform驅動的架構,下麵簡單記錄下自己看platform驅動時體會,簡單的說platform是一種虛擬匯流排,那麼它也是一條匯流排,所以它分為3個部分,platform_bus,


開發板自帶的LCD驅動是基於platform匯流排寫的,所以如果要使其它的LCD能夠在自己的開發板上跑起來,那麼就先瞭解platform驅動的架構,下麵簡單記錄下自己看platform驅動時體會,簡單的說platform是一種虛擬匯流排,那麼它也是一條匯流排,所以它分為3個部分,platform_bus,platform_device,platform_driver。在platform_device向platform_bus註冊設備,platform_driver向platform_bus註冊驅動,註冊後在platform_bus中會有一條device鏈表和driver鏈表,platform_bus中match函數將匹配兩者的名字,如果相同那就把驅動和設備進行綁定。Linux platform driver機制和傳統的device driver機制(通過driver_register進行註冊)相比,一個明顯的優勢在於platform機制將設備本身的資源註冊進內核,由內核統一管理,在驅動中使用這些資源時通過platform device提供的標準結構進行申請並使用。這樣提高了驅動和資源的獨立性,並且具有較好的可移植性和安全性(這些標準介面是安全的)。下麵舉一個簡單platform驅動的例子來分析platform_device和platform_driver是怎麼聯繫,platform_driver是怎麼使用platform_device提供硬體信息的。

led_dev.c

 

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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/timer.h> #include <linux/init.h> #include <linux/serial_core.h> #include <linux/platform_device.h>     /* 分配/設置/註冊一個platform_device */   static struct resource led_resource[] = {     [0] = {         .start = 0x56000010,             /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */         .end   = 0x56000010 + 8 - 1,         .flags = IORESOURCE_MEM,         /* 標識led控制器io埠*/     },     [1] = {         .start = 5,                      /* LED1 */         .end   = 5,         .flags = IORESOURCE_IRQ,         /* 標識LED中斷 */     }   }; /* 必須提供realease函數,可以不實現 */ static void led_release(struct device * dev) { }     static struct platform_device led_dev = {     .name         = "myled",   /* 設備名 */     .id       = -1,            /* 一般設為-1,表示同樣名字的設備只有一個 */     .num_resources    = ARRAY_SIZE(led_resource), /*  資源數量*/     .resource     = led_resource,     .dev = {         .release = led_release, /* 引用上面定義的資源 */     }, };   static int led_dev_init(void) {     platform_device_register(&led_dev);/* 註冊平臺設備 */     return 0; }   static void led_dev_exit(void) {     platform_device_unregister(&led_dev);/*  註銷平臺設備*/ }   module_init(led_dev_init); module_exit(led_dev_exit);   MODULE_LICENSE("GPL");

 

Led_drv.c

 

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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include <linux/fs.h> #include <linux/interrupt.h #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/io.h>   static int major; static struct class *cls; static volatile unsigned long *gpio_con; static volatile unsigned long *gpio_dat; static int pin;   static int led_open(struct inode *inode, struct file *file) {     //printk("first_drv_open\n");     /* 配置為輸出 */     *gpio_con &= ~(0x3<<(pin*2));     *gpio_con |= (0x1<<(pin*2));     return 0;   }   static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) {     int val;       //printk("first_drv_write\n");       copy_from_user(&val, buf, count); //    copy_to_user();       if (val == 1)     {         // 點燈         *gpio_dat &= ~(1<<pin);     }     else     {         // 滅燈         *gpio_dat |= (1<<pin);     }           return 0; } static struct file_operations led_fops = {     .owner  =   THIS_MODULE,    /* 這是一個巨集,推向編譯模塊時自動創建的__this_module變數 */     .open   =   led_open,         .write  =   led_write,     }; static int led_probe(struct platform_device *pdev) {     struct resource     *res;     /* 分配/設置/註冊一個platform_driver */     /* 根據platform_device的資源進行ioremap */     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);     gpio_con = ioremap(res->start, res->end - res->start + 1);     gpio_dat = gpio_con + 1;       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);     pin = res->start;       /* 註冊字元設備驅動程式 */     printk("led_probe, found led\n");       /* 註冊設備,生成設備文件*/     major = register_chrdev(0, "myled", &led_fops);     cls = class_create(THIS_MODULE, "myled");     class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */     return 0; } static int led_remove(struct platform_device *pdev) {     /* 卸載字元設備驅動程式 */     /* iounmap */     printk("led_remove, remove led\n");     class_device_destroy(cls, MKDEV(major, 0));     class_destroy(cls);     unregister_chrdev(major, "myled");     iounmap(gpio_con);     return 0; } struct platform_driver led_drv = {     .probe      = led_probe,     .remove     = led_remove,     .driver     = {         .name   = "myled",     } }; static int led_drv_init(void) {     platform_driver_register(&led_drv);  /* 註冊平臺驅動 */     return 0; } static void led_drv_exit(void) {     platform_driver_unregister(&led_drv); /* 註銷平臺驅動 */ } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL");

 

這就是一個簡單的platform驅動,這兩個文件我們完全可以寫道一個文件中去實現,現在看下由一個文件如何實現這個驅動:

 

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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h>   static struct class *seconddrv_class; static struct class_device  *seconddrv_class_dev;   volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; static int second_drv_open(struct inode *inode, struct file *file) {     /*      * K1,K2,K3,K4對應GPF1、GPF4、GPF2、GPF0      */       /* 配置GPF1、GPF4、GPF2、GPF0為輸入引腳 */     *gpfcon &= ~((0x3<<(1*2)) | (0x3<<(4*2)) | (0x3<<(2*2)) | (0x3<<(0*2)));     return 0; } ssize_t second_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {     /* 返回4個引腳的電平 */     unsigned char key_vals[4];     int regval;     if (size != sizeof(key_vals))         return -EINVAL;       regval = *gpfdat;     key_vals[0] = (regval & (1<<1)) ? 1 : 0;     key_vals[1] = (regval & (1<<4)) ? 1 : 0;     key_vals[2] = (regval & (1<<2)) ? 1 : 0;     key_vals[3] = (regval & (1<<0)) ? 1 : 0;       copy_to_user(buf, key_vals, sizeof(key_vals));           return sizeof(key_vals); } static struct file_operations sencod_drv_fops = {     .owner  =   THIS_MODULE,    /* 這是一個巨集,推向編譯模塊時自動創建的__this_module變數 */     .open   =   second_drv_open,         .read   =   second_drv_read,       }; int major; static int second_drv_init(void) {     major = register_chrdev(0, "second_drv", &sencod_drv_fops);       seconddrv_class = class_create(THIS_MODULE, "second_drv");       seconddrv_class_dev = class_device_create(seconddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */       gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);     gpfdat = gpfcon + 1;       return 0; } static void second_drv_exit(void) {     unregister_chrdev(major, "second_drv");     class_device_unregister(seconddrv_class_dev);     class_destroy(seconddrv_class);     iounmap(gpfcon);     return 0; } module_init(second_drv_init); module_exit(second_drv_exit); MODULE_LICENSE("GPL");

 

     由此可見,如果由platform驅動去實現led驅動將會多出很多東西,而這些多出來的就是我們如何把一個驅動程式融合到platform裡面,那既然用platform驅動要多寫那麼多東西那問什麼還要寫基於platform的驅動呢?我的理解是基於以下幾個原因:如果一個設備掛在匯流排上,其結果是配套的sysfs結點,設備電源管理都成為可能;隔離了BSP和驅動,在BSP中定義platform設備和設備使用的資源,設備的具體配置信息,而在驅動中,只要通過API去獲取資源和數據,做到了板相關代碼與驅動代碼的分離,使得驅動具有更好的可移植性和更好的擴展性和跨平臺性;假如我們要實現一個LCD驅動,那麼我們只需修改BSP相關的代碼,platform基本上不需修改,避免重覆著輪子的可能性;基於platformplatform機制將設備本身的資源註冊進內核,由內核統一管理。

    上面platform的例子還沒有完全按照linux platform的標準去寫,因為閱讀代碼可知linux platform驅動把platform_device相關的代碼放在一塊,然後統一進行註冊!

下麵記錄下如何把device添加到板級文件中(最開始還是建議寫成兩個文件,分別編譯成模塊載入,因為如果是放到板級文件中那麼需要重新編譯內核,這個將在編譯上浪費很多時間)

步驟:

1. 在/arch/arm/plat-s3c24xx/devs.c中定義led 相關的設備及資源,代碼如下:

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 static struct resource led_resource[] = {     [0] = {         .start = 0x56000010,             /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */         .end   = 0x56000010 + 8 - 1,         .flags = IORESOURCE_MEM,         /* 標識led控制器io埠*/     },     [1] = {         .start = 5,                      /* LED1 */         .end   = 5,         .flags = IORESOURCE_IRQ,         /* 標識LED中斷 */     }   }; EXPORT_SYMBOL(s3c_device_lcd);

 

請註意最後一行是導出led平臺設備,會在mach-smdk2440.c中添加到平臺設備列表中。

2. 如果還需定義led的其它信息,那麼在/arch/arm/plat-s3c2410/include/mach/fb.h 為led平臺設備定義一個s3c2410fb_mach_info結果體。led很簡單所以沒有必要再去定義,但是後面講的LCD的平臺設備需要定義!因為僅僅通過resource,驅動還無法操作LCD。

3. 不要實現,只是記錄下系統還會做什麼事! 在mach-smdk2440.c中將會調用platform_add_devices(),註冊平臺設備。

好了對platform有了一定瞭解後,那麼看看如果將LCD驅動寫成linux設備模型的platform驅動,同樣給出不使用platform架構是一個完整的字元設備驅動的lcd驅動,在給出一個符合platform模型的lcd驅動,通過這兩個文件的對比,就可以知道在符合platform模型的lcd驅動中可以找到LCD驅動的全部信息,也會發現基本上所有plat_form驅動出了驅動本身的東西外,框架性的東西基本上一樣的,所以如果理解了一兩個platform驅動,那麼以後寫platform驅動或看platform代碼跟不以flatform寫出的代碼是沒有什麼區別的!代碼如下:

 


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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • sqlite不支持類似sqlserver中的select into 語法 在SQL Server中,我們要將一個表中的數據複製到一個新表中,可以這樣寫: SELECT * INTO newtable FROM oldtable SQLite不支持以上語法,替代的語句是這樣: CREATE TABLE
  • CREATE OR REPLACE PACKAGE BODY ssl AS PROCEDURE insert_ssl(s In ssl_array) AS v_month varchar2(20); v_sql varchar2(5000); v_result number; v_status nu
  • CREATE OR REPLACE PACKAGE BODY BASE_INFO AS PROCEDURE insert_lapc(lapc In lapc_array) AS v_max number; v_result number; v_status number; BEGIN delete
  • 1.安裝x window 使用apt get 安裝 xorg 如果提示以下內容,就說明需要update下源列表,使用 即可 2.安裝LXDE 3.安裝tight vnc 4.啟動一下vnc然後再退出,為了讓它生成配置文件 中間過程可能會要求你輸入一個 8位的 VNC密碼,輸密碼的過程中要記住這個密碼
  • 一般Linux系統,有兩個配置文件可以設置PATH變數,一:.bashrc 二:.bash_profile; 還有一種方法可以臨時設置PATH變數(三) 一: 1、編輯.bashrc,添加 export PATH=$PATH:/home/myname/mydirectory 這是將路徑跟在已有路徑後
  • 一.概述 1.環境:我這裡是2台linux機器(host1和host2),發行版是kali2.0,內核版本是4.3。每台機器都安裝Docker、OpenvSwitch(ovs)。 2.host1和host2分別啟動1個ubuntu的docker容器。 3.網路結構: 2.1:host1的eth0:1
  • 左上角幾個管腳分別是J601和J801 J601: 第一排兩個管腳:可以使用跳線帽短接,作用是: 連接:當電源接通後就自動啟動 斷開:需要按下最上面的中間那個小按鍵3~4秒才能啟動 第二排兩個管腳: 連接:可以燒寫第一階段的啟動載入程式到emmc,必須先把usb otg連接到PC機上。 斷開:從em...
  • 1. 背景知識 在多媒體的推動下,彩色LCD越來越多地應用到嵌入式系統中,PDA和手機等大多都採用LCD作為顯示器材,因此學習LCD的應用很有實際意義! LCD工作的硬體需求:要使一塊LCD正常的顯示文字或圖像,不僅需要LCD驅動器,而且還需要相應的LCD控制器。在通常情況下,生產廠商把LCD驅動器
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...