micro2440写的第一个驱动-LED(2011/10/13)

12 篇文章 0 订阅
11 篇文章 0 订阅

        这是学习驱动开发中,在MICRO 2440开发板上写的第一个驱动程序。实现对S3C2440GPIO的控制,是一个ARM-LINUX上最简单的驱动。本驱动通过S3C2440GPB5~8控制4LED,属MISC(混杂)驱动,其实MISC也是一种特殊的字符驱动,只不过是把主设备号为10的字符驱动归类为MISC类驱动。

MISC类驱动结构如下:

static struct miscdevice misc = {
				.minor = MISC_DYNAMIC_MINOR,
				.name = DEVICE_NAME,
				.fops = &dev_fops,
  };


minor为指定次设备号,等于MISC_DYNAMIC_MINOR表示为动态获取次设备号。

name为设备名。

fops为 file_operations结构,设备的操作函数指针集合。

file_operations结构:

static struct file_operations dev_fops = {
				.owner	=	THIS_MODULE,
				.ioctl	=	sbc2440_leds_ioctl,
  };

    该设备省略了OPENRELESE操作函数,即默认为打开状态。我们只需要对IO口操作就可以实现LED灯的亮和灭,所以只需要一个IOCTL操作函数就可以满足要求了。

 

GPIO口的设置

    S3C2440设置GPIO口主要是设置3个寄存器。分别为GPxCON, GPxDAT, GPxUP(xA….J)。

    GPxCON用于设置IO口的功能,每两位对应一个IO口,输入=00,输出=01;特殊功能=10

    GPxDATIO口的数据寄存器,每一位对应一个IO口。

    GPxUP为是否使用上拉电阻,0为使用,注意的是并不是每组IO口都有内部集成上拉电阻,如果没有集成,则没该寄存器。

    

    设置这三个寄存器相对应的函数为:s3c2410_gpio_cfgpin,s3c2410_gpio_setpin。

 

 

驱动程序代码

 

加载函数:

static int __init dev_init(void)
{
	int ret;

	int i;
	
	for (i = 0; i < 4; i++) {
		s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
		s3c2410_gpio_setpin(led_table[i], 0);
	}

	ret = misc_register(&misc);

	printk (DEVICE_NAME"\tinitialized\n");

	return ret;
}


    在加载函数里,主要完成的就是初始化IO口和注册一个MISC类设备,MISC类在misc_register这个调用里,会帮我们根据struct miscdevice里面的成员帮我们注册一个字符设备,所以我们这个MISC的驱动加载函数比较短。

 

卸载函数:

static void __exit dev_exit(void)
  {
		misc_deregister(&misc);
  }


    在卸载函数里,只需要调用misc_deregister卸载掉MISC设备就OK了。

 

IOCTL函数

static int sbc2440_leds_ioctl(
	struct inode *inode, 
	struct file *file, 
	unsigned int cmd, 
	unsigned long arg)
{
	switch(cmd) {
	case 0:
	case 1:
		if (arg > 4) {
			return -EINVAL;
		}
		s3c2410_gpio_setpin(led_table[arg], !cmd);
		return 0;
	default:
		return -EINVAL;
	}
}


    在这个MISC设备里,主要用到的是控制函数。通过之前介绍的操作IO寄存器的函数,我们就可以很轻松地对IO口进行控制。

 

编译、加载:

把源码放进/drivers/char下。

修改内核源码目录linux-2.6.32.2/drivers/char下的Kconfig文件,添加以下文本把驱动程序加进代码树。

config LOCKER_LEDS             

tristate "Locker_Liang leds"

depends on MACH_MINI2440

default m if MACH_MINI2440

help

Locker_Liang leds.

config"为定义了一新的配置选项。Tristate为选项名称。depends on为基于哪类处理器。Default表示如果上一个处理器不可用则默认选这个。Help为帮助信息,下一行为帮助信息。Kconfig的语法详细说明看Kconfig文档

修改Makefile,添加

obj-$(CONFIG_LOCKER_LEDS)+= MY_leds.o

这里注意的是LOCKER_LEDS一定要和Kconfig上的相同,.o文件也要和源文件名相同。

在linux-2.6.32.2目录输入make menuconfig,在 Device Drivers -》Character devices中选中我们添加的模块,选为M,即编译为模块方式。

输入make modules编译模块。这时在/drivers/char就会生成一个MY_leds.ko文件。把该文件拷贝到开发板上,输入insmod MY_leds.ko 就可以加载模块到内核中。并且自动生成设备文件。输入rmmod MY_leds.ko可以卸载模块。

 

 

这样做就可以对设备操作了,但还有一个问题就是复位后,还要重新加载驱动模块和重新生成设备文件。这时我们可以在/etc/init.d/rcS文件里,加上加载驱动模块和生成设备文件的命令,这样每次开机后都会自动完成设置。

 

完整代码:


#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 <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME "MY_leds"

static unsigned long led_table [] = {
	S3C2410_GPB(5),
	S3C2410_GPB(6),
	S3C2410_GPB(7),
	S3C2410_GPB(8),
};

static unsigned int led_cfg_table [] = {
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
};

static int sbc2440_leds_ioctl(
	struct inode *inode, 
	struct file *file, 
	unsigned int cmd, 
	unsigned long arg)
{
	switch(cmd) {
	case 0:
	case 1:
		if (arg > 4) {
			return -EINVAL;
		}
		s3c2410_gpio_setpin(led_table[arg], !cmd);
		return 0;
	default:
		return -EINVAL;
	}
}

static struct file_operations dev_fops = {
	.owner	=	THIS_MODULE,
	.ioctl	=	sbc2440_leds_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(led_table[i], led_cfg_table[i]);
		s3c2410_gpio_setpin(led_table[i], 0);
	}

	ret = misc_register(&misc);

	printk (DEVICE_NAME"\tinitialized\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("Locker_Liang Inc.");


 

 

测试程序:

  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/ioctl.h>
  
  #define IOCTL_LED_ON    1
  #define IOCTL_LED_OFF   0
  
  void usage(char *exename)
  {
      printf("Usage:\n");
      printf("    %s <led_no> <on/off>\n", exename);
      printf("    led_no = 1, 2, 3 or 4\n");
  }
  
  int main(int argc, char **argv)
  {
      unsigned int led_no;
      int fd = -1;
  
      if (argc != 3)
          goto err;
          
      fd = open("/dev/MY_leds", 0);  // 打开设备
      if (fd < 0) {
          printf("Can't open /dev/leds\n");
          return -1;
      }
      
      led_no = strtoul(argv[1], 0, 0) - 1;    // 操作哪个LED?
      if (led_no > 3)
          goto err;
      
      if (!strcmp(argv[2], "on")) {
          ioctl(fd, IOCTL_LED_ON,led_no);    // 点亮它
      } else if (!strcmp(argv[2], "off")) {
          ioctl(fd, IOCTL_LED_OFF,led_no);   // 熄灭它
      } else {
          goto err;
      }
      
      close(fd);
      return 0;
      
  err:
      if (fd > 0) 
          close(fd);
      usage(argv[0]);
      return -1;
  }


 

测试指令   ./test 1 on 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值