日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux LED驱动开发实验(直接操作寄存器 -- 实际开发很少这样做)

發(fā)布時(shí)間:2023/12/10 linux 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux LED驱动开发实验(直接操作寄存器 -- 实际开发很少这样做) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • Linux 下LED 燈驅(qū)動(dòng)原理
    • 地址映射(ioremap映射、iounmap釋放)
    • I/O 內(nèi)存訪問(wèn)函數(shù)
  • 硬件原理圖分析
  • 實(shí)驗(yàn)程序編寫(xiě)
    • LED 燈驅(qū)動(dòng)程序編寫(xiě)
    • APP測(cè)試程序編寫(xiě)
  • 運(yùn)行測(cè)試
    • 編譯驅(qū)動(dòng)程序和測(cè)試APP
    • 拷貝led.ko 和ledApp到指定目錄
    • 加載led.ko 驅(qū)動(dòng)模塊到內(nèi)核
    • 創(chuàng)建應(yīng)用層“/dev/led”設(shè)備節(jié)點(diǎn)
    • 運(yùn)行測(cè)試
  • 其他:網(wǎng)絡(luò)問(wèn)題解決方法

上一章我們?cè)敿?xì)的講解了字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)步驟,并且用一個(gè)虛擬的chrdevbase 設(shè)備為例完成了第一個(gè)字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)。本章我們就開(kāi)始編寫(xiě)第一個(gè)真正的Linux 字符設(shè)備驅(qū)動(dòng)。在I.MX6U-ALPHA 開(kāi)發(fā)板上有一個(gè)LED 燈,我們?cè)诼銠C(jī)篇中已經(jīng)編寫(xiě)過(guò)此LED 燈的裸機(jī)驅(qū)動(dòng)。

Linux 下LED 燈驅(qū)動(dòng)原理

Linux 下的任何外設(shè)驅(qū)動(dòng),最終都是要配置相應(yīng)的硬件寄存器。所以本章的LED 燈驅(qū)動(dòng)最終也是對(duì)I.MX6ULL 的IO 口進(jìn)行配置,與裸機(jī)實(shí)驗(yàn)不同的是,在Linux 下編寫(xiě)驅(qū)動(dòng)要符合Linux的驅(qū)動(dòng)框架。I.MX6U-ALPHA 開(kāi)發(fā)板上的LED 連接到I.MX6ULL 的GPIO1_IO03 這個(gè)引腳上。

地址映射(ioremap映射、iounmap釋放)

在編寫(xiě)驅(qū)動(dòng)之前,我們需要先簡(jiǎn)單了解一下MMU,MMU 全稱(chēng)叫做Memory Manage Unit(內(nèi)存管理單元)。在老版本的Linux 中要求處理器必須有MMU,但是現(xiàn)在新版Linux 內(nèi)核已經(jīng)支持無(wú)MMU 的處理器了(STM32可以跑linux了)。MMU 主要完成的功能如下:

①、完成虛擬空間到物理空間的映射
②、內(nèi)存保護(hù),設(shè)置存儲(chǔ)器的訪問(wèn)權(quán)限,設(shè)置虛擬存儲(chǔ)空間的緩沖特性。

重點(diǎn)關(guān)注第①點(diǎn),也就是虛擬空間到物理空間的映射。首先了解兩個(gè)地址概念:虛擬地址(VA,Virtual Address)、物理地址(PA,Physcical Address)。對(duì)于32 位的處理器來(lái)說(shuō),虛擬地址范圍是2^32=4GB,我們的開(kāi)發(fā)板上有512MB 的DDR3,這512MB 的內(nèi)存就是物理內(nèi)存,經(jīng)過(guò)MMU 可以將其映射到整個(gè)4GB 的虛擬空間,如圖41.1.1 所示:

物理內(nèi)存只有512MB,虛擬內(nèi)存有4GB,那么肯定存在多個(gè)虛擬地址映射到同一個(gè)物理地址上去,虛擬地址范圍比物理地址范圍大的問(wèn)題處理器自會(huì)處理,這里我們不去深究,MMU是很復(fù)雜的一個(gè)東西。

這里就涉及到了物理內(nèi)存和虛擬內(nèi)存之間的轉(zhuǎn)換,需要用到兩個(gè)函數(shù):ioremap 和iounmap。

1、ioremap 函數(shù)

ioremap 函數(shù)用于獲取指定物理地址空間映射的虛擬地址空間,定義在
arch/arm/include/asm/io.h 文件中,定義如下:

1 #define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) 2 3 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 是個(gè)宏,有兩個(gè)參數(shù):cookie 和size,真正起作用的是函數(shù)__arm_ioremap,此函數(shù)有三個(gè)參數(shù)和一個(gè)返回值,這些參數(shù)和返回值的含義如下:

phys_addr:要映射給的物理起始地址。
size:要映射的內(nèi)存空間大小。
mtype:ioremap 的類(lèi)型,可以選擇MT_DEVICE、MT_DEVICE_NONSHARED、
MT_DEVICE_CACHED 和MT_DEVICE_WC,ioremap 函數(shù)選擇MT_DEVICE。

返回值:__iomem 類(lèi)型的指針,指向映射后的虛擬空間首地址。

假如我們要獲取I.MX6ULL 的IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器對(duì)應(yīng)的虛擬地址,使用如下代碼即可:

#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 是映射后的虛擬地址。對(duì)于I.MX6ULL 來(lái)說(shuō)一個(gè)寄存器是4 字節(jié)(32 位)的,因此映射的內(nèi)存長(zhǎng)度為4。映射完成以后直接對(duì)SW_MUX_GPIO1_IO03 進(jìn)行讀寫(xiě)操作即可。

2、iounmap 函數(shù)

卸載驅(qū)動(dòng)的時(shí)候需要使用iounmap 函數(shù)釋放掉ioremap 函數(shù)所做的映射,iounmap 函數(shù)原型如下:

void iounmap (volatile void __iomem *addr)

iounmap 只有一個(gè)參數(shù)addr,此參數(shù)就是要取消映射的虛擬地址空間首地址。假如我們現(xiàn)在要取消掉IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代碼即可:

iounmap(SW_MUX_GPIO1_IO03);

I/O 內(nèi)存訪問(wèn)函數(shù)

當(dāng)外部寄存器或內(nèi)存映射到虛擬內(nèi)存空間時(shí),稱(chēng)為I/O 內(nèi)存。但是對(duì)于ARM 來(lái)說(shuō)沒(méi)有I/O 空間這個(gè)概念,因此ARM 體系下只有I/O 內(nèi)存(可以直接理解為內(nèi)存)。使用ioremap 函數(shù)將寄存器的物理地址映射到虛擬地址以后,我們就可以直接通過(guò)指針訪問(wèn)這些地址,但是Linux 內(nèi)核不建議這么做,而是推薦使用一組操作函數(shù)來(lái)對(duì)映射后的內(nèi)存進(jìn)行讀寫(xiě)操作。

1、讀操作函數(shù)
讀操作函數(shù)有如下幾個(gè):

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 這三個(gè)函數(shù)分別對(duì)應(yīng)8bit、16bit 和32bit 讀操作,參數(shù)addr 就是要讀取寫(xiě)內(nèi)存地址,返回值就是讀取到的數(shù)據(jù)。

2、寫(xiě)操作函數(shù)

寫(xiě)操作函數(shù)有如下幾個(gè):

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 這三個(gè)函數(shù)分別對(duì)應(yīng)8bit、16bit 和32bit 寫(xiě)操作,參數(shù)value 是要寫(xiě)入的數(shù)值,addr 是要寫(xiě)入的地址。

硬件原理圖分析

本章實(shí)驗(yàn)硬件原理圖參考8.3 小節(jié)即可。

實(shí)驗(yàn)程序編寫(xiě)

本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開(kāi)發(fā)板光盤(pán)-> 2、Linux 驅(qū)動(dòng)例程-> 2_led。
本章實(shí)驗(yàn)編寫(xiě)Linux 下的LED 燈驅(qū)動(dòng),可以通過(guò)應(yīng)用程序?qū).MX6U-ALPHA 開(kāi)發(fā)板上的LED 燈進(jìn)行開(kāi)關(guān)操作。

LED 燈驅(qū)動(dòng)程序編寫(xiě)

新建名為“2_led”文件夾,然后在2_led 文件夾里面創(chuàng)建VSCode 工程,工作區(qū)命名為“l(fā)ed”。
工程創(chuàng)建好以后新建led.c 文件,此文件就是led 的驅(qū)動(dòng)文件,在led.c 里面輸入如下內(nèi)容:

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : led.c 作者 : 左忠凱 版本 : V1.0 描述 : LED驅(qū)動(dòng)文件。 其他 : 無(wú) 論壇 : www.openedv.com 日志 : 初版V1.0 2019/1/30 左忠凱創(chuàng)建 ***************************************************************/ #define LED_MAJOR 200 /* 主設(shè)備號(hào) */ #define LED_NAME "led" /* 設(shè)備名字 */#define LEDOFF 0 /* 關(guān)燈 */ #define LEDON 1 /* 開(kāi)燈 *//* 寄存器物理地址 */ #define CCM_CCGR1_BASE (0X020C406C) #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) #define GPIO1_DR_BASE (0X0209C000) #define GPIO1_GDIR_BASE (0X0209C004)/* 映射后的寄存器虛擬地址指針 */ static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR;/** @description : LED打開(kāi)/關(guān)閉* @param - sta : LEDON(0) 打開(kāi)LED,LEDOFF(1) 關(guān)閉LED* @return : 無(wú)*/ void led_switch(u8 sta) {u32 val = 0;if(sta == LEDON) {val = readl(GPIO1_DR);val &= ~(1 << 3); writel(val, GPIO1_DR);}else if(sta == LEDOFF) {val = readl(GPIO1_DR);val|= (1 << 3); writel(val, GPIO1_DR);} }/** @description : 打開(kāi)設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {return 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開(kāi)的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶(hù)空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設(shè)備寫(xiě)數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開(kāi)的文件描述符* @param - buf : 要寫(xiě)給設(shè)備寫(xiě)入的數(shù)據(jù)* @param - cnt : 要寫(xiě)入的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 寫(xiě)入的字節(jié)數(shù),如果為負(fù)值,表示寫(xiě)入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態(tài)值 */if(ledstat == LEDON) { led_switch(LEDON); /* 打開(kāi)LED燈 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 關(guān)閉LED燈 */}return 0; }/** @description : 關(guān)閉/釋放設(shè)備* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無(wú)* @return : 無(wú)*/ static int __init led_init(void) {int retvalue = 0;u32 val = 0;/* 初始化LED *//* 1、寄存器地址映射 */IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);//時(shí)鐘寄存器SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);//復(fù)用寄存器SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);//電氣屬性寄存器GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);//方向GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);//高低電平/* 2、使能GPIO1時(shí)鐘 */ //前面裸機(jī)的時(shí)候所有時(shí)鐘CCM_CCGR1~6都打開(kāi)了,這里就不用了val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26); /* 清除以前的設(shè)置 */val |= (3 << 26); /* 設(shè)置新值 */writel(val, IMX6U_CCM_CCGR1);/* 3、設(shè)置GPIO1_IO03的復(fù)用功能,將其復(fù)用為* GPIO1_IO03,最后設(shè)置IO屬性。*/writel(5, SW_MUX_GPIO1_IO03);/*寄存器SW_PAD_GPIO1_IO03設(shè)置IO屬性*bit 16:0 HYS關(guān)閉*bit [15:14]: 00 默認(rèn)下拉*bit [13]: 0 kepper功能*bit [12]: 1 pull/keeper使能*bit [11]: 0 關(guān)閉開(kāi)路輸出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 110 R0/6驅(qū)動(dòng)能力*bit [0]: 0 低轉(zhuǎn)換率*/writel(0x10B0, SW_PAD_GPIO1_IO03);/* 4、設(shè)置GPIO1_IO03為輸出功能 */val = readl(GPIO1_GDIR);val &= ~(1 << 3); /* 清除以前的設(shè)置 */val |= (1 << 3); /* 設(shè)置為輸出 */writel(val, GPIO1_GDIR);/* 5、默認(rèn)關(guān)閉LED */val = readl(GPIO1_DR);val |= (1 << 3); writel(val, GPIO1_DR);/* 6、注冊(cè)字符設(shè)備驅(qū)動(dòng) */retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);if(retvalue < 0){printk("register chrdev failed!\r\n");return -EIO;}return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無(wú)* @return : 無(wú)*/ static void __exit led_exit(void) {/* 取消映射 */iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/* 注銷(xiāo)字符設(shè)備驅(qū)動(dòng) */unregister_chrdev(LED_MAJOR, LED_NAME); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第22~26 行,定義了一些宏,包括主設(shè)備號(hào)、設(shè)備名字、LED 開(kāi)/關(guān)宏。
第29~33 行,本實(shí)驗(yàn)要用到的寄存器宏定義。
第36~40 行,經(jīng)過(guò)內(nèi)存映射以后的寄存器地址指針。
第47~59 行,led_switch 函數(shù),用于控制開(kāi)發(fā)板上的LED 燈亮滅,當(dāng)參數(shù)sta 為L(zhǎng)EDON(1)的時(shí)候打開(kāi)LED 燈,sta 為L(zhǎng)EDOFF(0)的時(shí)候關(guān)閉LED 燈。

第68~71 行,led_open 函數(shù),為空函數(shù),可以自行在此函數(shù)中添加相關(guān)內(nèi)容,一般在此函數(shù)中將設(shè)備結(jié)構(gòu)體作為參數(shù)filp 的私有數(shù)據(jù)(filp->private_data)。
第81~84 行,led_read 函數(shù),為空函數(shù),如果想在應(yīng)用程序中讀取LED 的狀態(tài),那么就可以在此函數(shù)中添加相應(yīng)的代碼,比如讀取GPIO1_DR 寄存器的值,然后返回給應(yīng)用程序。
第94~114 行,led_write 函數(shù),實(shí)現(xiàn)對(duì)LED 燈的開(kāi)關(guān)操作,當(dāng)應(yīng)用程序調(diào)用write 函數(shù)向led 設(shè)備寫(xiě)數(shù)據(jù)的時(shí)候此函數(shù)就會(huì)執(zhí)行。首先通過(guò)函數(shù)copy_from_user 獲取應(yīng)用程序發(fā)送過(guò)來(lái)的操作信息(打開(kāi)還是關(guān)閉LED),最后根據(jù)應(yīng)用程序的操作信息來(lái)打開(kāi)或關(guān)閉LED 燈。
第121~124 行,led_release 函數(shù),為空函數(shù),可以自行在此函數(shù)中添加相關(guān)內(nèi)容,一般關(guān)閉設(shè)備的時(shí)候會(huì)釋放掉led_open 函數(shù)中添加的私有數(shù)據(jù)。
第127~133 行,設(shè)備文件操作結(jié)構(gòu)體led_fops 的定義和初始化。
第140~185 行,驅(qū)動(dòng)入口函數(shù)led_init,此函數(shù)實(shí)現(xiàn)了LED 的初始化工作,147~151 行通過(guò)ioremap 函數(shù)獲取物理寄存器地址映射后的虛擬地址,得到寄存器對(duì)應(yīng)的虛擬地址以后就可以完成相關(guān)初始化工作了。比如使能GPIO1 時(shí)鐘、設(shè)置GPIO1_IO03 復(fù)用功能、配置GPIO1_IO03的屬性等等。最后,最重要的一步!使用register_chrdev 函數(shù)注冊(cè)led 這個(gè)字符設(shè)備。
第192~202 行,驅(qū)動(dòng)出口函數(shù)led_exit,首先使用函數(shù)iounmap 取消內(nèi)存映射,最后使用函數(shù)unregister_chrdev 注銷(xiāo)led 這個(gè)字符設(shè)備。
第205~206 行,使用module_init 和module_exit 這兩個(gè)函數(shù)指定led 設(shè)備驅(qū)動(dòng)加載和卸載函數(shù)。
第207~208 行,添加LICENSE 和作者信息。

APP測(cè)試程序編寫(xiě)

編寫(xiě)測(cè)試APP,led 驅(qū)動(dòng)加載成功以后手動(dòng)創(chuàng)建/dev/led 節(jié)點(diǎn),應(yīng)用APP 通過(guò)操作/dev/led文件來(lái)完成對(duì)LED 設(shè)備的控制。向/dev/led 文件寫(xiě)0 表示關(guān)閉LED 燈,寫(xiě)1 表示打開(kāi)LED 燈。
新建ledApp.c 文件,在里面輸入如下內(nèi)容:

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : ledApp.c 作者 : 左忠凱 版本 : V1.0 描述 : chrdevbase驅(qū)測(cè)試APP。 其他 : 無(wú) 使用方法 :./ledtest /dev/led 0 關(guān)閉LED./ledtest /dev/led 1 打開(kāi)LED 論壇 : www.openedv.com 日志 : 初版V1.0 2019/1/30 左忠凱創(chuàng)建 ***************************************************************/#define LEDOFF 0 #define LEDON 1/** @description : main主程序* @param - argc : argv數(shù)組元素個(gè)數(shù)* @param - argv : 具體參數(shù)* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開(kāi)led驅(qū)動(dòng) */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要執(zhí)行的操作:打開(kāi)或關(guān)閉 *//* 向/dev/led文件寫(xiě)入數(shù)據(jù) */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 關(guān)閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0; }

ledApp.c 的內(nèi)容還是很簡(jiǎn)單的,就是對(duì)led 的驅(qū)動(dòng)文件進(jìn)行最基本的打開(kāi)、關(guān)閉、寫(xiě)操作等。

運(yùn)行測(cè)試

編譯驅(qū)動(dòng)程序和測(cè)試APP

1、編譯驅(qū)動(dòng)程序
編寫(xiě)Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為led.o,Makefile 內(nèi)容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := led.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為led.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“l(fā)ed.ko”的驅(qū)動(dòng)模塊文件。

2、編譯測(cè)試APP輸入如下命令編譯測(cè)試ledApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

編譯成功以后就會(huì)生成ledApp 這個(gè)應(yīng)用程序。

注意!如果大家使用的正點(diǎn)原子出廠系統(tǒng)來(lái)做本實(shí)驗(yàn),那么會(huì)發(fā)現(xiàn)LED 燈會(huì)一直閃爍。這是因?yàn)檎c(diǎn)原子出廠系統(tǒng)默認(rèn)將LED 燈作為了心跳燈,因此系統(tǒng)啟動(dòng)以后LED 燈就會(huì)自動(dòng)閃爍,這樣會(huì)影響大家做實(shí)驗(yàn)。如果是完全按照本教程自行移植的內(nèi)核和根文件系統(tǒng),那么就不會(huì)遇到此問(wèn)題。如果直接使用出廠系統(tǒng)來(lái)做實(shí)驗(yàn),我們需要關(guān)閉LED 燈的心跳功能,關(guān)閉方法參考《【正點(diǎn)原子】I.MX6U 用戶(hù)快速體驗(yàn)》第3.1 小節(jié),或者輸入如下命令即可:

echo none > /sys/class/leds/sys-led/trigger // 改變LED 的觸發(fā)模式

拷貝led.ko 和ledApp到指定目錄

將上一小節(jié)編譯出來(lái)的led.ko 和ledApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15 目錄中【視頻里使用nfs文件夾網(wǎng)絡(luò)掛載】

加載led.ko 驅(qū)動(dòng)模塊到內(nèi)核

重啟開(kāi)發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載led.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe led.ko //加載驅(qū)動(dòng)

創(chuàng)建應(yīng)用層“/dev/led”設(shè)備節(jié)點(diǎn)

驅(qū)動(dòng)加載成功以后創(chuàng)建“/dev/led”設(shè)備節(jié)點(diǎn),命令如下:

mknod /dev/led c 200 0

運(yùn)行測(cè)試

驅(qū)動(dòng)節(jié)點(diǎn)創(chuàng)建成功以后就可以使用ledApp 軟件來(lái)測(cè)試驅(qū)動(dòng)是否工作正常,輸入如下命令打開(kāi)LED 燈:

./ledApp /dev/led 1 //打開(kāi)LED 燈

輸入上述命令以后觀察I.MX6U-ALPHA 開(kāi)發(fā)板上的紅色LED 燈是否點(diǎn)亮,如果點(diǎn)亮的話說(shuō)明驅(qū)動(dòng)工作正常。在輸入如下命令關(guān)閉LED 燈:

./ledApp /dev/led 0 //關(guān)閉LED 燈

輸入上述命令以后觀察I.MX6U-ALPHA 開(kāi)發(fā)板上的紅色LED 燈是否熄滅,如果熄滅的話說(shuō)明我們編寫(xiě)的LED 驅(qū)動(dòng)工作完全正常!至此,我們成功編寫(xiě)了第一個(gè)真正的Linux 驅(qū)動(dòng)設(shè)備程序。

如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:

rmmod led.ko

其他:網(wǎng)絡(luò)問(wèn)題解決方法

總結(jié)

以上是生活随笔為你收集整理的Linux LED驱动开发实验(直接操作寄存器 -- 实际开发很少这样做)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。