Linux2.6.33 自己动手写驱动

来源:互联网 发布:mac pretty boy好看吗 编辑:程序博客网 时间:2024/06/10 17:46

 

华清远见-嵌入式领域著名讲师  O(_)O  牛牛猛

点击链接加我好友!

http://student.csdn.net/invite.php?u=111047&c=758a60d66d3a92d1

欢迎大家去我CSDN博客上踩踩

我的个人主页

 

一、GPIO驱动编写和测试

1GPIO的这点事儿

GPIOGeneral-Purpose IO ports,通用的IO端口。一般的微处理器芯片都提供通用的可编程IO端口,用户可以通过配置寄存器将其配置成输入、输出或者其他特殊功能模式。

通用IO端口一般都可以实现按位操作,即用户可以针对IO端口的每一位(以下简称IO位)进行操作。

GPIO端口输出的高低电平可以通过控制一组LED发光二极管来加以验证,这里,我们截取TE2440-DEV-I教学板中的四个发光二极管做实验。

应用S3C2440GPF4-GPF7共四个IO位,各自控制一个发光二极管,分别对应D1-D4,硬件原理如图6.1所示。

6.1 IO控制LED原理图

原理图分析:

(1) 四个发光二极管采用共阳极的连接方法,即四个发光二极管的阳极连接在一起,并连接到3.3V电源。

(2) 发光二极管的负极各自连接一个1K限流电阻,再分别连接到S3C2440GPF4-GPF7

思考:为什么要接一个1K的限流电阻呢?

顾名思义,串接限流电阻就是为了显示通过发光二极管的电流,从而达到减少功耗或者满足端口对最大电流的限制。

一般发光二极管的点亮电流为5mA~10mATE2440采用贴片发光二极管,1K限流电阻,驱动电压为3.3V,发光二极管压降为1.6V,那么发光二极管的电流大约为1.7mA。发光二极管已经能点亮了,如果希望更亮一些,可以将限流电阻减小,但是二极管的电流不能超多ARM芯片IO口的最大驱动电流。

(3) 根据电路原理可知,当GPF4-GPF7输出低电平时,由于发光二极管两端加上电压,则发光二极管点亮;当GPF4-GPF7输出高电平只,由于发光二极管两端压差为0,则发光二极管熄灭。

思考:发光二极管为什么会亮呢?

发光二极管在其两端的电压差超出其导通压降时开始工作,普通发光二极管的导通压降一般为1.6V~2.1V,同时,工作电流要满足该二极管的工作电流。满足电压和电流的要求,该二极管就可以发光了。

二极管的正负极可以用万用表的二极管档量出来,如果表上有了一点多的读数,红表笔接的发光二极管的正极,黑表笔接的就是发光二极管的负极。

(4) 我们可以通过程序控制GPF4-GPF7有规律的输出高低电平,则实现发光二极管有规律的亮灭。

2、驱动怎样编写

了解的硬件的概念以后,我们开始我们的驱动编写工作。

然后再在内核源码的“drivers/char/”目录下新建一个名为“TopElec_GPIO.c”的文件,作为我们的驱动文件。内容如下:

/*************************************

 

NAME:TopElec_GPIO.c

 

COPYRIGHT:www.top-elec.com

 

Ver1.00  2010/05/19

 

*************************************/

 

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/mm.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/delay.h>

#include <linux/moduleparam.h>

#include <linux/slab.h>

#include <linux/errno.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/string.h>

#include <linux/list.h>

#include <linux/pci.h>

#include <asm/uaccess.h>

#include <asm/atomic.h>

#include <asm/unistd.h>

#include <linux/gpio.h>

 

#define DEVICE_NAME "GPIO-Control"

 

#define S3C2410_GPFCON         S3C2410_GPIOREG(0x50)

#define S3C2410_GPFDAT          S3C2410_GPIOREG(0x54)

#define S3C2410_GPFUP             S3C2410_GPIOREG(0x58)

 

 

#define S3C2410_GPF4        S3C2410_GPIONO(S3C2410_GPIO_BANKF, 4)

#define S3C2410_GPF5        S3C2410_GPIONO(S3C2410_GPIO_BANKF, 5)

#define S3C2410_GPF6        S3C2410_GPIONO(S3C2410_GPIO_BANKF, 6)

#define S3C2410_GPF7        S3C2410_GPIONO(S3C2410_GPIO_BANKF, 7)

 

#define S3C2410_GPF4_OUTP    (0x01 << 8)

#define S3C2410_GPF5_OUTP    (0x01 << 10)

#define S3C2410_GPF6_OUTP    (0x01 << 12)

#define S3C2410_GPF7_OUTP    (0x01 << 14)

 

 

 

 

 

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */

#define IOCTL_GPIO_ON         1

#define IOCTL_GPIO_OFF        0

 

/* 用来指定LED所用的GPIO引脚 */

static unsigned long gpio_table [] =

{

         S3C2410_GPF(4),

         S3C2410_GPF(5),

         S3C2410_GPF(6),

         S3C2410_GPF(7),

};

 

/* 用来指定GPIO引脚的功能:输出 */

