嵌入式 Linux LED 驱动开发实验
生活随笔
收集整理的這篇文章主要介紹了
嵌入式 Linux LED 驱动开发实验
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
41.1 Linux 下 LED 燈驅動原理
Linux 下的任何外設驅動,最終都是要配置相應的硬件寄存器。所以本章的 LED 燈驅動最 終也是對 I.MX6ULL 的 IO 口進行配置,與裸機實驗不同的是,在 Linux 下編寫驅動要符合 Linux 的驅動框架。 41.1.1 地址映射 在編寫驅動之前,我們需要先簡單了解一下 MMU 這個神器, MMU 全稱叫做 Memory Manage Unit,也就是內存管理單元。在老版本的 Linux 中要求處理器必須有 MMU,但是現在 Linux 內核已經支持無 MMU 的處理器了。 MMU 主要完成的功能如下: ①、完成虛擬空間到物理空間的映射。 ②、內存保護,設置存儲器的訪問權限,設置虛擬存儲空間的緩沖特性。 我們重點來看一下第①點,也就是虛擬空間到物理空間的映射,也叫做地址映射。首先了 解兩個地址概念:虛擬地址(VA,Virtual Address)、物理地址(PA, Physcical Address)。對于 32 位 的處理器來說,虛擬地址范圍是 2^32=4GB,我們的開發板上有 512MB 的 DDR3,這 512MB 的 內存就是物理內存,經過 MMU 可以將其映射到整個 4GB 的虛擬空間。 物理內存只有 512MB,虛擬內存有 4GB,那么肯定存在多個虛擬地址映射到同一個物理地 址上去,虛擬地址范圍比物理地址范圍大的問題處理器自會處理,這里我們不要去深究,因為 MMU 是很復雜的一個東西。 Linux 內核啟動的時候會初始化 MMU,設置好內存映射,設置好以后 CPU 訪問的都是虛 擬 地 址 。 比 如 I.MX6ULL 的 GPIO1_IO03 引 腳 的 復 用 寄 存 器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址為 0X020E0068。如果沒有開啟 MMU 的話 直接向 0X020E0068 這個寄存器地址寫入數據就可以配置 GPIO1_IO03 的復用功能。現在開啟 了 MMU,并且設置了內存映射,因此就不能直接向 0X020E0068 這個地址寫入數據了。我們必 須得到 0X020E0068 這個物理地址在 Linux 系統里面對應的虛擬地址,這里就涉及到了物理內 存和虛擬內存之間的轉換,需要用到兩個函數: ioremap 和 iounmap。 1、 ioremap 函數 ioremap 函 數 用 于 獲 取 指 定 物 理 地 址 空 間 對 應 的 虛 擬 地 址 空 間 , 定 義 在 arch/arm/include/asm/io.h 文件中,定義如下: 示例代碼 41.1.1.1 ioremap 函數 1 #define ioremap(cookie,size) __arm_ioremap((cookie), (size),MT_DEVICE) 2 void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,unsigned int mtype) 4 {5 return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0)); 6 } ioremap 是個宏,有兩個參數: cookie 和 size,真正起作用的是函數__arm_ioremap,此函 數有三個參數和一個返回值,這些參數和返回值的含義如下: phys_addr:要映射給的物理起始地址。 size:要映射的內存空間大小。 mtype: ioremap 的類型,可以選擇 MT_DEVICE、 MT_DEVICE_NONSHARED、 MT_DEVICE_CACHED 和 MT_DEVICE_WC, ioremap 函數選擇 MT_DEVICE。 返回值: __iomem 類型的指針,指向映射后的虛擬空間首地址。 假如我們要獲取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器對應 的虛擬地址,使用如下代碼即可: #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) static void __iomem* SW_MUX_GPIO1_IO03; SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); 宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址, SW_MUX_GPIO1_IO03 是映射后 的虛擬地址。對于 I.MX6ULL 來說一個寄存器是 4 字節(32 位)的,因此映射的內存長度為 4。 映射完成以后直接對 SW_MUX_GPIO1_IO03 進行讀寫操作即可。2、 iounmap 函數 卸載驅動的時候需要使用 iounmap 函數釋放掉 ioremap 函數所做的映射, iounmap 函數原 型如下: 示例代碼 41.1.1.2 iounmap 函數原型 void iounmap (volatile void __iomem *addr) iounmap 只有一個參數 addr,此參數就是要取消映射的虛擬地址空間首地址。假如我們現 在要取消掉 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代碼 即可: iounmap(SW_MUX_GPIO1_IO03);41.1.2 I/O 內存訪問函數 這里說的 I/O 是輸入/輸出的意思,并不是我們學習單片機的時候講的 GPIO 引腳。這里涉 及到兩個概念: I/O 端口和 I/O 內存。當外部寄存器或內存映射到 IO 空間時,稱為 I/O 端口。 當外部寄存器或內存映射到內存空間時,稱為 I/O 內存。但是對于 ARM 來說沒有 I/O 空間這個 概念,因此 ARM 體系下只有 I/O 內存(可以直接理解為內存)。使用 ioremap 函數將寄存器的物 理地址映射到虛擬地址以后,我們就可以直接通過指針訪問這些地址,但是 Linux 內核不建議 這么做,而是推薦使用一組操作函數來對映射后的內存進行讀寫操作。 1、讀操作函數 讀操作函數有如下幾個: 示例代碼 41.1.2.1 讀操作函數 1 u8 readb(const volatile void __iomem *addr) 2 u16 readw(const volatile void __iomem *addr) 3 u32 readl(const volatile void __iomem *addr) readb、 readw 和 readl 這三個函數分別對應 8bit、 16bit 和 32bit 讀操作,參數 addr 就是要 讀取寫內存地址,返回值就是讀取到的數據。 2、寫操作函數 寫操作函數有如下幾個: 示例代碼 41.1.2.2 寫操作函數 1 void writeb(u8 value, volatile void __iomem *addr) 2 void writew(u16 value, volatile void __iomem *addr) 3 void writel(u32 value, volatile void __iomem *addr) writeb、 writew 和 writel 這三個函數分別對應 8bit、 16bit 和 32bit 寫操作,參數 value 是要 寫入的數值, addr 是要寫入的地址。41.3 實驗程序編寫
本實驗對應的例程路徑為: 開發板光盤-> 2、 Linux 驅動例程-> 2_led。 示例代碼 41.3.1.1 led.c 驅動文件代碼 第 94~114 行, led_write 函數,實現對 LED 燈的開關操作,當應用程序調用 write 函數向 led 設備寫數據的時候此函數就會執行。首先通過函數 copy_from_user 獲取應用程序發送過來 的操作信息(打開還是關閉 LED),最后根據應用程序的操作信息來打開或關閉 LED 燈。 第 140~185 行,驅動入口函數 led_init,此函數實現了 LED 的初始化工作, 147~151 行通過 ioremap 函數獲取物理寄存器地址映射后的虛擬地址,得到寄存器對應的虛擬地址以后就可以 完成相關初始化工作了。比如是能 GPIO1 時鐘、設置 GPIO1_IO03 復用功能、配置 GPIO1_IO03 的屬性等等。最后,最重要的一步!使用 register_chrdev 函數注冊 led 這個字符設備。 第 192~202 行,驅動出口函數 led_exit,首先使用函數 iounmap 取消內存映射,最后使用函 數 unregister_chrdev 注銷 led 這個字符設備。 第 205~206 行,使用 module_init 和 module_exit 這兩個函數指定 led 設備驅動加載和卸載 函數。 第 207~208 行,添加 LICENSE 和作者信息。41.3.2 編寫測試 APP 編寫測試 APP, led 驅動加載成功以后手動創建/dev/led 節點,應用 APP 通過操作/dev/led 文件來完成對 LED 設備的控制。向/dev/led 文件寫 0 表示關閉 LED 燈,寫 1 表示打開 LED 燈。 新建 ledApp.c 文件41.4 運行測試
41.4.1 編譯驅動程序和測試 APP 1、編譯驅動程序 編寫 Makefile 文件,本章實驗的 Makefile 文件和第四十章實驗基本一樣,只是將 obj-m 變 量的值改為 led.o 輸入如下命令編譯出驅動模塊文件: make -j32 編譯成功以后就會生成一個名為“ led.ko”的驅動模塊文件。 2、編譯測試 APP 輸入如下命令編譯測試 ledApp.c 這個測試程序: arm-linux-gnueabihf-gcc ledApp.c -o ledApp41.4.2 運行測試 將上一小節編譯出來的 led.ko和 ledApp這兩個文件拷貝到 rootfs/lib/modules/4.1.15 目錄中, 重啟開發板,進入到目錄 lib/modules/4.1.15 中,輸入如下命令加載 led.ko 驅動模塊: depmod //第一次加載驅動的時候需要運行此命令 modprobe led.ko //加載驅動 驅動加載成功以后創建“ /dev/led”設備節點,命令如下: mknod /dev/led c 200 0 驅動節點創建成功以后就可以使用 ledApp 軟件來測試驅動是否工作正常,輸入如下命令打 開 LED 燈: ./ledApp /dev/led 1 //打開 LED 燈 在輸入如下命令關閉 LED 燈: ./ledApp /dev/led 0 //關閉 LED 燈 如果要卸載驅動的話輸入如下命令即可: rmmod led.ko參考文獻
【正點原子】I.MX6U嵌入式Linux驅動開發指南V1.3.pdf
總結
以上是生活随笔為你收集整理的嵌入式 Linux LED 驱动开发实验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二次元异次元发卡平台系统 荔枝发卡V3.
- 下一篇: 【Java】:基础入门知识