Linux下实现流水灯等功能的LED驱动代码及测试实例

驱动代码:

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
     
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
//-------------class_create,device_create------
#include <linux/device.h>

/*用udev机制自动添加设备节点*/
struct class *led_class;

static int led_major = 0;     /* 主设备号 */
static struct cdev LedDevs;

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define LED_MAGIC 'k'
#define IOCTL_LED_ON _IOW (LED_MAGIC, 1, int)
#define IOCTL_LED_OFF _IOW (LED_MAGIC, 2, int)
#define IOCTL_LED_RUN _IOW (LED_MAGIC, 3, int)
#define IOCTL_LED_SHINE _IOW (LED_MAGIC, 4, int)
#define IOCTL_LED_ALLON _IOW (LED_MAGIC, 5, int)
#define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int)



/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] = {
    S5PV210_MP04(4),
    S5PV210_MP04(5),
    S5PV210_MP04(6),
    S5PV210_MP04(7),

};
#define LED_NUM ARRAY_SIZE(led_table)


/* 应用程序对设备文件/dev/led执行open(...)时,
 * 就会调用leds_open函数
 */
static int leds_open(struct inode *inode, struct file *file)
{
    int i;    
    for (i = 0; i < 4; i++) {
        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
        s3c_gpio_cfgpin(led_table[i], S3C_GPIO_OUTPUT);
    }
    return 0;
}

//LEDS all light on
static void leds_all_on()
{
    int i;
    for (i=0; i<4; i++) {
        gpio_set_value(led_table[i], 0);
    }
}

//LEDs all light off
static void leds_all_off()
{
    int i;
    for (i=0; i<4; i++) {
        gpio_set_value(led_table[i], 1);
    }
}

/* 应用程序对设备文件/dev/leds执行ioctl(...)时,
 * 就会调用leds_ioctl函数
 */
static int leds_ioctl(struct file *file, 
                                unsigned int cmd, 
                                unsigned long arg)//没有inode,用邋unlocked_ioctl
{
    printk("in the leds_ioctl!!\n");


 //   if (__get_user(data, (unsigned int __user *)arg)) //方法二:指针参数传递
 //       return -EFAULT;
    printk("arg is %d!!\n",arg);



    switch(cmd) {
        case IOCTL_LED_ON:
            printk("in the IOCTL_LED_ON!!\n");
            // 设置指定引脚的输出电平为0
            gpio_set_value(led_table[arg], 0);
            break;

        case IOCTL_LED_OFF:
            printk("in the IOCTL_LED_OFF!!\n");
            // 设置指定引脚的输出电平为1
            gpio_set_value(led_table[arg], 1);
           break;
            
        case IOCTL_LED_RUN:
            // 跑马灯
            {
                printk("in the IOCTL_LED_RUN!!\n");
                int i,j;
                leds_all_off();            
                //printk("IOCTL_LED_RUN");
                for (i=0;i<arg;i++)
                    for (j=0;j<4;j++) {
                        gpio_set_value(led_table[j], 0);
                        mdelay(400); //delay 400ms
                        gpio_set_value(led_table[j], 1);
                        mdelay(400); //delay 400ms
                    }  
                break;
             }
          
        case IOCTL_LED_SHINE:
            // LED 闪烁
            {
                printk("in the IOCTL_LED_SHINE!!\n");
                int i,j;
                leds_all_off();
                printk("IOCTL_LED_SHINE\n");
                for (i=0;i<arg;i++) {
                    for (j=0;j<4;j++)
                        gpio_set_value(led_table[j], 0);
                    mdelay(400); //delay 400ms
                    for (j=0;j<4;j++)
                        gpio_set_value(led_table[j], 1);
                    mdelay(400);
                }
                break ;
           }
        case IOCTL_LED_ALLON:
            printk("in the IOCTL_LED_ALLON!!\n");
            // 设置指定引脚的输出电平为0
            leds_all_on();
            break;
        case IOCTL_LED_ALLOFF:
            printk("in the IOCTL_LED_ALLOFF!!\n");
            // 设置指定引脚的输出电平为1
            leds_all_off();
            break;

        default:
            printk("in the default!!\n");
            return -EINVAL;
    }
    return 0;
}

/* 这个结构是字符设备驱动程序的核心
 * 当应用程序操作设备文件时所调用的open、read、write等函数,
 * 最终会调用这个结构中指定的对应函数
 */
static struct file_operations leds_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   leds_open,     
    .unlocked_ioctl  =   leds_ioctl,
};


/*
 * Set up the cdev structure for a device.
 */
static void led_setup_cdev(struct cdev *dev, int minor,
        struct file_operations *fops)
{
    int err, devno = MKDEV(led_major, minor);
    
    cdev_init(dev, fops);
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    err = cdev_add (dev, devno, 1);
    /* Fail gracefully if need be */
    if (err)
        printk (KERN_NOTICE "Error %d adding Led%d", err, minor);
}

/*
 * 执行“insmod leds.ko”命令时就会调用这个函数
 */

static int __init leds_init(void)

{
    int result;
    dev_t dev = MKDEV(led_major, 0);
    char dev_name[]="led";  /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */

    /*gpio_request*/
    int i,ret;
    for (i = 0; i < LED_NUM; i++)
    {
       
        
        ret=gpio_request(led_table[i],"LED");
        if(ret)//注意,是ret
        {
            printk("%s:request GPIO %d for LED failed,ret= %d\n",dev_name,led_table[i],ret);
            return ret;
        }
        s3c_gpio_cfgpin(led_table[i],S3C_GPIO_SFN(1));//output
        gpio_set_value(led_table[i],1);
    }

    /* Figure out our device number. */
    if (led_major)
        result = register_chrdev_region(dev, 1, dev_name);
    else {
        result = alloc_chrdev_region(&dev, 0, 1, dev_name);
        led_major = MAJOR(dev);
    }
    if (result < 0) {
        printk(KERN_WARNING "leds: unable to get major %d\n", led_major);
        return result;
    }
    if (led_major == 0)
        led_major = result;

    /* Now set up cdev. */
    led_setup_cdev(&LedDevs, 0, &leds_fops);

    /*udev机制可以自动添加设备节点,只需要添加xxx_class这个类,以及device_create()*/
    led_class = class_create(THIS_MODULE, "led_class");/*在sys目录下创建xx_class这个类,/sys/class/~*/
    device_create(led_class, NULL, LedDevs.dev,  dev_name,  dev_name);/*自动创建设备/dev/$DEVICE_NAME*/
    

    
    printk("Led device installed, with major %d\n", led_major);
    printk("The device name is: %s\n", dev_name);
    return 0;

}

/*
 * 执行”rmmod leds”命令时就会调用这个函数 
 */
static void __exit leds_exit(void)
{
    /*gpio_free*/
    int i;
    for (i = 0; i < LED_NUM; i++)
    {
     
        gpio_free(led_table[i]);
    }

    /* 卸载驱动程序 */
    cdev_del(&LedDevs);
    unregister_chrdev_region(MKDEV(led_major, 0), 1);
    printk("Led device uninstalled\n");
}

/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(leds_init);
module_exit(leds_exit);

/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("");             // 驱动程序的作者
MODULE_DESCRIPTION("LED Driver");   // 一些描述信息
MODULE_LICENSE("Dual BSD/GPL");                             // 遵循的协议

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define LED_MAGIC 'k'
#define IOCTL_LED_ON _IOW (LED_MAGIC, 1, int)
#define IOCTL_LED_OFF _IOW (LED_MAGIC, 2, int)
#define IOCTL_LED_RUN _IOW (LED_MAGIC, 3, int)
#define IOCTL_LED_SHINE _IOW (LED_MAGIC, 4, int)
#define IOCTL_LED_ALLON _IOW (LED_MAGIC, 5, int)
#define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int)

/*
led_test on   //对应四个LED全亮
led_test off  // 对应四个LED全灭
led_test run  // 运行跑马灯实验
led_test shine //4个LED灯全灭、全亮交替闪烁

led_test 1 on   //对应LED1点亮
led_test 1 off  // 对应LED1熄灭
...
led_test 4 on   //对应LED4点亮
led_test 4 off  // 对应LED4熄灭

*/
void usage(char *exename)
{
    printf("Usage:\n");
    printf("    %s <led_no> <on/off>\n", exename);
    printf("    led_no = 1, 2, 3 or 4\n");
}

int main(int argc, char **argv)
{
    unsigned int led_no;
    int fd = -1;
        unsigned int count=10;
    
    if (argc > 3 || argc == 1)
        goto err;
        
    fd = open("/dev/led", 0);  // 打开设备
    if (fd < 0) {
        printf("Can't open /dev/led\n");
        return -1;    
    }    
    

    if (argc == 2) {
        if (!strcmp(argv[1], "on")) {
            ioctl(fd, IOCTL_LED_ALLON, count);    // 点亮它
        } else if (!strcmp(argv[1], "off")) {
            ioctl(fd, IOCTL_LED_ALLOFF, count);   // 熄灭它
        } else if (!strcmp(argv[1], "run")) {
            ioctl(fd, IOCTL_LED_RUN, count);   //运行跑马灯
                } else if (!strcmp(argv[1], "shine")) {
            ioctl(fd, IOCTL_LED_SHINE, count);   //闪烁
        } else {
            goto err;
        }
    }
        
    if (argc == 3) {
        led_no = atoi(argv[1]);    // 操作哪个LED?
        if (led_no > 3)
            goto err;        
        if (!strcmp(argv[2], "on")) {
            ioctl(fd, IOCTL_LED_ON, led_no);    // 点亮
        } else if (!strcmp(argv[2], "off")) {
            ioctl(fd, IOCTL_LED_OFF, led_no);   // 熄灭
        } else {
            goto err;
        }
    }
    
    close(fd);
    return 0;
    
err:
    if (fd > 0) 
        close(fd);
    usage(argv[0]);
    return -1;
    

}

 

posted on 2013-08-26 15:25  熊猫酒仙是也  阅读(4501)  评论(0编辑  收藏  举报

导航