目前的linux版本的许多驱动都是基于设备模型,LED也不例外。

简单地说,设备模型就是系统认为所有的设备都是挂接在总线上的,而要使设备工作,就需要相应的驱动。设备模型会产生一个虚拟的文件系统——sysfs,它给用户提供了一个从用户空间去访问内核设备的方法,它在linux里的路径是/sys。如果要写程序访问sysfs,可以像读写普通文件一样来操作/sys目录下的文件。

对于基于s3c2440的开发板来说,linux-3.6.6自动的LED驱动只需改变连接LED的IO端口,及高、低电平响应即可。我的开发板的四个LED连接在了B口的5到8引脚上,当输出低电平时被点亮,与linux自带的LED驱动一致,因此无需做任何改动。

使用menuconfig来配置内核,这里要加上对LED模块的内容,即:

Device Drivers--->

[*]LED Support--->

<*>LED Class Support

<*>LED Support for Samsung S3C24xx GPIO LEDs

编译内核,并把编译好的内核下载到开发板上,运行:

[root@zhaocj /]#ls

bin      etc     lib      proc     sys      usr

dev      home    linuxrc  sbin     temp

[root@zhaocj /]#cd sys

[root@zhaocj /sys]#ls

block     class    devices   fs        module

bus       dev      firmware  kernel    power

进入sys目录下,我们看到该目录下有一些子目录。

[root@zhaocj /sys]#cd class

[root@zhaocj class]#ls

backlight     hidraw       leds          rtc          vc

bdi          hwmon         mem           sound        video_output

block         i2c-adapter   misc         spi_master    vtconsole

firmware      i2c-dev       mmc_host     spidev        watchdog

gpio         input         mtd          tty

graphics      lcd          net           udc

进入class目录,我们会看到在该目录下有一些设备,其中leds就是本次我们要操作的LED。

[root@zhaocj class]#cd leds

[root@zhaocj leds]#ls

backlight  led1      led2       led3       led4

在leds目录下,会看到四个LED的目录,这就是开发板上的四个LED。另外backlight目录是关于LCD的背光,与LED无关。

[root@zhaocj leds]#cd led1

[root@zhaocj led1]#ls

brightness     max_brightness  subsystem

device         power           uevent

brightness文件就是LED设备,对其进行操作就可完成对LED的控制。

[root@zhaocj led1]#cat brightness

0

可以看出led1当前的状态是关闭。(0表示关闭,1表示打开)

[root@zhaocj led1]#cat >brightness<<eof

> 1

> eof

#[root@zhaocj led1]#

向brightness写1,表示打开LED。这时led1会被点亮。

当然,我们也可以编写用户程序来控制开发板上的四个LED

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

****leds.c**************

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

#include<stdint.h>

#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<linux/input.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
int fd, no;
/*判断是要控制哪个LED,并打开相应的文件*/
no=(int)argv[1][3]-48;
switch(no)
{
case 1:
fd = open("/sys/class/leds/led1/brightness", O_RDWR);
break;
case 2:
fd = open("/sys/class/leds/led2/brightness", O_RDWR);
break;
case 3:
fd = open("/sys/class/leds/led3/brightness", O_RDWR);
break;
case 4:
fd = open("/sys/class/leds/led4/brightness", O_RDWR);
break;
default:
return -1;
}
if(fd<0)
{
printf("can not open file.\n");
return -1;
}
/*完成打开或关闭LED操作*/
if(!strcmp(argv[2],"on"))   
write(fd, "1", 1);
else if(!strcmp(argv[2],"off"))
write(fd, "0", 1);
close(fd);
return 0;
}

上面的程序只做简单测试之用。编译该文件:

arm-linux-gcc  -o  leds  leds.c

把leds文件下载到temp目录下,运行:

[root@zhaocj /temp]# ./leds  led2  on

则点亮led2。

[root@zhaocj /temp]# ./leds  led2  off

则关闭led2。

下面我就来简单分析一下linux自带的LED子系统。

在mach-zhaocj2440.c文件,创建了LED设备,如下:

/* LEDS */

static struct s3c24xx_led_platdata zhaocj2440_led1_pdata = {

.name             = "led1",

.gpio              = S3C2410_GPB(5),

.flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

.def_trigger    = "heartbeat",

};

static struct s3c24xx_led_platdata zhaocj2440_led2_pdata = {

.name             = "led2",

.gpio              = S3C2410_GPB(6),

.flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

.def_trigger    = "nand-disk",

};

static struct s3c24xx_led_platdata zhaocj2440_led3_pdata = {

.name             = "led3",

.gpio              = S3C2410_GPB(7),

.flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

.def_trigger    = "mmc0",

};

static struct s3c24xx_led_platdata zhaocj2440_led4_pdata = {

.name             = "led4",

.gpio              = S3C2410_GPB(8),

.flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

.def_trigger    = "",

};

定义了四个LED数据,名字分别为led1~led4,这就是我们在leds目录下看到这四个子目录。它们所连接的引脚分别为B口的5~8,这是由S3C2410_GPB()宏定义完成的。标识S3C24XX_LEDF_ACTLOW表示的是低电平有效,S3C24XX_LEDF_TRISTATE表示的三态无效。另外def_trigger表示的是触发控制,如我们对nand进行读写操作时,led2会不停的闪,在这里我们没有用到这个功能,暂时不用理会。

static struct platform_device zhaocj2440_led1= {

.name             = "s3c24xx_led",

.id          = 1,

.dev        = {

.platform_data       = &zhaocj2440_led1_pdata,

},

};

static struct platform_device zhaocj2440_led2= {

.name             = "s3c24xx_led",

.id          = 2,

.dev        = {

.platform_data       = &zhaocj2440_led2_pdata,

},

};

static struct platform_device zhaocj2440_led3= {

.name             = "s3c24xx_led",

.id          = 3,

.dev        = {

.platform_data       = &zhaocj2440_led3_pdata,

},

};

static struct platform_device zhaocj2440_led4= {

.name             = "s3c24xx_led",

.id          = 4,

.dev        = {

.platform_data       = &zhaocj2440_led4_pdata,

},

};

上面则创建了总线平台设备,四个LED的设备名称都是s3c24xx_led,子设备id分别从1到4,设备数据则是上面定义的四个LED数据。然后把这四个LED设备再添加到开发板的设备数组中,即:

static struct platform_device *zhaocj2440_devices[]__initdata = {

……

&zhaocj2440_led1,

&zhaocj2440_led2,

&zhaocj2440_led3,

&zhaocj2440_led4,

……

};

最后,在开发板系统初始化过程中,再把设备数组中的设备逐一注册到系统总线上,即:

static void __init zhaocj2440_init(void)

{

……

platform_add_devices(zhaocj2440_devices,ARRAY_SIZE(zhaocj2440_devices));

……

}

这样就完成了LED设备的创建。

光有设备还不能工作,任何一个设备的运行还需要与之相对应的驱动。对于基于s3c24xx的LED来说,它的驱动是在drivers/leds目录下Leds-s3c24xx.c文件内创建的,即:

static struct platform_driver s3c24xx_led_driver = {

.probe            = s3c24xx_led_probe,

.remove          = s3c24xx_led_remove,

.driver            = {

.name             = "s3c24xx_led",

.owner           = THIS_MODULE,

},

};

设备和驱动是如何匹配的呢?即设备如何找到它所对应的驱动的呢?靠的就是name。我们会发现platform_device和platform_driver都有元素name,它们的内容如果一致,设备和驱动就会配对成功。对于LED来说,它们的name都是s3c24xx_led。当设备和驱动匹配上以后,就要运行probe所指定的函数,简单地说,它就是完成一些初始化工作。当需要移除设备时,就需要运行remove所指定的函数,它完成的任务是注销设备。对于支持热插拔的设备来说,尤为重要。

