#字符設備驅動--點燈驅動 ###1、前言 前面簡單的寫了下字符設備驅動框架[(一)](https://www.cnblogs.com/gulan-zmc/p/11518002.html"linux driver 1")、[(二)](https://www.cnblogs.com/gulan-zmc/p/11519066.html"linux driver 2"),接下來操做一下簡單的硬件——led燈 ###2、原理圖 html
(LED1 接到開發板的 GPF4, LED2 接到開發板的 GPF5, LED4 接到開發板的 GPF6) ###3、驅動程序 驅動程序和前面所寫的[(框架一)](https://www.cnblogs.com/gulan-zmc/p/11518002.html"linux driver 1")程序差很少,只是增長了寄存器操做(具體的寄存器描述,請閱讀s3c2440 datasheet ,這裏就不說明了)node
#include <linux/init.h> #include <linux/cdev.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/types.h> #include <asm/uaccess.h> volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static int myled_open (struct inode * inode, struct file * file){ /* 配置GPF4,5,6爲輸出 */ *gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2))); *gpfcon |= ((0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2))); /* 若是不想操做寄存器,還能夠直接使用linux 提供的接口,使用個函數,就不用寄存器映射了 */ /* s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); */ return 0; } ssize_t myled_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){ int val; /* 內核空間和用戶空間傳遞數據的方法 */ if(copy_from_user(&val, buf, size)){ printk("copy failed\n"); return -EFAULT; } if(val == 1){ //點燈,將GPF4,5,6拉低 *gpfdat &= ~((1<<4)|(1<<5)|(1<<6)); /*也可使用下面的函數設置gpio口*/ //s3c2410_gpio_setpin(S3C2410_GPF4, 1); }else{ //滅燈,將GPF4,5,6拉高 *gpfdat |= ((1<<4)|(1<<5)|(1<<6)); } return size; } /* file_operations 用來存儲驅動內核模塊提供的對設備進行各類操做的函數的指針。 * 該結構體的每一個域都對應着驅動內核模塊用來處理某個被請求的事務的函數的地址。 */ static struct file_operations myled_fop = { .owner = THIS_MODULE, .open = myled_open, .write = myled_write, }; static struct cdev *myled_cdev; static struct class *myled_class; static dev_t device; /* 驅動模塊的入口函數 */ static int myled_test_init(void){ /* 映射到0x56000050物理地址 */ gpfcon = (volatile unsigned long *)ioremap(0x56000050,16); gpfdat = gpfcon+1; if (device){ /* 靜態申請設備號 */ register_myleddev_region(device,1,"myled_dev"); }else{ /* 動態申請設備號 */ alloc_myleddev_region(&device,0, 1,"myled_dev"); } /* 申請空間 */ myled_cdev = cdev_alloc(); /* 註冊字符設備驅動 */ cdev_init(myled_cdev,&myled_fop); cdev_add(myled_cdev, device, 1); /* 建立設備節點 */ myled_class = class_create(THIS_MODULE, "mydev"); class_device_create(myled_class, NULL, device, NULL,"mydev"); return 0; } /* 驅動模塊的退出函數 */ static void myled_test_exit(void){ /* 註銷映射 */ iounmap(gpfcon); /* 刪除設備節點 */ class_device_destroy(myled_class, device); class_destroy(myled_class); /* 註銷字符設備驅動 */ cdev_put(myled_cdev); /* 釋放空間 */ cdev_del(myled_cdev); /* 釋放設備號和相應的設備名 */ unregister_myleddev_region(device, 1); } /* 這個宏將 myled_test_init 函數修飾爲模塊的入口函數 */ module_init(myled_test_init); /* 這個宏將 myled_test_exit 函數修飾爲模塊的退出函數 */ module_exit(myled_test_exit); /* 遵循GPL協議 */ MODULE_LICENSE("GPL");
###4、編寫MakeFilelinux
#內核源碼樹路徑 KERN_DIR = /work/system/kernel/source/linux-2.6.22.6 #目標文件 obj-m += myled.o all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean
###5、測試應用程序 測試程序爲點燈程序,在命令行終端執行時須要帶入參數。例如: ./led on
或者 ./led off
框架
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(int argc,char **argv){ int fd; int val = 1; fd = open("/dev/mydev",O_RDWR); if(fd < 0){ printf("can't open\n"); return -1; } if(argc != 2){ printf("Usage :\n"); printf("%s <on|off>\n",argv[0]); return 0; } if(strcmp(argv[1],"on") == 0){ val = 1; }else{ val = 0; } write(fd,&val,4); return 0; }
###6、編譯測試 測試方法和[(框架一)](https://www.cnblogs.com/gulan-zmc/p/11518002.html"linux driver 1")的測試方法相似。 實驗現象: 當執行在命令行終端執行./led on
時,開發板(對於個人開發板)的三盞燈都亮, 當執行在命令行終端執行./led off
時,開發板的三盞燈都滅。函數