1. Android 实现APP点亮一个灯 ---创建设备方式
2026/9/20 15:26:47 网站建设 项目流程

一、环境搭建

1. Android Studio 安装-CSDN博客

二、底层驱动开发

1.设备树修改

a.根节点/ 节点添加

led_test { // 设备兼容性声明 - 匹配驱动中的of_match_table compatible = "devled"; // pinctrl状态名称列表(通常至少包含"default"状态) pinctrl-names = "default"; // 设备状态 - 必须设为"okay"才能使节点生效 status = "okay"; // 引用的pinctrl配置(关联到具体引脚控制) pinctrl-0 = <&pinctrl_led_test>; // LED子节点配置 led { // GPIO控制配置: // <&gpio控制器 引脚编号 有效电平> // GPIO0_C0引脚,高电平有效(GPIO_ACTIVE_HIGH表示高电平点亮) gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; // 默认状态: default-state = "on"; }; };

b.&pinctrl 添加

led { pinctrl_led_test: led_test_grp { rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_down>; }; };

2.Linux内核驱动开发

/* * vendor/nxp-opensource/kernel_imx/drivers/char/led_drv.c */ #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/of_device.h> #include <linux/slab.h> #define LED_OFF 0 #define LED_ON 1 static int dev_major = 0; /* 存放led信息的结构体 */ struct gpio_led_data { char name[16]; // 设备名字 int pin; // gpio编号 int active; // 控制亮灭的标志 }; /* 存放led的私有属性 */ struct gpio_leds_priv { struct cdev cdev; // cdev结构体 struct class *dev_class; // 自动创建设备节点的类 int num_leds; // led的数量 struct gpio_led_data led; // 存放led信息的结构体数组 }; /* 为led私有属性开辟存储空间的函数 */ static inline int sizeof_gpio_leds_priv(int num_leds) { return sizeof(struct gpio_leds_priv) + (sizeof(struct gpio_led_data) * num_leds); } /* 解析设备树并初始化led属性 */ int parser_dt_init_led(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; // 当前设备节点 struct device_node *child; // 当前设备节点的子节点 struct gpio_leds_priv *priv; // 存放私有属性 int num_leds, gpio; // led数量和gpio编号 /* 获取该节点下子节点的数量 */ num_leds = 1; if(num_leds <= 0) { dev_err(&pdev->dev, "fail to find child node\n"); return -EINVAL; } /* devm_kzalloc是内核内存分配函数,跟设备有关的,驱动卸载时,内存会被自动释放 * void * devm_kzalloc (struct device * dev, size_t size, gfp_t gfp) * dev:申请内存的目标设备 size:申请的内存大小 gfp:申请内存的类型标志 * GFP_KERNEL是分配内核空间的内存时的一个标志位,无内存可用时可引起休眠 */ priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(num_leds), GFP_KERNEL); if (!priv) { return -ENOMEM; } priv->num_leds = 1; /* 找到子节点并传给child */ child = of_get_child_by_name(np, "led"); /* 解析dts并且获取gpio口,函数返回值就得到gpio号,并且读取gpio现在的标志 */ gpio = of_get_named_gpio(child, "gpios", 0); /* 将子节点的名字,传给私有属性结构体中的led信息结构体中的name属性 */ strncpy(priv->led.name, child->name, sizeof(priv->led.name)); /* 将gpio编号和控制亮灭的标志传给结构体 * active属性,1代表亮,0代表灭,初始属性为灭 */ priv->led.active = 0; priv->led.pin = gpio; /* 申请gpio口,相较于gpio_request增加了gpio资源获取与释放功能 */ gpio_request(priv->led.pin, "led"); /* 设置gpio为输出模式,并设置初始状态 */ gpio_direction_output(priv->led.pin, 1); /* 将led的私有属性放入platform_device结构体的device结构体中的私有数据中 */ platform_set_drvdata(pdev, priv); return 0; } static int led_open(struct inode *inode, struct file *file) { struct gpio_leds_priv *priv; priv = container_of(inode->i_cdev, struct gpio_leds_priv, cdev); file->private_data = priv; return 0; } static int led_release(struct inode *inode, struct file *file) { return 0; } static long led_ioctl(struct file *file , unsigned int cmd, unsigned long arg) { struct gpio_leds_priv *priv; priv = file->private_data; switch (cmd) { case LED_ON: printk("LED_ON:\n"); printk("gpio%d state: %d\n", priv->led.pin, gpio_get_value(priv->led.pin)); gpio_set_value(priv->led.pin, 1); priv->led.active = 1; printk("gpio_pin: %d\n", priv->led.pin); break; case LED_OFF: printk("LED_OFF:\n"); printk("gpio%d state: %d\n", priv->led.pin, gpio_get_value(priv->led.pin)); gpio_set_value(priv->led.pin, 0); priv->led.active = 0; printk("gpio_pin: %d\n", priv->led.pin); break; default: printk(KERN_ERR "Ioctl command=%d can't be supported\n", cmd); break; } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .unlocked_ioctl = led_ioctl, }; static int gpio_led_probe(struct platform_device *pdev) { struct gpio_leds_priv *priv; // 临时存放私有属性的结构体 struct device *dev; // 设备结构体 dev_t devno; // 设备的主次设备号 int i, rv = 0; dev_err(&pdev->dev, "Andre167 gpio_led_probe\n"); /* 1)解析设备树并初始化led状态 */ rv = parser_dt_init_led(pdev); if( rv < 0 ) return rv; dev_err(&pdev->dev, "Andre172 gpio_led_probe\n"); /* 将之前存入的私有属性,放入临时的结构体中 */ priv = platform_get_drvdata(pdev); /* 2)分配主次设备号 */ if (0 != dev_major) { /* 静态分配主次设备号 */ devno = MKDEV(dev_major, 0); rv = register_chrdev_region(devno, priv->num_leds, "devled"); } else { /* 动态分配主次设备号 */ rv = alloc_chrdev_region(&devno, 0, priv->num_leds, "devled"); dev_major = MAJOR(devno); } if (rv < 0) { dev_err(&pdev->dev, "andre major can't be allocated\n"); return rv; } /* 3)分配cdev结构体 */ cdev_init(&priv->cdev, &led_fops); priv->cdev.owner = THIS_MODULE; rv = cdev_add (&priv->cdev, devno , priv->num_leds); if( rv < 0) { dev_err(&pdev->dev, "andre struture cdev can't be allocated\n"); goto undo_major; } dev_err(&pdev->dev, "Andre205 gpio_led_probe\n"); /* 4)创建类,实现自动创建设备节点 */ priv->dev_class = class_create(THIS_MODULE, "led"); if( IS_ERR(priv->dev_class) ) { dev_err(&pdev->dev, "fail to create class\n"); rv = -ENOMEM; goto undo_cdev; } /* 5)创建设备 */ for(i=0; i<priv->num_leds; i++) { devno = MKDEV(dev_major, i); dev = device_create(priv->dev_class, NULL, devno, NULL, "led"); if( IS_ERR(dev) ) { dev_err(&pdev->dev, "fail to create device\n"); rv = -ENOMEM; goto undo_class; } } printk("success to install driver[major=%d]!\n", dev_major); return 0; undo_class: class_destroy(priv->dev_class); undo_cdev: cdev_del(&priv->cdev); undo_major: unregister_chrdev_region(devno, priv->num_leds); return rv; } static int gpio_led_remove(struct platform_device *pdev) { struct gpio_leds_priv *priv = platform_get_drvdata(pdev); int i; dev_t devno = MKDEV(dev_major, 0); /* 注销设备结构体,class结构体和cdev结构体 */ for(i=0; i<priv->num_leds; i++) { devno = MKDEV(dev_major, i); device_destroy(priv->dev_class, devno); } class_destroy(priv->dev_class); cdev_del(&priv->cdev); unregister_chrdev_region(MKDEV(dev_major, 0), priv->num_leds); /* 将led的状态设置为灭 */ for (i = 0; i < priv->num_leds; i++) { gpio_set_value(priv->led.pin, ~priv->led.active); } printk("success to remove driver[major=%d]!\n", dev_major); return 0; } /* 匹配列表 */ static const struct of_device_id gpio_led_of_match[] = { { .compatible = "devled" }, {} }; MODULE_DEVICE_TABLE(of, gpio_led_of_match); /* platform驱动结构体 */ static struct platform_driver gpio_led_driver = { .driver = { .name = "devled", // 无设备树时,用于设备和驱动间的匹配 .of_match_table = gpio_led_of_match, // 有设备树后,利用设备树匹配表 }, .probe = gpio_led_probe, .remove = gpio_led_remove, }; module_platform_driver(gpio_led_driver); MODULE_LICENSE("GPL");

3.makefile文件

KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel obj-m := led.o clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

3.编译后下载,首次DTS修改后,需要下载整个kernel,后面只需要led.ko文件

  • adb push led.ko文件到设备即可
  • 然后 insmod led.ko
  • 检查/dev下是否有led设备,如果有就说明led运行成功,然后:chmod 777 /dev/led

三 、

三、APP开发

1. 创建Android项目

  1. 新建项目

a. 选择"Empty views Activity"模板->next

b.配置项目名称和包名->finish

c.先添加 C++ 文件,如下图所示。

d.点击ok

e.成功生成后会有cpp文件夹

f.添加名为 HardControl 的 Java 类。

g.点击class

h.添加如下code

package com.example.huan; public class HardControl { public static native int ledCtrl(int which, int status); public static native int ledOpen(); /* 用 System.loadLibrary 函数,加载 C++ 库,也就是 native-lib 编译生成的库 */ static { System.loadLibrary("huan"); } }

i.创建JNI

j.添加如下code

#include <jni.h> #include <string> #include <android/log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <sys/select.h> int fd; /* 这种 JNI 函数是使用绝对路径的方式来使 native-lib 中的 JNI 函数和 HardConctrl.java 中的函数对应 * 因此,函数名显得很冗长,暂时先使用这种注册方式 */ /* ledOpen 中主要就是 open 设备节点 * ledCtrl 的作用就是控制 led 亮灭 */ extern "C" JNIEXPORT jint JNICALL Java_com_example_huan_HardControl_ledOpen(JNIEnv *env, jclass clazz) { __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen"); fd = open("/dev/led", O_RDWR, 0755); __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "fd = %d", fd); return 0; } extern "C" JNIEXPORT jint JNICALL Java_com_example_huan_HardControl_ledCtrl(JNIEnv *env, jclass clazz, jint which, jint status) { __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d", which, status); ioctl(fd, status); return 0; }

k.修改 MainActivity.java 并在 activity_main.xml 添加按钮和标签

java

package com.example.huan; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import com.example.huan.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private boolean ledTag = false; private Button ledButton = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); /* 按下按钮后,新建一个 HardControl 类的对象,然后利用这个对象调用其中声明的 native 函数 * 然后,通过这个 native 函数调到 native-lib 中声明的 JNI 函数 */ ledButton = (Button) findViewById(R.id.BUTTON); ledButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { HardControl hardControl = new HardControl(); hardControl.ledOpen(); ledTag = !ledTag; if(ledTag) { ledButton.setText("OFF"); hardControl.ledCtrl(1, 1); } else { ledButton.setText("ON"); hardControl.ledCtrl(1, 0); } } }); } }

activity_main.xml

替换

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="led demo" /> <Button android:id="@+id/BUTTON" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ON" /> </LinearLayout>

l. kts添加如下code,并sync

M.编译运行
模拟:

编译APK

安装APK运行

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询