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;
    

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM