pos機(jī)電腦驅(qū)動(dòng),我在ARM板上寫的第一個(gè)驅(qū)動(dòng)程序

 新聞資訊  |   2023-05-17 11:50  |  投稿人:pos機(jī)之家

網(wǎng)上有很多關(guān)于pos機(jī)電腦驅(qū)動(dòng),我在ARM板上寫的第一個(gè)驅(qū)動(dòng)程序的知識(shí),也有很多人為大家解答關(guān)于pos機(jī)電腦驅(qū)動(dòng)的問(wèn)題,今天pos機(jī)之家(m.dsth100338.com)為大家整理了關(guān)于這方面的知識(shí),讓我們一起來(lái)看下吧!

本文目錄一覽:

1、pos機(jī)電腦驅(qū)動(dòng)

pos機(jī)電腦驅(qū)動(dòng)

摘要:搞嵌入式有兩個(gè)方向,一個(gè)是嵌入式軟件開發(fā)(MCU方向),另一個(gè)是嵌入式軟件開發(fā)(linux方向)。其中MCU方向基本是裸機(jī)開發(fā)和RTOS開發(fā)。而Linux開發(fā)方向又分為驅(qū)動(dòng)開發(fā)和應(yīng)用開發(fā)。其中應(yīng)用開發(fā)相比于驅(qū)動(dòng)開發(fā)來(lái)說(shuō)簡(jiǎn)單一些,因?yàn)楦泸?qū)動(dòng)你要和Linux內(nèi)核打交道。而我們普通的單片機(jī)開發(fā)就是應(yīng)用開發(fā),和Linux開發(fā)沒多大區(qū)別,單片機(jī)你去調(diào)別人寫好的庫(kù),Linux應(yīng)用你也是調(diào)別人的驅(qū)動(dòng)程序。

很多人學(xué)習(xí)的路線是:?jiǎn)纹瑱C(jī)到RTOS,再到Linux,這個(gè)路線其實(shí)是非常好,循序漸進(jìn)。因?yàn)槟銓W(xué)了單片機(jī),所以你對(duì)RTOS的學(xué)習(xí)會(huì)很容易理解,單片機(jī)+RTOS在市面上也可以找到一個(gè)很好的工作。因?yàn)槟銓W(xué)了RTOS,你會(huì)發(fā)現(xiàn)Linux驅(qū)動(dòng)開發(fā)其實(shí)和RT-Thread的驅(qū)動(dòng)程序非常像,其實(shí)RT-Thread驅(qū)動(dòng)大概率可能是仿Linux驅(qū)動(dòng)而寫的。所以如果你現(xiàn)在在學(xué)RT-Thread,那么你后面去搞Linux驅(qū)動(dòng)也是非常容易上手。

當(dāng)然做驅(qū)動(dòng)去之前你還是要學(xué)習(xí)一下ubuntu操作系統(tǒng)、ARM裸機(jī)和linux系統(tǒng)移植,其目的就是為學(xué)習(xí)嵌入式linux驅(qū)動(dòng)開發(fā)做準(zhǔn)備。

話不多說(shuō)先來(lái)一個(gè)hello驅(qū)動(dòng)程序。

在Linux中,驅(qū)動(dòng)分為三大類:

字符設(shè)備驅(qū)動(dòng)字符設(shè)備驅(qū)動(dòng)是占用篇幅最大的一類驅(qū)動(dòng),因?yàn)樽址O(shè)備最多,從最簡(jiǎn)單的點(diǎn)燈到 I2C、SPI、音頻等都屬于字符設(shè)備驅(qū)動(dòng)的類型。塊設(shè)備驅(qū)動(dòng)塊設(shè)備和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)要比字符設(shè)備驅(qū)動(dòng)復(fù)雜,就是因?yàn)槠鋸?fù)雜所以半導(dǎo)體廠商一般都給我們編寫好了,大多數(shù)情況下都是直接可以使用的。所謂的塊設(shè)備驅(qū)動(dòng)就是存儲(chǔ)器設(shè)備的驅(qū)動(dòng),比如 EMMC、NAND、SD 卡和 U 盤等存儲(chǔ)設(shè)備,因?yàn)檫@些存儲(chǔ)設(shè)備的特點(diǎn)是以存儲(chǔ)塊為基礎(chǔ),因此叫做塊設(shè)備。網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)很好理解,不管是有線的還是無(wú)線的,都屬于網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的范疇。一個(gè)設(shè)備可以屬于多種設(shè)備驅(qū)動(dòng)類型,比如 USB WIFI,其使用 USB 接口,所以屬于字符設(shè)備,但是其又能上網(wǎng),所以也屬于網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)。

我使用的Linux內(nèi)核版本為 4.1.15,其支持設(shè)備樹Device tree。開發(fā)板是正點(diǎn)原子送的Linux-MINI板,你用其他家的板子也是一樣的,沒有任何影響。

一、字符設(shè)備驅(qū)動(dòng)簡(jiǎn)介

字符設(shè)備是Linux驅(qū)動(dòng)中最基本的一類設(shè)備驅(qū)動(dòng),字符設(shè)備就是一個(gè)一個(gè)字節(jié),按照字節(jié)流進(jìn)行讀寫操作的設(shè)備,讀寫數(shù)據(jù)是分先后順序的。比如我們最常見的點(diǎn)燈、按鍵、IIC、SPI,LCD 等等都是字符設(shè)備,這些設(shè)備的驅(qū)動(dòng)就叫做字符設(shè)備驅(qū)動(dòng)。

那么在Linux下的應(yīng)用程序是如何調(diào)用驅(qū)動(dòng)程序的呢?Linux 應(yīng)用程序?qū)︱?qū)動(dòng)程序的調(diào)用如圖所示:

Linux應(yīng)用程序?qū)︱?qū)動(dòng)程序的調(diào)用流程

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

寫驅(qū)動(dòng)的人必須要懂linux內(nèi)核,因?yàn)轵?qū)動(dòng)程序就是根據(jù)內(nèi)核的函數(shù)去寫的,寫應(yīng)用的人不需要懂linux內(nèi)核,只需要熟悉驅(qū)動(dòng)函數(shù)就可以了。

比如現(xiàn)在有個(gè)叫做/dev/led的驅(qū)動(dòng)文件,是led燈的驅(qū)動(dòng)文件。應(yīng)用程序使用open函數(shù)來(lái)打開文件/dev/led,使用完成以后使用close函數(shù)關(guān)閉/dev/led 這個(gè)文件。open和 close 就是打開和關(guān)閉led驅(qū)動(dòng)的函數(shù),如果要點(diǎn)亮或關(guān)閉led,那么就使用write 函數(shù)來(lái)操作,也就是向此驅(qū)動(dòng)寫入數(shù)據(jù),這個(gè)數(shù)據(jù)就是要關(guān)閉還是要打開led的控制參數(shù)。如果要獲取led 燈的狀態(tài),就用 read 函數(shù)從驅(qū)動(dòng)中讀取相應(yīng)的狀態(tài)。

應(yīng)用程序運(yùn)行在用戶空間,而Linux 驅(qū)動(dòng)屬于內(nèi)核的一部分,因此驅(qū)動(dòng)運(yùn)行于內(nèi)核空間。當(dāng)我們?cè)谟脩艨臻g想要實(shí)現(xiàn)對(duì)內(nèi)核的操作,比如使用open函數(shù)打開/dev/led這個(gè)驅(qū)動(dòng),因?yàn)?strong>用戶空間不能直接對(duì)內(nèi)核進(jìn)行操作,因此必須使用一個(gè)叫做“系統(tǒng)調(diào)用”的方法來(lái)實(shí)現(xiàn)從用戶空間“陷入”到內(nèi)核空間,這樣才能實(shí)現(xiàn)對(duì)底層驅(qū)動(dòng)的操作

open、close、write 和read等這些函數(shù)是由C庫(kù)提供的,在Linux系統(tǒng)中,系統(tǒng)調(diào)用作為C庫(kù)的一部分。當(dāng)我們調(diào)用 open 函數(shù)的時(shí)候流程如圖所示:

open函數(shù)調(diào)用流程

其中關(guān)于C庫(kù)以及如何通過(guò)系統(tǒng)調(diào)用“陷入”到內(nèi)核空間這個(gè)我們不用去管,我們關(guān)注的是應(yīng)用程序和具體的驅(qū)動(dòng),應(yīng)用程序使用到的函數(shù)在具體驅(qū)動(dòng)程序中都有與之對(duì)應(yīng)的函數(shù),比如應(yīng)用程序中調(diào)用了open這個(gè)函數(shù),那么在驅(qū)動(dòng)程序中也得有一個(gè)名為open的函數(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)體,此結(jié)構(gòu)體就是Linux內(nèi)核驅(qū)動(dòng)操作函數(shù)集合,我們可以將linux內(nèi)核文件下載下來(lái),然后用source insight打開看看。內(nèi)容如下所示:

點(diǎn)擊此處下載linux內(nèi)核源碼

嵌入式物聯(lián)網(wǎng)需要學(xué)的東西真的非常多,千萬(wàn)不要學(xué)錯(cuò)了路線和內(nèi)容,導(dǎo)致工資要不上去!

無(wú)償分享大家一個(gè)資料包,差不多150多G。里面學(xué)習(xí)內(nèi)容、面經(jīng)、項(xiàng)目都比較新也比較全!某魚上買估計(jì)至少要好幾十。

點(diǎn)擊這里找小助理0元領(lǐng)?。?/strong>加微信領(lǐng)取資料

Linux內(nèi)核

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);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);#endif};

第 1589 行,owner 擁有該結(jié)構(gòu)體的模塊的指針,一般設(shè)置為THIS_MODULE。第 1590 行,llseek函數(shù)用于修改文件當(dāng)前的讀寫位置。第 1591 行,read函數(shù)用于讀取設(shè)備文件。第 1592 行,write函數(shù)用于向設(shè)備文件寫入(發(fā)送)數(shù)據(jù)。第 1596 行,poll是個(gè)輪詢函數(shù),用于查詢?cè)O(shè)備是否可以進(jìn)行非阻塞的讀寫。第 1597 行,unlocked_ioctl函數(shù)提供對(duì)于設(shè)備的控制功能,與應(yīng)用程序中的ioctl函數(shù)對(duì)應(yīng)。第 1598 行,compat_ioctl函數(shù)與unlocked_ioctl函數(shù)功能一樣,區(qū)別在于在64位系統(tǒng)上,32位的應(yīng)用程序調(diào)用將會(huì)使用此函數(shù)。在32位的系統(tǒng)上運(yùn)行32位的應(yīng)用程序調(diào)用的是unlocked_ioctl。第 1599 行,mmap函數(shù)用于將將設(shè)備的內(nèi)存映射到進(jìn)程空間中(也就是用戶空間),一般幀緩沖設(shè)備會(huì)使用此函數(shù),比如LCD驅(qū)動(dòng)的顯存,將幀緩沖(LCD 顯存)映射到用戶空間中以后應(yīng)用程序就可以直接操作顯存了,這樣就不用在用戶空間和內(nèi)核空間之間來(lái)回復(fù)制。第 1601 行,open 函數(shù)用于打開設(shè)備文件。第 1603 行,release 函數(shù)用于釋放(關(guān)閉)設(shè)備文件,與應(yīng)用程序中的 close 函數(shù)對(duì)應(yīng)。第 1604 行,fasync 函數(shù)用于刷新待處理的數(shù)據(jù),用于將緩沖區(qū)中的數(shù)據(jù)刷新到磁盤中。第 1605 行,aio_fsync函數(shù)與 fasync 函數(shù)的功能類似,只是aio_fsync是異步刷新待處理的數(shù)據(jù)。二、字符設(shè)備驅(qū)動(dòng)開發(fā)

學(xué)習(xí)裸機(jī)或者STM32的時(shí)候關(guān)于驅(qū)動(dòng)的開發(fā)就是初始化相應(yīng)的外設(shè)寄存器,在Linux驅(qū)動(dòng)開發(fā)中肯定也是要初始化相應(yīng)的外設(shè)寄存器,這個(gè)是毫無(wú)疑問(wèn)的。只是在Linux驅(qū)動(dòng)開發(fā)中我們需要按照其規(guī)定的框架來(lái)編寫驅(qū)動(dòng),所以說(shuō)學(xué)Linux驅(qū)動(dòng)開發(fā)重點(diǎn)是學(xué)習(xí)其驅(qū)動(dòng)框架。

2.1 APP打開的文件在內(nèi)核中如何表示

APP使用open函數(shù)打開文件時(shí),可以得到一個(gè)整數(shù),這個(gè)整數(shù)被稱為文件句柄。對(duì)于APP的每一個(gè)文件句柄,在內(nèi)核里面都有一個(gè)struct file與之對(duì)應(yīng)。

struct file

我們使用open打開文件時(shí),傳入的 flags、mode等參數(shù)會(huì)被記錄在內(nèi)核中對(duì)應(yīng)的struct file結(jié)構(gòu)體里(f_flags、f_mode):

int open(const char *pathname, int flags, mode_t mode);

去讀寫文件時(shí),文件的當(dāng)前偏移地址也會(huì)保存在struct file結(jié)構(gòu)體的f_pos成員里。

open->struct file

打開字符設(shè)備節(jié)點(diǎn)時(shí),內(nèi)核中也有對(duì)應(yīng)的struct file注意這個(gè)結(jié)構(gòu)體中的結(jié)構(gòu)體:struct file_operations *f_op,這是由驅(qū)動(dòng)程序提供的。

驅(qū)動(dòng)程序的 struct file

驅(qū)動(dòng)程序的 open/read/write

結(jié)構(gòu)體struct file_operations的定義如下,上面也講過(guò)了。

2.2 編寫驅(qū)動(dòng)程序的步驟1、確定主設(shè)備號(hào),也可以讓內(nèi)核分配。2、定義自己的file_operations結(jié)構(gòu)體。3、實(shí)現(xiàn)對(duì)應(yīng)的drv_open/drv_read/drv_write等函數(shù),填入file_operations結(jié)構(gòu)體。4、把file_operations結(jié)構(gòu)體告訴內(nèi)核:register_chrdev。5、誰(shuí)來(lái)注冊(cè)驅(qū)動(dòng)程序?。康糜幸粋€(gè)入口函數(shù):安裝驅(qū)動(dòng)程序時(shí),就會(huì)去調(diào)用這個(gè)入口函數(shù)。6、有入口函數(shù)就應(yīng)該有出口函數(shù):卸載驅(qū)動(dòng)程序時(shí),出口函數(shù)調(diào)用unregister_chrdev。7、其他完善:提供設(shè)備信息,自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn):class_create,device_create。2.3 試驗(yàn)程序編寫

應(yīng)用程序調(diào)用open函數(shù)打開hello_drv這個(gè)設(shè)備,打開以后可以使用write 函數(shù)向hello_drv的寫緩沖區(qū)writebuf中寫入數(shù)據(jù)(不超過(guò) 100 個(gè)字節(jié)),也可以使用read函數(shù)讀取讀緩沖區(qū)readbuf中的數(shù)據(jù)操作,操作完成以后應(yīng)用程序使用close函數(shù)關(guān)閉chrdevbase設(shè)備。

hello_drv.c

#include <linux/module.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/miscdevice.h>#include <linux/KERNEL.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/device.h>#include <linux/tty.h>#include <linux/kmod.h>#include <linux/gfp.h>/* 1. 確定主設(shè)備號(hào)*/static int major = 200;static char kernel_buf[1024];static struct class *hello_class;#define MIN(a, b) (a < b ? a : b)/* 3. 實(shí)現(xiàn)對(duì)應(yīng)的open/read/write等函數(shù),填入file_operations結(jié)構(gòu)體 */static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset){ int err; printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); err = copy_to_user(buf, kernel_buf, MIN(1024, size)); return MIN(1024, size);}static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset){ int err; printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); err = copy_from_user(kernel_buf, buf, MIN(1024, size)); return MIN(1024, size);}static int hello_drv_open (struct inode *node, struct file *file){ printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); return 0;}static int hello_drv_close (struct inode *node, struct file *file){ printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); return 0;}/* 2. 定義自己的file_operations結(jié)構(gòu)體*/static struct file_operations hello_drv = { .owner = THIS_MODULE, .open = hello_drv_open, .read = hello_drv_read, .write = hello_drv_write, .release = hello_drv_close,};/* 4. 把file_operations結(jié)構(gòu)體告訴內(nèi)核:注冊(cè)驅(qū)動(dòng)程序 *//* 5. 誰(shuí)來(lái)注冊(cè)驅(qū)動(dòng)程序啊?得有一個(gè)入口函數(shù):安裝驅(qū)動(dòng)程序時(shí),就會(huì)去調(diào)用這個(gè)入口函數(shù) */static int __init hello_init(void){ int retvalue; printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); retvalue = register_chrdev(major, "hello_drv", &hello_drv); /* /dev/hello */ if(retvalue < 0){ printk("chrdevbase driver register failed\\"); } printk("chrdevbase init!\\"); return 0;}/* 6. 有入口函數(shù)就應(yīng)該有出口函數(shù):卸載驅(qū)動(dòng)程序時(shí),就會(huì)去調(diào)用這個(gè)出口函數(shù)*/static void __exit hello_exit(void){ printk("%s %s line %d\", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "hello_drv");}/* 7. 其他完善:提供設(shè)備信息,自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn) */module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zhiguoxin");2.4 測(cè)試程序編寫

驅(qū)動(dòng)編寫好以后是需要測(cè)試的,一般編寫一個(gè)簡(jiǎn)單的測(cè)試APP,測(cè)試APP運(yùn)行在用戶空間。測(cè)試APP很簡(jiǎn)單通過(guò)輸入相應(yīng)的指令來(lái)對(duì)hello_drv設(shè)備執(zhí)行讀或者寫操作。

hello_drv_test.c

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <string.h>/*app測(cè)試./hello_drv_test -w www.zhiguoxin.cn./hello_drv_test -r*/int main(int argc, char **argv){ int fd; char buf[1024]; int len; /* 1. 判斷參數(shù) */ if (argc < 2) { printf("Usage: %s -w <string>\", argv[0]); printf(" %s -r\", argv[0]); return -1; } /* 2. 打開文件 */ fd = open("/dev/hello", O_RDWR); if (fd == -1) { printf("can not open file /dev/hello\"); return -1; } /* 3. 寫文件或讀文件 */ if ((0 == strcmp(argv[1], "-w")) && (argc == 3)) { len = strlen(argv[2]) + 1; len = len < 1024 ? len : 1024; write(fd, argv[2], len); } else { len = read(fd, buf, 1024); buf[1023] = '\\0'; printf("APP read : %s\", buf); } close(fd); return 0;}

這里的代碼很簡(jiǎn)單就不用再說(shuō)了,這是linux應(yīng)用開發(fā)的知識(shí)。

2.5 編寫Makefile

KERNELDIR := /home/zhiguoxin/linux/IMX6ULL/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientekCURRENT_PATH := $(shell pwd)obj-m := hello_drv.obuild: kernel_moduleskernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules $(CROSS_COMPILE)arm-linux-gnueabihf-gcc -o hello_drv_test hello_drv_test.c clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean第1行,KERNELDIR表示開發(fā)板所使用的Linux內(nèi)核源碼目錄,使用絕對(duì)路徑,大家根據(jù)自己的實(shí)際情況填寫。第2行,CURRENT_PATH表示當(dāng)前路徑,直接通過(guò)運(yùn)行pwd命令來(lái)獲取當(dāng)前所處路徑。第3行,obj-m表示將hello_drv.c這個(gè)文件編譯為hello_drv.ko模塊。第8行,具體的編譯命令,后面的modules表示編譯模塊,-C表示將當(dāng)前的工作目錄切換到指定目錄中,也就是KERNERLDIR目錄。M表示模塊源碼目錄,make modules命令中加入M=dir以后程序會(huì)自動(dòng)到指定的 dir 目錄中讀取模塊的源碼并將其編譯為.ko 文件。第9行,使用交叉編譯工具鏈將hello_drv_test.c編譯成可以在arm板子上運(yùn)行的hello_drv_test可執(zhí)行文件。

Makefile 編寫好以后輸入make命令編譯驅(qū)動(dòng)模塊,編譯過(guò)程如圖所示

有時(shí)候你可能遇到下面的錯(cuò)誤

這個(gè)錯(cuò)誤的原因是ubuntu中的linux源碼沒有編譯導(dǎo)致的,使用下面的命令將源碼編譯一遍就好了。

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfigmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

編譯成功以后就會(huì)生成一個(gè)叫做hello_drv.ko的文件,此文件就是hello_drv設(shè)備的驅(qū)動(dòng)模塊。至此,hello_drv設(shè)備的驅(qū)動(dòng)就編譯成功。

2.6 運(yùn)行測(cè)試2.6.1 上傳程序到開發(fā)板執(zhí)行

開發(fā)板啟動(dòng)后通過(guò)NFS掛載Ubuntu目錄的方式,將相應(yīng)的文件拷貝到開發(fā)板上。簡(jiǎn)單來(lái)說(shuō),就是通過(guò)NFS在開發(fā)板上通過(guò)網(wǎng)絡(luò)直接訪問(wèn)ubuntu虛擬機(jī)上的文件,并且就相當(dāng)于自己本地的文件一樣。

因?yàn)槲业拇a都放在/home/zhiguoxin/myproject/alientek_drv_development_source這個(gè)目錄下,所以我們將這個(gè)目錄作為NFS共享文件夾。

Ubuntu IP為192.168.10.100,一般都是掛載在開發(fā)板的mnt目錄下,這個(gè)目錄是專門用來(lái)給我們作為臨時(shí)掛載的目錄。

文件系統(tǒng)目錄簡(jiǎn)介

然后使用MobaXterm軟件通過(guò)SSH訪問(wèn)開發(fā)板。

ubuntu ip:192.168.10.100windows ip:192.168.10.200開發(fā)板ip:192.168.10.50

在開發(fā)板上執(zhí)行以下命令就可以實(shí)現(xiàn)掛載了:

mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_drv_development_source /mnt

就將ARM板的mnt目錄掛載在ubuntu的/home/zhiguoxin/myproject/alientek_drv_development_source目錄下了。這樣我們就可以在Ubuntu下修改文件,然后可以直接在開發(fā)板上執(zhí)行可執(zhí)行文件了。當(dāng)然我這里的/home/zhiguoxin/myproject/和windows之間是一個(gè)共享目錄,我也可以直接在windows上面修改文件,然后ubuntu和開發(fā)板直接進(jìn)行文件同步了。

2.6.2 加載驅(qū)動(dòng)模塊

驅(qū)動(dòng)模塊hello_drv.ko和hello_drv_test可執(zhí)行文件都已經(jīng)準(zhǔn)備好了,接下來(lái)就是運(yùn)行測(cè)試。這里我是用掛載的方式將服務(wù)端的項(xiàng)目文件夾掛載到arm板的mnt目錄,進(jìn)入到/mnt/01_hello_drv目錄輸入如下命令加載hello_drv.ko驅(qū)動(dòng)文件:

insmod hello_drv.ko

如果模塊加載成功,不會(huì)有任何提示,如果失敗會(huì)有提示,可能會(huì)出錯(cuò)的是你的模塊版本和你的arm板內(nèi)版本不一致。

輸入lsmod命令即可查看當(dāng)前系統(tǒng)中存在的模塊

lsmod

當(dāng)前系統(tǒng)只有hello_drv這一個(gè)模塊。輸入如下命令查看當(dāng)前系統(tǒng)中有沒有hello_drv這個(gè)設(shè)備:

cat /proc/devices

可以看出,當(dāng)前系統(tǒng)存在hello_drv這個(gè)設(shè)備,主設(shè)備號(hào)為200,跟我們?cè)O(shè)置的主設(shè)備號(hào)一致。

2.7 創(chuàng)建設(shè)備節(jié)點(diǎn)文件

驅(qū)動(dòng)加載成功需要在/dev目錄下創(chuàng)建一個(gè)與之對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)文件,應(yīng)用程序就是通過(guò)操作這個(gè)設(shè)備節(jié)點(diǎn)文件來(lái)完成對(duì)具體設(shè)備的操作。輸入如下命令創(chuàng)建/dev/hello_drv這個(gè)設(shè)備節(jié)點(diǎn)文件:

mknod /dev/hello_drv c 200 0

其中mknod是創(chuàng)建節(jié)點(diǎn)命令,/dev/hello_drv 是要?jiǎng)?chuàng)建的節(jié)點(diǎn)文件,c表示這是個(gè)字符設(shè)備,200是設(shè)備的主設(shè)備號(hào),0是設(shè)備的次設(shè)備號(hào)。創(chuàng)建完成以后就會(huì)存在/dev/hello_drv 這個(gè)文件,可以使用ls /dev/chrdevbase -l命令查看

ls /dev/hello_drv -l

如果hello_drv_test想要讀寫hello_drv設(shè)備,直接對(duì)/dev/hello_drv進(jìn)行讀寫操作即可。相當(dāng)于/dev/hello_drv這個(gè)文件是hello_drv設(shè)備在用戶空間中的實(shí)現(xiàn)。Linux下一切皆文件,包括設(shè)備也是文件,現(xiàn)在大家應(yīng)該是有這個(gè)概念了吧?

2.8 hello_drv設(shè)備操作測(cè)試

一切準(zhǔn)備就緒。使用hello_drv_test軟件操作hello_drv這個(gè)設(shè)備,看看讀寫是否正常,首先進(jìn)行寫操作,將字符串輸入www.zhiguoxin.cn寫入到內(nèi)核中

./hello_drv_test -w www.zhiguoxin.cn

然后再?gòu)膬?nèi)核中將剛寫入的字符串讀出來(lái)

./hello_drv_test -r

可以看到讀寫正常,說(shuō)明我們編寫的hello_drv驅(qū)動(dòng)是沒有問(wèn)題的。

2.9 卸載驅(qū)動(dòng)模塊

如果不再使用某個(gè)設(shè)備的話可以將其驅(qū)動(dòng)卸載掉,比如輸入如下命令卸載掉 hello_drv這個(gè)設(shè)備:

rmmod hello_drv.ko

卸載以后使用lsmod命令查看hello_drv這個(gè)模塊還存不存在:

可以看出,此時(shí)系統(tǒng)已經(jīng)沒有任何模塊了,hello_drv這個(gè)模塊也不存在了,說(shuō)明模塊卸載成功。而且系統(tǒng)中也沒有了hello_drv這個(gè)設(shè)備。

至此,hello_drv這個(gè)設(shè)備的整個(gè)驅(qū)動(dòng)就驗(yàn)證完成了,驅(qū)動(dòng)工作正常。以后的字符設(shè)備驅(qū)動(dòng)實(shí)驗(yàn)基本都可以此為模板進(jìn)行編寫。

總結(jié)

上面就是Linux中的字符驅(qū)動(dòng),可能初學(xué)者看起來(lái)還有點(diǎn)難,這里我并沒有講解代碼,因?yàn)闆]有什么好講的,就是我前面在單片機(jī)開發(fā)中的常說(shuō)的面向?qū)ο缶幊毯椭羔樅瘮?shù)的實(shí)際運(yùn)用,所以做嵌入式還是要把C語(yǔ)言的基礎(chǔ)打牢,尤其是結(jié)構(gòu)體、指針和鏈表,如果這三個(gè)你能很好的理解那么Linux驅(qū)動(dòng)編程就非常容易,因?yàn)轵?qū)動(dòng)開發(fā)就=軟件架構(gòu)+硬件操作。而軟件架構(gòu)就需要你要非常熟悉C語(yǔ)言,硬件操作就是你單片機(jī)的那幾個(gè)寄存器操作。當(dāng)然如果你學(xué)了RT-Thread那么學(xué)習(xí)Linux驅(qū)動(dòng)也是非常容易的,因?yàn)镽T-Thread是內(nèi)核+驅(qū)動(dòng),而FreeRTOS僅僅只是一個(gè)內(nèi)核而已。

原文鏈接:https://mp.weixin.qq.com/s/4J5NGBo3GjLSIFbxIklCUw

轉(zhuǎn)載自:果果小師弟

原文鏈接:我在ARM板上寫的第一個(gè)驅(qū)動(dòng)程序

本文來(lái)源網(wǎng)絡(luò),免費(fèi)傳達(dá)知識(shí),版權(quán)歸原作者所有。如涉及作品版權(quán)問(wèn)題,請(qǐng)聯(lián)系我進(jìn)行刪除。

以上就是關(guān)于pos機(jī)電腦驅(qū)動(dòng),我在ARM板上寫的第一個(gè)驅(qū)動(dòng)程序的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于pos機(jī)電腦驅(qū)動(dòng)的知識(shí),希望能夠幫助到大家!

轉(zhuǎn)發(fā)請(qǐng)帶上網(wǎng)址:http://m.dsth100338.com/news/46494.html

你可能會(huì)喜歡:

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請(qǐng)發(fā)送郵件至 babsan@163.com 舉報(bào),一經(jīng)查實(shí),本站將立刻刪除。