当前位置: 首页 > news >正文

consumer 角度讲一下i2c外设

往期内容

I2C子系统专栏:

  1. I2C(IIC)协议讲解-CSDN博客
  2. SMBus 协议详解-CSDN博客
  3. I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客
  4. 内核提供的通用I2C设备驱动I2c-dev.c分析:注册篇
  5. 内核提供的通用I2C设备驱动I2C-dev.c分析:file_ops篇
  6. 设备驱动与设备树匹配机制详解
  7. 编写一个通用的i2c设备驱动框架
  8. 编写一个通用的i2c控制器驱动框架

总线和设备树专栏:

  1. 总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客

前言

在上一章节(编写一个通用的i2c控制器驱动框架)讲了i2c_adapter。这里再继之前的文章( 编写一个通用的i2c设备驱动框架)进行补充,讲一下l2c_cleint的设备形态等

1. 两种设备形态

img

可以看出是有两种你描述的这两种设备形态反映了设备通过不同的总线进行交互的差异,并且在设备树(DTS)和驱动程序的编写上有不同的表现。根据设备的主要功能和数据传输方式,它们在 Linux 设备模型中的位置不同,从而影响了 DTS 文件中的结构和驱动程序的设计。我们来分析这两种设备形态。

形态 1: I2C-only 设备(图左侧):

  • 特点:这类设备和 CPU 之间的所有数据交互都是通过 I2C 总线完成的,没有其他传输方式。
  • DTS 描述:设备直接作为 I2C 控制器的子节点存在。在 DTS 中,设备的定义紧随其所在的 I2C 总线节点之后。例如 PMIC,它是 I2C 总线上的一个设备,因此直接在 I2C 节点的子节点中定义。
&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";pmic: pf0100@08 {compatible = "fsl,pfuze100";reg = <0x08>;...};
};
  • 驱动模型:I2C 核心负责处理设备的创建和注册。驱动程序是以 I2C slave 设备的方式来实现,I2C 驱动框架通过 i2c_driver 结构和 i2c_device_id 来与设备匹配。设备的 proberemove 等函数由 I2C 核心进行管理。
static const struct i2c_device_id pmic_i2c_ids[] = {{ "pfuze100", 0 },{ /* sentinel */ }
};static struct i2c_driver pmic_driver = {.driver = {.name = "pfuze100",.of_match_table = pmic_of_match,},.probe = pmic_probe,.remove = pmic_remove,.id_table = pmic_i2c_ids,
};

I2C 是主数据交互总线,设备完全依赖 I2C,所有操作通过 I2C 完成。

驱动注册流程通过 I2C 子系统的 i2c_driver 完成,设备由 I2C 核心管理。


形态 2: 复合设备(I2C 作为辅助接口,图右侧):

  • 特点:这类设备通过多种方式与 CPU 交互,例如,主要的数据传输(如音视频数据)通过 TDMS 接口,而配置信息或控制信息通过 I2C(DDC)接口。这类设备的核心功能决定了它们的主要位置在其他总线(如平台总线 platform bus)上,而 I2C 只是作为一个辅助接口。
  • DTS 描述:HDMI 设备作为 platform device 定义,并且通过 ddc-i2c-bus 属性引用 I2C 总线,用于 DDC(Display Data Channel)接口的 I2C 通信。
&hdmi {ddc-i2c-bus = <&i2c2>;status = "okay";
};
  • 驱动模型:在这种情况下,HDMI 控制器是主要设备,注册为 platform_device。I2C 通信是 HDMI 控制器的一部分,用来处理 EDID 等信息读取。在驱动程序中,I2C 总线是通过 DTS 中的 ddc-i2c-bus 属性传递给 HDMI 驱动的,驱动程序会通过 of_find_i2c_adapter_by_node() 获取并绑定 I2C 控制器。
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {dev_err(dev, "failed to find ddc-i2c-bus node\n");return -ENODEV;
}ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;/* Use ddc for I2C transactions */

I2C 是辅助总线,用于少量数据交互(如 EDID、配置信息传输),主要功能通过其他总线(如 TDMS)完成。

驱动以 platform_device 形式注册,I2C 仅作为其中一个通信接口,I2C 控制器通过设备树中的引用绑定。

2. 驱动编写

根据上面所说的形态去编写设备不同的i2c_driver

形态1: 完全依赖I2C的设备(如PMIC)

  1. 确定I2C adapter
    根据设备硬件连接的实际情况,确定该设备所从属的I2C controller,即I2C adapter(在I2C framework中,I2C controller通常被称为I2C adapter)。例如,PMIC设备可能连接在 i2c1 总线上。
  2. 在DTS中添加设备描述
    在设备树的I2C adapter节点中,添加I2C从设备的描述。这个从设备的定义格式与普通的platform device一致,例如:
&i2c1 {pmic: pfuze100@08 {compatible = "fsl,pfuze100";reg = <0x08>;// 其他字段...};
};
  1. compatible 字段的使用
    DTS 中的 compatible 字段用于设备驱动的匹配和 probe。I2C驱动会根据 compatible 字段匹配到合适的驱动程序,例如 "compatible = "fsl,pfuze100";"
  2. 编写I2C设备的驱动程序

示例:

static const struct of_device_id pmic_of_match[] = {{ .compatible = "fsl,pfuze100", },{ /* sentinel */ }
};static struct i2c_driver pmic_driver = {.driver = {.name = "pfuze100",.of_match_table = pmic_of_match,},.probe = pmic_probe,.remove = pmic_remove,
};
static int __init i2c_driver_XXX_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&i2c_XXX_driver);
}
module_init(i2c_driver_XXX_init);
    • 定义并注册 i2c_driver:编写一个 struct i2c_driver 结构体,并使用 i2c_add_driver将其注册到I2C core中。
    • 匹配设备:在 i2c_driver 结构体中,定义 of_match_table,使其可以通过 compatible 字段进行匹配。
    • 实现 probe 回调:编写 probe 函数,用于设备初始化。
  1. I2C core处理设备注册
    当I2C adapter注册时,I2C framework的核心代码会自动为其下的所有I2C从设备创建 struct i2c_client 结构体,并根据设备树中的 compatible 字段,匹配合适的 i2c_driver,然后调用驱动程序的 probe 函数。
  2. probe
    当设备被I2C framework匹配到时,调用此函数进行设备初始化。主要负责获取设备资源(如设备地址、I2C Adapter),设置硬件寄存器,或者初始化其他系统资源(如注册字符设备)。
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{// 打印日志确认 probe 触发printk(KERN_INFO "ap3216c i2c device probe.\n");// 设置I2C client指针ap3216c_client = client;// 执行设备初始化:如设置初始寄存器值i2c_smbus_write_byte_data(client, 0x00, 0x03);  // 设置某些寄存器// 注册字符设备或其他资源major = register_chrdev(0, "ap3216c", &ap3216c_ops);ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c");return 0;
}

在注册的ap3216c_ops中,有关read和write的函数,使用到I2C数据有关的接口就有两类:

  • 一类是以i2c client为参数,进行简单的数据收发。该方法只可以通过标准方式,发送或者接收一定数量的数据。
  • 另一类是以i2c adapter和i2c msg为参数,可以更为灵活的read或者write数据,包括i2c_transfer。使用该方法可以以struct i2c_msg为参数,一次读取、或者写入、或者读取加写入,一定数量的数据。
  • 具体的函数去看\Linux-4.9.88\include\linux\i2c.h

形态2: 复合型设备(如HDMI)

  1. 确定I2C adapter
    根据设备硬件的连接方式,找到设备使用的I2C adapter。例如,HDMI 设备可以使用 i2c2 作为其 DDC 通道。
  2. 将设备作为平台设备描述
    将设备作为平台设备(platform device)进行描述。其 DTS 节点并不是直接位于I2C adapter下,而是位于根目录。例如:
&hdmi {ddc-i2c-bus = <&i2c2>;status = "okay";
};
  1. 获取I2C adapter的引用
    在DTS描述中使用 ddc-i2c-bus 属性引用I2C adapter节点。该属性通过 of_parse_phandle 来解析,并在驱动中获取相应的I2C adapter的指针。
  2. probe 函数中获取I2C adapter
    在平台设备的 probe 函数中,通过 of_parse_phandle 来获取I2C adapter节点,并通过 of_find_i2c_adapter_by_node() 获取对应的 struct i2c_adapter 指针。如下代码示例:
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {dev_err(dev, "failed to find ddc-i2c-bus node\n");return -ENODEV;
}ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;
  1. 使用 i2c_transfer 进行读写操作
    获取I2C adapter之后,便可以使用 i2c_transfer() 接口来进行I2C读写操作。例如,通过DDC接口读取EDID信息时:
struct i2c_msg msgs[] = {{.addr = ddc_addr,.flags = I2C_M_RD,.len = 128,.buf = edid_buf,},
};ret = i2c_transfer(ddc, msgs, 1);  // 读取EDID数据

3. i2c_client

i2c设备中用i2c_client结构体来指代:

/*** struct i2c_client - represent an I2C slave device* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;*	I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* @addr: Address used on the I2C bus connected to the parent adapter.* @name: Indicates the type of the device, usually a chip name that's*	generic enough to hide second-sourcing and compatible revisions.* @adapter: manages the bus segment hosting this I2C device* @dev: Driver model device node for the slave.* @irq: indicates the IRQ generated by this device (if any)* @detected: member of an i2c_driver.clients list or i2c-core's*	userspace_devices list* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter*	calls it to pass on slave events to the slave driver.** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/
struct i2c_client {unsigned short flags;		/* div., see below		*/unsigned short addr;		/* chip address - NOTE: 7bit	*//* addresses are stored in the	*//* _LOWER_ 7 bits		*/char name[I2C_NAME_SIZE];struct i2c_adapter *adapter;	/* the adapter we sit on	*/struct device dev;		/* the device structure		*/int irq;			/* irq issued by device		*/struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

flags:

  • 表示I2C设备的标志位。可以包含以下选项:

    • I2C_CLIENT_TEN:表示设备使用的是十位地址(而不是常见的七位地址)。
    • I2C_CLIENT_PEC:表示设备使用SMBus包错误校验(Packet Error Checking,PEC),这是一种错误检测机制。

addr:

  • I2C设备的地址。地址位于I2C总线上的低7位或低10位(如果 flags 指示设备使用十位地址的话)。

name:

  • 一个字符数组,表示设备的名称,通常是设备的芯片名称或类型。名称尽可能保持通用,以适应同一设备的不同版本或兼容的设备。

adapter:

  • 指向设备所在的I2C总线适配器(i2c_adapter)的指针。每个I2C设备都挂载在一个I2C适配器上,适配器管理与设备的通信。

dev:

  • Linux驱动模型中的设备节点(struct device),用于表示I2C从设备。这是Linux设备模型的标准数据结构,提供了与设备类、驱动程序以及电源管理等相关的接口。

irq:

  • 表示设备所使用的中断号(如果该设备可以生成中断)。不使用中断的设备此字段可能为0或无效值。

detected:

  • 用于链表结构中的节点。在内核中,这个字段用于将该设备连接到 i2c_driver.clients 列表或 i2c-coreuserspace_devices 列表中。

slave_cb:

  • 仅在启用了 I2C Slave 模式(即 CONFIG_I2C_SLAVE 选项启用时)时有效。
  • 这是用于适配器从模式的回调函数指针。当I2C适配器处于从模式时,适配器调用这个回调函数,将从模式的事件传递给从设备的驱动程序。

i2c_client一般是在register adapter的时候,解析adapter的child node自行创建的,这个在对上一章节中对i2c_add_adapte函数进行分析的时候有提到过

相关文章:

consumer 角度讲一下i2c外设

往期内容 I2C子系统专栏&#xff1a; I2C&#xff08;IIC&#xff09;协议讲解-CSDN博客SMBus 协议详解-CSDN博客I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客内核提供的通用I2C设备驱动I2c-dev.c分析&#xff1a;注册篇内核提供的通用I2C设备驱动I2C-dev.…...

面试经典150题刷题记录

数组部分 1. 合并两个有序的子数组 —— 倒序双指针避免覆盖 88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使…...

【HarmonyOS NEXT】实现保存base64图片到图库

上篇文章介绍了HarmonyOS NEXT如何保存base64文件到download目录下&#xff0c;本次介绍如何保存base64图片到图库&#xff0c;网络图片保存方式大同小异&#xff0c;先下载图片&#xff0c;然后再保存 phAccessHelper.showAssetsCreationDialog参考官方文档’ ohos.file.pho…...

开题答辩最怕被问什么?教你用ChatGPT轻松准备,稳拿高分!

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 开题答辩是学位论文写作过程中的重要环节&#xff0c;能帮助导师评估你的研究计划是否可行&#xff0c;并对后续写作起到指导作用。很多同学在面对导师提问时会感到紧张&#xff0c;因此提前准备好常见问题的…...

Unity3D功耗和发热分析与优化详解

前言 Unity3D作为广泛使用的游戏开发引擎&#xff0c;在游戏开发过程中&#xff0c;功耗和发热问题一直是开发者需要重点关注的问题。功耗和发热不仅影响用户体验&#xff0c;还可能对设备的硬件寿命造成一定影响。本文将从技术角度详细分析Unity3D游戏在移动设备上的功耗和发…...

深度学习中的掩码介绍

在深度学习中,**掩码(Mask)**通常用于屏蔽掉某些特定部分的数据,以确保模型在训练或推理时不处理无效或无关的输入。掩码的使用场景主要包括处理变长序列、生成模型中的自回归任务、处理填充数据等。掩码可以是二值矩阵(1 表示有效数据,0 表示屏蔽数据),在注意力机制中…...

rust高级进阶总结

文章目录 前言1. Rust生命周期进阶一、不太聪明的生命周期检查&#xff08;一&#xff09;例子1&#xff08;二&#xff09;例子2 二、无界生命周期三、生命周期约束&#xff08;HRTB&#xff09;&#xff08;一&#xff09;语法及含义&#xff08;二&#xff09;综合例子 四、…...

整理—计算机网络

目录 网络OSI模型和TCP/IP模型 应用层有哪些协议 HTTP报文有哪些部分 HTTP常用的状态码 Http 502和 504 的区别 HTTP层请求的类型有哪些&#xff1f; GET和POST的使用场景&#xff0c;有哪些区别&#xff1f; HTTP的长连接 HTTP默认的端口是什么&#xff1f; HTTP1.1怎…...

分布式数据库环境(HBase分布式数据库)的搭建与配置

分布式数据库环境&#xff08;HBase分布式数据库&#xff09;的搭建与配置 1. VMWare安装CentOS7.9.20091.1 下载 CentOS7.9.2009 映像文件1.2启动 VMware WorkstationPro&#xff0c;点击“创建新的虚拟机”1.3在新建虚拟机向导界面选择“典型&#xff08;推荐&#xff09;”1…...

100个JavaWeb(JDBC, Servlet, JSP)毕业设计选题

100个JavaWeb(JDBC, Servlet, JSP)毕业设计选题 教育行业 学生信息管理系统在线考试系统课程管理与选课系统教师评价管理系统图书馆管理系统学生成绩查询系统校园论坛作业提交与批改系统学生考勤管理系统教学资源共享平台 企业管理 员工管理系统考勤打卡系统办公用品申请管…...

05 go语言(golang) - 常量和条件语句

常量 在Go语言中&#xff0c;常量是使用 const 关键字定义的&#xff0c;并且一旦被赋值后&#xff0c;它们的值在程序运行期间不能改变。常量可以是字符、字符串、布尔或数值类型。 基本特性 不可修改&#xff1a;一旦一个常量被定义&#xff0c;它的值就不能被更新。编译时…...

【设计模式】深入理解Python中的适配器模式(Adapter Pattern)

深入理解Python中的适配器模式&#xff08;Adapter Pattern&#xff09; 在软件开发中&#xff0c;常常会遇到需要让不兼容的类或接口协同工作的问题。适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;通过提供一个包装器对象&#xff0c;…...

RuoYi-Vue若依框架-后端设置不登陆访问(白名单)

找到SecurityConfig类 确认自己的需求 /*** anyRequest | 匹配所有请求路径* access | SpringEl表达式结果为true时可以访问* anonymous | 匿名可以访问* denyAll | 用户不能访问* fullyAuthenticated | 用户完全认证可…...

C语言初阶小练习2(三子棋小游戏的实现代码)

这是C语言小游戏三子棋的代码实现 test.c文件是用来测试的部分 game.h文件是用来声明我们说写出的函数 game.c文件是用来编写我们的功能实现函数部分 1.test.c #define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() {printf("***************…...

金融行业合同管理如何利用AI技术进行风险预警?

2024年以来&#xff0c;金融行业的发展主线被锚定&#xff0c;强调了防风险的基调&#xff0c;尤其是系统性风险的防范。金融工作的重点在于实现六个强大&#xff1a;强大的货币、强大的中央银行、强大的金融机构、强大的国际金融中心、强大的金融监管、强大的金融人才队伍。这…...

世界数字农业盛宴与技术探索,25年3月聚焦世界灌溉科技大会

由中国农业节水和农村供水技术协会、中国农垦节水农业产业技术联盟、北京物联网智能技术应用协会、振威国际会展集团主办的“世界灌溉科技大会”、“第11届北京国际数字农业与灌溉技术博览会”&#xff0c;定于2025年3月31日至4月2日在北京国家会议中心举办。 作为世界三大灌溉…...

二百六十九、Kettle——ClickHouse清洗ODS层原始数据增量导入到DWD层表中

一、目的 清洗ClickHouse的ODS层原始数据&#xff0c;增量导入到DWD层表中 二、实施步骤 2.1 newtime select( select create_time from hurys_jw.dwd_statistics order by create_time desc limit 1) as create_time 2.2 替换NULL值 2.3 clickhouse输入 2.4 字段选择 2.5 …...

Maya---骨骼绑定

调节骨骼大小 回车键确认骨骼 FK子集跟父集走 IK子集不跟父集走 前视图中按shift键添加骨骼 清零、删除历史记录&#xff0c;创建新的物体...

携手并进,智驭教育!和鲸科技与智谱 AI 签署“101 数智领航计划”战略合作协议

近日&#xff0c;上海和今信息科技有限公司&#xff08;以下简称“和鲸科技”&#xff09;与北京智谱华章科技有限公司&#xff08;以下简称“智谱 AI”&#xff09;签署“101 数智领航计划”战略合作协议。双方将携手营造智能化学科教育与科研环境&#xff0c;提供多种大模型工…...

牛客周赛63

https://ac.nowcoder.com/acm/contest/91592 好数 简单的判断两位数&#xff0c;且十位等于个位 #include <bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define int long long using namespace std; using ll long long; using pii …...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能

指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...