IMX6ULL|LED子系统

发布时间:2024年01月22日

一.LED子系统

在Linux系统中,绝大多数硬件设备都有非常成熟的驱动框架,驱动工程师使用这些框架添加与板子相关的硬件支持,建立硬件与Linux内核的联系,内核再通过统一文件系统接口呈现给用户,用户通过对应的设备文件控制硬件。

对于LED设备,Linux提供了LED子系统驱动框架,在Linux内核源码中的“Documentation/leds/leds-class.txt”有相关的描述,它实现了一个leds类,用户层通过sysfs文件系统对LED进行控制。

二.LED设备目录

使用了LED子系统驱动的设备,会被展现在/sys/class/leds目录下,可在主机和开发板使用如下命令查看,命令的输出可能会因为硬件环境不同而不一样:

#在主机或ARM板的终端上执行如下命令:
ls /sys/class/leds/

#根据具体的目录内容继续查看:

#在主机上有input2::capslock目录,可在主机执行如下命令查看
ls /sys/class/leds/input2::capslock

#在开发板上有cpu目录,可在开发板上执行如下命令查看

ls /sys/class/leds/cpu

如下图
在这里插入图片描述
在这里插入图片描述

上图可看到,示例中的Ubuntu主机和开发板/sys/class/leds下包含了以LED设备名 字命名的目录,如“input2::capslock”、“input2::numlock”和“blue”、“cpu”等LED灯,这 些目录对应的具体LED灯如下表所示。

表 /sys/class/leds下目录对应的设备
好的,这是/sys/class/leds目录下LED灯设备的整理表格:

设备名描述
input2::capslock键盘大写锁定指示灯(input后的数字编号可能不同)
input2::numlock键盘数字键盘指示灯(input后的数字编号可能不同)
input2::scrolllock键盘ScrollLock指示灯(input后的数字编号可能不同)
cpu开发板的心跳灯
redPro开发板RGB灯的红色,Mini开发板的用户灯
greenPro开发板RGB灯的绿色,Mini开发板的用户灯
bluePro开发板RGB灯的蓝色,Mini开发板的用户灯
mmc0SD卡指示灯(出厂镜像默认没有启用)

三.LED设备属性

上图中,在具体的LED目录下又包含brightness、max_brightness、trigger等文件,这些文件包含了LED设备的属性和控制接口。

  1. max_brightness文件:表示LED灯的最大亮度值。
  2. brightness文件:表示当前LED灯的亮度值,它的可取 值范围为[0~max_brightness],一些LED设备不支持多级亮度,直接以非0值来 表示LED为点亮状态,0值表示灭状态。
  3. trigger文件:则指示了LED灯的触发方式,查看该文件的内容时,该文件会 列出它的所有可用触方式,而当前使用的触发方式会以“[]”符号括起。常见的触 发方式如下表所示。

下面是触发方式及其说明的整理表格:

触发方式说明
none无触发方式
disk-activity硬盘活动
nand-diskNAND Flash活动
mtdMTD设备活动
timer定时器
heartbeat系统心跳

四.实验代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//ARM 开发板LED设备的路径
#define RLED_DEV_PATH "/sys/class/leds/red/brightness"
#define GLED_DEV_PATH "/sys/class/leds/green/brightness"
#define BLED_DEV_PATH "/sys/class/leds/blue/brightness"

//Ubuntu主机LED设备的路径,具体请根据自己的主机LED设备修改
// #define RLED_DEV_PATH "/sys/class/leds/input2::capslock/brightness"
// #define GLED_DEV_PATH "/sys/class/leds/input2::numlock/brightness"
// #define BLED_DEV_PATH "/sys/class/leds/input2::scrolllock/brightness"


int main(int argc, char *argv[])
{
   FILE *r_fd, *g_fd, *b_fd;

   printf("This is the led demo\n");
   //获取红灯的设备文件描述符
   r_fd = fopen(RLED_DEV_PATH, "w");
   if(r_fd < 0){
      printf("Fail to Open %s device\n", RLED_DEV_PATH);
      exit(1);
   }

   //获取绿灯的设备文件描述符
   g_fd = fopen(GLED_DEV_PATH, "w");
   if(g_fd < 0){
      printf("Fail to Open %s device\n", GLED_DEV_PATH);
      exit(1);
   }

   //获取蓝灯的设备文件描述符
   b_fd = fopen(BLED_DEV_PATH, "w");
   if(b_fd < 0){
      printf("Fail to Open %s device\n", BLED_DEV_PATH);
      exit(1);
   }

   while(1){
      //红灯亮
      fwrite("255",3,1,r_fd);
      fflush(r_fd);
      //延时1s
      sleep(1);
      //红灯灭
      fwrite("0",1,1,r_fd);
      fflush(r_fd);

      //绿灯亮
      fwrite("255",3,1,g_fd);
      fflush(g_fd);
      //延时1s
      sleep(1);
      //绿灯灭
      fwrite("0",1,1,g_fd);
      fflush(g_fd);

      //蓝灯亮
      fwrite("255",3,1,b_fd);
      fflush(b_fd);
      //延时1s
      sleep(1);
      //蓝灯亮
      fwrite("0",1,1,b_fd);
      fflush(b_fd);
   }
}

可以发现,这个控制LED灯的过程就是一个普通的文件写入流程:

  • 第5~13行:定义了三盏LED灯的brightness文件路径。配套的程序默认使用 开发板RGB灯的路径,如果要在Ubuntu主机上测试请根据自己主机上的设备文件修改10~13行的内容。
  • 第18~41行:使用fopen库函数,以“w”的写模式打开了三盏LED的brightness文件,并获得文件描述符。
  • 第43~70行:在循环中分别对三盏灯写入“255”和“0”的字符串来控制LED灯的亮 度,写入后调用了fflush库函数要求立刻把缓冲区的内容写入到文件上。

本代码有两处值得注意的地方:

如果是普通文件,按代码while循环的执行流程,运行一段时间后,由于多次
写入,文件中的内容应该为“255025502550255”这样的字符串,但对于此
处的brightness设备文件,它的最终内容只是“255”或“0”,而不是像普通
文件那样记录了一连串前面输入的字符。这是因为在LED的设备驱动层中
,brightness文件就相当于一个函数的参数接口,每次对文件执行写入操
作时,会触发驱动代码以这次写入的内容作为参数,修改LED灯的亮度;而每次读
取操作时,则触发驱动代码更新当前LED灯亮度值到brightness文件,所以brightness始终
是一个0~255的亮度值,而不是“255025502550255”这样的字符串。特别地,
如果在一次写入操作中,直接写入“0255025502550”这样的
字符串,驱动层会把它当成数字255025502550,而该数字大于最大亮度值,所以它最终会以255的
亮度控制LED灯,若此时读取brightness文件,也会发现它的值确实是255。关于这些细节,
在学习了LED子系统框架后查看驱动源码可更好地了解。
另一处要注意的是代码中调用fwrite函数写入内容时,它可能只是把内容保存
到了C库的缓冲区,并没有执行真正的系统调用write函数把内容写入到设备文件,这种情况下LED灯
的状态是不会被改变的,代码中在fwrite函数后调用了fflush要求立刻把缓冲区的内容写入到文件,确保
执行了相应的操作。在实验时可以尝试把代码中的fflush都注释掉, 这种情况下有极大的几率是无法正常改变LED灯状态的。

如果不考虑操作的时间开销,其实控制硬件更推荐的做法是,每次控制LED灯都使用fopen—fwrite—fclose的
流程,这样就不需要考虑flseek、fflush的问题了。当然,我们最推崇的还是下一小节直接通过 系统调用来控制硬件的方式。

五.编译及测试

  1. 虚拟机检查编译

arm-linux-gnueabihf-gcc LED.c -o LED

2.通过NFS传到单边机系统。运行即可

六.控制LED实验(系统调用)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

//ARM 开发板LED设备的路径
#define RLED_DEV_PATH "/sys/class/leds/red/brightness"
#define GLED_DEV_PATH "/sys/class/leds/green/brightness"
#define BLED_DEV_PATH "/sys/class/leds/blue/brightness"

//Ubuntu主机LED设备的路径,具体请根据自己的主机LED设备修改
// #define RLED_DEV_PATH "/sys/class/leds/input2::capslock/brightness"
// #define GLED_DEV_PATH "/sys/class/leds/input2::numlock/brightness"
// #define BLED_DEV_PATH "/sys/class/leds/input2::scrolllock/brightness"


int main(int argc, char *argv[])
{
   int res = 0;
   int r_fd, g_fd, b_fd;

   printf("This is the led demo\n");
   //获取红灯的设备文件描述符
   r_fd = open(RLED_DEV_PATH, O_WRONLY);
   if(r_fd < 0){
      printf("Fail to Open %s device\n", RLED_DEV_PATH);
      exit(1);
   }
   //获取绿灯的设备文件描述符
   g_fd = open(GLED_DEV_PATH, O_WRONLY);
   if(g_fd < 0){
      printf("Fail to Open %s device\n", GLED_DEV_PATH);
      exit(1);
   }
   //获取蓝灯的设备文件描述符
   b_fd = open(BLED_DEV_PATH, O_WRONLY);
   if(b_fd < 0){
      printf("Fail to Open %s device\n", BLED_DEV_PATH);
      exit(1);
   }

   while(1){
      //红灯亮
      write(r_fd, "255", 3);
      //延时1s
      sleep(1);
      //红灯灭
      write(r_fd, "0", 1);

      //绿灯亮
      write(g_fd, "255", 3);
      //延时1s
      sleep(1);
      //绿灯灭
      write(g_fd, "0", 1);

      //蓝灯亮
      write(b_fd, "255", 3);
      //延时1s
      sleep(1);
      //蓝灯亮
      write(b_fd, "0", 1);
   }
}

程序执行后终端会有输出,开发板上的三盏用户LED灯也会轮流闪烁,实验现象 与使用C库函数操作方式是一样的。

文章来源:https://blog.csdn.net/m0_73731708/article/details/135739619
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。