static unsigned int gpio_cfg_table [] =

{

//由于2.6.33内核中没有定义GPIO的动作,所以我在上面自己定义了一下。

          S3C2410_GPF4_OUTP,                    

          S3C2410_GPF5_OUTP,

          S3C2410_GPF6_OUTP,

          S3C2410_GPF7_OUTP,

};

 

static int te2440_gpio_ioctl(

         struct inode *inode,

         struct file *file,

         unsigned int cmd,

         unsigned long arg)

{

         if (arg > 4)

         {

                   return -EINVAL;

         }

 

         switch(cmd)

         {

                   case IOCTL_GPIO_ON:

                            // 设置指定引脚的输出电平为0

                            s3c2410_gpio_setpin(gpio_table[arg], 0);

                            return 0;

 

                   case IOCTL_GPIO_OFF:

                            // 设置指定引脚的输出电平为1

                            s3c2410_gpio_setpin(gpio_table[arg], 1);

                            return 0;

 

                   default:

                            return -EINVAL;

         }

}

 

static struct file_operations dev_fops = {

         .owner     =       THIS_MODULE,

         .ioctl         =       te2440_gpio_ioctl,

};

 

static struct miscdevice misc = {

         .minor = MISC_DYNAMIC_MINOR,

         .name = DEVICE_NAME,

         .fops = &dev_fops,

};

 

static int __init dev_init(void)

{

         int ret;

 

         int i;

        

         for (i = 0; i < 4; i++)

         {

                   s3c2410_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]);

                   s3c2410_gpio_setpin(gpio_table[i], 0);

         }

 

         ret = misc_register(&misc);

 

         printk (DEVICE_NAME" initialized/n");

 

         return ret;

}

 

static void __exit dev_exit(void)

{

         misc_deregister(&misc);

}

 

module_init(dev_init);

module_exit(dev_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("www.top-elec.com");

MODULE_DESCRIPTION("GPIO control for Topelec TE2440DEV_I Board");

 

3、让驱动到内核里混一下

在内核源码中添加对LED 灯驱动的支持,修改同目录下的Kconfig文件,添加如下内容:

config TE2440_GPIO_TEST

       tristate "TE2440DEV-I Board GPIO Test(LED TEST)"

       depends on ARCH_S3C2440

       default y if ARCH_S3C2440

       help

         GPIO control for TE2440DEV-I Board.

 

同样的道理,修改同目录下的Makefile文件,修改的内容如下:

obj-$(CONFIG_TE2440_GPIO_TEST)              += TopElec_GPIO.o

 

4、配置和编译内核

进入内核目录,键入make menuconfig命令,进入内核配置菜单,如下图所示:

Diver->chacarter device-> TE2440DEV-I Board GPIO Test 的支持,当然,你也可以选择<M>将驱动编译成模块。

好了,OK,大胆的make吧!

当然可能有错误,因为内核的版本不一致,或是编译器的问题,总之,Linux虽然玩起来和带劲,但是问题也会很多,要有越挫越勇的精神才能成为真正的高手哦!

至于,怎么把内核搞到板子上不在这里就不说了。

5、编写测试程序

/*************************************

NAME:GPIO_TEST.c

COPYRIGHT:www.top-elec.com

*************************************/

#include <stdio.h>                               /*定义函数要用到的头文件*/

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

int main(int argc, char **argv)                  /*运行时参数传递,开或关哪个LED*/

{

int on;                                                                        /*定义led状态变量,1表示灯亮,2表示灯灭*/

int led_no;                                                                /*定义led变量--哪个led*/

int fd;                                                                         /*定义led设备文件描述符的变量*/

 

if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||

on < 0 || on > 1 || led_no < 1 || led_no > 4)  /*判断命令输入参数个数*/

 

{

         fprintf(stderr, "Usage: Please enter led number and led status,For example GPIO_TEST 1 0 /n");

        

         exit(1);

}

fd = open("/dev/GPIO-Control", 0);                                               /*为只读打开GPIO-Control设备文件,取出文件描述符*/

                  

if (fd < 0) {                                                       

perror("open device /dev/GPIO-Control");                        /*如果打开led文件出错,拿不到文件描述符,用perror宏输出错原因及信息*/

 

exit(1);    

}

ioctl(fd, on, (led_no-1));   /*ioctl()函数控制LED,其中fd--是前面打开的LED文件描述符,on--是开关命令01led_no--是哪个LED*/

 

close(fd);

return 0;

}

 

顺便这个Makefile文件,看起来专业点:

CROSS=arm-softfloat-linux-gnu-

all:GPIO_TEST

GPIO_TEST:GPIO_TEST.c

         $(CROSS)gcc    GPIO_TEST.c    -o      GPIO_TEST

         $(CROSS)strip  GPIO_TEST

clean:

         @rm         -vf     GPIO_TEST       *.o   *~

 

有了上面的两个 make就可以了

 

最后把测试程序搞的开发板上运行一下:

bash-3.2# ./GPIO_TEST 1 1  意思是D1被点亮

 

 

很多资料来源于网络,谢谢网络的上的无私奉献大侠们!