您的位置:首页 > 其它

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

2012-03-29 14:43 274 查看
LED灯驱动编写--寄存器操作

(转载请写明出处: /article/11807466.html

这里没有用的内存映射的方法,而是直接对寄存器进行操作,我建议在开发驱动的时候,用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的驱动程序就写好了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: