Purple Pi OH2 开发板 I2C 驱动入门教程(基于 HYM8563 RTC) 电子说
本文基于触觉智能Purple Pi OH2 开发板讲解和调试,聚焦I2C驱动开发核心知识点,以HYM8563 RTC芯片为例,从协议原理到驱动源码全维度解析。
一、I2C 基本概念
1.1 什么是 I2C?
I2C(Inter-Integrated Circuit)是一种两线串行总线协议,由 Philips(现 NXP)在 1980 年代发明,广泛用于连接低速外设:
•SDA(Serial Data):数据线,双向传输,开漏输出
•SCL(Serial Clock):时钟线,由主设备产生,开漏输出
•外部需接上拉电阻(典型 4.7kΩ)将总线拉高
1.2 I2C 总线特点
| 特性 | 说明 |
| 线数 | 2 根(SDA + SCL),节省 PCB 空间 |
| 设备数量 | 最多 127 个(7 位地址)或 1023 个(10 位地址) |
| 速率 | 标准模式 100 kHz,快速模式 400 kHz,高速模式 3.4 MHz |
| 通信方式 | 主从架构,主设备(Master)发起通信并产生时钟 |
| 数据传输 | MSB 优先,每个字节后跟一个 ACK/NACK 位 |
1.3 I2C 与 SPI 的简要对比
| 特性 | I2C | SPI |
| 引脚数 | 2(SDA + SCL) | 3+N(SCK + MOSI + MISO + N×CS) |
| 速率 | ~100 kHz–3.4 MHz | ~MHz–几十 MHz |
| 寻址 | 软件寻址(7/10 位地址) | 硬件片选(每个设备一根 CS) |
| 多主支持 | 支持(冲突检测) | 不支持 |
| 功耗 | 较低(开漏+上拉) | 较高(推挽输出) |
| 适合场景 | 低复杂度、节省引脚的外设 | 高速、大批量数据传输 |
1.4 RK3576 I2C 资源
RK3576 提供 10 路 I2C 控制器(I2C0 ~ I2C9),每个控制器可通过 pinctrl 选择多个引脚复用(mux)选项:
i2c0 = &i2c0; // 通常用于 PMIC (RK806)i2c1 = &i2c1; // 通用i2c2 = &i2c2; // 本教程使用:RTC (hym8563)i2c3 = &i2c3; // Audio Codec...i2c9 = &i2c9;
二、I2C 通信协议
2.1 I2C 协议交互流程
I2C 总线上一次完整的数据传输遵循以下流程:
起始条件(S) → 7位从机地址 → R/W位 → ACK → 8位数据 → ACK → 停止条件(P)
以下是主设备与从设备之间的完整协议交互序列图(含起始条件、地址发送、数据读写、应答和停止条件):

协议时序参数表:
| 阶段 | 时钟周期数 | 说明 |
| S (起始条件) | 0 | SCL=High,SDA 下降沿 |
| 7位地址 | 7 个 SCL | MSB (A6) 先发,LSB (A0) 最后 |
| R/W 位 | 1 个 SCL | 0 = 写操作,1 = 读操作 |
| ACK | 1 个 SCL(第9个) | 从机拉低 SDA 应答 |
| 数据字节 | 8 个 SCL | 数据位 D7~D0,MSB 先发 |
| P (停止条件) | 0 | SCL=High,SDA 上升沿 |
数据帧结构 — I2C 总线上一个完整数据帧的比特布局(共 19 个 SCL 时钟周期):
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐│ S │A6 │A5 │A4 │A3 │A2 │A1 │A0 │R/W│ACK│D7 │D6 │D5 │D4 │D3 │D2 │D1 │D0 │ P ││ ↓ │◄──────── 从机地址7bit ────────►│ │◄──────── 数据8bit────────────►│ ↑ │└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ S ▲──── SCL 时钟采样 SDA ────▲ P起始 每个时钟1位数据 停止条件 条件
2.2 起始条件与停止条件
这是 I2C 总线最核心的时序特征 — SDA 在 SCL 高电平时跳变,此信号在数据传输阶段被严格禁止。以下是两个关键边沿的序列图:

规则:在数据传输阶段(S 和 P 之间),SDA 只能在 SCL 低电平时变化,SCL 高电平时 SDA 必须保持稳定 — 这是 I2C 协议的根本规则。
2.3 数据有效性规则
以下是 SCL 与 SDA 配合关系的序列图:

2.4 应答机制(ACK/NACK)
每个字节传输完成后,接收方必须在第 9 个 SCL 时钟周期驱动 SDA 发送应答位。以下是应答机制的序列图:

| 应答 | SDA 电平 | 发送方 | 含义 |
| ACK | Low | 接收方驱动 | 接收成功,继续发送下一字节 |
| NACK | High | 上拉电阻拉高 | 接收失败或传输结束 |
2.5 I2C 设备地址
I2C 7 位地址由芯片厂商分配(高位)和硬件引脚电平(低位)共同决定,在总线上地址左移 1 位后附加 R/W 位发送:
7'h51 (HYM8563) → 左移1位 → 0xA2 (写) / 0xA3 (读) bit: 6 5 4 3 2 1 0 R/W 1 0 1 0 0 0 1 0/1 └──────── 0xA2 (写) / 0xA3 (读)
| 设备 | 7位地址 | 写地址(<<1) | 典型用途 |
| HYM8563 RTC | 0x51 | 0xA2 | 本教程 RTC 芯片 |
| PCF8563 RTC | 0x51 | 0xA2 | NXP 兼容 RTC |
| EEPROM (AT24Cxx) | 0x50 | 0xA0 | 存储 |
| ES8316 Audio Codec | 0x18 | 0x30 | 音频编解码 |
| RK806 PMIC | 0x23 | 0x46 | 电源管理 |
三、HYM8563 RTC 硬件原理
3.1 HYM8563 芯片简介
HYM8563 是 Haoyu Microelectronics 生产的低功耗 RTC(实时时钟)芯片,与 NXP PCF8563 兼容,通过 I2C 接口与主控通信。
关键特性:
•I2C 总线接口(最大 400 kHz)
•计时精度依赖外部 32768 Hz 晶振
•内置秒、分、时、日、星期、月、年计数(BCD 格式)
•具有闹钟(Alarm)和定时器(Timer)功能
•可编程 CLKOUT 引脚输出时钟(32768 / 1024 / 32 / 1 Hz)
•低功耗:典型 0.25 μA(VDD=3.0V,定时器关闭)
•宽电压:1.0V ~ 5.5V
3.2 引脚定义与典型电路
应用电路:

3.3 寄存器映射表(核心)
HYM8563 内部寄存器共 16 个(0x00 ~ 0x0f),其中时间寄存器从 0x02 开始,全部采用 BCD 编码:
| 地址 | 名称 | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | 取值范围 |
| 0x00 | CTL1 | TEST | — | STOP | — | TESTC | — | — | — | — |
| 0x01 | CTL2 | — | — | TI/TP | AF | TF | AIE | TIE | — | — |
| 0x02 | SEC | VL | 秒(BCD 十位) | 秒(BCD 个位) | 00–59 | |||||
| 0x03 | MIN | — | 分(BCD 十位) | 分(BCD 个位) | 00–59 | |||||
| 0x04 | HOUR | — | — | 时(BCD 十位) | 时(BCD 个位) | 00–23 | ||||
| 0x05 | DAY | — | — | 日(BCD 十位) | 日(BCD 个位) | 01–31 | ||||
| 0x06 | WEEKDAY | — | — | — | — | — | 星期 | 00–06 | ||
| 0x07 | MONTH | C | — | — | 月(BCD 十位) | 月(BCD 个位) | 01–12 | |||
| 0x08 | YEAR | 年(BCD 千位+百位) | 年(BCD 十位) | 年(BCD 个位) | 00–99 | |||||
| 0x09 | ALM_MIN | AE | — | 闹钟分(BCD) | 00–59 | |||||
| 0x0A | ALM_HOUR | AE | — | — | 闹钟时(BCD) | 00–23 | ||||
| 0x0B | ALM_DAY | AE | — | — | 闹钟日(BCD) | 01–31 | ||||
| 0x0C | ALM_WEEK | AE | — | — | — | 闹钟星期 | 00–06 | |||
| 0x0D | CLKOUT | OE | — | — | — | — | FD1 | FD0 | — | — |
| 0x0E | TMR_CTL | TE | — | — | — | — | TD1 | TD0 | — | — |
| 0x0F | TMR_CNT | 定时器倒计数值 | 0–255 |
关键位说明:
| 位 | 所在寄存器 | 功能 |
| STOP (Bit 5) | CTL1 (0x00) | 1 = RTC 停止计数,0 = 正常运行 |
| VL (Bit 7) | SEC (0x02) | 0 = 时间有效,1 = 时间无效(掉电或晶振停振) |
| C (Bit 7) | MONTH (0x07) | 世纪位— 但驱动实测不可靠,内核限制为 2000–2099 |
| AE (Bit 7) | ALM_* (0x09~0x0C) | 1 = 该闹钟字段不参与匹配(即忽略此项) |
| OE (Bit 7) | CLKOUT (0x0D) | 1 = 使能 CLKOUT 输出 |
| FD[1:0] | CLKOUT (0x0D) | 00=32768Hz, 01=1024Hz, 10=32Hz, 11=1Hz |
| TE (Bit 7) | TMR_CTL (0x0E) | 1 = 使能定时器 |
| TD[1:0] | TMR_CTL (0x0E) | 定时器时钟源选择 |
3.4 BCD 编码说明
时间寄存器使用 BCD(Binary-Coded Decimal)编码,即每个十进制数字用 4 位二进制表示,驱动中使用bcd2bin() / bin2bcd() 完成转换:
BCD 编码示例:时间 1256 秒(0x56) = 0101 0110 → 高4位(5) + 低4位(6) = 56 秒 分(0x34) = 0011 0100 → 高4位(3) + 低4位(4) = 34 分 时(0x12) = 0001 0010 → 高4位(1) + 低4位(2) = 12 时驱动中的 BCD 转换函数(定义在 linux/bcd.h): bcd2bin(0x56) = 5*10 + 6 = 56 // BCD → 整数 bin2bcd(34) = (3<<4) | 4 = 0x34 // 整数 → BCD
四、I2C 设备树配置
4.1 使能 I2C 控制器并添加 RTC 设备
在设备树文件(如ido-evb7609-v1a.dtsi)中配置 I2C2 控制器及 HYM8563 子设备:
/* I2C2 - 挂载 HYM8563 RTC */&i2c2 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c2m0_xfer>; hym8563: hym8563@51 { compatible = "haoyu,hym8563"; // 驱动匹配的关键字符串 reg = <0x51>; // I2C 7 位从机地址 (7'h51) #clock-cells = <0>; // 可作为时钟提供者 clock-frequency = <32768>; // CLKOUT 输出频率 clock-output-names = "hym8563"; // 时钟输出名称 interrupt-parent = <&gpio0>; // 中断控制器 interrupts = ; // 中断引脚与触发方式 wakeup-source; // 支持系统唤醒 };};
各属性详解:
| 属性 | 作用 | 说明 |
| compatible | 驱动匹配 | 内核据此查找of_device_id 表中匹配的驱动 |
| reg | I2C 地址 | 7 位地址左对齐写入,0x51 即器件地址 |
| #clock-cells | 时钟接口 | 0 表示直接输出固定频率时钟 |
| clock-frequency | 输出频率 | 仅描述信息,实际由 CLKOUT 寄存器控制 |
| interrupts | 中断配置 | 指定 INT 引脚对应的 GPIO 和触发类型 |
| wakeup-source | 唤醒标记 | 系统 Suspend 时 RTC 闹钟可唤醒 SoC |
4.2 pinctrl 引脚复用配置
I2C2 的 SDA/SCL 引脚可以复用到不同的 GPIO 组,需要根据实际硬件原理图选择对应的 mux:
&pinctrl { i2c2 { /* I2C2 M0 组:SCL = GPIO1_B4, SDA = GPIO1_B3 */ i2c2m0_xfer: i2c2m0-xfer { rockchip,pins = <1 RK_PB4 9 &pcfg_pull_none>, /* i2c2_scl_m0 */ <1 RK_PB3 9 &pcfg_pull_none>; /* i2c2_sda_m0 */ }; /* I2C2 M1 组:备用引脚(如需要) */ i2c2m1_xfer: i2c2m1-xfer { rockchip,pins = <1 RK_PB4 10 &pcfg_pull_none>, /* i2c2_scl_m1 */ <1 RK_PB3 10 &pcfg_pull_none>; /* i2c2_sda_m1 */ }; };};
pinctrl 字段说明:
•<1 RK_PB4 9 &pcfg_pull_none> =
•每个 SoC 的 function 编号不同,需参考 RK3576 TRM(Technical Reference Manual) 的 GPIO 复用表格
4.3 查看当前引脚复用状态
# 查看 GPIO1_B3 (I2C2_SDA) 当前功能cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep i2c2# 或者检查 pinctrl 设置cat /sys/kernel/debug/gpio | grep i2c
五、hym8563 驱动源码分析
5.1 驱动文件概览
源文件:kernel-6.1/drivers/rtc/rtc-hym8563.c(643 行)
编译配置:
// Kconfigconfig RTC_DRV_HYM8563 tristate "Haoyu Microelectronics HYM8563" depends on OF help Say Y to enable support for the HYM8563 I2C RTC chip.// Makefileobj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
5.2 数据结构与寄存器定义
struct hym8563 { struct i2c_client *client; // I2C 客户端(关联设备和适配器) struct rtc_device *rtc; // RTC 核心框架设备#ifdef CONFIG_COMMON_CLK struct clk_hw clkout_hw; // CLKOUT 时钟提供者#endif int alarm_or_timer_irq; // 0=闹钟模式, 1=定时器模式 int alarm_tm_sec; // 闹钟秒数(定时器模式用)};
寄存器宏定义(驱动中命名对应数据手册):
| 宏 | 值 | 对应寄存器 |
| HYM8563_CTL1 | 0x00 | 控制寄存器 1 |
| HYM8563_CTL2 | 0x01 | 控制寄存器 2 |
| HYM8563_SEC | 0x02 | 秒寄存器(含 VL 位) |
| HYM8563_MIN | 0x03 | 分钟寄存器 |
| HYM8563_HOUR | 0x04 | 小时寄存器 |
| HYM8563_DAY | 0x05 | 日寄存器 |
| HYM8563_WEEKDAY | 0x06 | 星期寄存器 |
| HYM8563_MONTH | 0x07 | 月寄存器(含 C 世纪位) |
| HYM8563_YEAR | 0x08 | 年寄存器 |
| HYM8563_ALM_MIN | 0x09 | 闹钟分钟 |
| HYM8563_CLKOUT | 0x0D | 时钟输出控制 |
| HYM8563_TMR_CTL | 0x0E | 定时器控制 |
| HYM8563_TMR_CNT | 0x0F | 定时器计数值 |
5.3 核心函数分析:读取时间
static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm){ struct i2c_client *client = to_i2c_client(dev); u8 buf[7]; int ret; // ---- I2C 通信:一次读取 7 个时间寄存器 (SEC ~ YEAR) ---- // SMBus 协议:发送寄存器地址 0x02 → 连续读取 7 个字节 ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf); if (ret < 0) return ret; // ---- BCD 解码:将 BCD 格式转为内核 tm 结构体 ---- tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK); // buf[0]=SEC tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK); // buf[1]=MIN tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK); // buf[2]=HOUR tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK); // buf[3]=DAY tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); // buf[4]=WEEKDAY, 0=周日 tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; // buf[5]=MONTH, -1=0基 tm->tm_year = bcd2bin(buf[6]) + 100; // buf[6]=YEAR, +100=2000基 return 0;}
I2C 读写复合时序(i2c_smbus_read_i2c_block_data 的实际总线波形):

数据结构转换关系:

5.4 核心函数分析:写入时间
static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm){ struct i2c_client *client = to_i2c_client(dev); u8 buf[7]; int ret; // ---- 年份范围检查 ---- // 硬件世纪位不可靠,驱动限制:2000–2099 if (tm->tm_year < 100 || tm->tm_year >= 200) return -EINVAL; // ---- BCD 编码:将 tm 结构体转为 BCD 格式 ---- buf[0] = bin2bcd(tm->tm_sec); buf[1] = bin2bcd(tm->tm_min); buf[2] = bin2bcd(tm->tm_hour); buf[3] = bin2bcd(tm->tm_mday); buf[4] = bin2bcd(tm->tm_wday); buf[5] = bin2bcd(tm->tm_mon + 1); // +1: 0基→1基 buf[6] = bin2bcd(tm->tm_year - 100); // -100: 2000基→0基 // ---- 写入时序:STOP → 写时间 → 清除STOP ---- // ① 先设置 STOP 位,暂停 RTC 计数(防止写入过程中时间变化) ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, HYM8563_CTL1_STOP); if (ret < 0) return ret; // ② 一次性写入 7 个时间寄存器 ret = i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf); if (ret < 0) return ret; // ③ 清除 STOP 位,RTC 恢复计数(从新设定的时间开始) ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0); if (ret < 0) return ret; return 0;}
写入时序要点 — 为什么需要先暂停 RTC?

5.5 Probe 函数分析
static int hym8563_probe(struct i2c_client *client){ struct hym8563 *hym8563; int ret; struct rtc_time tm_read, tm = { .tm_wday = 0, .tm_year = 121, // 2021年 .tm_mon = 0, // 1月 .tm_mday = 1, .tm_hour = 12, .tm_min = 0, .tm_sec = 0, }; // ① 分配私有数据结构 hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL); if (!hym8563) return -ENOMEM; // ② 分配 RTC 核心设备 hym8563->rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(hym8563->rtc)) return PTR_ERR(hym8563->rtc); hym8563->client = client; i2c_set_clientdata(client, hym8563); // ③ 初始化硬件:清除STOP/中断/标志位 ret = hym8563_init_device(client); if (ret) { dev_err(&client->dev, "could not init device, %dn", ret); return ret; } // ④ 注册中断(仅当 DT 中配置了 interrupt 时才执行) if (client->irq > 0) { ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, hym8563_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, hym8563); // ... 错误处理 ... } // ⑤ 检查 VL 位 — 判断 RTC 时间是否有效 ret = i2c_smbus_read_byte_data(client, HYM8563_SEC); dev_info(&client->dev, "rtc information is %sn", (ret & HYM8563_SEC_VL) ? "invalid" : "valid"); // ⑥ 如果时间无效或超出范围,重置为默认时间 (2021-01-01 1200) hym8563_rtc_read_time(&client->dev, &tm_read); if ((ret & HYM8563_SEC_VL) || (tm_read.tm_year < 70) || (tm_read.tm_year > 200) || (rtc_valid_tm(&tm_read) != 0)) hym8563_rtc_set_time(&client->dev, &tm); // ⑦ 注册 RTC 设备到 Linux RTC 核心框架 hym8563->rtc->ops = &hym8563_rtc_ops; clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, hym8563->rtc->features); return devm_rtc_register_device(hym8563->rtc);}
Probe 完整流程图:

5.6 初始化设备分析
static int hym8563_init_device(struct i2c_client *client){ int ret; ret = i2c_smbus_read_byte_data(client, HYM8563_CTL1); // ... error check ... // 清除 STOP 位(如果 RTC 之前被停止过) ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0); if (ret < 0) return ret; ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); if (ret < 0) return ret; // 禁用闹钟中断 (AIE) 和定时器中断 (TIE) ret &= ~HYM8563_CTL2_AIE; ret &= ~HYM8563_CTL2_TIE; // 清除任何待处理的闹钟/定时器标志 if (ret & HYM8563_CTL2_AF) ret &= ~HYM8563_CTL2_AF; if (ret & HYM8563_CTL2_TF) ret &= ~HYM8563_CTL2_TF; // 复位定时器(写回配置) i2c_smbus_write_byte_data(client, HYM8563_TMR_CNT, 0); i2c_smbus_write_byte_data(client, HYM8563_TMR_CTL, HYM8563_TMR_CFG); // HYM8563_TMR_CFG = TE(使能) | 01(时钟源=64Hz) // ↑每 1/64 秒减1 return i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);}
5.7 中断处理分析
static irqreturn_t hym8563_irq(int irq, void *dev_id){ struct hym8563 *hym8563 = (struct hym8563 *)dev_id; struct i2c_client *client = hym8563->client; int data, ret; rtc_lock(hym8563->rtc); // 读取 CTL2,获取中断状态 data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); // 清除闹钟标志 (AF) 和定时器标志 (TF) data &= ~HYM8563_CTL2_AF; data &= ~HYM8563_CTL2_TF; // 复位定时器计数器 i2c_smbus_write_byte_data(client, HYM8563_TMR_CNT, 0); // 写回清除后的 CTL2 ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);out: rtc_unlock(hym8563->rtc); return IRQ_HANDLED;}// 中断注册参数:// IRQF_TRIGGER_LOW — INT 引脚为低电平触发// IRQF_ONESHOT — 线程化中断,中断线在handler运行期间保持屏蔽
5.8 闹钟与定时器双模式
这是一个巧妙的设计:HYM8563 硬件同时支持闹钟(Alarm)和定时器(Timer),驱动根据唤醒间隔自动选择:
static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm){ // ... interval = alarm - now; // 计算到闹钟的秒数 if (interval < HYM8563_TMR_MAXCNT) { // < 255 秒 // ---- 定时器模式(1秒精度) ---- // 使用硬件定时器做短间隔唤醒 // 精度高,但最长只能 255 秒 hym8563->alarm_or_timer_irq = 1; i2c_smbus_write_byte_data(client, HYM8563_TMR_CNT, (u8)interval); } else { // ---- 闹钟模式(分钟精度) ---- // 使用硬件闹钟做长间隔唤醒 // 没有秒级精度(tm_sec 被强制设为 0) // 但可以设置任意未来的时间 hym8563->alarm_or_timer_irq = 0; alm_tm->tm_sec = 0; // 秒强制归零 } // ...}
双模式选择流程图:

5.9 CLKOUT 时钟提供者
HYM8563 的 CLKOUT 引脚可以输出 32768/1024/32/1 Hz 的方波,驱动通过 Common Clock Framework 注册为时钟提供者:
#ifdef CONFIG_COMMON_CLKstatic int clkout_rates[] = { 32768, // FD[1:0] = 00 1024, // FD[1:0] = 01 32, // FD[1:0] = 10 1, // FD[1:0] = 11};static int hym8563_clkout_control(struct clk_hw *hw, bool enable){ // 读 CLKOUT 寄存器 → 设置/清除 OE 位 → 写回 int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); if (enable) ret |= HYM8563_CLKOUT_ENABLE; else ret &= ~HYM8563_CLKOUT_ENABLE; return i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, ret);}// 驱动标记为 CLK_IS_CRITICAL — 防止系统关闭 CLKOUT// 某些 SoC 依赖此时钟源,关闭会导致系统问题init.flags = CLK_IS_CRITICAL;#endif
5.10 I2C 驱动注册
// 设备树匹配表 — 与 dts 中的 compatible 关联static const struct of_device_id hym8563_dt_idtable[] = { { .compatible = "haoyu,hym8563" }, {},};MODULE_DEVICE_TABLE(of, hym8563_dt_idtable);// I2C 设备 ID 表(用于非设备树平台)static const struct i2c_device_id hym8563_id[] = { { "hym8563", 0 }, {},};MODULE_DEVICE_TABLE(i2c, hym8563_id);// I2C 驱动结构体static struct i2c_driver hym8563_driver = { .driver = { .name = "hym8563", .of_match_table = hym8563_dt_idtable, }, .probe = hym8563_probe, .id_table = hym8563_id,};module_i2c_driver(hym8563_driver);MODULE_DESCRIPTION("Haoyu HYM8563 RTC driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Haoyu Microelectronics");
本文完整覆盖了I2C协议原理、HYM8563硬件设计、设备树配置及驱动源码核心逻辑,是嵌入式开发者学习I2C驱动开发的实用参考。基于Purple Pi OH2开发板的实战场景,所有代码和配置均可直接落地调试。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !