基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动

简介: 这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,使用GPIO和LED子系统来实现LED驱动的教程,包括了DTS设备树配置、驱动源码编写以及如何在用户空间控制LED的亮度和开关。

一、篇头

  • 本章介绍LED子系统的使用。
  • 使用LED子系统,可以轻松实现对LED,例如常见的闪烁和亮度控制功能。
  • 简单起见,本章先使用GPIO实现,在不模拟PWM的情况下,只能实现点亮和灭灯的效果,重点是介绍GPIO、LED子系统的使用。

本文基于Amlogic T972 , Android 9.0, 内核版本 4.9.113

二、系列文章

第1篇:基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
第2篇:基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
第3篇:基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
第4篇:基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
第5篇:基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动

三、准备工作

3.1 原理图:挑选测试用GPIO脚

(1) 红色LED: GPIOZ_6
(2) 绿色LED: GPIOZ_2
(3) 黄色LED: GPIOZ_1
(4) GND接地
在这里插入图片描述

3.2 LED模块

• 如下4个PIN脚,分别连接至开发板的4个引脚
在这里插入图片描述

四、源码解析

4.1 DTS设备树

    aml_led_class{
   
   
        status = "okay";
        compatible = "szhou,aml_led_class";
        /*
        *     (1) amlled-gpios 是使用新版GPIO子系统API的固定写法,必须以 -gpios 结尾
        *     (2)&gpio” : 引用的GPIO控制器的名称
        *     (3) “GPIOZ_X ” :是一个宏定义,可转换成具体的gpio index
        *     (4) 最后的GPIO_ACTIVE_HIGH,代表此PIN脚物理上是高电平有效
        */
        amlled-gpios =  <&gpio GPIOZ_6 GPIO_ACTIVE_HIGH>,  //red_led
                        <&gpio GPIOZ_2 GPIO_ACTIVE_HIGH>,  //green_led
                        <&gpio GPIOZ_1 GPIO_ACTIVE_HIGH>;  //yellow_led
    };

4.2 驱动源码

如下是驱动源码,已添加详细解释

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/leds.h> /* 包含 struct led_classdev 结构体,及API*/
#include <linux/gpio/consumer.h> /* 包含新版本GPIO子系统: -gpiod- API*/


/*
*
 私有结构体,  可自行增加所需成员, 但必须包含 struct led_classdev
*/
struct led_dev
{
   
   
    struct led_classdev cdev;
    struct gpio_desc *desc;
};

static char *name_red = "red";
static char *name_green = "green";
static char *name_yellow = "yellow";
static struct led_dev *led_device[3];




/* 
* (1)对 /sys/class/leds/red/brightness 等LED属性进行控制时,会回调此函数 
*/
static void led_control(struct led_classdev *led_cdev, enum led_brightness brightness)
{
   
   
    struct led_dev *led = container_of(led_cdev, struct led_dev, cdev);

    pr_info(" ENTER led_control  led(0x%p)->desc=0x%p \n", led, led->desc);    

    //根据设定,设置亮或灭
    if (brightness != LED_OFF) {
   
    
        gpiod_set_value(led->desc , 1);
        pr_info(" gpiod_set_value(led->desc , 1) \n");        

    }else{
   
   

        gpiod_set_value(led->desc , 0);
        pr_info(" gpiod_set_value(led->desc , 0) \n");        
    }

    pr_info("EXIT led_control \n");
}

/*
* (1)在检测到DT中.compatible = "szhou,aml_led_class"的节点后,会自动调用此函数,实现初始化
*/
static int __init ledclass_plat_probe(struct platform_device *pdev)
{
   
   

    struct device *dev = &pdev->dev;
    int i, count, ret;

    pr_info( "ledclass_plat_probe enter\n");

    /* 获取amlled-gpios里面gpio的数量 */
    count = gpiod_count(dev, "amlled");
    if (count ==  -ENOENT)
        return -ENOENT;

    dev_info(dev, "gpiod_count = %d \n", count);

    /* 填充每个gpio-led对象,并注册到LED子系统 */
    for(i=0; i<count; i++){
   
   

        //struct led_dev *led_device;
        //struct gpio_desc *desc = NULL;

        led_device[i] = devm_kzalloc(dev, sizeof(struct led_dev), GFP_KERNEL);
        if (!led_device[i])
            return -ENOMEM;

        /* 申请并初始化gpio描述符,电平初始化为GPIOD_OUT_LOW,即设为输出脚、低电平 */
        led_device[i]->desc = gpiod_get_index(dev, "amlled", i, GPIOD_OUT_LOW);
        pr_info(" ledclass_plat_probe  led(0x%p)->desc=0x%p \n", led_device[i], led_device[i]->desc);    

        switch(i){
   
   

            case 0:
                led_device[0]->cdev.name = name_red;
                pr_info("zs, 000000000   \n");
                //led_device->cdev.default_trigger = "heartbeat";//红灯将使用默认的闪烁程序,若启用,则要添加类似互斥锁
                break;
            case 1:
                led_device[1]->cdev.name = name_green;
                pr_info("zs, 111111111111111   \n");                
                break;
            case 2:
                led_device[2]->cdev.name = name_yellow;
                pr_info("zs, 22222222222   \n");                
                break;    
            default:
                pr_info( "zs, i=%d, default \n", i);

        }

        pr_info("zs, 33333333333333   \n");    

        /* 初始化亮度值,以及LED的控制函数 */
        led_device[i]->cdev.brightness = LED_OFF;
        led_device[i]->cdev.brightness_set = led_control;

        /* 注册到LED子系统 */
        ret = devm_led_classdev_register(dev, &led_device[i]->cdev);
        pr_info( "zs, platform_probe  devm_led_classdev_register [%d], cdev->name=%s \n", i,  led_device[i]->cdev.name);
        if (ret) {
   
   
            dev_err(dev, "failed to register the led %s\n", led_device[i]->cdev.name);
            return ret;
        }

    }

    dev_info(dev, "ledclass_plat_probe exit\n");
    return 0;
}

static int __exit ledclass_plat_remove(struct platform_device *pdev)
{
   
   
    int i=0;
    dev_info(&pdev->dev, "ledclass_plat_remove enter\n");

    for(i=0; i<3; i++)
    {
   
    
        gpiod_put(led_device[i]->desc);  //释放GPIO描述符      
    }

    dev_info(&pdev->dev, "ledclass_plat_remove exit\n");
    return 0;
}


/*
* 设备树的匹配属性 .compatible ,需完全相同才会匹配
*/
static const struct of_device_id my_of_ids[] = {
   
   
    {
   
    .compatible = "szhou,aml_led_class"},
    {
   
   },
};

MODULE_DEVICE_TABLE(of, my_of_ids);


/*
* led_platform_driver 结构体
*/
static struct platform_driver led_platform_driver = {
   
   
    .probe = ledclass_plat_probe,
    .remove = ledclass_plat_remove,
    .driver = {
   
   
        .name = "aml_class_leds",
        .of_match_table = my_of_ids,
        .owner = THIS_MODULE,
    }
};


/*
* 注册 led_platform_driver 结构体到Platform子系统
*/
static int aml_GpioLedClass_plat_init(void)
{
   
   
    int ret_val;
    pr_info("aml_GpioLedClass_plat_init enter\n");

    ret_val = platform_driver_register(&led_platform_driver);
    if (ret_val !=0){
   
   
        pr_err("platform value returned %d\n", ret_val);
        return ret_val;
    }

    pr_info("aml_GpioLedClass_plat_init exit\n");
    return 0;
}

/*
* 从Platform子系统注销 led_platform_driver 结构体
*/
static void aml_GpioLedClass_plat_exit(void)
{
   
   
    pr_info("aml_GpioLedClass_plat_exit enter\n");

    platform_driver_unregister(&led_platform_driver);

    pr_info("aml_GpioLedClass_plat_exit exit\n");
}

module_init(aml_GpioLedClass_plat_init);
module_exit(aml_GpioLedClass_plat_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("simple say[5]: led-class device driver");

五、测试

(1)因为LED子系统自动为我们创建了用户接口,所以通过命令行就可以测试对LED的控制。
(2)编译、部署方法,参考之前的系列文章,不再赘述

5.1 加载KO

执行打印:

:/sys/class/leds # insmod /data/aml_gpio_led_class_platform.ko 

[ 2647.823967@3]- aml_GpioLedClass_plat_init enter
[ 2647.824465@3]- ledclass_plat_probe enter
[ 2647.826870@3]- aml_class_leds aml_led_class: gpiod_count = 3 
[ 2647.832558@3]-  ledclass_plat_probe  led(0xe9c8c110)->desc=0xed96b860 
[ 2647.839026@3]- zs, 000000000   
[ 2647.842103@3]- zs, 33333333333333   
[ 2647.846052@3]- zs, platform_probe  devm_led_classdev_register [0], cdev->name=red 
[ 2647.853321@3]-  ledclass_plat_probe  led(0xe9c8c410)->desc=0xed96b820 
[ 2647.859739@3]- zs, 111111111111111   
[ 2647.863350@3]- zs, 33333333333333   
[ 2647.867275@3]- zs, platform_probe  devm_led_classdev_register [1], cdev->name=green 
[ 2647.874691@3]-  ledclass_plat_probe  led(0xe9c8c010)->desc=0xed96b810 
[ 2647.881132@3]- zs, 22222222222   
[ 2647.884409@3]- zs, 33333333333333   
[ 2647.888257@3]- zs, platform_probe  devm_led_classdev_register [2], cdev->name=yellow 
[ 2647.895792@3]- aml_class_leds aml_led_class: ledclass_plat_probe exit
[ 2647.902477@3]- aml_GpioLedClass_plat_init exit
:/sys/class/leds # echo 1 > red/brightness [ 2673.284925@0]-  ENTER led_control  led(0xe9c8c110)->desc=0xed96b860 
[ 2673.285676@0]-  gpiod_set_value(led->desc , 1) 
[ 2673.290656@0]- EXIT led_control

图示如下:
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/0b508175afbe4d4e88cededed4a28a75.png
在这里插入图片描述

5.2 查看设备

如下可见到在probe里面命名的3个LED灯,分别是red、green、yellow

执行打印:

x301:/ # cd /sys/class/leds/
x301:/sys/class/leds # ls -al
total 0
drwxr-xr-x   2 root root 0 2023-01-05 01:16 .
drwxr-xr-x 131 root root 0 2023-01-05 01:16 ..
lrwxrwxrwx   1 root root 0 2023-01-05 01:38 green -> ../../devices/platform/aml_led_class/leds/green
lrwxrwxrwx   1 root root 0 2023-01-05 01:38 pwm_e1 -> ../../devices/platform/pwmleds/leds/pwm_e1
lrwxrwxrwx   1 root root 0 2023-01-05 01:38 red -> ../../devices/platform/aml_led_class/leds/red
lrwxrwxrwx   1 root root 0 2023-01-05 01:38 yellow -> ../../devices/platform/aml_led_class/leds/yellow
x301:/sys/class/leds #

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

5.3 LED控制

1)查看LED灯的属性,例如red
x301:/sys/class/leds # cd red/
x301:/sys/class/leds/red # ls -al
total 0
drwxr-xr-x 3 root root    0 2023-01-05 01:33 .
drwxr-xr-x 5 root root    0 2023-01-05 01:33 ..
-rw-r--r-- 1 root root 4096 2023-01-05 01:41 brightness
lrwxrwxrwx 1 root root    0 2023-01-05 01:41 device -> ../../../aml_led_class
-r--r--r-- 1 root root 4096 2023-01-05 01:41 max_brightness
drwxr-xr-x 2 root root    0 2023-01-05 01:41 power
lrwxrwxrwx 1 root root    0 2023-01-05 01:41 subsystem -> ../../../../../class/leds
-rw-r--r-- 1 root root 4096 2023-01-05 01:41 trigger
-rw-r--r-- 1 root root 4096 2023-01-05 01:41 uevent

(2)可见red默认为0,灯灭,符合设定
x301:/sys/class/leds/red # cat brightness
03)点亮红灯
x301:/sys/class/leds/red # echo 1 > brightness
x301:/sys/class/leds/red # cd ..4)点亮绿灯
127|x301:/sys/class/leds # echo 1 > green/brightness

(5)点亮黄灯
x301:/sys/class/leds # echo 1 > yellow/brightness
x301:/sys/class/leds #

命令图示如下:
在这里插入图片描述

5.4 点灯效果

在这里插入图片描述

六、源码下载

git clone git@gitee.com:amizhou/amlogic_t972_android9_driver.git

七、篇尾

保持持续学习, 欢迎交流。

相关文章
|
3月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
本文介绍了如何在基于Amlogic T972的Android 9.0系统上使用Platform平台驱动框架和设备树(DTS),实现设备与驱动的分离,并通过静态枚举在设备树中描述设备,自动触发驱动程序的加载和设备创建。
50 0
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
|
3月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
如何使用Amlogic T972安卓9.0系统上的misc框架来简化驱动程序开发,通过misc框架自动分配设备号并创建设备文件,从而减少代码量并避免设备号冲突。
43 0
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
|
3月前
|
Android开发 C语言
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,通过自动分配设备号和自动创建设备节点文件的方式,开发字符设备驱动程序的教程。
55 0
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
|
3月前
|
自然语言处理 Shell Linux
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
本文是关于在Amlogic安卓9.0平台上创建字符设备驱动的教程,详细介绍了驱动程序的编写、编译、部署和测试过程,并提供了完整的源码和应用层调用示例。
78 0
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
|
3月前
|
传感器 Android开发 芯片
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
本文是系列文章的第三篇,展示了如何在Android系统中利用现有的i2c bus驱动,通过编写设备树节点和应用层的控制代码,实现对基于i2c bus的Slaver设备(如六轴陀螺仪模块QMI8658C)的控制,而无需编写设备驱动代码。
48 0
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
|
3月前
|
Android开发
不写一行代码(二):实现安卓基于PWM的LED设备驱动
本文介绍了在Android系统中不编写任何代码,通过设备树配置和内核支持的通用PWM LED驱动来实现基于PWM的LED设备驱动,并通过测试命令调整LED亮度级别。
43 0
不写一行代码(二):实现安卓基于PWM的LED设备驱动
|
3月前
|
Linux Android开发 C语言
不写一行代码(一):实现安卓基于GPIO的LED设备驱动
本文通过实践操作,展示了在Android系统中不编写任何代码,利用设备树(DTS)配置和内核支持的通用GPIO LED驱动来控制LED设备,并进一步通过C语言编写NDK测试APP来实现LED的闪烁效果。
140 0
不写一行代码(一):实现安卓基于GPIO的LED设备驱动
|
3天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
5天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
7天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。