在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

i.MX6ULL驅(qū)動(dòng)開(kāi)發(fā)1—字符設(shè)備開(kāi)發(fā)模板

碼農(nóng)愛(ài)學(xué)習(xí) ? 來(lái)源:碼農(nóng)愛(ài)學(xué)習(xí) ? 作者:碼農(nóng)愛(ài)學(xué)習(xí) ? 2022-03-17 09:13 ? 次閱讀

之前的幾篇文章(從i.MX6ULL嵌入式Linux開(kāi)發(fā)1-uboot移植初探起),介紹了嵌入式了Linux的系統(tǒng)移植(uboot、內(nèi)核與根文件系統(tǒng))以及使用MfgTool工具將系統(tǒng)燒寫到板子的EMMC中。

本篇開(kāi)始介紹嵌入式Linux驅(qū)動(dòng)開(kāi)發(fā)。

1 Linux驅(qū)動(dòng)分類

Linux中的外設(shè)驅(qū)動(dòng)可以分為三大類:字符設(shè)備驅(qū)動(dòng)、塊設(shè)備驅(qū)動(dòng)和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)。

字符設(shè)備驅(qū)動(dòng):字符設(shè)備是能夠按照字節(jié)流(比如文件)進(jìn)行讀寫操作的設(shè)備。字符設(shè)備最常見(jiàn),從最簡(jiǎn)單的點(diǎn)燈到I2C、SPI、音頻等都屬于字符設(shè)備驅(qū)動(dòng)

塊設(shè)備驅(qū)動(dòng):以存儲(chǔ)塊為基礎(chǔ)的設(shè)備驅(qū)動(dòng),如EMMC、NAND、SD卡等。對(duì)用戶而言,字符設(shè)備與塊設(shè)備的訪問(wèn)方式?jīng)]有差別。

網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng):即網(wǎng)絡(luò)驅(qū)動(dòng),它同時(shí)具有字符設(shè)備和塊設(shè)備的特點(diǎn),因?yàn)樗禽斎胼敵鍪怯薪Y(jié)構(gòu)塊的(報(bào)文,包,幀),但它的塊的大小又不是固定的。

poYBAGIx_3KAJV1gAAB19ZrvOjc355.png

2 Linux驅(qū)動(dòng)基本原理

Linux中一切皆文件,驅(qū)動(dòng)加載成功以后會(huì)在“/dev”目錄下生成一個(gè)相應(yīng)的文件,應(yīng)用程序通過(guò)對(duì)這個(gè)名為“/dev/xxx”的文件進(jìn)行相應(yīng)的操作即可實(shí)現(xiàn)對(duì)硬件的操作。

比如最簡(jiǎn)單的點(diǎn)燈功能,會(huì)有/dev/led這樣的驅(qū)動(dòng)文件,應(yīng)用程序使用open函數(shù)來(lái)打開(kāi)文件/dev/led,如果要點(diǎn)亮或關(guān)閉led,那么就使用write函數(shù)寫入開(kāi)關(guān)值,如果要獲取led的狀態(tài),就用read函數(shù)從驅(qū)動(dòng)中讀取相應(yīng)的狀態(tài),使用完成以后使用close函數(shù)關(guān)閉/dev/led這個(gè)文件。

2.1 Linux軟件分層結(jié)構(gòu)

Linux軟件從上到下可以分層4層結(jié)構(gòu),以控制LED為例:

應(yīng)用層:應(yīng)用程序使用庫(kù)提供的open函數(shù)打開(kāi)LED設(shè)備

庫(kù):庫(kù)根據(jù)open函數(shù)傳入的參數(shù)執(zhí)行“swi”指令,進(jìn)而引起CPU異常,進(jìn)入內(nèi)核

內(nèi)核:內(nèi)核的異常處理函數(shù)根據(jù)傳入的參數(shù)找到對(duì)應(yīng)的驅(qū)動(dòng)程序,返回文件句柄給庫(kù),進(jìn)而返回給應(yīng)用層

應(yīng)用層得到文件句柄后,使用庫(kù)提供的write或ioctl發(fā)出控制指令

庫(kù)根據(jù)write或ioctl函數(shù)傳入的參數(shù)執(zhí)行“swi”指令,進(jìn)入內(nèi)核

內(nèi)核的異常處理函數(shù)根據(jù)傳入的參數(shù)找到對(duì)應(yīng)的驅(qū)動(dòng)程序

驅(qū)動(dòng):驅(qū)動(dòng)程序控制硬件,點(diǎn)亮LED

應(yīng)用程序運(yùn)行在用戶空間,而Linux驅(qū)動(dòng)屬于內(nèi)核的一部分,因此驅(qū)動(dòng)運(yùn)行于內(nèi)核空間。當(dāng)應(yīng)用層通過(guò)open函數(shù)打開(kāi)/dev/led 這個(gè)驅(qū)動(dòng)時(shí),因用戶空間不能直接操作內(nèi)核,因此會(huì)使用“系統(tǒng)調(diào)用”的方法來(lái)從用戶空間“陷入”到內(nèi)核空間,實(shí)現(xiàn)對(duì)底層驅(qū)動(dòng)的操作。

pYYBAGIx_3yAY25pAAA6Q-W_Aa8447.png

比如應(yīng)用程序調(diào)用了open這個(gè)函數(shù),則在驅(qū)動(dòng)程序中也應(yīng)有一個(gè)對(duì)應(yīng)的open的函數(shù)。

2.2 Linux內(nèi)核驅(qū)動(dòng)操作函數(shù)

每一個(gè)系統(tǒng)調(diào)用,在驅(qū)動(dòng)中都有與之對(duì)應(yīng)的一個(gè)驅(qū)動(dòng)函數(shù),在Linux內(nèi)核文件include/linux/fs.h中有個(gè)file_operations結(jié)構(gòu)體,就是Linux內(nèi)核驅(qū)動(dòng)操作函數(shù)集合:

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*mremap)(struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	/*省略若干行...*/
};

其中有關(guān)字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中常用的函數(shù)有:

owner:擁有該結(jié)構(gòu)體的模塊的指針,一般設(shè)置為THIS_MODULE。

llseek函數(shù):用于修改文件當(dāng)前的讀寫位置。

read函數(shù):用于讀取設(shè)備文件。

write函數(shù):用于向設(shè)備文件寫入(發(fā)送)數(shù)據(jù)。

poll函數(shù):是個(gè)輪詢函數(shù),用于查詢?cè)O(shè)備是否可以進(jìn)行非阻塞的讀寫。

unlocked_ioctl函數(shù):提供對(duì)于設(shè)備的控制功能, 與應(yīng)用程序中的 ioctl 函數(shù)對(duì)應(yīng)。

compat_ioctl函數(shù):與 unlocked_ioctl功能一樣,區(qū)別在于在 64 位系統(tǒng)上,32 位的應(yīng)用程序調(diào)用將會(huì)使用此函數(shù)。在 32 位的系統(tǒng)上運(yùn)行 32 位的應(yīng)用程序調(diào)用的是unlocked_ioctl。

mmap函數(shù):用于將將設(shè)備的內(nèi)存映射到進(jìn)程空間中(也就是用戶空間),一般幀緩沖設(shè)備會(huì)使用此函數(shù), 比如 LCD 驅(qū)動(dòng)的顯存,將幀緩沖(LCD 顯存)映射到用戶空間中以后應(yīng)用程序就可以直接操作顯存了,這樣就不用在用戶空間和內(nèi)核空間之間來(lái)回復(fù)制。

open函數(shù):用于打開(kāi)設(shè)備文件。

release函數(shù):用于釋放(關(guān)閉)設(shè)備文件,與應(yīng)用程序中的 close 函數(shù)對(duì)應(yīng)。

fasync函數(shù):用于刷新待處理的數(shù)據(jù),用于將緩沖區(qū)中的數(shù)據(jù)刷新到磁盤中。

aio_fsync函數(shù):與fasync功能類似,只是 aio_fsync 是異步刷新待處理的

2.3 Linux驅(qū)動(dòng)運(yùn)行方式

Linux 驅(qū)動(dòng)有兩種運(yùn)行方式:

將驅(qū)動(dòng)編譯進(jìn)Linux內(nèi)核中, 這樣當(dāng)Linux內(nèi)核啟動(dòng)的時(shí)候就會(huì)自動(dòng)運(yùn)行驅(qū)動(dòng)程序。

將驅(qū)動(dòng)編譯成模塊(擴(kuò)展名為 .ko), 在Linux內(nèi)核啟動(dòng)以后使用“insmod”命令加載驅(qū)動(dòng)模塊。

在驅(qū)動(dòng)開(kāi)發(fā)階段一般都將其編譯為模塊,不需要編譯整個(gè)Linux代碼,方便調(diào)試驅(qū)動(dòng)程序。當(dāng)驅(qū)動(dòng)開(kāi)發(fā)完成后,根據(jù)實(shí)際需要,可以選擇是否將驅(qū)動(dòng)編譯進(jìn)Linux內(nèi)核中。

2.4 Linux設(shè)備號(hào)

2.4.1 設(shè)備號(hào)的組成

Linux中每個(gè)設(shè)備都有一個(gè)設(shè)備號(hào),設(shè)備號(hào)由主設(shè)備號(hào)和次設(shè)備號(hào)兩部分組成。

主設(shè)備號(hào):表示某一個(gè)具體的驅(qū)動(dòng)

次設(shè)備號(hào):表示使用這個(gè)驅(qū)動(dòng)的各個(gè)設(shè)備

Linux 提供了名為dev_t的數(shù)據(jù)類型表示設(shè)備號(hào),其本質(zhì)是32位的unsigned int數(shù)據(jù)類型,其中高12位為主設(shè)備號(hào),低20位為次設(shè)備號(hào),因此Linux中主設(shè)備號(hào)范圍為0~4095

在文件include/linux/kdev_t.h中提供了幾個(gè)關(guān)于設(shè)備號(hào)操作的宏定義:

#define MINORBITS   20 

#define MINORMASK   ((1U << MINORBITS) - 1) 

?

#define MAJOR(dev) ?  ((unsigned int) ((dev) >> MINORBITS)) 

#define MINOR(dev)   ((unsigned int) ((dev) & MINORMASK)) 

#define MKDEV(ma,mi)  (((ma) << MINORBITS) | (mi))

MINORBITS:表示次設(shè)備號(hào)位數(shù),一共20位

MINORMASK:表示次設(shè)備號(hào)掩碼

MAJOR:用于從dev_t中獲取主設(shè)備號(hào),將dev_t右移20位即可

MINOR:用于從dev_t中獲取次設(shè)備號(hào),取dev_t的低20位的值即可

MKDEV:用于將給定的主設(shè)備號(hào)和次設(shè)備號(hào)的值組合成dev_t類型的設(shè)備號(hào)

2.4.2 主設(shè)備號(hào)的分配

主設(shè)備號(hào)的分配包括靜態(tài)分配和動(dòng)態(tài)分配

靜態(tài)分配需要手動(dòng)指定設(shè)備號(hào),并且要注意不能與已有的重復(fù),一些常用的設(shè)備號(hào)已經(jīng)被Linux內(nèi)核開(kāi)發(fā)者給分配掉了,使用“cat /proc/devices”命令可查看當(dāng)前系統(tǒng)中所有已經(jīng)使用了的設(shè)備號(hào)。

動(dòng)態(tài)分配是在注冊(cè)字符設(shè)備之前先申請(qǐng)一個(gè)設(shè)備號(hào),系統(tǒng)會(huì)自動(dòng)分配一個(gè)沒(méi)有被使用的設(shè)備號(hào), 這樣就避免了沖突。在卸載驅(qū)動(dòng)的時(shí)候釋放掉這個(gè)設(shè)備號(hào)即可。

設(shè)備號(hào)的申請(qǐng)函數(shù):

/*
* dev:保存申請(qǐng)到的設(shè)備號(hào)
* baseminor:次設(shè)備號(hào)起始地址,一般baseminor為0 (次設(shè)備號(hào)以baseminor為起始地址地址開(kāi)始遞)
* count:要申請(qǐng)的設(shè)備號(hào)數(shù)量
* name:設(shè)備名字
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) 

設(shè)備號(hào)的釋放函數(shù):

/*
* from:要釋放的設(shè)備號(hào)
* count:表示從from開(kāi)始,要釋放的設(shè)備號(hào)數(shù)量
*/
void unregister_chrdev_region(dev_t from, unsigned count) 

3 字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)模板

3.1 加載與卸載

在編寫驅(qū)動(dòng)的時(shí)候需要注冊(cè)模塊加載和卸載這兩種函數(shù):

module_init(xxx_init);   //注冊(cè)模塊加載函數(shù) 
module_exit(xxx_exit);   //注冊(cè)模塊卸載函數(shù) 

module_init()用來(lái)向Linux內(nèi)核注冊(cè)一個(gè)模塊加載函數(shù),參數(shù)xxx_init就是需要注冊(cè)的具體函數(shù),當(dāng)使用 “insmod” 命令加載驅(qū)動(dòng)的時(shí)候,xxx_init這個(gè)函數(shù)就會(huì)被調(diào)用。

module_exit()用來(lái)向Linux內(nèi)核注冊(cè)一個(gè)模塊卸載函數(shù),參數(shù)xxx_exit就是需要注冊(cè)的具體函數(shù),當(dāng)使 用“rmmod”命令卸載具體驅(qū)動(dòng)的時(shí)候 xxx_exit函數(shù)就會(huì)被調(diào)用。

字符設(shè)備驅(qū)動(dòng)模塊加載和卸載模板如下所示:

/* 驅(qū)動(dòng)入口函數(shù) */ 
static int __init xxx_init(void) 
{ 
	/*入口函數(shù)內(nèi)容 */ 
	return 0; 
} 

/* 驅(qū)動(dòng)出口函數(shù) */ 
static void __exit xxx_exit(void) 
{ 
	/*出口函數(shù)內(nèi)容*/ 
} 

/*指定為驅(qū)動(dòng)的入口和出口函數(shù) */ 
module_init(xxx_init); 
module_exit(xxx_exit); 

驅(qū)動(dòng)編譯完成以后擴(kuò)展名為.ko, 有兩種命令可以加載驅(qū)動(dòng)模塊:

insmod:最簡(jiǎn)單的模塊加載命令,用于加載指定的.ko模塊,此命令不能解決模塊的依賴關(guān)系

modprobe:該命令會(huì)分析模塊的依賴關(guān)系,將所有的依賴模塊都加載到內(nèi)核中,因此更智能

modprobe 命令默認(rèn)會(huì)去/lib/modules/目錄中查找模塊(自制的根文件系統(tǒng)沒(méi)有這個(gè)目錄,需要手動(dòng)創(chuàng)建)

卸載驅(qū)動(dòng)也有兩種命令:

rmmod:例如使用rmmod drv.ko來(lái)卸載 drv.ko這一個(gè)模塊

modprobe -r:該命令除了卸載指定的驅(qū)動(dòng),還卸載其所依賴的其他模塊,若這些依賴模塊還在被其它模塊使用,就不能使用 modprobe來(lái)卸載驅(qū)動(dòng)模塊!!!

3.2 注冊(cè)與注銷

對(duì)于字符設(shè)備驅(qū)動(dòng)而言,當(dāng)驅(qū)動(dòng)模塊加載成功以后需要注冊(cè)字符設(shè)備,同樣,卸載驅(qū)動(dòng)模塊的時(shí)候也需要注銷掉字符設(shè)備

字符設(shè)備的注冊(cè)函數(shù)原型如下所示:

/* func: register_chrdev 注冊(cè)字符設(shè)備
* major:主設(shè)備號(hào)
* name:設(shè)備名字,指向一串字符串
* fops:結(jié)構(gòu)體 file_operations 類型指針,指向設(shè)備的操作函數(shù)集合變量
*/
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) 

字符設(shè)備的注銷函數(shù)原型如下所示:

/* func: unregister_chrdev 注銷字符設(shè)備
* majo:要注銷的設(shè)備對(duì)應(yīng)的主設(shè)備號(hào) 
* name:要注銷的設(shè)備對(duì)應(yīng)的設(shè)備名
*/
static inline void unregister_chrdev(unsigned int major, const char *name) 

一般字符設(shè)備的注冊(cè)在驅(qū)動(dòng)模塊的入口函數(shù) xxx_init 中進(jìn)行,字符設(shè)備的注銷在驅(qū)動(dòng)模塊的出口函數(shù) xxx_exit 中進(jìn)行。

static struct file_operations test_fops;

/* 驅(qū)動(dòng)入口函數(shù) */ 
static int __init xxx_init(void) 
{ 
	/* 入口函數(shù)具體內(nèi)容 */ 
    int retvalue = 0; 
    /* 注冊(cè)字符設(shè)備驅(qū)動(dòng) */ 
	retvalue = register_chrdev(200, "chrtest", &test_fops); 
	if(retvalue < 0)
    { 
		/*  字符設(shè)備注冊(cè)失敗, 自行處理 */ 
	} 
	return 0; 
} 

/* 驅(qū)動(dòng)出口函數(shù) */ 
static void __exit xxx_exit(void) 
{ 
	/* 注銷字符設(shè)備驅(qū)動(dòng) */ 
	unregister_chrdev(200, "chrtest"); 
} 

/* 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */ 
module_init(xxx_init); 
module_exit(xxx_exit); 

注:選擇沒(méi)有被使用的主設(shè)備號(hào),可輸入命令“cat /proc/devices”來(lái)查看當(dāng)前已經(jīng)被使用掉的設(shè)備號(hào)

3.3 實(shí)現(xiàn)設(shè)備的具體操作函數(shù)

file_operations 結(jié)構(gòu)體就是設(shè)備的具體操作函數(shù)。

假設(shè)對(duì)chrtest這個(gè)設(shè)備有如下兩個(gè)要求:

能夠?qū)崿F(xiàn)打開(kāi)和關(guān)閉操作:需要實(shí)現(xiàn) file_operations 中的openrelease這兩個(gè)函數(shù)

能夠?qū)崿F(xiàn)進(jìn)行讀寫操作:需要實(shí)現(xiàn) file_operations 中的readwrite這兩個(gè)函數(shù)

首先是 打開(kāi)(open)、讀取(read)、寫入(write)、釋放(release) 4個(gè)基本操作

/*打開(kāi)設(shè)備*/ 
static int chrtest_open(struct inode *inode, struct file *filp) 
{ 
	/*用戶實(shí)現(xiàn)具體功能*/
	return 0; 
} 

/*從設(shè)備讀取*/ 
static ssize_t chrtest_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{ 
	/*用戶實(shí)現(xiàn)具體功能*/
	return 0; 
} 

/*向設(shè)備寫數(shù)據(jù)*/ 
static ssize_t chrtest_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 
{ 
	/*用戶實(shí)現(xiàn)具體功能*/
	return 0; 
} 

/*關(guān)閉釋放設(shè)備*/ 
static int chrtest_release(struct inode *inode, struct file *filp) 
{ 
	 /*用戶實(shí)現(xiàn)具體功能*/ 
	return 0; 
} 

然后是 驅(qū)動(dòng)的入口(init)和出口(exit) 函數(shù):

/*文件操作結(jié)構(gòu)體*/
static struct file_operations test_fops = { 
	.owner = THIS_MODULE,    
	.open = chrtest_open, 
	.read = chrtest_read, 
	.write = chrtest_write, 
	.release = chrtest_release, 
}; 

/*驅(qū)動(dòng)入口函數(shù)*/ 
static int __init xxx_init(void) 
{ 
	/*入口函數(shù)具體內(nèi)容*/ 
	int retvalue = 0; 

    /*注冊(cè)字符設(shè)備驅(qū)動(dòng)*/ 
	retvalue = register_chrdev(200, "chrtest", &test_fops); 
	if(retvalue < 0)
    { 
		/*字符設(shè)備注冊(cè)失敗*/ 
	} 
	return 0; 
}

/*驅(qū)動(dòng)出口函數(shù)*/ 
static void __exit xxx_exit(void) 
{
	/*注銷字符設(shè)備驅(qū)動(dòng)*/ 
	unregister_chrdev(200, "chrtest"); 
}

/*指定為驅(qū)動(dòng)的入口和出口函數(shù)*/ 
module_init(xxx_init); 
module_exit(xxx_exit); 

3.4 添加LICENSE和作者信息

LICENSE是必須添加的,否則編譯時(shí)會(huì)報(bào)錯(cuò),作者信息可加可不加。

MODULE_LICENSE()  //添加模塊 LICENSE 信息 
MODULE_AUTHOR()   //添加模塊作者信息

總結(jié)一下:

poYBAGIx_5GAAfbZAAEAxs0IOYU711.png

4 字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)實(shí)驗(yàn)

下面以正點(diǎn)原子提供的教程中的chrdevbase這個(gè)虛擬設(shè)備為例,完整的編寫一個(gè)字符設(shè)備驅(qū)動(dòng)模塊。chrdevbase不是實(shí)際存在的一個(gè)設(shè)備,只是為了學(xué)習(xí)字符設(shè)備的開(kāi)發(fā)的流程。

4.1 程序編寫

需要分別編寫驅(qū)動(dòng)程序應(yīng)用程序

注:為了區(qū)分兩個(gè)程序的打印信息,在驅(qū)動(dòng)程序的打印前都添加“[BSP]”標(biāo)識(shí),在應(yīng)用程序的打印前都添加”[APP]“標(biāo)識(shí)

4.1.1 編寫驅(qū)動(dòng)程序

一些定義

#define CHRDEVBASE_MAJOR	200				/*主設(shè)備號(hào)*/
#define CHRDEVBASE_NAME		"chrdevbase" 	/*設(shè)備名*/

static char readbuf[100];		/*讀緩沖區(qū)*/
static char writebuf[100];		/*寫緩沖區(qū)*/
static char kerneldata[] = {"kernel data!"}; /*內(nèi)核驅(qū)動(dòng)中的數(shù)據(jù),用來(lái)測(cè)試應(yīng)用程序讀取該數(shù)據(jù)*/

打開(kāi)、關(guān)閉、讀取、寫入

/*
 * @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 chrdevbase_open(struct inode *inode, struct file *filp)
{
	printk("[BSP] chrdevbase open!\n");
	return 0;
}

/*
 * @description		: 從設(shè)備讀取數(shù)據(jù) 
 * @param - filp 	: 要打開(kāi)的設(shè)備文件(文件描述符)
 * @param - buf 	: 返回給用戶空間的數(shù)據(jù)緩沖區(qū)
 * @param - cnt 	: 要讀取的數(shù)據(jù)長(zhǎng)度
 * @param - offt 	: 相對(duì)于文件首地址的偏移
 * @return 			: 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	
	/* 向用戶空間發(fā)送數(shù)據(jù) */
	memcpy(readbuf, kerneldata, sizeof(kerneldata));
    
	retvalue = copy_to_user(buf, readbuf, cnt);
	if(retvalue == 0)
    {
		printk("[BSP] kernel senddata ok!\n");
	}
    else
    {
		printk("[BSP] kernel senddata failed!\n");
	}
	
	printk("[BSP] chrdevbase read!\n");
	return 0;
}

/*
 * @description		: 向設(shè)備寫數(shù)據(jù) 
 * @param - filp 	: 設(shè)備文件,表示打開(kāi)的文件描述符
 * @param - buf 	: 要寫給設(shè)備寫入的數(shù)據(jù)
 * @param - cnt 	: 要寫入的數(shù)據(jù)長(zhǎng)度
 * @param - offt 	: 相對(duì)于文件首地址的偏移
 * @return 			: 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
    
	/* 接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來(lái) */
	retvalue = copy_from_user(writebuf, buf, cnt);
	if(retvalue == 0)
    {
		printk("[BSP] kernel recevdata:%s\n", writebuf);
	}
    else
    {
		printk("[BSP] kernel recevdata failed!\n");
	}
	
	printk("[BSP] chrdevbase write!\n");
	return 0;
}

/*
 * @description		: 關(guān)閉/釋放設(shè)備
 * @param - filp 	: 要關(guān)閉的設(shè)備文件(文件描述符)
 * @return 			: 0 成功;其他 失敗
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
	printk("[BSP] chrdevbase release!\n");
	return 0;
}

驅(qū)動(dòng)加載與注銷

/*
 * 設(shè)備操作函數(shù)結(jié)構(gòu)體
 */
static struct file_operations chrdevbase_fops = {
	.owner = THIS_MODULE,	
	.open = chrdevbase_open,
	.read = chrdevbase_read,
	.write = chrdevbase_write,
	.release = chrdevbase_release,
};

/*
 * @description	: 驅(qū)動(dòng)入口函數(shù) 
 * @param 		: 無(wú)
 * @return 		: 0 成功;其他 失敗
 */
static int __init chrdevbase_init(void)
{
	int retvalue = 0;

	/* 注冊(cè)字符設(shè)備驅(qū)動(dòng) */
	retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
	if(retvalue < 0)
    {
		printk("[BSP] chrdevbase driver register failed\n");
	}
	printk("[BSP] chrdevbase init!\n");
	return 0;
}

/*
 * @description	: 驅(qū)動(dòng)出口函數(shù)
 * @param 		: 無(wú)
 * @return 		: 無(wú)
 */
static void __exit chrdevbase_exit(void)
{
	/* 注銷字符設(shè)備驅(qū)動(dòng) */
	unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
	printk("[BSP] chrdevbase exit!\n");
}

/*將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù)*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

最后的LIENSE與作者

/*LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai & xxpcb"); //本篇的程序代碼在“正點(diǎn)原子”左大神提供的代碼上進(jìn)行修改

4.1.2 編寫應(yīng)用程序

這里把程序截取為3段分析,首先看開(kāi)頭

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

static char usrdata[] = {"usr data!"}; /*應(yīng)用程序中的數(shù)據(jù),用于測(cè)試通過(guò)驅(qū)動(dòng)訪問(wèn)寫入內(nèi)核*/

int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	char readbuf[100], writebuf[100];

	if(argc != 3)
    {
		printf("[APP] Error Usage!\n");
		return -1;
	}

    //參數(shù)1是驅(qū)動(dòng)的文件名,用來(lái)指定驅(qū)動(dòng)的位置
	filename = argv[1];

	//【1】打開(kāi)驅(qū)動(dòng)文件
	fd  = open(filename, O_RDWR);
	if(fd < 0)
    {
		printf("[APP] Can't open file %s\n", filename);
		return -1;
	}
    printf("[APP] open file: '%s' success\n", filename);

主要是一些頭文件和main函數(shù)入口,調(diào)用main函數(shù)時(shí)需要傳入2個(gè)參數(shù)(實(shí)際是3個(gè)參數(shù),函數(shù)名本身是默認(rèn)的第0個(gè)參數(shù),不需要手動(dòng)指定),具體作用為:

參數(shù)0:argv[0],函數(shù)名本身,這里不作用途

參數(shù)1:argv[1],filename,這里不作用途

參數(shù)2:argv[2],自定義的操作參數(shù),下面函數(shù)會(huì)講到,1為從驅(qū)動(dòng)文件中讀取,2為向驅(qū)動(dòng)文件中寫入數(shù)據(jù)

再來(lái)看具體操作:

    //【2】從驅(qū)動(dòng)文件讀取數(shù)據(jù)
	if(atoi(argv[2]) == 1)//參數(shù)1表示【讀取】?jī)?nèi)核中的數(shù)據(jù)
    { 
		retvalue = read(fd, readbuf, 50);
		if(retvalue < 0)
        {
			printf("[APP] read file '%s' failed!\n", filename);
		}
        else
        {
			/* 讀取成功,打印出讀取成功的數(shù)據(jù) */
			printf("[APP] read data:%s\n",readbuf);
		}
	}
    //【3】向設(shè)備驅(qū)動(dòng)寫數(shù)據(jù)
	if(atoi(argv[2]) == 2)//參數(shù)2表示向內(nèi)核中【寫入】數(shù)據(jù)
    {
		memcpy(writebuf, usrdata, sizeof(usrdata));
		retvalue = write(fd, writebuf, 50);
		if(retvalue < 0)
        {
			printf("[APP] write file %s failed!\n", filename);
		}
        else
        {
            printf("[APP] write data:'%s' to file ok\n", writebuf);
        }
	}

最后是關(guān)閉設(shè)備

	//【4】關(guān)閉設(shè)備
	retvalue = close(fd);
	if(retvalue < 0)
    {
		printf("[APP] Can't close file %s\n", filename);
		return -1;
	}
	printf("[APP] close file ok\r\n");

	return 0;
}

關(guān)閉即表示不再使用該設(shè)備了(若要再使用則重新打開(kāi)即可),通過(guò)關(guān)閉驅(qū)動(dòng)文件來(lái)實(shí)現(xiàn)字符設(shè)備驅(qū)動(dòng)的關(guān)閉。

4.2 程序編譯

4.2.1 編譯驅(qū)動(dòng)程序

編譯驅(qū)動(dòng),即編譯chrdevbase.c這個(gè)文件為.ko 模塊,使用Makefile來(lái)編譯,先創(chuàng)建Makefile:

KERNELDIR := /home/xxpcb/myTest/imx6ull/kernel/nxp_kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

各行含義:

KERNELDIR:開(kāi)發(fā)板所使用的Linux內(nèi)核源碼目錄

CURRENT_PATH:當(dāng)前路徑,通過(guò)運(yùn)行“pwd”命令獲取

obj-m:將 chrdevbase.c 這個(gè)文件編譯為chrdevbase.ko模塊

具體的編譯命令:后面的modules表示編譯模塊,-C 表示切換工作目錄到KERNERLDIR目錄,M表示模塊源碼目錄

輸入“make”命令即可編譯,編譯后會(huì)出現(xiàn)許多編譯文件

pYYBAGIx_6SALhsxAAFV2abmvkw000.png

注:若直接make編譯報(bào)如下錯(cuò)誤,是因?yàn)閗ernel中沒(méi)有指定編譯器和架構(gòu),使用了默認(rèn)的x86平臺(tái)編譯報(bào)錯(cuò)。

pYYBAGIx_6uAB8ssAAFRkPfoi04933.png

修改Kernel工程的頂層Makefile,直接定義ARCH和CROSS_COMPILE 這兩個(gè)的變量值為 arm 和 arm-linux-gnueabihf-

(內(nèi)核篇的介紹見(jiàn):i.MX6ULL嵌入式Linux開(kāi)發(fā)3-Kernel移植)

poYBAGIx_7OAFZGEAAHM8Vq9p9g685.png

4.2.2 編譯應(yīng)用程序

編譯應(yīng)用程序不需要內(nèi)核文件參與,只有一個(gè)文件就能編譯,因此直接輸入指令進(jìn)行編譯:

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

編譯會(huì)生chrdevbaseApp,它是32位LSB格式的ARM版本可執(zhí)行文件

pYYBAGIx_7qAYyOQAADRLEqdxKc780.png

4.3 測(cè)試

上一篇文章(i.MX6ULL嵌入式Linux開(kāi)發(fā)6-系統(tǒng)燒寫到eMM)已經(jīng)實(shí)現(xiàn)了系統(tǒng)移植的打包燒錄工作,系統(tǒng)已經(jīng)燒錄的EMMC中了。這次我們就直接在這個(gè)基礎(chǔ)上進(jìn)行實(shí)驗(yàn)。

4.3.1 創(chuàng)建驅(qū)動(dòng)模塊目錄

加載驅(qū)動(dòng)模塊,使用的modprobe命令,會(huì)從特定的目錄下尋找文件。比如開(kāi)發(fā)板使用的是4.1.15版的Linux內(nèi)核 ,則是“/lib/modules/4.1.15”這個(gè)目錄,這個(gè)目錄一般是沒(méi)有的,需要根據(jù)Linux內(nèi)核的版本自己創(chuàng)建。

注意這是開(kāi)發(fā)板的文件系統(tǒng)中的路徑,可以通過(guò)串口連接進(jìn)入開(kāi)發(fā)板,通過(guò)linux指令創(chuàng)建該目錄。

4.3.2 發(fā)送文件到開(kāi)發(fā)板(TFTP傳輸)

此次測(cè)試首先需要將ubuntu中編譯的文件傳輸?shù)桨遄?/strong>中運(yùn)行,怎么傳輸呢?可以使用TFTP傳輸服務(wù)。

poYBAGIx__mAJAMxAAFqlflsgR8688.png

之前的文章(i.MX6ULL嵌入式Linux開(kāi)發(fā)2-uboot移植實(shí)踐)中已經(jīng)介紹了如何在ubuntu中搭建TFTP服務(wù)器

搭建好TFTP服務(wù)后,開(kāi)始傳輸文件到開(kāi)發(fā)板具體的傳輸步驟為:

開(kāi)發(fā)板連接網(wǎng)線,與ubuntu虛擬機(jī)處于同一局域網(wǎng)內(nèi)

確保ubuntu已安裝的TFTP服務(wù),并設(shè)置了TFTP服務(wù)文件夾

將ubuntu中編譯好的文件復(fù)制到ubuntu的TFTP服務(wù)文件夾中!!!

mv chrdevbaseApp ~/myTest/tftpboot/
mv chrdevbase.ko ~/myTest/tftpboot/

注:編譯完程序,在傳輸?shù)桨遄又埃欢ㄒ浀冒盐募葟?fù)制到TFTP文件夾中,否則板子獲取到的可能是TFTP文件夾中的舊文件。

開(kāi)發(fā)板的串口中通過(guò)如下指令來(lái)將ubuntu中的文件傳輸?shù)介_(kāi)發(fā)板中

cd /lib/modules/4.1.15   /*確保在要下載文件的目錄中,若已在,則忽略*/
tftp -g -r chrdevbaseApp 192.168.5.101 /*獲取chrdevbaseApp文件*/
tftp -g -r chrdevbase.ko 192.168.5.101 /*獲取chrdevbase.ko文件*/

這里的-g代表get,即下載文件,-r代表remote file,即遠(yuǎn)程主機(jī)的文件名,然后是要下載的文件名,最后的遠(yuǎn)程主機(jī)ubuntu的IP地址

輸入該指令后,可以看到文件傳輸進(jìn)度,如下圖:

poYBAGIyAAGAOFfdAAAnPNVU2dE378.png

4.3.3 開(kāi)始測(cè)試

驅(qū)動(dòng)文件chrdevbase.ko和應(yīng)用文件chrdevbaseApp傳輸?shù)桨遄又械?lib/modules/4.1.15目錄后,就可以測(cè)試了。

首先使用insmod命令來(lái)加載驅(qū)動(dòng),然后使用lsmod查看當(dāng)前的驅(qū)動(dòng)(只有一個(gè)我們剛加載的字符驅(qū)動(dòng)),再使用使用cat指令查看devices 信息,確認(rèn)系統(tǒng)中是否已經(jīng)列舉了該設(shè)備,3條指令如下:

insmod chrdevbase.ko 
lsmod
cat /proc/devices 

具體是輸出信息:

pYYBAGIyAAmAFyp0AAB4iFrzgsc420.png

可以看出,系統(tǒng)中存在chrdevbase設(shè)備,主設(shè)備號(hào)為程序中設(shè)定的200。

驅(qū)動(dòng)加載后,還要在/dev目錄下創(chuàng)建一個(gè)對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)文件(應(yīng)用程序就是通過(guò)該節(jié)點(diǎn)文件實(shí)現(xiàn)對(duì)設(shè)備的操作)。

輸入如下2條命令創(chuàng)建/dev/chrdevbase這個(gè)設(shè)備節(jié)點(diǎn)文件,并查看結(jié)果:

mknod /dev/chrdevbase c 200 0 
ls /dev/chrdevbase -l
pYYBAGIyAA-AX6J5AAAy_IcL8LE995.png

至此,字符設(shè)備驅(qū)動(dòng)已經(jīng)加載完成,可以測(cè)試我們的應(yīng)用程序了,也就是

按照上面程序的設(shè)定,1是讀,2是寫:

./chrdevbaseApp /dev/chrdevbase 1  
./chrdevbaseApp /dev/chrdevbase 2

先來(lái)看“讀測(cè)試”,注意要給chrdevbaseApp可執(zhí)行的權(quán)限,否則無(wú)法運(yùn)行。

poYBAGIyABWAU-ruAADTDlIHaAc973.png

圖中下部是程序輸出信息,但似乎只有BSP驅(qū)動(dòng)程序的的輸出,沒(méi)有APP應(yīng)用程序的輸出,應(yīng)該是內(nèi)核打印printk與應(yīng)用的打印printf沖突了,導(dǎo)致APP的打印被擠掉了。

再來(lái)看“寫測(cè)試'',同樣也是只有BSP的打印

pYYBAGIyABuAGx8pAAAVuxovM8w140.png

4.3.4 打印沖突問(wèn)題規(guī)避

對(duì)于打印沖突問(wèn)題,我們可以先在每個(gè)printf前后加個(gè)sleep(1)的1秒延時(shí),這樣可以先避免打印沖突。

增加延時(shí)后再次測(cè)試,打印正常:

poYBAGIyACKACZljAAB8A9RNG8I614.png

測(cè)試完,最后是rmmod命令卸載模塊:

pYYBAGIyACaAAjSlAAAKmjegX_U693.png

5 總結(jié)

本篇介紹了嵌入式Linux驅(qū)動(dòng)開(kāi)發(fā)中的基礎(chǔ)驅(qū)動(dòng)——字符驅(qū)動(dòng)開(kāi)發(fā)的基本模式,使用了一個(gè)虛擬的字符設(shè)備驅(qū)動(dòng)進(jìn)行測(cè)試,了解驅(qū)動(dòng)程序與應(yīng)用程序之間的調(diào)用關(guān)系。
審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 設(shè)備
    +關(guān)注

    關(guān)注

    2

    文章

    4543

    瀏覽量

    70853
  • 模板
    +關(guān)注

    關(guān)注

    0

    文章

    108

    瀏覽量

    20608
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    i.MX6ULL 驅(qū)動(dòng)開(kāi)發(fā)7—按鍵輸入捕獲與GPIO輸入配置與高低電平讀取

    本篇主要介紹了i.MX6ULL的按鍵檢測(cè)的使用,主要的知識(shí)點(diǎn)是設(shè)備樹(shù)的修改,以及GPIO的輸入配置與高低電平的讀取。
    的頭像 發(fā)表于 05-24 09:11 ?6340次閱讀
    <b class='flag-5'>i.MX6ULL</b> <b class='flag-5'>驅(qū)動(dòng)</b><b class='flag-5'>開(kāi)發(fā)</b>7—按鍵輸入捕獲與GPIO輸入配置與高低電平讀取

    i.MX6ULL嵌入式Linux開(kāi)發(fā)1-uboot移植初探

    本系列教程以i.MX6ULL處理器的ARM開(kāi)發(fā)板為實(shí)驗(yàn)基礎(chǔ),學(xué)習(xí)記錄嵌入式Linux開(kāi)發(fā)的各種知識(shí)與經(jīng)驗(yàn),主要內(nèi)容包括嵌入式Linux移植,嵌入式Linux驅(qū)動(dòng)
    的頭像 發(fā)表于 03-07 08:57 ?3932次閱讀
    <b class='flag-5'>i.MX6ULL</b>嵌入式Linux<b class='flag-5'>開(kāi)發(fā)</b><b class='flag-5'>1</b>-uboot移植初探

    i.MX6ULL驅(qū)動(dòng)開(kāi)發(fā)2—新字符設(shè)備開(kāi)發(fā)模板

    上篇文章介紹了字符設(shè)備開(kāi)發(fā)模板,但那是一種舊版本的驅(qū)動(dòng)開(kāi)發(fā)模式,
    的頭像 發(fā)表于 03-17 09:11 ?3230次閱讀
    <b class='flag-5'>i.MX6ULL</b><b class='flag-5'>驅(qū)動(dòng)</b><b class='flag-5'>開(kāi)發(fā)</b>2—新<b class='flag-5'>字符</b><b class='flag-5'>設(shè)備</b><b class='flag-5'>開(kāi)發(fā)</b><b class='flag-5'>模板</b>

    使用i.MX6ULL開(kāi)發(fā)板進(jìn)行Linux根文件系統(tǒng)的完善

    上一篇推文講了怎么移植根文件系統(tǒng),并在i.MX6ULL開(kāi)發(fā)板中運(yùn)行起來(lái),但是會(huì)出現(xiàn)一些提示,現(xiàn)在來(lái)進(jìn)行根文件的完善。
    發(fā)表于 10-17 11:13 ?831次閱讀

    移植NXP官方linux 5.4內(nèi)核到i.MX6ULL開(kāi)發(fā)

    本文描述移植NXP官方 linux 5.4 內(nèi)核到i.MX6ULL開(kāi)發(fā)板。
    發(fā)表于 12-19 11:10 ?2111次閱讀

    I.MX6ULL終結(jié)者開(kāi)發(fā)板裸機(jī)仿真jlink調(diào)試

    I.MX6ULL‘終結(jié)者’開(kāi)發(fā)板預(yù)留了JTAG仿真接口,并給出了開(kāi)發(fā)文檔,可以實(shí)現(xiàn)在JLINK仿真器條件下的單步跟蹤、斷點(diǎn)調(diào)試等功能,使得開(kāi)發(fā)研究i
    發(fā)表于 07-07 10:56

    i.MX6ULL開(kāi)發(fā)板硬件資源

    迅為i.MX6ULL 終結(jié)者開(kāi)發(fā)板硬件資源非常豐富,幾乎將 i.MX6ULL 芯片的所有資源都擴(kuò)展引出到底板上了,底板提供了豐富的外設(shè)接口,開(kāi)發(fā)板的尺寸是 190mm*125mm,充分
    發(fā)表于 12-29 06:18

    初識(shí) i.MX6ULL 寄存器

    裸機(jī)開(kāi)發(fā)_L1_匯編LED實(shí)驗(yàn)0. 本節(jié)目標(biāo)1. 硬件層電路2. 初識(shí) i.MX6ULL 寄存器2.1 i.MX6ULL 時(shí)鐘控制寄存器2.
    發(fā)表于 12-20 07:13

    飛凌i.MX6ULL開(kāi)發(fā)板的評(píng)測(cè),再次進(jìn)階擁有更高的性價(jià)比

    處理器MCIMX6Y2開(kāi)發(fā)設(shè)計(jì),采用先進(jìn)的ARMCortex-A7內(nèi)核,運(yùn)行速度高達(dá)800MHz。i.MX6ULL應(yīng)用處理器包括一個(gè)集成的電源管理模塊,降低了外接電源的復(fù)雜性,并簡(jiǎn)化了上電時(shí)序。
    發(fā)表于 10-27 11:55 ?1507次閱讀
    飛凌<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>開(kāi)發(fā)</b>板的評(píng)測(cè),再次進(jìn)階擁有更高的性價(jià)比

    基于NXP i.MX6ULL處理器的FETMX6ULL-C核心板

    合作伙伴,飛凌不負(fù)美譽(yù),基于i.MX6ULL匠心打造的FETMX6ULL-S核心板一經(jīng)問(wèn)世便好評(píng)不斷,且已有數(shù)百家來(lái)自工業(yè)、醫(yī)療、電力、物聯(lián)網(wǎng)等行業(yè)的用戶采用此款核心板快速完成了整機(jī)產(chǎn)品的開(kāi)發(fā)上市。
    發(fā)表于 04-11 15:05 ?1173次閱讀
    基于NXP <b class='flag-5'>i.MX6ULL</b>處理器的FETMX<b class='flag-5'>6ULL</b>-C核心板

    i.MX6ULL驅(qū)動(dòng)開(kāi)發(fā)4——點(diǎn)亮LED(寄存器版)

    本篇主要介紹了如何通過(guò)操作寄存器來(lái)點(diǎn)亮i.MX6ULL開(kāi)發(fā)板上的led,通過(guò)編寫LED對(duì)應(yīng)的驅(qū)動(dòng)程序和應(yīng)用程序,實(shí)現(xiàn)程序設(shè)計(jì)的分層。
    的頭像 發(fā)表于 05-21 21:26 ?3034次閱讀
    【<b class='flag-5'>i.MX6ULL</b>】<b class='flag-5'>驅(qū)動(dòng)</b><b class='flag-5'>開(kāi)發(fā)</b>4——點(diǎn)亮LED(寄存器版)

    i.MX6ULL|字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)實(shí)踐

    字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的基本步驟可以看上一篇,本節(jié)就以 chrdevbase 這個(gè)虛擬設(shè)備為例,完整的編寫一個(gè)
    的頭像 發(fā)表于 10-31 11:27 ?720次閱讀

    【北京迅為】i.MX6ULL開(kāi)發(fā)板移植 Debian 文件系統(tǒng)

    【北京迅為】i.MX6ULL開(kāi)發(fā)板移植 Debian 文件系統(tǒng)
    的頭像 發(fā)表于 02-10 15:34 ?1233次閱讀
    【北京迅為】<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>開(kāi)發(fā)</b>板移植 Debian 文件系統(tǒng)

    基于i.MX6ULL的掉電檢測(cè)設(shè)計(jì)與軟件測(cè)試

    基于i.MX6ULL的掉電檢測(cè)設(shè)計(jì)與軟件測(cè)試基于i.MX6ULL平臺(tái)設(shè)計(jì)實(shí)現(xiàn)掉電檢測(cè)功能,首先選擇一路IO,利用IO電平變化觸發(fā)中斷,在編寫驅(qū)動(dòng)時(shí)捕獲該路GPIO的中斷,然后在中斷響應(yīng)函數(shù)中發(fā)
    的頭像 發(fā)表于 11-09 10:40 ?925次閱讀
    基于<b class='flag-5'>i.MX6ULL</b>的掉電檢測(cè)設(shè)計(jì)與軟件測(cè)試

    【迅為電子】i.MX6UL和i.MX6ULL芯片區(qū)別與開(kāi)發(fā)板對(duì)比

    【迅為電子】i.MX6UL和i.MX6ULL芯片區(qū)別與開(kāi)發(fā)板對(duì)比
    的頭像 發(fā)表于 11-28 14:31 ?570次閱讀
    【迅為電子】<b class='flag-5'>i.MX6</b>UL和<b class='flag-5'>i.MX6ULL</b>芯片區(qū)別與<b class='flag-5'>開(kāi)發(fā)</b>板對(duì)比
    主站蜘蛛池模板: 日日摸夜夜添免费毛片小说 | cijilu刺激 国产免费的 | 免费看的一级毛片 | 黑粗硬大欧美视频 | 综合激情六月 | 中文天堂在线最新版在线www | 天堂在线视频观看 | 免费四虎永久在线精品 | 亚洲成色www久久网站 | 激情五月视频 | 酒色影院 | 夜夜春夜夜夜夜猛噜噜噜噜噜 | 9久热久re爱免费精品视频 | 狠狠色狠狠色综合婷婷tag | 久久精品免费看 | 天天干天天爱天天操 | 激情综合五月网 | 四虎永久在线精品免费观看地址 | 亚洲午夜精品久久久久久成年 | 日本三级免费观看 | 亚洲一区二区在线视频 | 九九视频这里只有精品 | 2019天天操夜夜操 | 狠狠色狠狠色狠狠五月ady | 中文字幕 亚洲一区 | 91极品女神嫩模在线播放 | 亚洲理论在线 | 欧美一级爱操视频 | 国产成人a一区二区 | 色偷偷888欧美精品久久久 | 欧美黄免在线播放 | 9色网站| 天天躁夜夜躁狠狠躁2018a | 国产三级久久久精品三级 | 九九热精品在线 | 在线午夜 | 色婷婷久久免费网站 | 丁香六月纪婷婷激情综合 | 国产精品伦理一区二区三区 | 日韩特级毛片免费观看视频 | 男人天堂伊人网 |