首页
商城
  • English
  • 简体中文
首页
商城
  • English
  • 简体中文
  • 产品系列

    • FPGA+ARM

      • GM-3568JHF

        • 一、简介

          • GM-3568JHF 简介
        • 二、快速开始

          • 00 前言
          • 01 环境搭建
          • 02 编译说明
          • 03 烧录指南
          • 04 调试工具
          • 05 软件更新
          • 06 查看信息
          • 07 测试命令
          • 08 应用编译
          • 09 源码获取
        • 三、外设与接口

          • 01 USB
          • 02 显示与触摸
          • 03 以太网
          • 04 WIFI
          • 05 蓝牙
          • 06 TF-Card
          • 07 音频
          • 08 串口
          • 09 CAN
          • 10 RTC
        • 四、应用开发

          • 01 UART读写案例
          • 02 按键检测案例
          • 03 LED灯闪烁案例
          • 04 MIPI屏幕检测案例
          • 05 读取 USB 设备信息案例
          • 06 FAN 检测案例
          • 07 FPGA FSPI 通信案例
          • 08 FPGA DMA 读写案例
          • 09 GPS调试案例
          • 10 以太网测试案例
          • 11 RS485读写案例
          • 12 FPGA IIC 读写案例
          • 13 PN532 NFC读卡案例
          • 14 TF卡读写案例
        • 五、QT开发

          • 01 ARM64交叉编译器环境搭建
          • 02 QT 程序加入开机自启服务
        • 六、RKNN_NPU开发

          • 01 RK3568 NPU 概述
          • 02 开发环境搭建
          • 运行官方 YOLOv5 示例
        • 七、FPGA开发

          • ARM与FPGA通讯
          • FPGA开发手册
        • 八、其他

          • 01 根目录文件系统的修改
          • 02 系统自启服务
        • 九、资料下载

          • 资料下载
    • ShimetaPi

      • M4-R1

        • 一、简介

          • M4-R1简介
        • 二、快速上手

          • 01 OpenHarmony概述
          • 02 镜像烧录
          • 03 应用开发快速上手
          • 04 设备开发快速上手
        • 三、应用开发

          • 3.1 ArkUI

            • 1 ArkTS语言简介
            • 2 UI 组件-Row 容器介绍
            • 3 UI 组件-Column 容器介绍
            • 4 UI 组件-Text 组件
            • 5 UI 组件-Toggle 组件
            • 6 UI 组件-Slider 组件
            • 7 UI 组件-Animation 组件&Transition 组件
          • 3.2 资料获取

            • 1 OpenHarmony 官方资料
          • 3.3 开发须知

            • 1 Full-SDK替换教程
            • 2 引入和使用三方库
            • 3 HDC调试
            • 4 命令行恢复出厂模式
            • 5 升级App为system权限
          • 3.4 构建第一个应用

            • 1 构建第一个ArkTs应用-HelloWorld
          • 3.5 案例

            • 01 串口调试助手应用案例
            • 02 手写板应用案例
            • 03 数字时钟应用案例
            • 04 WIFI 信息获取应用案例
        • 四、设备开发

          • 4.1 Ubuntu环境开发

            • 01 环境搭建
            • 02 下载源码
            • 03 编译源码
          • 4.2 使用DevEco Device Tool 工具

            • 01 工具简介
            • 02 开发环境的搭建
            • 03 导入SDK
            • 04 HUAWEI DevEco Tool 功能介绍
        • 五、内核外设与接口

          • 5.1 指南
          • 5.2 设备树介绍
          • 5.3 NAPI 入门
          • 5.4 ArkTS入门
          • 5.5 NAPI开发实战演示
          • 5.6 GPIO介绍
          • 5.7 I2C通讯
          • 5.8 SPI通信
          • 5.9 PWM 控制
          • 5.10 串口通讯
          • 5.11 TF卡
          • 5.12 屏幕
          • 5.13 触摸
          • 5.14 Ethernet(以太网)
          • 5.15 M.2 硬盘
          • 5.16 音频
          • 5.17 WIFI & BT
          • 5.18 摄像头
        • 六、资料下载

          • 资料下载
      • M5-R1

        • 一、简介

          • M5-R1 开发文档
        • 二、快速上手

          • 镜像烧录
          • 环境搭建
          • 下载源码
        • 三、外设与接口

          • 3.1 树莓派接口
          • 3.2 GPIO接口
          • 3.3 I2C接口
          • 3.4 SPI通信
          • 3.5 PWM控制
          • 3.6 串口通信
          • 3.7 TF卡插槽
          • 3.8 显示屏
          • 3.9 触摸屏
          • 3.10 音频
          • 3.11 RTC
          • 3.12 以太网
          • 3.13 M.2接口
          • 3.14 MINI PCIE接口
          • 3.15 摄像头
          • 3.16 WIFI蓝牙
        • 四、资料下载

          • 资料下载
    • 开源鸿蒙

      • SC-3568HA

        • 一、简介

          • SC-3568HA简介
        • 二、快速上手

          • OpenHarmony概述
          • 镜像烧录
          • 开发环境准备
          • Hello World应用以及部署
        • 三、应用开发

          • 3.1 ArkUI

            • 第一章 ArkTS语言简介
            • 第二章 UI组件介绍和实际应用(上)
            • 第三章 UI组件介绍和实际应用(中)
            • 第四章 UI组件介绍和实际应用(下)
          • 3.2 拓展

            • 第一章 入门指引
            • 第二章 三方库的引用和使用
            • 第三章 应用编译以及部署
            • 第四章 命令行恢复出厂设置
            • 第五章 系统调试--HDC调试
            • 第六章 APP 稳定性测试
            • 第七章 应用测试
        • 四、设备开发

          • 4.1 环境搭建
          • 4.2 源码下载
          • 4.3 源码编译
        • 五、内核的外设与接口

          • 5.1 树莓派接口
          • 5.2 GPIO 接口
          • 5.3 I2C 接口
          • 5.4 SPI 通信
          • 5.5 PWM 控制
          • 5.6 串口通信
          • 5.7 TF卡插槽
          • 5.8 显示屏
          • 5.9 触摸屏
          • 5.10 音频
          • 5.11 RTC
          • 5.12 以太网
          • 5.13 M.2接口
          • 5.14 MINI PCIE接口
          • 5.15 摄像头
          • 5.16 WIFI蓝牙
          • 5.17 树莓派拓展板
        • 六、资料下载

          • 资料下载
      • M-K1HSE

        • 一、简介

          • 1.1 产品简介
        • 二、快速开始

          • 2.1 调试工具安装
          • 2.2 开发环境搭建
          • 2.3 源码下载
          • 2.4 编译说明
          • 2.5 烧录指南
          • 2.6 APT 更新源
          • 2.7 查看板卡信息
          • 2.8 命令行 LED 和按键测试
          • 2.9 GCC 编译程序
        • 三、应用开发

          • 3.1 基础应用开发

            • 3.1.1 开发环境准备
            • 3.1.2 第一个应用 HelloWorld
            • 3.1.3 开发 HAR 包
          • 3.2 外设应用案例

            • 3.2.1 UART 读写
            • 3.2.2 按键实验
            • 3.2.3 LED 闪烁
        • 四、外设与接口

          • 4.1 标准外设

            • 4.1.1 USB
            • 4.1.2 显示与触摸
            • 4.1.3 以太网
            • 4.1.4 WIFI
            • 4.1.5 蓝牙
            • 4.1.6 TF卡
            • 4.1.7 音频
            • 4.1.8 串口
            • 4.1.9 CAN
            • 4.1.10 RTC
          • 4.2 接口

            • 4.2.1 音频
            • 4.2.2 RS485
            • 4.2.3 显示
            • 4.2.4 触摸
        • 五、系统定制开发

          • 5.1 系统移植
          • 5.2 系统定制
          • 5.3 驱动开发
          • 5.4 系统调试
          • 5.5 OTA 升级
        • 六、资料下载

          • 6.1 资料下载
    • EVS相机

      • CF-NRS1

        • 一、简介

          • 1.1 关于 CF-NRS1
          • 1.2 基于事件的概念
          • 1.3 快速开始
          • 1.4 资源
        • 二、开发

          • 2.1 开发概览

            • 2.1.1 Shimetapi 混合相机 SDK 简介
          • 2.2 环境与API

            • 2.2.1 环境说明
            • 2.2.2 开发 API 说明
          • 2.3 Linux开发

            • 2.3.1 Linux SDK 简介
            • 2.3.2 Linux SDK API
            • 2.3.3 Linux 算法
            • 2.3.4 Linux 算法 API
          • 2.4 服务与Web

            • 2.4.1 EVS 服务器
            • 2.4.2 时间服务器
            • 2.4.3 EVS Web
        • 三、资料下载

          • 3.1 资料下载
        • 四、常见问题

          • 4.1 常见问题
      • CF-CRA2

        • 一、简介

          • 1.1 关于 CF-CRA2
        • 二、资料下载

          • 2.1 资料下载
      • EVS模块

        • 一、相关概念
        • 二、硬件准备与环境配置
        • 三、示例程序使用指南
        • 资料下载
    • AI硬件

      • 1684XB-32T

        • 一、简介

          • AIBOX-1684XB-32简介
        • 二、快速上手

          • 初次使用
          • 网络配置
          • 磁盘使用
          • 内存分配
          • 风扇策略
          • 固件升级
          • 交叉编译
          • 模型量化
        • 三、应用开发

          • 3.1 开发简介

            • Sophgo SDK开发
            • SOPHON-DEMO简介
          • 3.2 大语言模型

            • 部署Llama3示例
            • Sophon LLM_api_server开发
            • 部署MiniCPM-V-2_6
            • Qwen-2-5-VL图片视频识别DEMO
            • Qwen3-chat-DEMO
            • Qwen3-Qwen Agent-MCP开发
            • Qwen3-langchain-AI Agent
          • 3.3 深度学习

            • ResNet(图像分类)
            • LPRNet(车牌识别)
            • SAM(通用图像分割基础模型)
            • YOLOv5(目标检测)
            • OpenPose(人体关键点检测)
            • PP-OCR(光学字符识别)
        • 四、资料下载

          • 资料下载
      • 1684X-416T

        • 一、简介

          • 1.1 产品简介
        • 二、Demo简单操作指引

          • 2.1 智慧监控Demo使用说明
      • RDK-X5

        • 一、简介

          • RDK-X5 硬件简介
        • 二、快速开始

          • RDK-X5 快速开始
        • 三、应用开发

          • 3.1 AI在线模型开发

            • 实验01-接入火山引擎豆包 AI
            • 实验02-图片分析
            • 实验03-多模态视觉分析定位
            • 实验04-多模态图文比较分析
            • 实验05-多模态文档表格分析
            • 实验06-摄像头运用-AI视觉分析
          • 3.2 大语言模型

            • 实验01-语音识别
            • 实验02-语音对话
            • 实验03-多模态图片分析-语音对话
            • 实验04-多模态图片比较-语音对话
            • 实验05-多模态文档分析-语音对话
            • 实验06-多模态视觉运用-语音对话
          • 3.3 40pin-IO开发

            • 实验01-GPIO 输出(LED闪烁)
            • 实验02-GPIO 输入
            • 实验03-按键控制 LED
            • 实验04-PWM 输出
            • 实验05-串口输出
            • 实验06-IIC 实验
            • 实验07-SPI 实验
          • 3.4 USB模块开发使用

            • 实验01-USB 语音模块使用
            • 实验02-声源定位模块使用
          • 3.5 机器视觉技术实战

            • 实验01-打开 USB 摄像头
            • 实验02-颜色识别检测
            • 实验03-手势识别体验
            • 实验04-YOLOv5物体检测
          • 3.6 ROS2基础开发

            • 实验01-搭建环境
            • 实验02-工作包的创建及编译
            • 实验03-运行 ROS2 话题通信节点
            • 实验04-ROS2 相机应用
      • RDK-S100

        • 一、简介

          • 1.1 关于 RDK-S100
        • 二、快速开始

          • 2.1 首次使用
        • 三、应用开发

          • 3.1 AI在线模型开发

            • 3.1.1 接入火山引擎豆包 AI
            • 3.1.2 图片分析
            • 3.1.3 多模态视觉分析定位
            • 3.1.4 多模态图文比较分析
            • 3.1.5 多模态文档表格分析
            • 3.1.6 摄像头运用-AI视觉分析
          • 3.2 大语言模型

            • 3.2.1 语音识别
            • 3.2.2 语音对话
            • 3.2.3 多模态图片分析-语音对话
            • 3.2.4 多模态图片比较-语音对话
            • 3.2.5 多模态文档分析-语音对话
            • 3.2.6 多模态视觉运用-语音对话
          • 3.3 40pin-IO开发

            • 3.3.1 GPIO 输出(LED闪烁)
            • 3.3.2 GPIO 输入
            • 3.3.3 按键控制 LED
            • 3.3.4 PWM 输出
            • 3.3.5 串口输出
            • 3.3.6 IIC 实验
            • 3.3.7 SPI 实验
          • 3.4 USB模块开发使用

            • 3.4.1 USB 语音模块使用
            • 3.4.2 声源定位模块使用
          • 3.5 机器视觉技术实战

            • 3.5.1 打开 USB 摄像头
            • 3.5.2 图像处理基础
            • 3.5.3 目标检测
            • 3.5.4 图像分割
          • 3.6 ROS2基础开发

            • 3.6.1 搭建环境
            • 3.6.2 工作包的创建及编译
            • 3.6.3 运行 ROS2 话题通信节点
            • 3.6.4 ROS2 相机应用
    • 核心板

      • C-3568BQ

        • 一、简介

          • C-3568BQ 简介
      • C-3588LQ

        • 一、简介

          • C-3588LQ 简介
      • GC-3568JBAF

        • 一、简介

          • GC-3568JBAF 简介
      • C-K1BA

        • 一、简介

          • C-K1BA 简介

07 I2C通讯

1 I2C介绍

I2C 总线控制器通过串行数据(SDA)线和串行时钟(SCL)线在连接到总线的器件间传递信息。每个器件都有一个唯一的地址识别(无论是微控制器——MCU、LCD 驱动器、存储器或键盘接口),而且都可以作为一个发送器或接收器(由器件的功能决定)。

I2C总线结构图

关于详细的I2C介绍请参考:

  • CSDN博客文章

2 I2C板卡接口

I2C板卡接口

板子的引脚一共引出了2组I2C接口,分别是i2c-3,i2c-5。

3 I2C使用---命令行的方法

3.1 I2C设备树配置

下面,我们根据设备树章节的介绍,来解析一下I2C3和I2C5的设备树配置。

提示

下文的文件路径:out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/需要先编译码源。

我们先在 rk3568.dtsi 中找到I2C3和I2C5的基础配置内容如下:

i2c3: i2c@fe5c0000 {
    compatible = "rockchip,rk3399-i2c";
    reg = <0x0 0xfe5c0000 0x0 0x1000>;  // 寄存器地址
    clocks = <&cru CLK_I2C3>, <&cru PCLK_I2C3>;  // 时钟配置
    clock-names = "i2c", "pclk";
    interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;  // 中断配置
    pinctrl-names = "default";
    pinctrl-0 = <&i2c3m0_xfer>;  // 引脚复用配置
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";  // 默认禁用
};

i2c5: i2c@fe5e0000 {
    compatible = "rockchip,rk3399-i2c";
    reg = <0x0 0xfe5e0000 0x0 0x1000>;  // 寄存器地址
    clocks = <&cru CLK_I2C5>, <&cru PCLK_I2C5>;  // 时钟配置
    clock-names = "i2c", "pclk";
    interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;  // 中断配置
    pinctrl-names = "default";
    pinctrl-0 = <&i2c5m0_xfer>;  // 引脚复用配置
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";  // 默认禁用
};

再到到rk3568-pinctrl.dtsi中查看I2C的引脚配置:

i2c3m0_xfer: i2c3m0-xfer {
    rockchip,pins =
        /* i2c3_sclm0 - 时钟线 */
        <1 RK_PA1 1 &pcfg_pull_none_smt>,
        /* i2c3_sdam0 - 数据线 */
        <1 RK_PA0 1 &pcfg_pull_none_smt>;
};

i2c5m0_xfer: i2c5m0-xfer {
    rockchip,pins =
        /* i2c5_sclm0 - 时钟线 */
        <3 RK_PB3 4 &pcfg_pull_none_smt>,
        /* i2c5_sdam0 - 数据线 */
        <3 RK_PB4 4 &pcfg_pull_none_smt>;
};

最后找到板级配置文件查看I2C外设的具体配置

在 rk3568-toybrick.dtsi 中,I2C5被使能并配置了传感器设备:

&i2c5 {
	status = "okay";

	gs_mxc6655xa: gs_mxc6655xa@15 {
		status = "okay";
		compatible = "gs_mxc6655xa";
		pinctrl-names = "default";
		pinctrl-0 = <&mxc6655xa_irq_gpio>;
		reg = <0x15>;
		irq-gpio = <&gpio3 RK_PC1 IRQ_TYPE_LEVEL_LOW>;
		irq_enable = <0>;
		poll_delay_ms = <30>;
		type = <SENSOR_TYPE_ACCEL>;
		power-off-in-suspend = <1>;
		layout = <1>;
	};

    mxc6655xa: mxc6655xa@15 {
		status = "disabled";
		compatible = "gs_mxc6655xa";
		pinctrl-names = "default";
		pinctrl-0 = <&mxc6655xa_irq_gpio>;
		reg = <0x15>;
		irq-gpio = <&gpio3 RK_PC1 IRQ_TYPE_LEVEL_LOW>;
		irq_enable = <0>;
		poll_delay_ms = <30>;
		type = <SENSOR_TYPE_ACCEL>;
		power-off-in-suspend = <1>;
		layout = <1>;
	};

    hym8563: hym8563@51 {
		compatible = "haoyu,hym8563";
		reg = <0x51>;
		pinctrl-names = "default";
		pinctrl-0 = <&rtc_int>;

		interrupt-parent = <&gpio0>;
		interrupts = <RK_PD3 IRQ_TYPE_LEVEL_LOW>;
	};
};

相关信息

MXC6655XA 是美新半导体(MEMSIC)推出的一款数字输出三轴加速度计 hym8563是一款I2C接口的实时时钟(RTC)芯片,开发板实际使能的就是这一个设备

在 ‘rk3568-toybrick-x0-linux.dts‘ 中,I2C3被使能并配置了NCA9555:

&i2c3{
    nca9555:nca9555@20{
        reg=<0x20>;  // I2C设备地址为0x20
        compatible = "novosense,nca9555";  // 设备兼容性字符串
        status="okay";  // 设备状态为使能
        gpio-controller;  // 声明为GPIO控制器
        #gpio-cells = <2>;  // GPIO单元格数量
    };
};

相关信息

NCA9555是一款24引脚CMOS器件,提供16位通用并行I2C总线数输入/输出GPIO扩展功能

3.2 操作I2C的常用指令

检查I2C设备:

ls dev/i2c*

测试I2C命令:

I2C tool 是一个开源工具,我们提供的SDK已下载好并进行了交叉编译,编译后已在板卡中生成 i2cdetect、i2cdump、i2cset、i2cget 等测试命令,可以直接在命令行上调试使用:

  • i2cdetect – 用来列举 I2C bus 和上面所有的设备
  • i2cdump – 显示 i2c 设备所有 register 的值
  • i2cget – 读取 i2c 设备某个 register 的值
  • i2cset – 写入 i2c 设备某个 register 的值

3.3 具体功能演示

以下是对上述指令的常见使用示例:

1. 检测当前系统有几组i2c总线:

i2cdetect -l
I2C总线检测

2. 查看i2c-3接口上的设备:

i2cdetect -a 3
I2C设备扫描

UU代表设备地址为20的设备驱动已加载成功,即上文中提到的I2C3上挂载的NCA9555。

3. 读取指定设备的全部寄存器的值:

i2cdump -f -y 3 0x20

(显示i2c3总线上的从设备 0x20 上从 0x00 到 0xff 的所有寄存器地址的值)

I2C寄存器读取

命令成功执行并输出了数据,说明总线3上存在地址为 0x20 的设备,并且基本通信是正常的。

4. 读取指定IIC设备的某个寄存器的值:

i2cget -f -y 3 0x20 0x01

(读取地址为0x20器件中的0x01寄存器值)

I2C单个寄存器读取

4. I2C使用---NAPI方式

资料路径

hap包:\05-开发资料\01-OpenHarmory 开发资料\外设测试APP\HAP\I2C_TEST.hap

工程码源:\05-开发资料\01-OpenHarmory 开发资料\外设测试APP\SRC\I2C_TEST

4.1 内核权限设置

我们在终端中执行以下命令为I2C3增加权限:

chmod 777 /dev/i2c-3

4.2 测试程序讲解

为了直观的看到数据,这里使用逻辑分析仪来捕获发送的I2C信号,用来验证发送数据的正确性。

增加权限后,首先点击打开I2C设备,提示打开成功后,点击开始发送"Shimeta Pi"。

I2C设备打开

点击以后I2C3会每间隔100ms发送一次字符串"Shimeta Pi"。

I2C自动发送

开启自动发送以后,我们使用逻辑分析仪采集数据,1S内采集的数据如下:

I2C数据采集

我们可以看到每隔100ms发送的数据如上图所示。

我们以最后2个数据为例进行查看:

I2C最后两个数据

最后两个数据包为0x50和0x69,分别对应十进制的80和105,查看ASCII表如下:

ASCII表P字符

对应的正是字符串"ShiMeta Pi"的最后2位数据。

同理整理得数据如下表所示:

字符十六进制值十进制值说明
'S'0x5383字符S
'h'0x68104字符h
'i'0x69105字符i
'M'0x4D77字符M
'e'0x65101字符e
't'0x74116字符t
'a'0x6197字符a
' '0x2032空格
'P'0x5080字符P
'i'0x69105字符i

与逻辑分析仪实际观察的值并无差异。

I2C数据对比

4.3 部分代码详解

我们这里使用读取系统节点的方式操作I2C从外设进行读写操作,截取部分代码进行介绍。

我们首先介绍一个函数ioctl,是嵌入式领域中非常重要的一个控制操作函数,你可以想象成一个万能遥控器,对着指定的设备按下对应的按键即可让设备执行相应的操作。

函数原型为:

int ioctl(int fd, unsigned long request, ...);

参数介绍:

fd是文件描述符,指定操作的设备文件;request是请求码。举个例子,你按下空调遥控器以后,遥控器会发送一段ENC红外编码,空调接收到这串编码以后进行解码操作,比如解码后得到"0x9E",空调内部就会看自己的"任务清单",如果看到"0x9E"表示要开启制冷,那就执行制冷操作。相应的,内核中已经定义了一些I2C的操作,我们只需要发送对应的请求码,内核接收到以后就会执行对应的操作。

...是可变的参数,根据不同的请求码发送不同的数据,比如空调指令码"0x88"表示设置温度,那这个数据可能就是温度数据。

再看我们程序中最重要的一个函数:ioctl(i2c_fd, I2C_RDWR, &i2c_data),是不是就不难理解,它告诉Linux内核:"i2c_fd对应的I2C控制器,要执行一个I2C读写操作,具体的数据是&i2c_data"。

我们再看一下Linux内核提供的i2c-dev.h文件,工程中放置于napi_init.cpp的同级目录下,代码如下:

/*
    i2c-dev.h - i2c-bus driver, char device interface

    Copyright (C) 1995-97 Simon G. Vogl
    Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef _LINUX_I2C_DEV_H
#define _LINUX_I2C_DEV_H

#include <linux/types.h>
#include <linux/compiler.h>

/* /dev/i2c-X ioctl commands.  The ioctl's parameter is always an
 * unsigned long, except for:
 *  - I2C_FUNCS, takes pointer to an unsigned long
 *  - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
 *  - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
 */
#define I2C_RETRIES 0x0701 /* number of times a device address should
                be polled when not acknowledging */
#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */

/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
 * are NOT supported! (due to code brokenness)
 */
#define I2C_SLAVE   0x0703 /* Use this slave address */
#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it
                is already in use by a driver! */
#define I2C_TENBIT  0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */

#define I2C_FUNCS   0x0705 /* Get the adapter functionality mask */

#define I2C_RDWR    0x0707 /* Combined R/W transfer (one STOP only) */

#define I2C_PEC    0x0708 /* != 0 to use PEC with SMBus */
#define I2C_SMBUS   0x0720 /* SMBus transfer */


/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
    __u8 read_write;
    __u8 command;
    __u32 size;
    union i2c_smbus_data __user *data;
};

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
    struct i2c_msg __user *msgs;   /* pointers to i2c_msgs */
    __u32 nmsgs;         /* number of i2c_msgs */
};

#define  I2C_RDRW_IOCTL_MAX_MSGS    42

#ifdef __KERNEL__
#define I2C_MAJOR   89    /* Device major number    */
#endif

#endif /* _LINUX_I2C_DEV_H */

我们可以发现主要就是一些宏定义,定义了接收到对应请求码需要执行的操作。

#define I2C_RDWR    0x0707 /* Combined R/W transfer (one STOP only) */
/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
    struct i2c_msg __user *msgs;   /* pointers to i2c_msgs */
    __u32 nmsgs;         /* number of i2c_msgs */
};

我们揪出本工程中用到的I2C_RDWR为例(代码如上),它的功能就是执行一个复杂的组合I2C消息传输。参数是一个指向i2c_rdwr_ioctl_data的指针。具体实现的功能是创建一个数组i2c_msg,包含读写操作以及读写组合操作,过程中只产生一个符合I2C标准的STOP信号。

这时候再看发送I2C数据的NAPI函数就好说了:

// I2C写入一个字节
// 参数:offset (寄存器偏移地址), data (要写入的字节)
// 返回:成功返回0,失败返回-1
static napi_value I2cWriteByte(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_value result;

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 2) {
        napi_create_int32(env, -1, &result);  // 参数错误返回-1
        return result;
    }

    if (!i2c_opened || i2c_fd < 0) {
        napi_create_int32(env, -1, &result);  // 设备未打开返回-1
        return result;
    }

    int32_t offset, data;
    napi_get_value_int32(env, args[0], &offset);
    napi_get_value_int32(env, args[1], &data);

    // 使用ioctl直接操作I2C设备
    struct i2c_rdwr_ioctl_data i2c_data;
    struct i2c_msg msg;
    unsigned char buf[2];

    buf[0] = (unsigned char)offset;  // 寄存器地址
    buf[1] = (unsigned char)data;    // 要写入的数据

    msg.addr = I2C_SLAVE_ADDR;       // I2C设备地址
    msg.flags = 0;                   // 写操作
    msg.len = 2;                     // 数据长度
    msg.buf = buf;                   // 数据缓冲区

    i2c_data.msgs = &msg;
    i2c_data.nmsgs = 1;

    int ret = ioctl(i2c_fd, I2C_RDWR, &i2c_data);

    if (ret < 0) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, I2C_TAG,
                   "I2C write byte failed: %{public}s", strerror(errno));
        napi_create_int32(env, -1, &result);  // 写入失败返回-1
        return result;
    }

    OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, I2C_TAG,
               "I2C write byte success: addr=0x%{public}02X, offset=0x%{public}02X, data=0x%{public}02X",
               I2C_SLAVE_ADDR, offset, data);

    napi_create_int32(env, 0, &result);  // 成功返回0
    return result;
}

我们先看函数I2cWriteByte,具体实现功能的过程如下:

  1. 通过函数napi_get_cb_info和napi_get_value_int32获取从JavaScript端传进来的偏移值(offset)和数据(data)
  2. 再把2个字节的数据写入到数组i2c_data中
  3. 通过ioctl函数告诉内核执行I2C_RDWR操作,数据为i2c_data
  4. 底层会自动驱动物理层产生对应的I2C起始信号发送对应的2个字节数据后产生一个停止信号完成I2C信号传输过程
// I2C写入字符串
// 参数:offset (寄存器偏移地址), data (要写入的字符串)
// 返回:成功返回0,失败返回-1
static napi_value I2cWriteString(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_value result;

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 2) {
        napi_create_int32(env, -1, &result);  // 参数错误返回-1
        return result;
    }

    if (!i2c_opened || i2c_fd < 0) {
        napi_create_int32(env, -1, &result);  // 设备未打开返回-1
        return result;
    }

    int32_t offset;
    napi_get_value_int32(env, args[0], &offset);

    size_t str_length;
    napi_get_value_string_utf8(env, args[1], nullptr, 0, &str_length);

    if (str_length == 0 || str_length > 256) {
        napi_create_int32(env, -1, &result);  // 字符串长度无效返回-1
        return result;
    }

    char* str_buffer = new char[str_length + 1];
    napi_get_value_string_utf8(env, args[1], str_buffer, str_length + 1, &str_length);

    // 使用ioctl直接操作I2C设备,逐字节写入
    struct i2c_rdwr_ioctl_data i2c_data;
    struct i2c_msg msg;
    unsigned char buf[2];
    int success_count = 0;

    for (size_t i = 0; i < str_length; i++) {
        buf[0] = (unsigned char)(offset + i);  // 寄存器地址
        buf[1] = (unsigned char)str_buffer[i]; // 要写入的数据

        msg.addr = I2C_SLAVE_ADDR;             // I2C设备地址
        msg.flags = 0;                         // 写操作
        msg.len = 2;                           // 数据长度
        msg.buf = buf;                         // 数据缓冲区

        i2c_data.msgs = &msg;
        i2c_data.nmsgs = 1;

        int ret = ioctl(i2c_fd, I2C_RDWR, &i2c_data);

        if (ret < 0) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, I2C_TAG,
                       "I2C write string failed at byte %{public}zu: %{public}s", i, strerror(errno));
            break;
        }
        success_count++;
    }

    delete[] str_buffer;

    if (success_count == (int)str_length) {
        OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, I2C_TAG,
                   "I2C write string success: addr=0x%{public}02X, offset=0x%{public}02X, length=%{public}d",
                   I2C_SLAVE_ADDR, offset, success_count);

        napi_create_int32(env, 0, &result);  // 成功返回0
    } else {
        OH_LOG_Print(LOG_APP, LOG_WARN, GLOBAL_RESMGR, I2C_TAG,
                   "I2C write string partial success: %{public}d/%{public}zu bytes written", success_count, str_length);

        napi_create_int32(env, -1, &result);  // 部分失败返回-1
    }

    return result;
}

而I2C写字符串的函数就简单了,获取字符串长度后,执行对应次数的字符传输即可,这里就不再进行解释了。

在 GitHub 上编辑此页
上次更新:
贡献者: fxx, ZSL, hjf
Prev
5.6 GPIO介绍
Next
5.8 SPI通信