• 远程视频监控之驱动篇(LED)


    转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38515205        


           之前一直在考虑该不该写这篇,由于我之前在博客里有写过LED的驱动,可是没有具体的解说。后来本着叫大家都能看懂驱动的想法,我还是决定要写一下。我想通过LED的驱动,让不了解驱动的小伙伴。可以有一个感性的认识。

    一.代码

    #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 <linux/device.h>
    #include <mach/gpio.h>
    
    static struct class *wvm_led_class;
    static  int major;
    
    volatile unsigned long *gpbcon = NULL;
    volatile unsigned long *gpbdat = NULL;
    
    
    static int wvm_led_drv_open(struct inode *inode, struct file *file)
    {
    	/*
    	 * LED1,LED2,LED4相应GPB5、GPB6、GPB7、GPB8
    	 */
    	/* 配置GPB5,6,7,8为输出 */
    	*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
    	*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
    	return 0;
    }
    
    static int wvm_led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
    	int val,ret;
    
    	ret=copy_from_user(&val, buf, count); //	copy_to_user();
    	if (ret)
    	return -EAGAIN;
    
    	if (val == 1)
    	{
    		// 点灯
    		*gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
    	}
    	else
    	{
    		// 灭灯
    		*gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);
    	}
    	
    	return 0;
    }
    
    static struct file_operations wvm_led_drv_fops = {
        .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自己主动创建的__this_module变量 */
        .open   =   wvm_led_drv_open,     
        .write  =	wvm_led_drv_write,	   
    };
    
    static int wvm_led_drv_init(void)  //入口函数(做初始化,创建设备等工作)
    {
    	major = register_chrdev(0, "wvm_led_drv", &wvm_led_drv_fops); // 注冊, 告诉内核
    	if(major < 0)
            {
              printk(  " wvm_led_drv register falid!/n");
              return major;
            }
            
    	wvm_led_class = class_create(THIS_MODULE, "wvm_led");
    	if(IS_ERR(wvm_led_class))
            {
              printk( " wvm_led_drv register class falid!/n");
              return -1;
            }
            
            device_create(wvm_led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    
    	gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
    	gpbdat = gpbcon + 1;
    
    	return 0;
    }
    
    static void wvm_led_drv_exit(void)  //出口函数(做卸载和销毁工作)
    {
    	unregister_chrdev(major, "wvm_led_drv"); // 卸载
    	device_destroy(wvm_led_class, MKDEV(major, 0));
    	class_destroy(wvm_led_class);
    	iounmap(gpbcon);
    }
    
    module_init(wvm_led_drv_init);  //定义入口函数
    module_exit(wvm_led_drv_exit);  //定义出口函数
    
    MODULE_LICENSE("GPL");


    二.驱动结构

         正所谓麻雀虽小五脏俱全,它包含了一个驱动的基本功能。以下我写一个类似于模板的东西给大家。

    //头文件

    ...

    //定义一些变量和结构体等

    ...

    //各种操作函数

    xx_open()

    {

    .....

    }

    xx_close()

    {

    .....

    }

    xx_ioctl()

    {

    .....

    }

    ...

    //file_operations结构体

    static struct file_operations XXX_drv_fops = {
            .owner   =  THIS_MODULE,    /* 这是一个宏。推向编译模块时自己主动创建的__this_module变量 */
            .open     =  XXX_open,           //后面的名字要与操作函数一致
            .close    =  XXX_close,   

            .ioctl       =  XXX_ioctl
      
    };

    //入口函数

    static int XXX_init(void)

    {

    主要做创建设备等初始化工作。參照前面驱动(要推断返回值)。

    }

    //出口函数

    static voidXXX_exit(void)

    {

    主要卸载创建的设备。做一些清理工作。參考前面的驱动去写

    }

    //入口、出口、证书的声明

    module_init(XXX_init);
    module_exit(XXX_exit);
    MODULE_LICENSE("GPL");


    三.应用測试

         应用就是简单的測试一下开灯和关灯

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    /* led_test on    开灯
      * led_test off  关灯
      */
    int main(int argc, char **argv)
    {
    	int fd;
    	int val = 1;
    	fd = open("/dev/led", O_RDWR);
    	if (fd < 0)
    	{
    		printf("can't open!
    ");
    	}
    	if (argc != 2)
    	{
    		printf("Usage :
    ");
    		printf("%s <on|off>
    ", argv[0]);
    		return 0;
    	}
    
    	if (strcmp(argv[1], "on") == 0)
    	{
    		val  = 1;
    	}
    	else
    	{
    		val = 0;
    	}
    	
    	write(fd, &val, 4);
    	return 0;
    }


    看到这也许有的小伙伴们已经买白了驱动怎么写,可是灯怎么亮的呢?

            应用程序:write()----->驱动程序:write()

            细心的小伙伴已经注意到驱动中的write()中有ret=copy_from_user(&val, buf, count);

            然后就能够開始运行亮灯和灭灯了

    两步搞定亮灭灯:

            因为一个引脚可能同意有不同的功能,所以引脚的寄存器分为两类。一类为控制寄存器。一类数据寄存器

    要操作某个引脚先设置控制寄存器(配置为某种功能)。然后设置数据寄存器(实现功能)

        *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));  //清零
        *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));     //配置为输出引脚


            // 点灯
            *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));

            // 灭灯
            *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);


    这里面要注意一点,在曾经单片机敲代码的的时候我们能够直接操作物理地址,可是如今驱动要操作虚拟地址。

    所以我们要做一个映射

    gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);  //物理地址0x56000010 ,映射长度16字节

    gpbdat = gpbcon + 1;


  • 相关阅读:
    js验证邮箱
    输出一个金字塔
    仿QQ聊天软件2.0版
    zoj 3662 第37届ACM/ICPC长春赛区H题(DP)
    zoj 3659 第37届ACM/ICPC 长春赛区现场赛E题 (并查集)
    zoj 3640 概率dp
    hdu 5203 && BC Round #37 1002
    poj 3071 概率dp
    poj 2151 概率dp
    zoj 3460 二分+二分图匹配
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7071207.html
Copyright © 2020-2023  润新知