LED电路板驱动程序

编程入门 行业动态 更新时间:2024-10-11 07:35:24

LED<a href=https://www.elefans.com/category/jswz/34/1766617.html style=电路板驱动程序"/>

LED电路板驱动程序

为了学习parport提供的服务,让我们编写一个简单的驱动程序。考虑一个有8个发光二极管(LED),提供和标准25针并行端口接口的电路板。因为PC上的8位并行端口数据寄存器直接映射到并行端口的2~9针,所以这些针脚和电路板上的LED连通。向并行端口数据寄存器写数据可以控制这些针脚的电平,进而控制LED的开关。如下代码为一个字符设备驱动程序,它通过系统并行端口和此电路板通信。代码内的注释解释了其中所使用的parport服务例程。 并行端口LED电路板驱动程序(led.c) #include<linux/fs.h> #include<linux/cdev.h> #include<linux/parport.h> #include<asm/uaccess.h> #include<linux/platform_device.h>
#define DEVICE_NAME   "led"
static dev_t dev_number;                    /*Allotted device number */ static struct class *led_class;              /* Class to which this device belongs */ struct cdev led_cdev;                           /* Associated cdev */ struct pardevice *pdev                         /* Parallel port device */
/*LED open */ int led_open(struct inode *inode, struct file *file) {         return 0; }
/* Write to the LED */ ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *ppos) {         char kbuf;
        if (copy_from_user(&kbuf, buf, 1)) return -EFAULT;
        /* Write to the device */         parport_write_data(pdev->port, kbuf);
        /* Release the port */         parport_release(pdev);
        return count; }
/* Release the device */ int led_release(struct inode *inode, struct file *file) {         return 0; }
/* file Operations */ static struct file_operations led_fops = {         .owner        = THIS_MODULE,         .open          = led_open,         .write          = led_write,         .release     = led_release, };
static int led_preempt(void *handle) {         return 1; }
/* Parport attach method */ static void led_attach(struct parport *port) {         /* Register the paralled LED device with parport */         pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL);         if (pdev == NULL) printk ("Bad register\n"); }
/* Parport detach method */ static void led_detach(struct parport *port) {         /* Do nothing */ }
/* Parport driver operations */ static struct parport_driver led_driver = {         .name        = "led",         .attach       = led_attach,         .detach      = led_detach, };
/* Driver Initialization */ /* 新的设备模型将驱动程序和设备区分开来。led_init()通过parport_register_driver()调用项parport注册LED驱动程序。当内核在led_attach()中查找到LED板时,它调用parport_register_device()注册设备 */ int __init led_init(void) {         /* Request dynamic allocatino of a device major number */         if (alloc_chrdev_region(&dev_number, 0, 1, DEVICE_NAME) < 0) {                 printk(KERN_DEUG "Can't register device\n");                 return -1;         }                  /* create the led class */         led_class = class_create(THIS_MODULE, DEVICE_NAME);         if (IS_ERR(led_class)) printk("Bad class create\n");
        /* Connect the file operation with the cdev */         cdev_init(&led_cdev, &led_fops);
        led_cdev.owner = THIS_MODULE;
        /* Connect the major/minor number to the cdev */         if (cdev_add(&led_cdev, dev_number, 1)) {                 printk("Bad cdev add\n");                 return 1;         }
        class_device_create(led_class, NULL, dev_number, NULL, DEVICE_NAME); /* 该函数创建设备节点/dev/led,可以用此设备节点控制每个LED的状态 编译并将驱动程序模块加入到内核中: bash> make -C /path/to/kerneltree/ M=$PWD modules PS:也许你不一定能成功,因为Linux Kernel 2.6要求编译模块之前,必须先在内核源代码目录下执行make,换言之,必须先配置过内核,执行过make,然后才能make自己的内核。(仔细想想你没有配置过内核,内核怎么知道该部分是编译成模块还是编译进内核的呢) bash> insmod ./led.ko LED Driver Initialized PS:早期的版本使用class_create()和class_device_create()这两个函数创建设备节点,到了 2.6.29内核以后,使用的函数则变成了class_create()和device_create(),并且要在声明中加入#include <linux/device.h>,不过使用这两个函数的前提是用户空间已经移植了udev。 内核中定义了struct class结构体,一个struct class结构体类型变量对应一个类(有待商榷), 内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于/sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。 */
        /* Register this driver with parport */         if (parport_register_driver(&led_driver)) {                 printk(KERN_ERR "Bad Parport Register\n");                 return -EIO;         }
        printk("LED Driver Initialized. \n");         return 0; }
/*Driver Exit */ void __exit led_cleanup(void) {         unregister_chrdev_region(dev_number, 1);         class_device_destroy(led_class, dev_number);         class_destroy(led_class);         return ; }
module_init(led_init); module_exit(led_cleanup);
MODULE_LICENSE("GPL");
为了有选择地驱动一些并行端口针脚,点亮相应的LED,将相应的值赋给/dev/led: bash> echo 1 > /dev/led 因为1的ASCII值是31(00110001),第1,5和6个LED将会发亮。 前述命令触发led_write()调用。此驱动程序方法首先通过copy_from_user()将用户内存数据(在本例中为31)复制到内核缓冲区。然后占用并行端口,写入数据,释放端口,所有这些都使用parport接口。 相比于/dev,sysfs是更好的控制设备状态的地方。因此将LED控制委托给sysfs效果更佳。下面代码为此种驱动程序的实现代码,其中哦功能sysfs操作代码也可以作为模版用到其它的设备控制中去
使用sysfs控制并行端口LED电路板: #include<linux/fs.h> #include<linux/cdev.h> #include<linux/parport.h> #include<asm/uaccess.h> #include<linux/pci.h> static dev_t dev_number;          /* Allotted Device Number */ static struct class *led_class;    /* Class Device Model */ struct cdev led_cdev;                 /* Character dev struct*/ struct pardevice *pdev;              /*Parallel Port device */
struct kobject kobj;                     /* Sysfs directory object */
/* sysfs attribute of the leds */ struct led_attr {         struct attribute attr;         ssize_t (*show)(char *);
        ssize_t (*store)(const char *, size_t cont); };
#define glow_show_led(number)                                                                                \ static ssize_t glow_led_##number(const char *buffer, size_t count)                        \ {                                                                                                                                         \         unsigned char buf;                                                                                                 \         int value;                                                                                                                  \                                                                                                                                           \         sscanf(buffer, "%d", &value);                                                                                \                                                                                                                                           \         parport_claim_or_block(pdev);                                                                            \         buf = parport_read_data(pdev->port);                                                                 \         if (value) {                                                                                                                \                 parport_write_data(pdev->port, buf | (1<<number));                                \         } else {                                                                                                                     \                 parport_write_data(pdev->port, buf & ~(1<<number));                            \         }                                                                                                                               \         parport_release(pdev);                                                                                        \         return count;                                                                                                          \ }                                                                                                                                       \                                                                                                                                        \ static ssize_t                                                                                                                \ show_led_##number(char *buffer)                                                                          \ {                                                                                                                                     \         unsigned char buf;                                                                                             \                                                                                                                                       \         parport_claim_or_block(pdev);                                                                        \                                                                                                                                       \         buf = parport_read_data(pdev->port);                                                             \         parport_release(pdev);                                                                                     \                                                                                                                                      \         if (buf & (1 << number)) {                                                                                  \                 return sprintf(buffer, "ON\n");                                                                   \         } else {                                                                                                                 \                 return sprintf(buffer, "OFF\n");                                                                 \         }                                                                                                                            \ }                                                                                                                                    \ static struct led_attr led##number =                                                                       \ __ATTR(led##number, 0644, show_led_##number, glow_led_##number);
glow_show_led(0); glow_show_led(1); glow_show_led(2); glow_show_led(3); glow_show_led(4); glow_show_led(5); glow_show_led(6); glow_show_led(7);  /* glow_show_led()使用了内核源代码中经常使用的技术,以便简洁地定义几个类似的函数。定义的read()和write()方法(在sysfs中用术语show()和store()表示)同8个/sys文件相关,电路板上每个LED对应一个文件。因此glow_show_led(0)将glow_led_0()和show_led_0()与第一个LED对应一个文件。这些函数分别负责点亮/熄灭第一个LED,并读取其状态。##在宏定义中用于把字符串连接在一起,因此当编译器处理语句glow_show_led(0)时,glow_led_##number就变成glow_led_0()。 */ #define DEVICE_NAME "led"
static int led_preempt (void * handle) {         return 1; }
/* Parport attach method */ static void led_attach (struct parport *port) {         pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL); }
/* Parent sysfs show() method. Calls the show() method corresponding to the individual sysfs file */ static ssize_t l_show(struct kobject *kobj, struct attribute *a, char *buf) {         int ret;         struct led_attr *lattr = container_of(a, struct led_attr, attr);
        ret = lattr->show ? lattr->show(buf) : -EIO;         return ret; }
/* Sysfs store() method. Calls the store()method corresponding to the individual sysfs file */ static ssize_t l_store(struct kobject *kobj, struct attribute 8a, const char *buf, size_t count) {         int ret;         struct led_attr *lattr = container_of(a, struct led_attr, attr);
        ret = lattr->store ? lattr->store(buf, count) : -EIO;         return ret; }
/* Sysfs operations structure */ static struct sysfs_ops sysfs_ops = {         .show = l_show,         .store = l_store, };
/* Attributes of the /sys/class/pardevice/led/control/ kobject.    Each file in this directory corresponds to one LED. Control    each LED by writing or reading  the associated sysfs file */ static struct attribute *led_atrrs[] = {         &led0.attr,         &led1.attr,         &led2.attr,         &led3.attr,         &led4.attr,         &led5.attr,         &led6.attr,         &led7.attr,         NULL };
/* This describes the kobject. The kobject has 8 files, one corresponding to each LED. This representation is called the ktype of the kobject */ static struct kobj_type ktype_led = {         .sysfs_ops        = &sysfs_ops,         .default_attrs    =  led_attrs, };
/* Parport methods. We don't have a detach method */ static struct parport_driver led_driver = {         .name        = "led",         .attach       = led_atach, };
/* Driver Initialization */ int __init led_init(void) {         struct class_device *c_d;         if calloc_chrdev_region(&dev_number, 01, DEVICE_NAME <0) {                 printk(KERN_DEBUG "can't register device \n");                 return -1;         }
        /* Create the pardevice class - /sys/class/pardevice */         led_class = class_create(THIS_MODULE, "pardevice");         if (IS_ERR(led_class)) printk("Bad class create\n");
        /* Create the led class device - /sys/class/pardevice/led/ */         c_d = class_device_create(led_class, NULL, dev_number, NULL, DEVICE_NAME);
        /* Register this driver with parport */         if (parport_register_driver(&led_driver)) {                 printk(KERN_ERR "Bad parport Register \n");                 return -EIO;         }
        /* Instantiate a kobject to control each LED on the board */
        /* Parent is /sys/classpardevice/led */         kobj.parent = &c_d->kobj;
        /* the sysfs file corresponding to kboj is /sys/class/pardevice/led/control/    */         strlcpy(kobj.name, "control", KOBJ_NAME_LED);
        /* Description of the kobject. Specifies the list of attribute files in /sys/class/pardevice/led/control/      */         kobj.ktype  =  &ktype_led; /* ktype描述了kobject。ktype_led结构描述了“控制”kobject,它包含指向属性数组的指针led_attrs[]。led_attrs[]数组包含每个LED的设备属性的地址。每个LED的属性通过下列语句连接在一起:         static struct led_attr led##number =         __ATTR(led##number, 0644, show_led_##number, glow_led_##number);         其结果是为每个LED产生一个控制文件/sys/class/pardevice/led/control/ledX,其中X是LED序号。为了改变ledX的状态,将1(或者0)回送给相应的控制文件。如为了点亮第一个LED,可做如下操作:         bash> echo 1 > /sys/class/pardevice/led/control/led0 在模块退出期间,驱动程序使用kobject_unregister(),class_device_destroy()和class_destroy()移除kobject和class。 */         /* Register the kobject */         kobject_register(&kobj); /* 基于sysfs版本的驱动程序使用了kobject用于代表“控制”抽象,它模拟了一个软件按钮控制LED。sysfs下的每个目录名代表一个kobject,因此代码清单中的kobject_register()创建了/sys/class/pardevice/led/control/目录 */         printk("LED Driver Initialized. \n");         return 0; }
/* Driver Exit */ void led_cleanup(void) {         /* unregister kobject corresponding to /sys/class/pardevice/led/control */         kobject_unregister(&kobj);
        /* Destroy class device corresponding to /sys/class/pardevice/led/   */         class_device_destroy(led_class, dev_number);
        /* Destroy /sys/class/pardevice */         class_destroy(led_class);
        return; }
module_init(led_init); module_exit(led_cleanup);
MODULE_LICENSE("GPL");
编写字符驱动程序不再像2.4内核中那样简单了。在上下文中,为了开发简单的LED驱动彻骨女婿,我们使用了6个数据抽象:cdev,sysfs,kobject,class,class device 和parport。当然,这些数据抽象也有优点,如编译模块无bug,代码可重用,设计流程严谨。

更多推荐

LED电路板驱动程序

本文发布于:2024-03-14 01:09:21,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1735293.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:电路板   驱动程序   LED

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!