linux 字符设备——硬件操作(三)
发布时间:2020-12-14 00:21:26 所属栏目:Linux 来源:网络整理
导读:字符设备驱动--点灯驱动 一、前言 前面简单的写了下字符设备驱动框架(一)、(二),接下来操作一下简单的硬件——led灯 二、原理图 (LED1 接到开发板的 GPF4,LED2 接到开发板的 GPF5,LED4 接到开发板的 GPF6) 三、驱动程序 驱动程序和前面所写的(框架一)程序差
字符设备驱动--点灯驱动一、前言前面简单的写了下字符设备驱动框架(一)、(二),接下来操作一下简单的硬件——led灯 二、原理图(LED1 接到开发板的 GPF4,LED2 接到开发板的 GPF5,LED4 接到开发板的 GPF6) 三、驱动程序驱动程序和前面所写的(框架一)程序差不多,只是增加了寄存器操作(具体的寄存器描述,请阅读s3c2440 datasheet,这里就不说明了) #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 failedn"); return -EFAULT; } if(val == 1){ //点灯,将GPF4,6拉低 *gpfdat &= ~((1<<4)|(1<<5)|(1<<6)); /*也可以使用下面的函数设置gpio口*/ //s3c2410_gpio_setpin(S3C2410_GPF4,1); }else{ //灭灯,将GPF4,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,"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,"mydev"); return 0; } /* 驱动模块的退出函数 */ static void myled_test_exit(void){ /* 注销映射 */ iounmap(gpfcon); /* 删除设备节点 */ class_device_destroy(myled_class,device); class_destroy(myled_class); /* 注销字符设备驱动 */ cdev_del(myled_cdev); /* 释放空间 */ cdev_put(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"); 四、编写MakeFile#内核源码树路径 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 五、测试应用程序测试程序为点灯程序,在命令行终端执行时需要带入参数。例如: #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 openn"); 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; } 六、编译测试测试方法和(框架一)的测试方法类似。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |