• led驱动


    驱动步骤:

          1、驱动框架:一般读驱动代码需要module_init一层层找代码

        

    
    

          2、硬件配置

      代码中led_ioctl函数设置引脚的电平高低,该函数是驱动程序对设备的通道进行统一设置/控制的函数 

       一、  在用户空间,使用ioctl系统调用来控制设备,原型如下:

        int ioctl(int fd,unsigned long cmd,...);
      fd:文件描述符
    cmd:控制命令
    ...:可选参数:插入*argp,具体内容依赖于cmd
    用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情
    二、驱动ioctl方法
    int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
    /*
    inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
    cmd 由用户空间直接不经修改的传递给驱动程序
    arg 可选。
    */
    在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

      用户使用  int ioctl(int fd,unsinged long cmd,...)  时,...就是要传递的参数;
      再通过  int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long  arg)  中的arg传递;不同颜色代表对应的参数

    现阶段能够理解成每一个用户程序的ioctl对应其内核中的一个ioctl函数,且参数需要用户层传递给驱动层 (该例中) 

    代码很简单 :

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #define DEVICE_NAME "myled"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
    static struct class *leds_class;
    static struct class *led_dev_class;
    int major;
    
    
    /* 用来指定LED所用的GPIO引脚 */
    static unsigned long led_table[]={
        
        S3C2410_GPF5,
        S3C2410_GPF6,
        
    };
    
    /* 用来指定GPIO引脚的功能:输出 */
    static unsigned int led_cfg_table [] = {
        
        S3C2410_GPF5_OUTP,
        S3C2410_GPF6_OUTP,
    };
    
    /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
    #define LED_ON    0
    #define LED_OFF   1
    /* 应用程序对设备文件/dev/leds执行open(...)时,
     * 就会调用s3c24xx_leds_open函数
     */
    static int led_open (struct inode *inode, struct file *filep)  
    {  
        int i;
        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
         for (i = 0; i <2; i++) {
            // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
            s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
        }
        return 0;  
    }  
    static int led_ioctl(
        struct inode *inode, 
        struct file *file, 
        unsigned int cmd, 
        unsigned long arg)
    {
        
        if (arg > 2) {
            return -EINVAL;
        }
         switch(cmd) {
             case LED_ON:
            // 设置指定引脚的输出电平为0
            s3c2410_gpio_setpin(led_table[arg], 0);
            return 0;
            case LED_OFF:
            // 设置指定引脚的输出电平为1
            s3c2410_gpio_setpin(led_table[arg], 1);
            return 0;
              default:
            return -EINVAL;
        } 
    }
    /* 这个结构是字符设备驱动程序的核心
     * 当应用程序操作设备文件时所调用的open、read、write等函数,
     * 最终会调用这个结构中指定的对应函数
     */
    static struct file_operations led_ops=  
    {    
        .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open   = led_open,  
        .ioctl  = led_ioctl ,
        
        
    }; 
     
    /*
     * 执行insmod命令时就会调用这个函数 
     */
      
    static int led_init(void)  
    {  
        int ret;
        /* 注册字符设备
         * 参数为主设备号、设备名字、file_operations结构;
         * 这样,主设备号就和具体的file_operations结构联系起来了,
         * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
         * LED_MAJOR可以设为0,表示由内核自动分配主设备号
         */
         major = register_chrdev(0, DEVICE_NAME, &led_ops);
          if (major < 0) 
          {
          printk(DEVICE_NAME  " can't register major number number::%d
    ",major);
          return ret;
          }
        printk(DEVICE_NAME " initialized1
    ");
        leds_class = class_create(THIS_MODULE, "leds");
        if (IS_ERR(leds_class))
            return PTR_ERR(leds_class);
        led_dev_class = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "perled"); /* /dev/leds */ 
        return 0;
    
    }
    
    /*
     * 执行rmmod命令时就会调用这个函数 
     */
    static void led_exit(void)
    {
        class_device_unregister(led_dev_class, MKDEV(major, 0));
        class_destroy(leds_class);       
         /* 卸载驱动程序 */
        unregister_chrdev(major, DEVICE_NAME);
      
    }
    
    
    
    
    
    
    module_init(led_init);  
    module_exit(led_exit);  
    
    
    MODULE_AUTHOR("http://www.100ask.net");
    MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
    MODULE_LICENSE("GPL");  

     make

    insmod XXX.ko

    测试代码(应用程序代码):

    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <fcntl.h>  
    #include <stdio.h>  
    #include <sys/ioctl.h>
    #include <stdlib.h>
    /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
    #define IOCTL_LED_ON    0
    #define IOCTL_LED_OFF   1
    /*
      *  ledtest <dev> <on|off>
      */
    
    void print_usage(char *file)
    {
        printf("Usage:
    ");
        printf("./a.out 1 on
    ");
    }
    
    
    int main(int argc,char**argv)  
    {  
        int fd;  
        int ret;
        int led_NO;
        char val;    
        if(argc!=3)
        {
            
            printf("error USAGE
    ");
            exit(1);
        }
      //1、打开设备
        fd = open("/dev/perled",O_RDWR);  
        led_NO=strtoul(argv[1],0,0)-1;//确定LED
        if(fd<0)  
        {  
            perror("open fail 
    ");  
            return -1;  
        }  
       if (!strcmp("on", argv[2]))
        {
            // 亮灯
           ioctl(fd,IOCTL_LED_ON,led_NO);// 设备 亮灭 那个led、
        }
        else if (!strcmp("off", argv[2]))
        {
            // 灭灯
            ioctl(fd,IOCTL_LED_OFF,led_NO);
        }
        else
        {
            print_usage(argv[0]);
            return 0;
        }
        
      
        close(fd);
        return 0;
    }  
    /// //   // / //./a.out 1 on

     驱动程序中的ioctl也可以用write函数但是需要用到copy_from_user

    将数据由其用户 空间上传到内核空间了

    static ssize_t led_write(struct file *file, const char __user *data,
                      size_t len, loff_t * ppos)
    {
        int val;
        copy_from_user(&val, data, 1); //    copy_to_user();
        s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1));//避免使用if语句   
        
        return 1;
                          
    }

    ioctl:http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html

  • 相关阅读:
    C#递归读取GIS目录文件格式
    ArcGIS Pro 2.5离线安装方案
    ASP.NET跨域解决方法
    C# GDAL编码问题3——读取中文图层
    C# GDAL编码问题2——读取中文属性
    C# GDAL编码问题1——打开mdb中文路径
    Word标题编号变黑框
    SVN设置全局忽略样式
    DevExpress中DockPanel样式修改
    解决XML根级别上的数据无效
  • 原文地址:https://www.cnblogs.com/zhaobinyouth/p/6246042.html
Copyright © 2020-2023  润新知