GPIO(led灯)驱动和测试程序

来源:互联网 发布:财务报表主要数据分析 编辑:程序博客网 时间:2024/06/08 02:26

看着内核分析,慢慢的感觉这些内核里面的驱动就那么纠结呢,先把内核分析放一边吧,看下以前弄的驱动,算是借鉴别人的驱动,修改了下而已。

 

环境:

内核:Linux-2.6.25.4

交叉编译器:arm-linux-3.4.5


GPIO_led.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>                /*所用到的头文件*/

#define DEVICE_NAME    "GPIO-leds"    /*device name,you will see device name when execute "cat /proc/devices" */   
#define LED_MAJOR    231            /*major devive number*/

/*the second parameter of ioctl(fd,cmd,arg) */
#define IOCTL_LED_ON    1
#define IOCTL_LED_OFF    0

/*制定LED所用的 GPIO pins */
static unsigned long led_port [] =
{
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,//我用的就是TQ2440板,用到的GPIO是GPB5-8。
};

/*用来指定GPIO pin的功能:output*/
static unsigned int led_out [] =
{
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};

/* 应用程序对设备文件/dev/GPIO-leds执行 open(...)时,
 * 就会调用iFico_leds_open函数
 */
static int iFico_leds_open(struct inode *inode, struct file *file)
{
//    int i;

//    for (i = 0; i < 4; i++)
//    {
        //
//        s3c2410_gpio_cfgpin(led_led[i], led_out[i]);
//    }
    return 0;
}

/* 应用程序对设备文件/dev/GPIO-leds执行 ioctl(...)时,
 * 就会调用iFico_leds_ioctl函数
 */
static int iFico_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    if (arg > 4)
    {
        return -EINVAL;
    }

    switch(cmd)
    {
        case IOCTL_LED_ON:
            // 设置指定引脚的输出电平为0
            s3c2410_gpio_setpin(led_port[arg], 0);
            return 0;

        case IOCTL_LED_OFF:
            // 设置指定引脚的输出电平为1
            s3c2410_gpio_setpin(led_port[arg], 1);
            return 0;

        default:
            return -EINVAL;
    }
}

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

static char __initdata banner[] = "iFico2440 LEDS,Sunny/n";
static struct class *led_class;

/*
 * 执行“insmod GPIO_leds.ko”命令时就会调用这个函数
 */
static int __init iFico_leds_init(void)
{
    int ret;
    printk(banner);

    /* 注册字符设备驱动程序
     * 参数为主设备号、设备名字、file_operation结构;
     * 这样,主设备号就和具体的file_operation结构联系起来了,
     * 操作主设备为LED_MAJOR的设备文件时,就会调用iFico_leds_fops中的相关成员函数
     * LED_MAJOR可以设为0,表示由内核自动分配主设备号
     */
    ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &iFico_leds_fops);
    if (ret < 0)
    {
        printk(DEVICE_NAME " can't register major number/n");
        return ret;
    }

    //注册一个类,是mdev可以在“/dev/”目录下面建立设备节点
    led_class = class_create(THIS_MODULE, DEVICE_NAME);
    if(IS_ERR(led_class))
    {
        printk("Err: failed in GPIO-leds class. /n");
        return -1;
    }
    //创建一个设备节点,节点名为DEVICE_NAME
    class_device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);

    printk(DEVICE_NAME " initialized/n");
    return 0;
}

/*
 * 执行“rmmod GPIO_leds.ko”命令时就会调用这个函数
 */
static void __exit iFico_leds_exit(void)
{
    /* 卸载驱动程序 */
    unregister_chrdev(LED_MAJOR, DEVICE_NAME);
    class_device_destroy(led_class, MKDEV(LED_MAJOR, 0));    //删掉设备节点
    class_destroy(led_class);                    //注销类
}

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

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

 

编写好驱动程序后,放到drivers/char/目录下,修改此目录下的Makefiel 和Kconfig:

 

修改“Kconfig”文件,在6行开始添加如下内容(红色部分所示):

#

# Character device configuration

#

 

menu "Character devices"

 

 

 

config iFico_LEDS

        tristate "Sunny LEDs Driver"

        depends on ARCH_S3C2440

        help

          2440 User leds.

 

然后修改“Makefile”文件,在大概12行添加如下内容(红色部分所示):

#

# Makefile for the kernel character device drivers.

#

 

#

# This file contains the font map for the default (hardware) font

#

FONTMAPFILE = cp437.uni

 

obj-y    += mem.o random.o tty_io.o n_tty.o tty_ioctl.o

 

obj-$(CONFIG_iFico_LEDS)        += GPIO_leds.o

obj-$(CONFIG_LEGACY_PTYS)       += pty.o

 

添加完毕以上内容之后,输入:#make menuconfig,然后配置如下:

Device Drivers  ---> 

Character devices  --->  

         <*> Sunny LEDs Driver

保存编译内核,烧写到开发板中。在系统中的/dev目录下会看到GPIO-leds,说明驱动加载成功。

 

然后再编写测试程序。

 

/*************************************
NAME:leds.c
*************************************/

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

#define IOCTL_LED_ON  1
#define IOCTL_LED_OFF 0

void usage(char *exename)
{
    printf("Usage:/n");
    printf("  %s<led_no><on/off>/n",exename);
    printf(" led_no=1,2/n");
}
int main(int argc, char **argv)
{
    unsigned int led_no;
    int fd;
    if(argc !=3)
        goto err;
//    system("/etc/rc.d/init.d/leds stop");
    fd = open("/dev/GPIO-leds", 0);
    if (fd < 0) {
        perror("Can't open /dev/GPIO-leds/n");
        return -1;
    }
   
    led_no= strtoul(argv[1],0,0)-1;
    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;
}

[root@sunny /]# arm-linux-gcc -o ledtest leds.c

[root@sunny /]# arm-linux-strip ledtest

 

然后将ledtest通过NFS或者U盘拷贝到开发板中/sbin目录下,重启

[root@sunny /]# ledtest 1 on  就会看到开发板上的led1亮;

[root@sunny /]# ledtest 1 off  就会看到开发板上的led1灭;

 

这是一个比较简单的例子,仅仅是控制IO口,驱动是一个复杂的圈圈,还是要好好的学习啊!

 

原创粉丝点击