关闭

LED灯驱动编写----对寄存器操作

标签: structcmdfileiomodulec
4039人阅读 评论(2) 收藏 举报
分类:

LED灯驱动编写--寄存器操作


(转载请写明出处: http://blog.csdn.net/yby19870627/article/details/7407130 )


这里没有用的内存映射的方法,而是直接对寄存器进行操作,我建议在开发驱动的时候,用NFS挂载的方式进行开发,这样可以节省很多时间,NFS挂载方法可以从我以前的文章中找到。

平台:Fedora14

内核:linux-2.632.2

 

一、首先要编写最基本的模块,因为编程就是要一步一步调试的,这样才能发现问题,如果一开始不管三七二十一,先把代码写完再说,那当你make完看到那些错误的时候,那个时候,我估计你连死的心都有了。

 

1.        添加最基本的头文件:#include <linux/module.h>

                                                   #include <linux/init.h>

2.        编写模块初始化函数和模块推出函数

static      int   __init   Led_init(void)

{

                  printk("<0>module--->Led_init\n");

}
//--------------------------------------------------------------------------------

static    void   __exit   Led_exit(void)

{

             printk("<0>module--->exitok!\n");

}

//---------------------------------------------------------------------------------

module_init(Led_init);

module_exit(Led_exit);

 

3.        编译程序,然后再insmod led.ko/rmmod led

看看是否输出了代码里面的两条打印语句。

 

二、第一步通过之后,那么就要开始添加新的代码了,我们是要注册设备的,所以需要定义一个设备号变量,同时要注册到内核中还需要一个struct cdev结构的变量,所以要定义     

             struct  cdev  led_cdev;   

             dev_t   led_devno;

定义完之后还需要考虑到要有一个变量来存储主设备号,所以还要定义一个主设备号变量。而且还要定义主设备号和次设备号两个宏。

             #define   MAINLEDNO   108

            #define   MINORLEDNO   0 

            static   int   led_major;

 

 

定义struct  file_operations结构,一开始里面可以什么都不写。

 

struct  file_operations  led_ops={

         

 };

修改模块初始化函数。

static   int   __init   Led_init(void)

{

         printk("<0>module--->Led_init\n");

         int  result;

         //申请设备号

         led_devno =MKDEV(MAINLEDNO,MINORLEDNO);

         result =register_chrdev_region(led_devno,1,"myled");

        

          if(result<0)

         {

                   result =alloc_chrdev_region(&led_devno,0,1,"myled");

                   led_major = MAJOR(led_devno);

         }

         //注册设备

         cdev_init(&led_cdev,&led_ops);

          led_cdev.owner = THIS_MODULE;

          result =cdev_add(&led_cdev,led_devno,1);

         if(result<0)

         {

                   printk("<0>module--->adderror!\n");

                   return result;

         }

         printk("<0>module--->cdev_addok!\n");

         return 0;

}

 

修改模块退出函数。

         static void __exit Led_exit(void)

         {       

                  cdev_del(&led_cdev);

                  unregister_chrdev_region(led_devno,1);

                  printk("<0>module--->exitok!\n");

         }

编译,通过后安装和卸载模块,看看打印信息是否正确。

 

 

三、设备注册完之后,就要开始编写struct  file_operations结构变量里面的对应的函数了,这个结构里面的函数在这就不花时间讲述了,只要记住里面是系统调用函数和自己编写函数对应关系就OK了。

struct   file_operations  led_ops={

.open     = led_open,

.release   =led_close,

};

 

里面把自己的led_open函数与系统的open函数联系起来,说白了,就是app程序用open函数打开这个设备的时候,就会执行led_open函数里面的代码,所以,我们要实现led_open函数和led_close函数。

 

首先要知道我们是对ARM的寄存器进行操作,所以首先添加头文件

#include <linux/types.h>

#include<mach/regs-gpio.h> //寄存器的地址头文件

#include<asm/io.h>

 

我们的目的是控制LED等,所以我们要看开发板上LED灯是怎么连接的。


通过电路图,我们可以知道,连接的是GPB5、GPB6、GPB7、 GPB8这四个引脚,当引脚电平为低电平时,LED就会亮,当引脚电平为高电平时,LED就熄灭。

 

staticint led_open(struct inode *inode,struct file *file)

{

         int   value = 0;

         value =(1<<10)|(1<<12)|(1<<14)|(1<<16);

         iowrite32(value,S3C2410_GPBCON);

         value = 0x0;

         iowrite32(value,S3C2410_GPBUP);

         value = 0xffffffff;

         iowrite32(value,S3C2410_GPBDAT);

         printk("<0>module--->Ledrun!\n");

         return 0;

}

 

static    int   led_close(struct inode *inode,struct file *file)

{

         return 0;

 }

当中的iowrite32函数是2.6内核的写函数,老版本的是writel,我们可以看到,用iowrite32函数对寄存器进行赋值,来完成相应的功能。

像S3C2410_GPBCON这些函数都是#include <mach/regs-gpio.h>头文件定义好的,所以不需要自己定义。

 

编写完之后,我们可以编译一下,但是安装完之后,没有反应,是因为没有应用程序来对这个驱动进行操作,所以我们现在来写应用程序。

 #include<stdio.h>

int  main(int argc,char* argv[])

{

         int   fd = 0;

         int   cmd= 0;

         char  arg[10];

        

         fd = open("/dev/myled0",0);

         getchar();

         close(fd);

         return 0;

         }

运行这个应用程序,看看LED灯是否熄灭,注意在insmod模块之后,别忘了mknod设备哦:mknod /dev/myled0 c 108 0.

 

四、如果做到上面那一步了,那我们就离成功不远了,现在只需要在内核中定义命令就可以实现对LED灯的控制了。

 

首先在struct  file_operations 结构变量中添加ioctl的对象。

structfile_operations led_ops={

         .open            = led_open,

         .release          = led_close,

         .unlocked_ioctl    = led_ioctl,

};

 

 

 

然后再定义ioctl的相关命令,这里要提到命令中的宏定义。具体的ioctl函数的命令这里就不做过多的解释。

 

#defineLED_MAGIC 'y'                   //定义幻数

 

#defineLED1_ON   _IO(LED_MAGIC,1)   //定义相关命令

#defineLED1_OFF  _IO(LED_MAGIC,2)

 

#defineLED2_ON   _IO(LED_MAGIC,3)

#defineLED2_OFF  _IO(LED_MAGIC,4)

 

#defineLED3_ON   _IO(LED_MAGIC,5)

#defineLED3_OFF  _IO(LED_MAGIC,6)

 

#defineLED4_ON   _IO(LED_MAGIC,7)

#defineLED4_OFF  _IO(LED_MAGIC,8)

 

宏定义好了之后,那么就来实现ioctl函数。

static   long led_ioctl(struct   file *file,unsigned   int   cmd,unsigned   long   arg)

{

         printk("<0>module--->Led_ioctlin!\n");

        

         int   gpbdate;

         gpbdate = ioread32(S3C2410_GPBDAT);

         printk("<0>module--->gpbdate:%d\n",gpbdate);

        

         switch(cmd)

         {

                   case LED1_ON:

                            printk("<0>module--->CMDLED1_ON!\n");

                            iowrite32((~(0x01<<5)& gpbdate),S3C2410_GPBDAT);

                            break;

                   case LED1_OFF:

                            printk("<0>module--->CMDLED1_OFF!\n");

                            iowrite32(((0x01<<5)| gpbdate),S3C2410_GPBDAT);

                            break;

                           

                   case LED2_ON:

                            printk("<0>module--->CMDLED2_ON!\n");

                            iowrite32((~(0x01<<6)& gpbdate),S3C2410_GPBDAT);

                            break;

                   case LED2_OFF:

                            printk("<0>module--->CMDLED2_OFF!\n");

                            iowrite32(((0x01<<6)| gpbdate),S3C2410_GPBDAT);

                            break;

                           

                   case LED3_ON:

                            printk("<0>module--->CMD LED3_ON!\n");

                            iowrite32((~(0x01<<7)& gpbdate),S3C2410_GPBDAT);

                            break;

                   case LED3_OFF:

                            printk("<0>module--->CMDLED3_OFF!\n");

                            iowrite32(((0x01<<7)| gpbdate),S3C2410_GPBDAT);

                            break;

                           

                   case LED4_ON:

                            printk("<0>module--->CMDLED4_ON!\n");

                            iowrite32((~(0x01<<8)& gpbdate),S3C2410_GPBDAT);

                            break;

                   case LED4_OFF:

                            printk("<0>module--->CMDLED4_OFF!\n");

                            iowrite32(((0x01<<8)| gpbdate),S3C2410_GPBDAT);

                            break;

         }

 }

代码都是上面讲过的,所以不做过多的解释。

 

五、驱动代码写好之后,那么要怎么用应用代码来验证呢,我们在内核中定义的ioctl定义的命令,要怎么让应用层知道呢,我开始也迷茫了很久,并且也在网上搜索答案,一直没有找到解决办法,后面还是自己一步一步试出来的。

 

只需要在应用代码中重新定义,当然我们也可以全部定义在一个头文件里。

#define   LED_MAGIC 'y'                   //定义幻数

 

#define    LED1_ON   _IO(LED_MAGIC,1)   //定义相关命令

#define    LED1_OFF  _IO(LED_MAGIC,2)

 

#define   LED2_ON   _IO(LED_MAGIC,3)

#define   LED2_OFF  _IO(LED_MAGIC,4)

 

#define   LED3_ON   _IO(LED_MAGIC,5)

#define   LED3_OFF  _IO(LED_MAGIC,6)

 

#define   LED4_ON   _IO(LED_MAGIC,7)

#defineLED4_OFF  _IO(LED_MAGIC,8)

 

并且添加头文件#include <linux/fs.h>就OK了,记住这是应用代码中。

命令定义好之后,我们就可以直接调用ioctl了。

int main(intargc,char* argv[])

{

         int fd = 0;

         int cmd= 0;

         char arg[10];

        

         fd = open("/dev/myled0",0);

         memset(arg,'\0',sizeof(arg));

        

         while(1)

         {

                   printf("Please inputon/off led number!\n");

                   scanf("%s",arg);

                  

                   if(strcmp(arg,"on1")== 0)

                   {

                            cmd = LED1_ON;

                   }

 

                   if(strcmp(arg,"off1")== 0)

                   {

                            cmd = LED1_OFF;

                   }

                   if(strcmp(arg,"on2")== 0)

                   {

                            cmd = LED2_ON;

                   }

                   if(strcmp(arg,"off2")== 0)

                   {

                            cmd = LED2_OFF;

                   }

                   if(strcmp(arg,"on3")== 0)

                   {

                            cmd = LED3_ON;

                   }

                   if(strcmp(arg,"off3")== 0)

                   {

                            cmd = LED3_OFF;

                   }

                   if(strcmp(arg,"on4")== 0)

                   {

                            cmd = LED4_ON;

                   }

                   if(strcmp(arg,"off4")== 0)

                   {

                            cmd = LED4_OFF;

                   }

                   printf("cmd =%d\n",cmd);

                   memset(arg,'\0',sizeof(arg));

                   ioctl(fd,cmd,0);

                  

         }

         getchar();

         close(fd);

         return 0;

}

 

这样一个LED的驱动程序就写好了。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:25361次
    • 积分:301
    • 等级:
    • 排名:千里之外
    • 原创:4篇
    • 转载:5篇
    • 译文:0篇
    • 评论:5条
    文章存档
    最新评论