micro2440写的第一个驱动-LED

2023-11-01 67浏览
百检网是一家专业的第三方检测平台,汇聚众多拥有权威资质的第三方检测机构为你提供一站式的检测服务,做检测就上百检网。百检网让检测从此检测,一份报告全国通用,专业值得信赖。

这是学习驱动开发中,在MICRO 2440开发板上写的**个驱动程序。实现对S3C2440的GPIO的控制,是一个ARM-LINUX上*简单的驱动。本驱动通过S3C2440的GPB5~8控制4个LED,属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,

  };

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

GPIO口的设置:

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

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

GPxDAT为IO口的数据寄存器,每一位对应一个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"tinitializedn");

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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"tinitializedn");

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

  #include

  #include

  #include

  

  #define IOCTL_LED_ON 1

  #define IOCTL_LED_OFF 0

  

  void usage(char *exename)

  {

   printf("Usage:n");

   printf(" %s n", exename);

   printf(" led_no = 1, 2, 3 or 4n");

  }

  

  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/ledsn");

   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


百检网秉承“客户至上,服务为先,精诚合作,以人为本”的经营理念,始终站在用户的角度解决问题,为客户提供“一站购物式”的新奇检测体验,打开网站,像挑选商品一样简单,方便。打破行业信息壁垒,建构消费和检测机构之间高效的沟通平台