现在就来说一下s3c24xx_led_probe函数:

static int s3c24xx_led_probe(struct platform_device *dev)

{

structs3c24xx_led_platdata*pdata = dev->dev.platform_data;

structs3c24xx_gpio_led *led;

intret;

/*用于给LED分配内存空间*/

led =devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),

GFP_KERNEL);

if(led == NULL) {

dev_err(&dev->dev,"No memory for device\n");

return-ENOMEM;

}

/*保存LED设备结构*/

platform_set_drvdata(dev,led);

/*给LED结构体赋值,其中s3c24xx_led_set就是具体负责操作LED的函数*/

led->cdev.brightness_set= s3c24xx_led_set;

led->cdev.default_trigger= pdata->def_trigger;

led->cdev.name= pdata->name;

led->cdev.flags|= LED_CORE_SUSPENDRESUME;

led->pdata = pdata;

/*为LED分配io引脚*/

ret =devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED");

if(ret < 0)

returnret;

/*no point in having a pull-up if we are always driving */

/*无需上拉*/

s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE);

/*设置io引脚为输入*/

if(pdata->flags & S3C24XX_LEDF_TRISTATE)

gpio_direction_input(pdata->gpio);

else

gpio_direction_output(pdata->gpio,

pdata->flags& S3C24XX_LEDF_ACTLOW ? 1 :0);

/*register our new led device */

/*注册一个新的LED设备类对象

该函数是在drivers/leds目录下的Led-class.c文件内定义的*/

ret= led_classdev_register(&dev->dev, &led->cdev);

if(ret < 0)

dev_err(&dev->dev,"led_classdev_register failed\n");

return ret;

}

从以上分析可以看出,s3c24xx_led_probe函数主要就是完成LED设备的一些初始化工作。而负责开、关LED任务的是s3c24xx_led_set函数,在该函数内,gpio_set_value(pd->gpio, state);是具体完成为相应引脚置1或清零的任务。

drivers/leds目录下的Led-class.c文件是LED子系统的底层核心文件,它主要负责创建LED类,以及创建设备节点,上面提到的led_classdev_register函数就是在这个文件中定义的。为了更好的理解LED子系统,我们再简单分析一下该文件。

在子系统初始化时,会调用leds_init函数,它的第一段代码:

leds_class = class_create(THIS_MODULE,"leds");

就是创建leds类,也就是我们在sys/class目录下看到的leds目录。另外

leds_class->dev_attrs = led_class_attrs;

是赋予该类的属性。那么我们再来看看led_class_attrs结构的第一句代码:

__ATTR(brightness, 0644, led_brightness_show,led_brightness_store)

其中brightness就是我们对LED具体操作的设备文件名,0644是该文件的权限,led_brightness_show是读文件所调用的函数,led_brightness_store是写文件所调用的函数。读文件也就是读取LED的状态(是关还是开),写文件也就是完成打开LED或关闭LED操作。

最后再分析一下前面提到的led_classdev_register函数。在该函数内首先利用device_create函数创建设备节点,也就是在leds目录下,生成led1~led4这四个目录。另一项重要的任务就是把设备节点添加到leds的链表中。

对linux自带的LED子系统的分析就到这里。我想只要理解了该子系统,那么自己完全可以写出关于GPIO读写操作的任何驱动程序来。

基于S3C2440的linux-3.6.6移植——LED驱动相关推荐

  1. [转载]基于ARM的linux内核裁剪与移植

    基于ARM的linux内核裁剪与移植 http://bbs.elecfans.com/forum.php?mod=viewthread&tid=185020  wutaimin( 楼主 ) 2 ...

  2. linux根文件系统的移植 课程设计,定稿基基于ARM9嵌入式Linux引导程序研究与移植嵌入式综合实验报告完整版...

    <基<基于ARM9嵌入式Linux引导程序研究与移植>嵌入式综合实验报告.doc>由会员分享,可免费在线阅读全文,更多与<(定稿)基基于ARM9嵌入式Linux引导程序研 ...

  3. NanoPi NEO Air使用九:使用Linux内核自带的LED驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  4. 基于2440的Linux开发原理,基于S3C2440和Linux的嵌入式网络驱动程序开发

    摘要: 嵌入式与互联网已经成为最热门的技术.使嵌入式系统具备网络功能,并将它们与Internet或企业局域网连接起来,增强了嵌入式系统多方面的实用性. 本文采用S3C2440微处理器为硬件开发平台,根 ...

  5. [linux驱动开发] 基于gpiod API的platform总线多个led驱动开发

    gpiod API对platform-led进行驱动开发 修改设备树源码 如何在驱动中获取设备树节点信息 计算设备子节点数量 给私有属性分配内存 对子节点进行遍历 gpiod的获取 根据设备树字节给的 ...

  6. 如何使用linux系统自带的led驱动

    使用Linux 内核的platform 框架驱动led灯 1.使用menuconfig使能(可跳过,系统默认配置好) 2.设备树节点编写 3.运行测试 我们平时要使用设备驱动的时候,总是需要我们自己去 ...

  7. 嵌入式Linux(二)汇编LED驱动实验

    目的: 并不是系统的学习汇编,而是在linux开发中有时候需要使用汇编置零进行一些初始化的工作. 1. I.MX6ULL的IO初始化流程: 6ULL的IO命名:IOMUXC_SW_MUC_CTL_PA ...

  8. 全志linux led驱动程序,芯灵思Sinlinx A64 linux通过设备树写LED驱动(附参考代码,未测试)...

    #include #include #include #include #include #include #include #include #include #include #include # ...

  9. NanoPi NEO Air使用十五:移植RTL8723BU驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

最新文章

  1. C++智能指针(设计和使用)
  2. Hibernate 一对一关联关系
  3. rffc2071_基于 RFFC2071的变频器设计
  4. 计算机跨专业考研方向java,【计算机考研】 初试出成绩前应该做的6件事
  5. react学习(71)--render使用
  6. 8.0/9.0 Email 设置
  7. 知道一个数组某个index对应的值 不知道下标的情况下删除该值
  8. 【clickhouse】clickhouse强大智能的 Projection (投影) 功能
  9. HTML设置不生效的原因,CSS中hover出现不生效的几个原因 ?
  10. esp8266 防掉线方法_ESP8266-12F 中断
  11. Linux下LAMP服务配置
  12. 抽象工厂模式---创建型
  13. python计算器gui设计_python 实现简单的计算器(gui界面)
  14. java接收ajax上传文件_使用ajax方式上传文件
  15. Unity3D圣典学习【2】之CharacterController
  16. 升余弦滤波器与无码间串扰(一)
  17. [leetcode题解] 第995题Minimum Number of K Consecutive Bit Flips
  18. 电脑突然复制粘贴不了
  19. svchost.exe 占用 21 端口,无法开启FTP服务(已解决)
  20. 实战 | UI 自动化测试框架设计与 PageObject 改造

热门文章

  1. 小米手机如何显示悬浮窗
  2. 多任务学习模型ESMM原理与实现(附代码)
  3. 第26届金鸡奖揭晓 富大龙称帝刘嘉玲颜丙燕封后
  4. 银行智能风险防控系统(第一版)
  5. 纯JavaScript山寨腾讯手机游戏《天天爱消除》开发过程详细
  6. idea中build和rebuild的区别
  7. php发送电子邮件,PHP发送电子邮件
  8. 2017——腾讯产培生笔试、面经
  9. 微信小程序保存图片到本地
  10. 2017年1月1日书法欣赏奉上