RK3568笔记三十六:LED驱动开发(设备树)
若该文为原创文章,转载请注明原文出处。
记录使用设备树编写一个简单的 LED 灯驱动程序
一、编程思路
程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性,编写测试应用程序。
• 首先向设备树添加 LED 设备节点。
• 其次编写平台设备驱动框架,主要包驱动入口函数、驱动注销函数、平台设备结构体定义 三部分内容。
• 实现.probe 函数,对 LED 进行设备注册和初始化。
• 实现字符设备操作函数集,这里主要实现.write 操作。
• 编写测试应用程序,对于输入不同的值控制 LED 亮灭
二、编写设备树
RK3568支持设备树,设备树相关知识执行了解。
设备树目录在arch/arm64/boot/dts/rockchip下。
修改 rk3568-atk-evb1-ddr4-v10.dtsi文件,在根目录 /下建立一个节点:
led_test {compatible="yifeng,led_test";status="okay";reg = <0x0 0xFDC20010 0x0 0x08 /* PMU_GRF_GPIO0C_IOMUX_L */ 0x0 0xFDC20090 0x0 0x08 /* PMU_GRF_GPIO0C_DS_0 */ 0x0 0xFDD60004 0x0 0x08 /* GPIO0_SWPORT_DR_H */ 0x0 0xFDD6000C 0x0 0x08 >; /* GPIO0_SWPORT_DDR_H */};
reg里的值参考LED驱动开发,比如“0x0 0xFDC20010 0x0 0x08”表示 RK3568 的 PMU_GRF_GPIO0C_IOMUX_L 寄存器,其中寄存器地址为 0xFDC20010,长度为 8 个字节。
修改如下:

