1.引言
很早之前就有網(wǎng)友建議寫一篇關(guān)于Linux驅(qū)動的文章。之所以拖到現(xiàn)在才寫,原因之一是我之前沒有在工作中遇到需要自己手動去寫驅(qū)動的需求,主要是現(xiàn)在Linux內(nèi)核驅(qū)動的支持已經(jīng)比較完善了,另外一個原因是自己水平實在有限,不敢寫驅(qū)動這個話題,Linux驅(qū)動里涉及到的東西太多了,很多年前專門買過驅(qū)動相關(guān)的書籍,厚厚的,看的云里霧里。借此機會,在這里給大家做個非常非常入門級的介紹,希望對大家有所幫助。
2.環(huán)境介紹
2.1.硬件
網(wǎng)上的一個第三方做的NUC972開發(fā)板,這里會用到板子上的MPU6050傳感器芯片,相關(guān)部分原理圖如下:
2.2.軟件
1) Uboot不需要改動
2) Kernel不需要改動
3) Rootfs不需要重新編譯
3.最簡單的驅(qū)動例子
第1步:編寫hello.c
#include
這是一個簡單的內(nèi)核模塊程序,可以動態(tài)加載和卸載。模塊加載的時候系統(tǒng)會打印module init success,模塊卸載的時候系統(tǒng)會打印module exit success。
開頭的兩個頭文件,init.h 定義了驅(qū)動的初始化和退出相關(guān)的函數(shù),module.h 定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏。然后module_init和module_exit是模組加載和卸載相關(guān)的兩個函數(shù),
第2步:編寫Makefile
obj-m := hello.oPWD := $(shell pwd)KDIR :=/home/topsemic/nuc972/kernel/NUC970_Linux_Kernel-master/all: $(MAKE) -C $(KDIR) M=$(PWD)clean: rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a
注意:KDIR 取決于你自己Linux內(nèi)核安裝的位置,一定要設(shè)置正確,否則編譯會報錯。
第3步:編譯
將hello.c和Makefile放在同一路徑下進行編譯,輸入make即可。編譯成功后,會在當前路徑下生成hello.ko,這就是我們將要加載到內(nèi)核的模塊。
第4步:將生成的hello.ko放到板子上,然后登錄板子輸入:
insmod hello.ko
如果模塊加載成功的話,可以查看模塊加載情況,使用lsmod命令
并且可以查看內(nèi)核打印的消息,使用dmesg命令,
rmmod hello.ko,用來卸載模塊,使用dmesg命令可以看到相關(guān)輸出信息
4.MPU6050驅(qū)動
本章以板子上的MPU6050 傳感器為例,來介紹驅(qū)動的編寫。由于板子上使用的是PE10和PE11,它們不是真正的I2C引腳,所以這里我們使用GPIO來模擬I2C時序。編寫驅(qū)動前,首先需要下載被控制器件的datasheet,在官網(wǎng) 可以下載。
第1步:寫驅(qū)動文件,我們這里在驅(qū)動文件里放了三個文件,分別為mpu6050.c、mpu6050bsp.c和mpu6050bsp.h
其中mpu6050.c代碼如下:
#include"mpu6050bsp.h"int MPU6050_MAJOR = 0;int MPU6050_MINOR = 0;int NUMBER_OF_DEVICES = 2; struct class *my_class;struct cdev cdev;dev_t devno;/*************************************************************************************/ #define DRIVER_NAME "mpu6050"int mpu6050_open(struct inode *inode,struct file *filp){u8 reg;reg=InitMPU6050();printk("mpu6050:%d\n",reg);return nonseekable_open(inode,filp);}long mpu6050_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){switch(cmd){default:return -2;}return 0;}int mpu6050_read(struct file *filp, char *buffer,size_t count, loff_t *ppos){mpu_get_data();return copy_to_user(buffer, mpu_data, 14);}int mpu6050_write(struct file *filp, char *buffer, size_t count, loff_t *ppos){return 0;}struct file_operations mpu6050_fops = {.owner = THIS_MODULE,.read = mpu6050_read,.write = mpu6050_write,.open = mpu6050_open,.unlocked_ioctl = mpu6050_ioctl,};/**************************************************************************************/static int __init mpu6050_init(void){ int result; devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR); if (MPU6050_MAJOR) result = register_chrdev_region(devno, 2, "mpu6050"); else { result = alloc_chrdev_region(&devno, 0, 2, "mpu6050"); MPU6050_MAJOR = MAJOR(devno); } printk("MAJOR IS %d\n",MPU6050_MAJOR); my_class = class_create(THIS_MODULE,"mpu6050_class"); //類名為 if(IS_ERR(my_class)) { printk("Err: failed in creating class.\n"); return -1; } device_create(my_class,NULL,devno,NULL,"mpu6050"); //設(shè)備名為mpu6050 if (result<0) { printk (KERN_WARNING "hello: can't get major number %d\n", MPU6050_MAJOR); return result; } cdev_init(&cdev, &mpu6050_fops); cdev.owner = THIS_MODULE; cdev_add(&cdev, devno, NUMBER_OF_DEVICES); printk (KERN_INFO "mpu6050 driver Registered\n"); return 0;} static void __exit mpu6050_exit (void){ cdev_del (&cdev); device_destroy(my_class, devno); //delete device node under /dev//必須先刪除設(shè)備,再刪除class類 class_destroy(my_class); //delete class created by us unregister_chrdev_region (devno,NUMBER_OF_DEVICES); printk (KERN_INFO "char driver cleaned up\n");} module_init (mpu6050_init );module_exit (mpu6050_exit ); MODULE_LICENSE ("GPL");
上述代碼整體結(jié)構(gòu)和第3章介紹的hello.c類似,不過為了支持對字符設(shè)備的操作,多了open/write/read的幾個函數(shù)實現(xiàn)。
mpu6050bsp.c由于內(nèi)容較多,不把代碼貼到這里了,大家一看就明白了,它就是用gpio來模擬i2c功能,實現(xiàn)寄存器操作功能。mpu6050bsp.h主要是相關(guān)寄存器定義。
第2步:編譯,然后把ko文件放到板子,insmod mpu6050d.ko 。模塊如果加載成功,在/dev目錄下可以看到mpu6050的設(shè)備名出現(xiàn)。
第3步:寫個應(yīng)用程序mpu6050app.c,
#include
編譯arm-linux-gcc mpu6050app.c -o mpu6050app
第4步:將板子水平擺放朝上,運行例子結(jié)果如下,
我們來計算下z軸加速度和溫度的實際數(shù)值。
因為驅(qū)動里AFS_SEL寄存器設(shè)置的值是2,所以對應(yīng)量程8g。數(shù)字-32767對應(yīng)-8g,32767對應(yīng)8g。把32767除以8,就可以得到4096,即1g對應(yīng)的數(shù)值。把從加速度計讀出的數(shù)字除以4096,就可以換算成加速度的數(shù)值。上面我們從加速度計z軸讀到的數(shù)字是3723,那么對應(yīng)的加速度數(shù)據(jù)是3723/4096≈0.91g。g為加速度的單位,重力加速度定義為1g, 等于9.8米每平方秒。由于桌上不是很平,加上傳感器自身誤差,所以這個值是合理的。
再看看溫度計算,從手冊中可以看到如下的計算公式
上述的-2352計算后得到溫度為29.6℃,注意這個溫度不是環(huán)境溫度,是芯片內(nèi)部的溫度,環(huán)境溫度會比這個值略低。
由于我是在北京,冬天屋里有暖氣,所以這個值也是合理的。
5.結(jié)束語
本期給大家介紹關(guān)于Linux驅(qū)動最簡單的使用,可以看到驅(qū)動開發(fā)和應(yīng)用開發(fā)還是有很大的差異,驅(qū)動需要關(guān)注底層,需要深入的閱讀芯片的數(shù)據(jù)手冊,同時也得具備內(nèi)核的相關(guān)知識。市場上Linux應(yīng)用開發(fā)人員相對更多,真正懂驅(qū)動的人相對較少,大部分集中在芯片原廠公司。推薦大家在實際做產(chǎn)品時盡量選擇官方推薦的元器件,或者選擇可以提供Linux驅(qū)動的元器件,以降低開發(fā)難度。
-
傳感器
+關(guān)注
關(guān)注
2545文章
50445瀏覽量
751031 -
嵌入式
+關(guān)注
關(guān)注
5059文章
18973瀏覽量
302002 -
Linux
+關(guān)注
關(guān)注
87文章
11207瀏覽量
208712
發(fā)布評論請先 登錄
相關(guān)推薦
評論