linux cached释放_正点原子Linux第四十一章嵌入式Linux LED驱动开发实验
1)資料下載:點(diǎn)擊資料即可下載
2)對(duì)正點(diǎn)原子Linux感興趣的同學(xué)可以加群討論:935446741
3)關(guān)注正點(diǎn)原子公眾號(hào),獲取最新資料更新
第四十一章嵌入式Linux LED驅(qū)動(dòng)開發(fā)實(shí)驗(yàn)
上一章我們?cè)敿?xì)的講解了字符設(shè)備驅(qū)動(dòng)開發(fā)步驟,并且用一個(gè)虛擬的chrdevbase設(shè)備為例帶領(lǐng)大家完成了第一個(gè)字符設(shè)備驅(qū)動(dòng)的開發(fā)。本章我們就開始編寫第一個(gè)真正的Linux字符設(shè)備驅(qū)動(dòng)。在I.MX6U-ALPHA開發(fā)板上有一個(gè)LED燈,我們?cè)诼銠C(jī)篇中已經(jīng)編寫過(guò)此LED燈的裸機(jī)驅(qū)動(dòng),本章我們就來(lái)學(xué)習(xí)一下如何編寫Linux下的LED燈驅(qū)動(dòng)。
41.1 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下編寫驅(qū)動(dòng)要符合Linux的驅(qū)動(dòng)框架。I.MX6U-ALPHA開發(fā)板上的LED連接到I.MX6ULL的GPIO1_IO03這個(gè)引腳上,因此本章實(shí)驗(yàn)的重點(diǎn)就是編寫Linux下I.MX6UL引腳控制驅(qū)動(dòng)。關(guān)于I.MX6ULL的GPIO詳細(xì)講解請(qǐng)參考第八章。
41.1.1地址映射
在編寫驅(qū)動(dòng)之前,我們需要先簡(jiǎn)單了解一下MMU這個(gè)神器,MMU全稱叫做Memory Manage Unit,也就是內(nèi)存管理單元。在老版本的Linux中要求處理器必須有MMU,但是現(xiàn)在Linux內(nèi)核已經(jīng)支持無(wú)MMU的處理器了。MMU主要完成的功能如下:
①、完成虛擬空間到物理空間的映射。
②、內(nèi)存保護(hù),設(shè)置存儲(chǔ)器的訪問(wèn)權(quán)限,設(shè)置虛擬存儲(chǔ)空間的緩沖特性。
我們重點(diǎn)來(lái)看一下第①點(diǎn),也就是虛擬空間到物理空間的映射,也叫做地址映射。首先了解兩個(gè)地址概念:虛擬地址(VA,Virtual Address)、物理地址(PA,Physcical Address)。對(duì)于32位的處理器來(lái)說(shuō),虛擬地址范圍是2^32=4GB,我們的開發(fā)板上有512MB的DDR3,這512MB的內(nèi)存就是物理內(nèi)存,經(jīng)過(guò)MMU可以將其映射到整個(gè)4GB的虛擬空間,如圖41.1.1所示:
圖41.1.1內(nèi)存映射
物理內(nèi)存只有512MB,虛擬內(nèi)存有4GB,那么肯定存在多個(gè)虛擬地址映射到同一個(gè)物理地址上去,虛擬地址范圍比物理地址范圍大的問(wèn)題處理器自會(huì)處理,這里我們不要去深究,因?yàn)镸MU是很復(fù)雜的一個(gè)東西,后續(xù)有時(shí)間的話正點(diǎn)原子Linux團(tuán)隊(duì)會(huì)專門做MMU專題教程。
Linux內(nèi)核啟動(dòng)的時(shí)候會(huì)初始化MMU,設(shè)置好內(nèi)存映射,設(shè)置好以后CPU訪問(wèn)的都是虛擬地址。比如I.MX6ULL的GPIO1_IO03引腳的復(fù)用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的地址為0X020E0068。如果沒(méi)有開啟MMU的話直接向0X020E0068這個(gè)寄存器地址寫入數(shù)據(jù)就可以配置GPIO1_IO03的復(fù)用功能。現(xiàn)在開啟了MMU,并且設(shè)置了內(nèi)存映射,因此就不能直接向0X020E0068這個(gè)地址寫入數(shù)據(jù)了。我們必須得到0X020E0068這個(gè)物理地址在Linux系統(tǒng)里面對(duì)應(yīng)的虛擬地址,這里就涉及到了物理內(nèi)存和虛擬內(nèi)存之間的轉(zhuǎn)換,需要用到兩個(gè)函數(shù):ioremap和iounmap。
1、ioremap函數(shù)
ioremap函數(shù)用于獲取指定物理地址空間對(duì)應(yīng)的虛擬地址空間,定義在arch/arm/include/asm/io.h文件中,定義如下:
示例代碼41.1.1 ioremap函數(shù)
1 #define ioremap(cookie,size) __arm_ioremap((cookie),(size), MT_DEVICE)
2
3void __iomem * __arm_ioremap(phys_addr_t phys_addr,size_t size,unsignedint mtype)
4{
5return 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的類型,可以選擇MT_DEVICE、MT_DEVICE_NONSHARED、MT_DEVICE_CACHED和MT_DEVICE_WC,ioremap函數(shù)選擇MT_DEVICE。
返回值:__iomem類型的指針,指向映射后的虛擬空間首地址。
假如我們要獲取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(GPIO1_GDIR_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)行讀寫操作即可。
2、iounmap函數(shù)
卸載驅(qū)動(dòng)的時(shí)候需要使用iounmap函數(shù)釋放掉ioremap函數(shù)所做的映射,iounmap函數(shù)原型如下:
示例代碼41.1.2 iounmap函數(shù)原型
void iounmap (volatilevoid __iomem *addr)
iounmap只有一個(gè)參數(shù)addr,此參數(shù)就是要取消映射的虛擬地址空間首地址。假如我們現(xiàn)在要取消掉IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器的地址映射,使用如下代碼即可:
iounmap(SW_MUX_GPIO1_IO03);
41.1.2 I/O內(nèi)存訪問(wèn)函數(shù)
這里說(shuō)的I/O是輸入/輸出的意思,并不是我們學(xué)習(xí)單片機(jī)的時(shí)候講的GPIO引腳。這里涉及到兩個(gè)概念:I/O端口和I/O內(nèi)存。當(dāng)外部寄存器或內(nèi)存映射到IO空間時(shí),稱為I/O端口。當(dāng)外部寄存器或內(nèi)存映射到內(nèi)存空間時(shí),稱為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)行讀寫操作。
1、讀操作函數(shù)
讀操作函數(shù)有如下幾個(gè):
示例代碼41.1.2.1 讀操作函數(shù)
1 u8 readb(constvolatilevoid __iomem *addr)
2 u16 readw(constvolatilevoid __iomem *addr)
3 u32 readl(constvolatilevoid __iomem *addr)
readb、readw和readl這三個(gè)函數(shù)分別對(duì)應(yīng)8bit、16bit和32bit讀操作,參數(shù)addr就是要讀取寫內(nèi)存地址,返回值就是讀取到的數(shù)據(jù)。
2、寫操作函數(shù)
寫操作函數(shù)有如下幾個(gè):
示例代碼41.1.2.2 寫操作函數(shù)
1void writeb(u8 value,volatilevoid __iomem *addr)
2void writew(u16 value,volatilevoid __iomem *addr)
3void writel(u32 value,volatilevoid __iomem *addr)
writeb、writew和writel這三個(gè)函數(shù)分別對(duì)應(yīng)8bit、16bit和32bit寫操作,參數(shù)value是要寫入的數(shù)值,addr是要寫入的地址。
41.2 硬件原理圖分析
本章實(shí)驗(yàn)硬件原理圖參考8.3小節(jié)即可。
41.3 實(shí)驗(yàn)程序編寫
本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開發(fā)板光盤->2、Linux驅(qū)動(dòng)例程->2_led。
本章實(shí)驗(yàn)編寫Linux下的LED燈驅(qū)動(dòng),可以通過(guò)應(yīng)用程序?qū).MX6U-ALPHA開發(fā)板上的LED燈進(jìn)行開關(guān)操作。
41.3.1 LED燈驅(qū)動(dòng)程序編寫
新建名為“2_led”文件夾,然后在2_led文件夾里面創(chuàng)建VSCode工程,工作區(qū)命名為“l(fā)ed”。工程創(chuàng)建好以后新建led.c文件,此文件就是led的驅(qū)動(dòng)文件,在led.c里面輸入如下內(nèi)容:
示例代碼41.3.1.1 led.c驅(qū)動(dòng)文件代碼
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <asm/mach/map.h>
10 #include <asm/uaccess.h>
11 #include <asm/io.h>
12/***************************************************************
13 Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
14文件名 : led.c
15作者 : 左忠凱
16版本 : V1.0
17描述 : LED驅(qū)動(dòng)文件。
18其他 : 無(wú)
19論壇 : http://www.openedv.com
20日志 : 初版V1.0 2019/1/30 左忠凱創(chuàng)建
21 ***************************************************************/
22 #define LED_MAJOR 200 /* 主設(shè)備號(hào) */
23 #define LED_NAME "led" /* 設(shè)備名字 */
24
25 #define LEDOFF 0 /* 關(guān)燈 */
26 #define LEDON 1 /* 開燈 */
27
28/* 寄存器物理地址 */
29 #define CCM_CCGR1_BASE (0X020C406C)
30 #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
31 #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
32 #define GPIO1_DR_BASE (0X0209C000)
33 #define GPIO1_GDIR_BASE (0X0209C004)
34
35/* 映射后的寄存器虛擬地址指針 */
36staticvoid __iomem *IMX6U_CCM_CCGR1;
37staticvoid __iomem *SW_MUX_GPIO1_IO03;
38staticvoid __iomem *SW_PAD_GPIO1_IO03;
39staticvoid __iomem *GPIO1_DR;
40staticvoid __iomem *GPIO1_GDIR;
41
42/*
43 * @description : LED打開/關(guān)閉
44 * @param - sta : LEDON(0) 打開LED,LEDOFF(1) 關(guān)閉LED
45 * @return : 無(wú)
46 */
47void led_switch(u8 sta)
48{
49 u32 val =0;
50if(sta == LEDON){
51 val = readl(GPIO1_DR);
52 val &=~(1<<3);
53 writel(val, GPIO1_DR);
54}elseif(sta == LEDOFF){
55 val = readl(GPIO1_DR);
56 val|=(1<<3);
57 writel(val, GPIO1_DR);
58}
59}
60
61/*
62 * @description : 打開設(shè)備
63 * @param – inode : 傳遞給驅(qū)動(dòng)的inode
64 * @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量
65 * 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。
66 * @return : 0 成功;其他失敗
67 */
68staticint led_open(struct inode *inode,struct file *filp)
69{
70return0;
71}
72
73/*
74 * @description : 從設(shè)備讀取數(shù)據(jù)
75 * @param - filp : 要打開的設(shè)備文件(文件描述符)
76 * @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)
77 * @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度
78 * @param - offt : 相對(duì)于文件首地址的偏移
79 * @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗
80 */
81static ssize_t led_read(struct file *filp,char __user *buf,
size_t cnt, loff_t *offt)
82{
83return0;
84}
85
86/*
87 * @description : 向設(shè)備寫數(shù)據(jù)
88 * @param - filp : 設(shè)備文件,表示打開的文件描述符
89 * @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)
90 * @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度
91 * @param - offt : 相對(duì)于文件首地址的偏移
92 * @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗
93 */
94static ssize_t led_write(struct file *filp,constchar __user *buf,
size_t cnt, loff_t *offt)
95{
96int retvalue;
97unsignedchar databuf[1];
98unsignedchar ledstat;
99
100 retvalue = copy_from_user(databuf, buf, cnt);
101if(retvalue <0){
102 printk("kernel write failed!rn");
103return-EFAULT;
104}
105
106 ledstat = databuf[0]; /* 獲取狀態(tài)值 */
107
108if(ledstat == LEDON){
109 led_switch(LEDON); /* 打開LED燈 */
110}elseif(ledstat == LEDOFF){
111 led_switch(LEDOFF); /* 關(guān)閉LED燈 */
112}
113return0;
114}
115
116/*
117 * @description : 關(guān)閉/釋放設(shè)備
118 * @param – filp : 要關(guān)閉的設(shè)備文件(文件描述符)
119 * @return : 0 成功;其他失敗
120 */
121staticint led_release(struct inode *inode,struct file *filp)
122{
123return0;
124}
125
126/* 設(shè)備操作函數(shù) */
127staticstruct file_operations led_fops ={
128.owner = THIS_MODULE,
129.open = led_open,
130.read = led_read,
131.write = led_write,
132.release = led_release,
133};
134
135/*
136 * @description : 驅(qū)動(dòng)出口函數(shù)
137 * @param : 無(wú)
138 * @return : 無(wú)
139 */
140staticint __init led_init(void)
141{
142int retvalue =0;
143 u32 val =0;
144
145/* 初始化LED */
146/* 1、寄存器地址映射 */
147 IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
148 SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
149 SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
150 GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
151 GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);
152
153/* 2、使能GPIO1時(shí)鐘 */
154 val = readl(IMX6U_CCM_CCGR1);
155 val &=~(3<<26);/* 清除以前的設(shè)置 */
156 val |=(3<<26);/* 設(shè)置新值 */
157 writel(val, IMX6U_CCM_CCGR1);
158
159/* 3、設(shè)置GPIO1_IO03的復(fù)用功能,將其復(fù)用為
160 * GPIO1_IO03,最后設(shè)置IO屬性。
161 */
162 writel(5, SW_MUX_GPIO1_IO03);
163
164/* 寄存器SW_PAD_GPIO1_IO03設(shè)置IO屬性 */
165 writel(0x10B0, SW_PAD_GPIO1_IO03);
166
167/* 4、設(shè)置GPIO1_IO03為輸出功能 */
168 val = readl(GPIO1_GDIR);
169 val &=~(1<<3);/* 清除以前的設(shè)置 */
170 val |=(1<<3);/* 設(shè)置為輸出 */
171 writel(val, GPIO1_GDIR);
172
173/* 5、默認(rèn)關(guān)閉LED */
174 val = readl(GPIO1_DR);
175 val |=(1<<3);
176 writel(val, GPIO1_DR);
177
178/* 6、注冊(cè)字符設(shè)備驅(qū)動(dòng) */
179 retvalue = register_chrdev(LED_MAJOR, LED_NAME,&led_fops);
180if(retvalue <0){
181 printk("register chrdev failed!rn");
182return-EIO;
183}
184return0;
185}
186
187/*
188 * @description : 驅(qū)動(dòng)出口函數(shù)
189 * @param : 無(wú)
190 * @return : 無(wú)
191 */
192staticvoid __exit led_exit(void)
193{
194/* 取消映射 */
195 iounmap(IMX6U_CCM_CCGR1);
196 iounmap(SW_MUX_GPIO1_IO03);
197 iounmap(SW_PAD_GPIO1_IO03);
198 iounmap(GPIO1_DR);
199 iounmap(GPIO1_GDIR);
200
201/* 注銷字符設(shè)備驅(qū)動(dòng) */
202 unregister_chrdev(LED_MAJOR, LED_NAME);
203}
204
205 module_init(led_init);
206 module_exit(led_exit);
207 MODULE_LICENSE("GPL");
208 MODULE_AUTHOR("zuozhongkai");
第22~26行,定義了一些宏,包括主設(shè)備號(hào)、設(shè)備名字、LED開/關(guān)宏。
第29~33行,本實(shí)驗(yàn)要用到的寄存器宏定義。
第36~40行,經(jīng)過(guò)內(nèi)存映射以后的寄存器地址指針。
第47~59行,led_switch函數(shù),用于控制開發(fā)板上的LED燈亮滅,當(dāng)參數(shù)sta為L(zhǎng)EDON(0)的時(shí)候打開LED燈,sta為L(zhǎng)EDOFF(1)的時(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燈的開關(guān)操作,當(dāng)應(yīng)用程序調(diào)用write函數(shù)向led設(shè)備寫數(shù)據(jù)的時(shí)候此函數(shù)就會(huì)執(zhí)行。首先通過(guò)函數(shù)copy_from_user獲取應(yīng)用程序發(fā)送過(guò)來(lái)的操作信息(打開還是關(guān)閉LED),最后根據(jù)應(yīng)用程序的操作信息來(lá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注銷led這個(gè)字符設(shè)備。
第205~206行,使用module_init和module_exit這兩個(gè)函數(shù)指定led設(shè)備驅(qū)動(dòng)加載和卸載函數(shù)。
第207~208行,添加LICENSE和作者信息。
41.3.2 編寫測(cè)試APP
編寫測(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文件寫0表示關(guān)閉LED燈,寫1表示打開LED燈。新建ledApp.c文件,在里面輸入如下內(nèi)容:
示例代碼41.3.2.1 ledApp.c文件代碼
1 #include "stdio.h"
2 #include "unistd.h"
3 #include "sys/types.h"
4 #include "sys/stat.h"
5 #include "fcntl.h"
6 #include "stdlib.h"
7 #include "string.h"
8/***************************************************************
9 Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
10文件名 : ledApp.c
11作者 : 左忠凱
12版本 : V1.0
13描述 : LED驅(qū)測(cè)試APP。
14其他 : 無(wú)
15使用方法 :./ledtest /dev/led 0 關(guān)閉LED
16 ./ledtest /dev/led 1 打開LED
17論壇 : http://www.openedv.com
18日志 : 初版V1.0 2019/1/30 左忠凱創(chuàng)建
19 ***************************************************************/
20
21 #define LEDOFF 0
22 #define LEDON 1
23
24/*
25 * @description : main主程序
26 * @param - argc : argv數(shù)組元素個(gè)數(shù)
27 * @param - argv : 具體參數(shù)
28 * @return : 0 成功;其他失敗
29 */
30int main(int argc,char*argv[])
31{
32 int fd, retvalue;
33 char*filename;
34 unsignedchar databuf[1];
35
36 if(argc !=3){
37 printf("Error Usage!rn");
38 return-1;
39 }
40
41 filename = argv[1];
42
43 /* 打開led驅(qū)動(dòng) */
44 fd = open(filename, O_RDWR);
45 if(fd <0){
46 printf("file %s open failed!rn", argv[1]);
47 return-1;
48 }
49
50 databuf[0]= atoi(argv[2]);/* 要執(zhí)行的操作:打開或關(guān)閉 */
51
52 /* 向/dev/led文件寫入數(shù)據(jù) */
53 retvalue = write(fd, databuf,sizeof(databuf));
54 if(retvalue <0){
55 printf("LED Control Failed!rn");
56 close(fd);
57 return-1;
58 }
59
60 retvalue = close(fd);/* 關(guān)閉文件 */
61 if(retvalue <0){
62 printf("file %s close failed!rn", argv[1]);
63 return-1;
64 }
65 return0;
66}
ledApp.c的內(nèi)容還是很簡(jiǎn)單的,就是對(duì)led的驅(qū)動(dòng)文件進(jìn)行最基本的打開、關(guān)閉、寫操作等。
41.4 運(yùn)行測(cè)試
41.4.1 編譯驅(qū)動(dòng)程序和測(cè)試APP
1、編譯驅(qū)動(dòng)程序
編寫Makefile文件,本章實(shí)驗(yàn)的Makefile文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m變量的值改為led.o,Makefile內(nèi)容如下所示:
示例代碼41.4.1.1 Makefile文件
1 KERNELDIR:= /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
......
4 obj-m := led.o
......
11 clean:
12$(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)用程序。
41.4.2 運(yùn)行測(cè)試
將上一小節(jié)編譯出來(lái)的led.ko和ledApp這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(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)
驅(qū)動(dòng)加載成功以后創(chuàng)建“/dev/led”設(shè)備節(jié)點(diǎn),命令如下:
mknod /dev/led c 200 0
驅(qū)動(dòng)節(jié)點(diǎn)創(chuàng)建成功以后就可以使用ledApp軟件來(lái)測(cè)試驅(qū)動(dòng)是否工作正常,輸入如下命令打開LED燈:
./ledApp /dev/led 1 //打開LED燈
輸入上述命令以后觀察I.MX6U-ALPHA開發(fā)板上的紅色LED燈是否點(diǎn)亮,如果點(diǎn)亮的話說(shuō)明驅(qū)動(dòng)工作正常。在輸入如下命令關(guān)閉LED燈:
./ledApp /dev/led 0 //關(guān)閉LED燈
輸入上述命令以后觀察I.MX6U-ALPHA開發(fā)板上的紅色LED燈是否熄滅,如果熄滅的話說(shuō)明我們編寫的LED驅(qū)動(dòng)工作完全正常!至此,我們成功編寫了第一個(gè)真正的Linux驅(qū)動(dòng)設(shè)備程序。
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:
rmmodled.ko
總結(jié)
以上是生活随笔為你收集整理的linux cached释放_正点原子Linux第四十一章嵌入式Linux LED驱动开发实验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c调用python gensim包_Ju
- 下一篇: linux dhcp服务软包,dpkg包