设备树修改完成以后在 SDK 顶层目录输入如下命令重新编译一下内核:
# 指定 SDK 的板级配置文件
./build.sh lunch
# 编译内核
./build.sh kernel
只需要重新烧写boot.img。
三、编写驱动
led_test.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
//#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define DTSLED_CNT 1 /* 设备号个数 */
#define DTSLED_NAME "led_test" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;/* dtsled设备结构体 */
struct dtsled_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备节点 */
};struct dtsled_dev dtsled; /* led设备 *//** @description : LED打开/关闭* @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return : 无*/
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平*/writel(val, GPIO0_SWPORT_DR_H_PI);}else if(sta == LEDOFF) { val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);}
}/** @description : 取消映射* @return : 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);iounmap(PMU_GRF_GPIO0C_DS_0_PI);iounmap(GPIO0_SWPORT_DR_H_PI);iounmap(GPIO0_SWPORT_DDR_H_PI);
}/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &dtsled; /* 设置私有数据 */return 0;
}/** @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符* @param - buf : 要写给设备写入的数据* @param - cnt : 要写入的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if(ledstat == LEDON) { led_switch(LEDON); /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations dtsled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,
};/** @description : 驱动出口函数* @param : 无* @return : 无*/
static int __init led_init(void)
{u32 val = 0;int ret;u32 regdata[16];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点:led_test */dtsled.nd = of_find_node_by_path("/led_test");if(dtsled.nd == NULL) {printk("led_test node not find!\r\n");goto fail_find_node;} else {printk("led_test node find!\r\n");}/* 2、获取compatible属性内容 */proper = of_find_property(dtsled.nd, "compatible", NULL);if(proper == NULL) {printk("compatible property find failed\r\n");} else {printk("compatible = %s\r\n", (char*)proper->value);}/* 3、获取status属性内容 */ret = of_property_read_string(dtsled.nd, "status", &str);if(ret < 0){printk("status read failed!\r\n");} else {printk("status = %s\r\n",str);}/* 4、获取reg属性内容 */ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 16);if(ret < 0) {printk("reg property read failed!\r\n");} else {u8 i = 0;printk("reg data:\r\n");for(i = 0; i < 16; i++)printk("%#X ", regdata[i]);printk("\r\n");}/* 初始化LED *//* 1、寄存器地址映射 */PMU_GRF_GPIO0C_IOMUX_L_PI = of_iomap(dtsled.nd, 0);PMU_GRF_GPIO0C_DS_0_PI = of_iomap(dtsled.nd, 1);GPIO0_SWPORT_DR_H_PI = of_iomap(dtsled.nd, 2);GPIO0_SWPORT_DDR_H_PI = of_iomap(dtsled.nd, 3);/* 2、设置GPIO0_C0为GPIO功能。*/val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val &= ~(0X7 << 0); /* bit2:0,清零 */val |= ((0X7 << 16) | (0X0 << 0)); /* bit18:16 置1,允许写bit2:0,bit2:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);/* 3、设置GPIO0_C0驱动能力为level5 */val = readl(PMU_GRF_GPIO0C_DS_0_PI);val &= ~(0X3F << 0); /* bit5:0清零*/val |= ((0X3F << 16) | (0X3F << 0)); /* bit21:16 置1,允许写bit5:0,bit5:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_DS_0_PI);/* 4、设置GPIO0_C0为输出 */val = readl(GPIO0_SWPORT_DDR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平 */writel(val, GPIO0_SWPORT_DDR_H_PI);/* 5、设置GPIO0_C0为低电平,关闭LED灯。*/val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (dtsled.major) { /* 定义了设备号 */dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT);goto fail_devid;}} else { /* 没有定义设备号 */ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DTSLED_NAME, ret);goto fail_devid;}dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */}printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor); /* 2、初始化cdev */dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);/* 3、添加一个cdev */ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);if(ret < 0)goto del_unregister;/* 4、创建类 */dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {goto del_cdev;}/* 5、创建设备 */dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(dtsled.class);
del_cdev:cdev_del(&dtsled.cdev);
del_unregister:unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
fail_devid:led_unmap();
fail_find_node:return -EIO;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */cdev_del(&dtsled.cdev);/* 删除cdev */unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */device_destroy(dtsled.class, dtsled.devid);class_destroy(dtsled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
makefile
KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-export ARCH CROSS_COMPILECURRENT_PATH := $(shell pwd)
obj-m := led_test.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
四、编写应用
test_app.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF 0
#define LEDON 1/** @description : main主程序* @param - argc : argv数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}
编译
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc ledApp.c -o ledApp
五、测试
1、关闭 LED 的心跳灯
echo none > /sys/class/leds/work/trigger
2、加载和卸载驱动模块
depmod //第一次加载驱动的时候需要运行此命令
modprobe dtsled //加载驱动或
insmod led_test.ko卸载
rmmod led_test
3、测试
./ledApp /dev/led_test 1 //打开 LED 灯 ./ledApp /dev/led_test 0 // 关闭 LED 灯 如有侵权,或需要完整代码,请及时联系博主。
相关文章:
RK3568笔记三十六:LED驱动开发(设备树)
若该文为原创文章,转载请注明原文出处。 记录使用设备树编写一个简单的 LED 灯驱动程序 一、编程思路 程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性,编写测试应用程序。 • 首先向设备树添加 LED 设备…...
AC修炼计划(AtCoder Regular Contest 180) A~C
A - ABA and BAB A - ABA and BAB (atcoder.jp) 这道题我一开始想复杂了,一直在想怎么dp,没注意到其实是个很简单的规律题。 我们可以发现我们住需要统计一下类似ABABA这样不同字母相互交替的所有子段的长度,而每个字段的的情况有ÿ…...
云计算练习题
第一题:每周日晚上11点59分需要将/data目录打包压缩到/mnt目录下并以时间命名 #crontab -e 59 23 * * 7 /bin/tar czvf /mnt/date %F-data.tar.gz /data 59 23 * * 7 /bin/tar czvf /mnt/date %T.tar.gz /data 第二题:查找出系统中/application目录下所有…...
《战甲神兵》开发者报告:游戏崩溃问题80%发生在Intel可超频酷睿i9处理器上——酷睿i7 K系列CPU也表现出高崩溃率
在Intel持续面临第13代和第14代CPU崩溃问题的背景下,近日,《战甲神兵》(Warframe)的开发者们于7月9日披露了游戏崩溃的统计数据,并描述了诊断该问题的过程。根据开发团队的说法,一名未进行超频且使用全新PC的员工,即便…...
Postman下载及使用说明
Postman使用说明 Postman是什么? Postman是一款接口对接工具【接口测试工具】 接口(前端接口)是什么? 前端发送的请求普遍被称为接口 通常有网页的uri参数格式json/key-value请求方式post/get响应请求的格式json 接…...
什么是im即时通讯?WorkPlus im即时通讯私有化部署安全可控
IM即时通讯是Instant Messaging的缩写,指的是一种实时的、即时的电子信息交流方式,也被称为即时通讯。它通过互联网和移动通信网络,使用户能够及时交换文本消息、语音通话、视频通话、文件共享等信息。而WorkPlus im即时通讯私有化部署则提供…...
hnust 1794: 机器翻译
hnust 1794: 机器翻译 题目描述 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存…...
AI人工智能开源大模型生态体系分析
人工智能开源大模型生态体系研究 "人工智能开源大模型生态体系研究报告v1.0"揭示,AI(A)的飞速发展依赖于三大核心:数据、算法和算力。这一理念已得到业界广泛认同,三者兼备才能推动AI的壮大发展。随着AI大模型的扩大与普及…...
ArkTS学习笔记_封装复用之@Styles装饰器
ArkTS学习笔记_封装复用之Styles装饰器 背景: 在开发中,如果每个组件的样式都需要单独设置,就会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,给出的思路是ÿ…...
根据vue学习react
react的函数式组件与vue2是很像的 一、基础类似点 1、组件下拥有一个根节点,vue2是template,react是幽灵标签<> 2、vue2是{{}}以及v-model,react的绑定是{} 3、vue2编译html是v-html,react是{},并且react的jsx中…...
Hi3861 OpenHarmony嵌入式应用入门--HTTPD
httpd 是 Apache HTTP Server 的守护进程名称,Apache HTTP Server 是一种广泛使用的开源网页服务器软件。 本项目是从LwIP中抽取的HTTP服务器代码; Hi3861 SDK中已经包含了一份预编译的lwip,但没有开启HTTP服务器功能(静态库无法…...
MICS2024|少样本学习、多模态技术以及大语言模型在医学图像处理领域的研究进展|24-07-14
小罗碎碎念 本期推文主题 今天的会议很多主题都集中在大模型、多模态这两个方面,很明显,这两个方向都是目前的研究热点。 所以,我这一期推文会先简单的分析一下秦文健(中科院)和史淼晶(同济大学)…...
ConfigMap-secrets-静态pod
一.ConfigMap 1.概述 ConfigMap资源,简称CM资源,它生成的键值对数据,存储在ETCD数据库中 应用场景:主要是对应用程序的配置 pod通过env变量引入ConfigMap,或者通过数据卷挂载volume的方式引入ConfigMap资源 官方解释…...
SQL Error: 1406, SQLState: 22001
SQL错误代码1406和SQLState 22001通常表示“列数据过长”错误。这意味着尝试插入或更新列中的值,但该值的长度超过了该列允许的最大长度。 解决此问题的几个步骤: 检查列长度: 确定引起错误的列。检查数据库架构中该列允许的最大长度。 验证…...
【密码学基础】基于LWE(Learning with Errors)的全同态加密方案
学习资源: 全同态加密I:理论与基础(上海交通大学 郁昱老师) 全同态加密II:全同态加密的理论与构造(Xiang Xie老师) 现在第二代(如BGV和BFV)和第三代全同态加密方案都是基…...
Linux - 基础开发工具(yum、vim、gcc、g++、make/Makefile、git)
目录 Linux软件包管理器 - yum Linux下安装软件的方式 认识yum 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式各命令汇总 vim底行模式各命令汇总 vim的简单配置 Linux编译器 - gc…...
网络安全法律框架更新:最新合规要求与企业应对策略
网络安全法律框架的最新更新 近期,中国的网络安全法律框架经历了重要的更新。2022年,《网络安全法》迎来了首次修改,这一修订主要是为了与《数据安全法》和《个人信息保护法》等新实施的法律进行衔接协调,完善法律责任制度&#x…...
数仓工具—Hive语法之正则表达式函数
正则表达式函数 之前我们介绍过like rlike regexp 这些关键字,都是和匹配有关的,今天我们介绍一下hive 的REGEXP_REPLACE 和REGEXP_EXTRACT 函数,背景是使用Hive正则表达式函数提取数字 在我的其他文章中,我们已经看到了如何使用Hive正则表达式从字符串中提取日期值。正则…...
WKCTF 2024 easy_heap
很经典的house of orange unsortedbin attack FSOP 变量覆盖 不能 free,那首先想到就是 house of orange泄露Libc基址,然后unsortedbin attack。 但是只能show(8),就不能用largebin的套路来泄露堆地址了,那怎么办呢? …...
SQL 多变关联使用子查询去重
不去重状态 select a.*,b.recon_amt from free_settlement_first aleft join free_settlement_second b on a.settlement_first_id b.settlement_first_id 有2条数据出现了重复 使用子查询去重 select a.*,b.recon_amt from free_settlement_first aleft join free_settlem…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

