• 轻轻松松编写自己linux驱动


    环境:

    编译平台:虚拟机上安装linux操作系统(fedroa 10)

    应用平台:TQ2440移植linux(内核2.6.30.4)

    编译器:交叉编译器arm-linu-gcc(4.3.3)

    首先说明下编写驱动所涉及的的目录和文件:

    1、TQ2440移植的系统内核在我PC上存放的路径:/opt/EmbedSky/linux2.6.30.4

    2、编译器存放在:/opt/EmbedSky/4.3.3/bin

    3、我们编写文件的工作目录在PC机的:/home/test

    4、TQ2440启动NFS文件系统是挂载虚拟机linux目录:/opt /EmbedSky/root_nfs

    5、我们将编译好对驱动模块*.ko,这放在:/opt/EmbedSky/root_nfs/lib 即放在TQ2440文件系统目录下/lib下

    6、我们将编译好对驱动测试软件程序执行文件,放在:/opt/EmbedSky/root_nfs/usr/sbin/

    驱动程序是用于连接硬件与系统内核:驱动编译方式有两种,一是编译入内核,二是编译成模块(可以像我们windows一样运行做系统下载驱动程序)。

    这我们用编译模块驱动来介绍,因为这种方式比编译内核好;编译入内核需要重新编译内核文件,重新移植内核入TQ2440(开发板),对flash烧写次数多伤害大。

    首先知道我们要编写4个文件:2个C文件,2个Makefile文件;分别是驱动源程序C文件(testdev.c),驱动源程序编译相关的Makefile文件,测试驱动程序的"软件程序"(testsoft.c),编译测试软件程序的Makefile。

    开始编写:

    第一步:在/home/test/testdev目录下建立testdev.c

    #cat testdev.c      //该命令是查看testdev.c的内容,且当前目录下运行。

    [root@EmbedSky testdev]# cat testdev.c
    /*************************************

    NAME:testdev.c
    COPYRIGHT:http://www.cnblogs.com/CJye/

    *************************************/
    #include <linux/miscdevice.h>
    #include <linux/delay.h>
    #include <asm/irq.h>
    #include <mach/regs-gpio.h>
    #include <mach/hardware.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/types.h>
    #include <linux/delay.h>
    #include <linux/moduleparam.h>
    #include <linux/slab.h>
    #include <linux/errno.h>
    #include <linux/ioctl.h>
    #include <linux/cdev.h>
    #include <linux/string.h>
    #include <linux/list.h>
    #include <linux/pci.h>
    #include <asm/uaccess.h>

    #define DEVICE_NAME "testdev"
    #define LED_MAJOR 241
    /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
    #define IOCTL_LED_ON    1
    #define IOCTL_LED_OFF    0

    /* 用来指定LED所用的GPIO引脚 */
    static unsigned long led_table [] =
    {
        S3C2410_GPB5,
        S3C2410_GPB6,
        S3C2410_GPB7,
        S3C2410_GPB8,
    };

    /* 用来指定GPIO引脚的功能:输出 */
    static unsigned int led_cfg_table [] =
    {
        S3C2410_GPB5_OUTP,
        S3C2410_GPB6_OUTP,
        S3C2410_GPB7_OUTP,
        S3C2410_GPB8_OUTP,
    };

    static int testdev_open(struct inode *inode,struct file *file)
    {
            return 0;
    }

    static int testdev_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_table[arg], 0);
                return 0;
            case IOCTL_LED_OFF:
                // 设置指定引脚的输出电平为1
                s3c2410_gpio_setpin(led_table[arg], 1);
                return 0;
            default:
                return -EINVAL;
        }
    }

    static struct file_operations testdev_fops = {        //给内核结构体赋值
            .owner    =     THIS_MODULE,
            .open     =       testdev_open,
            .ioctl      =       testdev_ioctl,
    };

    static char __initdata banner[] = "TQ2440/DIY LEDS,http://www.cnblogs.com/CJye/\n";

    static int __init testdev_init(void)
    {
        int ret;
        int i;
            for (i = 0; i < 4; i++)
        {
            s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //将GPB5~8脚作为输出
            s3c2410_gpio_setpin(led_table[i], 0);    //GPB5~8输出低电平4个灯全部点亮
        }
        printk(banner);
                ret=register_chrdev(LED_MAJOR,DEVICE_NAME,&testdev_fops);  //向内核写入驱动模块
        if(ret<0)
        {
            printk(DEVICE_NAME"can not register major number\n");
                return ret;
            }     
    }

    static void __exit testdev_exit(void)
    {
        unregister_chrdev(LED_MAJOR,DEVICE_NAME);
        printk(DEVICE_NAME"give up/cancel\n");
    }

    module_init(testdev_init);              //insmod *.ko函数调用入口
    module_exit(testdev_exit);        //rmmod *.ko 函数调用入口

    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("http://www.cnblogs.com/CJye/");
    MODULE_DESCRIPTION("TQ2440/DIY LEDS Driver");

    第二步:在/home/test/testdev目录下建立Makefile

    #cat Makefile    //该命令是查看testdev.c的内容,且当前目录下运行。

    [root@EmbedSky testdev]# cat Makefile

    PWD    = $(shell pwd)

    KERNELDIR =/opt/EmbedSky/linux-2.6.30.4

    obj-m    =testdev.o

    modules:
        make -C $(KERNELDIR) M=$(PWD) modules
        cp -f testdev.ko /opt/EmbedSky/root_nfs/lib/.
        
    clean:
        make -C $(KERNELDIR) M=$(PWD) clean
        rm -f $(PWD)/*~

    #ls                  //该命令是查看编译生成的文件,在当前目录运行。

    [root@EmbedSky testdev]# ls
    Makefile       Module.symvers  testdev.ko     testdev.mod.o
    modules.order  testdev.c       testdev.mod.c  testdev.o

    第三步:在/home/test/testsoft目录下建立testsoft.c

    #cat testdev.c      //该命令是查看testdev.c的内容,且当前目录下运行。

    [root@EmbedSky testsoft]# cat testsoft.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>          //open() close()
    #include <unistd.h>         //read() write()

    #define DEVICE_NAME "/dev/testdev"  //this is dev name
    #define LED_ON 1
    #define LED_OFF 0

    int main(void)
    {
        int fd;
        int ret;
        char *i;
        printf("\nstart gpio_led_driver test\n\n");
        fd = open(DEVICE_NAME,O_RDWR);
        printf("fd = %d\n",fd);
        if(fd==-1)
           {
                 printf("open device %s error\n",DEVICE_NAME);
           }
        else
           {
              while(1)        //对GPB5~8的LED控制操作
            {
            ioctl(fd,LED_ON,0);      
            ioctl(fd,LED_ON,2);        //点亮GPB5,GPB7
            ioctl(fd,LED_OFF,1);        //关闭GPB6,GPB8
            ioctl(fd,LED_OFF,3);       
            sleep(1);
            ioctl(fd,LED_ON,1);
            ioctl(fd,LED_ON,3);        //点亮GPB6,GPB8
            ioctl(fd,LED_OFF,0);        //点亮GPB5,GPB7
            ioctl(fd,LED_OFF,2);
            sleep(1);
            }  
               ret = close(fd);
               printf("ret=%d\n",ret);
               printf("close gpio_led_drier test\n");
           }
        return 0;
    }

    第四步:在/home/test/testsoft目录下建立Makefile

    #cat Makefile    //该命令是查看Makefile的内容,且当前目录下运行。

    [root@EmbedSky testsoft]# cat Makefile

    CC  =/opt/EmbedSky/4.3.3/bin/arm-linux-gcc

    OBJ  =testsoft.o

    EXEC  =testsoft

    $(EXEC):$(OBJ)

      $(CC) -o $@ $^

    copy :

      cp -f  $(EXEC) /opt/EmbedSky/root_nfs/usr/sbin/.

    clean:

      rm -f $(EXEC) *.o *~

      rm -f /opt/EmbedSky/root_nfs/usr/sbin/$(EXEC)

    #ls                  //该命令是查看编译生成的文件,在当前目录运行。

     [root@EmbedSky testsoft]# ls
    Makefile        testsoft.c      testsoft.o    testsoft

    到此,驱动文件编译工作完成了。

    下面通过windons上secureCRT终端进入TQ2440操作。

    TQ2440如果有相关的GPIO控制LED的驱动,可以先停用,然后再加载我们自己的驱动。

    #/etc/rc.d/init.d/ledctl stop      ///etc/rc.d/init.d目录下是一些TQ2440启动文件系统自启动的应用程序,根据这命令格式可以停用相应的应用程序。

      ledctl这是我启动文件系统加载的一个LED跑马灯,要加载下面我们的应用程序,所以我就先停用这个ledctl。

    #cd /lib

    #insmod testdev.ko        //加载这个模块到内核

    #lsmod                         //查看是否有这个模块

    #cd /dev                       //进入系统驱动文件目录

    #mknod testdev c 241 0          //给testdev.ko建议一个设备节点,字符型驱动文件,主设备号241与驱动文件里一致,次设备号0

    #cd /usr/sbin               //进入测试驱动软件程序执行文件存放目录

    #ls                              //查看是否有testsoft执行文件。

    # ./testsoft                  //此时你实现对LED灯控制效果就出现在你眼前了。

    是不是很轻松,很有成就,那就去开只香槟庆祝下。



  • 相关阅读:
    huffman编码压缩算法(转)
    ReLU 和sigmoid 函数对比以及droupout
    分类中数据不平衡问题的解决经验(转)
    C++ 虚函数表解析
    const 和宏的区别
    static小结
    javascript技巧字典【转藏】
    七个心理寓言【转】
    购物车悬浮 + 购物数量显示
    好看的图标
  • 原文地址:https://www.cnblogs.com/CJye/p/3066147.html
Copyright © 2020-2023  润新知