码迷,mamicode.com
首页 > 其他好文 > 详细

s3c2440 LED驱动分析

时间:2015-04-19 09:00:51      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:linux驱动   led驱动   

        这个开发板已经很久没有动了,这一次辞职后想来想去还是选择去做驱动吧。以前写的那些驱动代码早就不知道哪里去了,当然更不记得了。所以现在从头开始学习,也顺便记录下笔记;


原理

        首先看看LED的电路图:

        技术分享

        不难看出,LED1==GPB5   LED2==GPB6   LED3==GPB7    LED4==GPB8

        然后就去看看IO端口图:

        技术分享

        要设置的非常简单,就是把GPBCON设置为输出,GPBDAT设置为0时,则灯亮;设置为1时,则灯灭;


相关知识点

        其实上一篇中s3c2440系统自带的管脚宏和函数已经分析过那些引脚宏;

        S3C2410_GPB(5):表示GPB5的虚拟地址;

        s3c2410_gpio_setpin(S3C2410_GPB(5), 0):表示给GPB5数据位的虚拟地址上设置为0;其实就是GPBDAT中对应位设置为0;

        s3c2410_gpio_cfgpin(S3C2410_GPB(5), S3C2410_GPIO_OUTPUT):表示给GPB5控制位的虚拟地址上设置为输出;其实就是GPBCON中对应位设置为输出状态;


下面涉及的结构体和函数都在  include/cdev.h中可以找到

字符设备结构体cdev:     

struct cdev {
	struct kobject kobj;
	struct module *owner;// 模块属于谁
	const struct file_operations *ops;// 对应的操作函数
	struct list_head list;
	dev_t dev;// 设备号
	unsigned int count;
};

struct cdev *cdev_alloc(void);为字符设备结构体申请内存;

void cdev_init(struct cdev *, const struct file_operations *);把字符设备结构体和实现的操作函数绑定起来;

int cdev_add(struct cdev *, dev_t, unsigned);把设备号和字符设备结构体绑定起来,其实这里就相当于把我们自己实现的操作函数和设备号对接起来。上层应用打开的文件是根据设备号,也就是说上层操作的仅仅是和设备号有关系,而要上层应用去使用我们自己设计的函数,就必须把实现的函数和设备号绑定起来。这样他们之间就有关系了。(语言表达有限,自己可以体会下)

void cdev_del(struct cdev *);把字符设备卸载下来;

void cdev_put(struct cdev *p);对字符设备结构体内存的释放;(我程序中使用到这个却是说未定义,但在源代码cdev.h中却能找到这个函数,不知道为什么会这样)

还有些函数等使用到的时候再去分析;


实现代码

驱动实现代码

#include<linux/init.h>
 #include<linux/module.h>
 #include<linux/fs.h>
 #include<linux/cdev.h>
 #include<linux/errno.h>
 #include<mach/regs-gpio.h>
 #include<linux/gpio.h>
 
 #define MAJOR_NUM 250 // 主设备号
 #define MINOR_NUM 0   // 次设备号
 #define DEV_NAME "yzh_led_test" //驱动名称,加载完后用命令 cat /proc/devices可以查看到这个
 
 #define led_on      1
 #define led_off     2
 #define all_led_on  3
 #define all_led_off 4
 
 static unsigned long led_IO_port[]=
 {
     S3C2410_GPB(5),
     S3C2410_GPB(6),
     S3C2410_GPB(7),
     S3C2410_GPB(8),
 };
  
 static struct cdev *cdevp = NULL; // 字符设备结构体cdev
 static int devNum;
 
 int myOpen(struct inode *inode, struct file *file)
 {
     printk(KERN_INFO"in myOpen() will open led!\n");
 
     if (file->f_mode & FMODE_READ)
         printk(KERN_INFO"file is readonly!\n");
     else if (file->f_mode & FMODE_WRITE)
         printk(KERN_WARNING"file is writeonly!\n");
     else
         printk(KERN_DEBUG"file is read and write!\n");
 
     return 0;
 }
 
 static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
     int i;
    
     if (0 > arg || 4 < arg)
         return -EINVAL;

    printk(KERN_INFO"cmd:%u, ledNum:%ld\n", cmd, arg);
     switch(cmd){
         case led_on:
             s3c2410_gpio_setpin(led_IO_port[arg], 0);
             break;
 
         //case led_off:
         case 5:
             s3c2410_gpio_setpin(led_IO_port[arg], 1);
             break;
 
         case all_led_on:
             for (i = 0; i < 4; i++)
                 s3c2410_gpio_setpin(led_IO_port[i], 0);
             break;
 
         case all_led_off:
             for (i = 0; i < 4; i++)
                 s3c2410_gpio_setpin(led_IO_port[i], 1);
             break;
 
         default:
             return -EINVAL;
     }
     return 0;
 }

struct file_operations fops =
 {
     .owner = THIS_MODULE,
     .open  = myOpen,
     .ioctl = led_ioctl,
 };
 
 static int __init myDrive_init(void)
 {
     int ret, i;
     dev_t allocDevNum;
 
     printk(KERN_INFO"led init!\n");
 
     devNum = MKDEV(MAJOR_NUM, MINOR_NUM);//根据主设备号和次设备号得到设备号
     ret = register_chrdev_region(devNum, 1, DEV_NAME);// 静态申请设备号,设备号不变
     if (ret < 0){
         printk(KERN_INFO"register_chrdev_region error!\n");
         ret = alloc_chrdev_region(&allocDevNum, 0, 1, DEV_NAME);// 动态申请设备号,设备号是系统分配的
         if (ret < 0){
             printk(KERN_INFO"alloc_chrdev_region error!\n");
             return -EINVAL;
         }
         devNum = allocDevNum;
     }

     printk(KERN_INFO"devNum:%d, major:%d, minor:%d\n", devNum, MAJOR(devNum), MINOR(devNum));
 
     cdevp = cdev_alloc();
     cdev_init(cdevp, &fops);
     cdevp->owner = THIS_MODULE;
 
     ret = cdev_add(cdevp, devNum, 1);
     if (ret){
         printk(KERN_NOTICE"Error %d adding cdev", ret);
         return -EINVAL;
     }
 
     for (i = 0; i < 4; i++){// 初始化LED  GPBCON和熄灭所有灯
         s3c2410_gpio_cfgpin(led_IO_port[i], S3C2410_GPIO_OUTPUT);
         s3c2410_gpio_setpin(led_IO_port[i], 1);
     }
     return 0;
 }
 
 static void __exit myDrive_exit(void)
 {
     printk(KERN_INFO"myDrive exit!\n");
     cdev_del(cdevp);
     unregister_chrdev_region(devNum, 1);
 }
 module_init(myDrive_init);
 module_exit(myDrive_exit);
 MODULE_LICENSE("Dual BSD/GPL");
        上面的代码都通过测试的,我用ioctl(fd, 2, n)总是报错误地址,所以在代码中我用 5 来替换 led_off(其实这是2)。其他的就没问题,当然这是这基本的一个代码,至于流水灯,跑马灯啊,有兴趣就自己去实现,这里就给个点灯的代码思路;


编译和测试

        编译驱动有两种方法,可以参考上一篇blog:驱动的两种编译方法;我两种方法都实验过,我们可以先用动态加载,然后测试,最后确定好了,再把该驱动放到内核中;

        首先把 .ko文件用nfs共享给开发板,开发板中insmod xx.ko,把驱动加载进内核,用命令 cat /proc/devices可以查看到加载的驱动,用lsmod也可以看到。当然我们要的是主设备号。

        接下来就在 /dev/下创建一个设备文件,用来对应你的驱动。 mknod /dev/myLed  c  250  0;根据主设备号在/dev下创建个驱动对应的设备文件。我们测试的时候就是操作这个文件的;

下面看看测试代码:

#include<stdio.h>
 #include<stdlib.h>
 #include<fcntl.h>
 #include<errno.h>
 
 int main(int argc, char* argv[])
 {
     int flag, ledNum, ret;
     int fd = open("/dev/charDevice", O_RDWR);
     if (fd < 0){ 
         printf("open fild!\n");
         printf("errno:%d\n", errno);
         return -1; 
     }   
     
     sscanf(argv[1], "%d", &flag);
     sscanf(argv[2], "%d", &ledNum);
     
     printf("fd:%d, argv[1]--flag:%d, argv[2]--ledNum:%d\n\n", fd, flag, ledNum);
     ret = ioctl(fd, flag, ledNum);
     if (ret < 0){ 
         printf("error in ioctl, errno:%d\n", errno);
     }   
     return 0;
 }
        可能有些多余的语句,那是我开始用来调试驱动的。这里有个errno比较好用,当出错时,可以查看下errno的错误码,方便调试;


        当然也可以用write()函数来实现点灯操作,或者其他方法,总之都是大同小异。     


        转载请注明作者和原文出处,原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/45116185
        若有不正确之处,望大家指正,共同学习!谢谢!!!


s3c2440 LED驱动分析

标签:linux驱动   led驱动   

原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/45116185

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!