1. Android 实现APP点亮一个灯 ---创建设备方式
一、环境搭建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 cant 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 cant 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 cant 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(i0; ipriv-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(i0; ipriv-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) clean3.编译后下载首次DTS修改后需要下载整个kernel后面只需要led.ko文件adb push led.ko文件到设备即可然后 insmod led.ko检查/dev下是否有led设备如果有就说明led运行成功然后chmod 777 /dev/led三 、三、APP开发1. 创建Android项目新建项目a. 选择Empty views Activity模板-nextb.配置项目名称和包名-finishc.先添加 C 文件如下图所示。d.点击oke.成功生成后会有cpp文件夹f.添加名为 HardControl 的 Java 类。g.点击classh.添加如下codepackage 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.创建JNIj.添加如下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 添加按钮和标签javapackage 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 version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto xmlns:toolshttp://schemas.android.com/tools android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:gravitycenter tools:context.MainActivity TextView android:layout_widthwrap_content android:layout_heightwrap_content android:textled demo / Button android:idid/BUTTON android:layout_widthwrap_content android:layout_heightwrap_content android:textON / /LinearLayoutl. kts添加如下code并syncM.编译运行模拟编译APK安装APK运行

相关新闻

RNN中激活函数的改进:从tanh到Mamba的实践对比

RNN中激活函数的改进:从tanh到Mamba的实践对比

简介:一份聚焦循环神经网络结构优化的专业研究文献,面向AI算法工程师、深度学习研究者及机器学习方向的高校师生,针对传统RNN在长时依赖学习中出现的梯度消失与梯度爆炸问题,系统探讨了激活函数的改进策略。内容以河海大学研究团队…

2026/9/20 15:26:58 阅读更多 →
1. Android Studio 安装

1. Android Studio 安装

1.下载安装包: 访问下载 Android Studio 和应用工具 - Android 开发者 | Android Developers下载最新版本 支持Windows、macOS和Linux平台 2.安装环境(这里以Windows讲解步骤): 2.1点击下载​ 2.2 勾选同意->点击下载 2.3 安装下载的 .exe 文件(如android-stu…

2026/9/20 15:26:58 阅读更多 →
AI对话中的错误提示:从原理到实践

AI对话中的错误提示:从原理到实践

简介:这份资源是2023年电子设计大赛E题“运动目标控制与自动追踪系统”的完整实现方案,面向参赛学生、嵌入式爱好者及OpenMV与STM32开发者。方案以二维电控云台为基础,通过红色激光光斑模拟运动目标、绿色激光光斑完成自动追踪,涵…

2026/9/20 15:26:58 阅读更多 →

最新新闻

grok-build / xai-grok-shell 0.2.10 变更解读:`/check-work` 命令迁移与小于 8×8 像素图片的拒绝策略

grok-build / xai-grok-shell 0.2.10 变更解读:`/check-work` 命令迁移与小于 8×8 像素图片的拒绝策略

grok-build / xai-grok-shell 0.2.10 变更解读:/check-work 命令迁移与小于 88 像素图片的拒绝策略 【免费下载链接】grok-build SpaceXAIs coding agent harness and TUI. Fullscreen, mouse interactive, extensible. 项目地址: https://gitcode.com/gh_mirrors…

2026/9/20 16:01:36 阅读更多 →
如何用Semantica做本体建模?从实体关系数据到Turtle导出的实操指南

如何用Semantica做本体建模?从实体关系数据到Turtle导出的实操指南

如何用Semantica做本体建模?从实体关系数据到Turtle导出的实操指南 【免费下载链接】semantica Graph-Native Infrastructure for Context and Accountable AI Systems 项目地址: https://gitcode.com/GitHub_Trending/sema/semantica 没有本体的知识图谱&am…

2026/9/20 16:01:36 阅读更多 →
低频功率放大器DIY全攻略:从LM1875选型到电源接地调试

低频功率放大器DIY全攻略:从LM1875选型到电源接地调试

简介:一份围绕低频功率放大器设计的完整赛题资料,适合电子设计竞赛参赛者、课程设计学生及功率放大电路爱好者参考。内容从任务说明、技术指标到设计思路均有覆盖,明确给出额定输出功率≥10W、带宽50Hz~10kHz、非线性失真≤3%、效…

2026/9/20 16:01:36 阅读更多 →
大模型权重下载慢?用91n多线程与断点续传提速

大模型权重下载慢?用91n多线程与断点续传提速

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 16:01:36 阅读更多 →
大模型应用技术选型:提示工程、RAG与微调对比

大模型应用技术选型:提示工程、RAG与微调对比

1. 大模型应用技术选型全景图当企业或开发者准备将大语言模型(LLM)落地到实际业务场景时,通常会面临三个主流技术路线的选择:提示工程(Prompt Engineering)、检索增强生成(RAG)和模型…

2026/9/20 16:01:36 阅读更多 →
绵阳网站建设科雨网络报价单揭秘:源码下载才不亏

绵阳网站建设科雨网络报价单揭秘:源码下载才不亏

绵阳网站建设科雨网络报价单揭秘:源码下载才不亏 域名和服务器到底选哪家?这问题问得我头疼。 很多老板在找绵阳网站建设科雨网络这类服务商时,第一反应就是看价格,但往往忽略了最核心的“资产归属”。 如果你拿到手只是一堆编译好的文件,甚至连后台账号密码都控制不了,那你花钱买的就是个“牢笼”。…

2026/9/20 16:01:04 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →