
文章目录前言一、核心属性二、I2C的时序三、I2C寻址体系四、Linux中的I2C子系统4.1 I2C子系统相关概念4.2 I2C子系统结构框架图4.3 I2C子系统中重要的结构体4.4 I2C相关函数五、LM75温度传感器5.1 LM75相关参数5.2 LM75驱动编写5.2.1 设备树节点添加5.2.1 驱动代码编写六、BH1750光照传感器6.1 BH1750传感器参数6.2 BH1750驱动编写6.2.1 设备树节点添加6.2.1 驱动代码编写前言I2C全称 Inter-Integrated Circuit集成电路间总线口语简写 IIC、标准书写I²C)1982 年由飞利浦现 NXP 恩智浦 发布专为同一块 PCB 板内芯片短距离低速通信设计是嵌入式最通用的板级串行总线之一。许多传感器都会采用I2C协议来与开发板进行通信。在本片文章中将会介绍I2C的相关概念与一些I2C传感器如LM75、BH1750等。一、核心属性通信类型 同步、半双工、串行逐位传输架构 多主多从支持多个主机抢占总线物理引脚仅 2 根信号线 SCL时钟、 SDA数据驱动电气全部设备引脚为开漏输出总线必须外接4.7kΩ 上拉电阻至 VCC无法主动推高电平速率。模式速率适用场景Standard 标准100 kbps通用传感器、EEPROMFast 快速400 kbps绝大多数消费电子外设Fast Plus1 Mbps高速 ADC、显示屏High Speed3.4 Mbps高端摄像头、高清外设Ultra Fast5 Mbps单向专用场景注大部分传感器都采用100k的速率。二、I2C的时序起始信号 STARTS总线空闲时 SCL、 SDA 均为高 SCL 保持高电平 SDA 由高跳低宣告一次通信开始总线进入忙状态。停止信号 STOPPSCL 保持高电平 SDA 由低跳高通信结束总线回到空闲高电平状态。重复起始 Repeated STARTSr不发送 STOP直接再次触发 START常用于先写寄存器地址、再读数据的场景避免释放总线被其他主机抢占。应答位 ACK / NACK每传输 8bit1 字节后紧跟第 9 位应答位ACK(0)接收方拉低 SDA → 接收成功允许继续发下一字节NACK(1)接收方释放 SDA高电平 → 接收失败 / 结束传输主机发数据从机回复 ACK/NACK从机发数据主机回复 ACK读完最后 1 字节主机必须发 NACK 告知结束。MSB , Most Significant Bit (MSB) first, one byte bit7 send first在多字节数据先发高字节。三、I2C寻址体系7 位标准地址最常用START 后先发 8bit高位先行[bit7~bit17位从机地址][bit0 R/W读写位]R/W0主机写数据到从机R/W1主机读从机数据总线最多容纳 (2^7128) 个地址部分地址为协议保留专用0x00、 0x01 等实际可用 112 个左右从设备。10 位扩展地址兼容 7 位设备支持更多外设高位分两次传输工业多设备场景使用较少。四、Linux中的I2C子系统4.1 I2C子系统相关概念采用传统的字符设备驱动编写方式在处理总线编程时非常复杂假设有M个控制器每个控制器上可能接不同的N个外设那么我们需要编写的驱动将是M*N个如下图所示这样显然是不能被接受的 M*N是一种强耦合的设计方式所以我们提炼出一个中间层让M与中间层耦合 N也与中间层耦合所以就有了如下模型那么我们逐渐将主机控制器驱动与外设传感器驱动进行分离这样主机控制器驱动与外设控制器驱动互不关心外设可以通过访问核心层的通用API进行数据传输主机和外设之间可以进行任意组合。所以后续的I2c子系统框架、 SPI子系统框架、 USB子系统框架等总线驱动都是这种模型。在linux中 I2c驱动最终分为2个部分。①、I2C 总线驱动 I2C 总线驱动就是 SOC 的I2C 控制器驱动也叫做 I2C 适配器驱动。这部分已经有NXP完成可以直接调用②、I2C 设备驱动 I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。 需要咱们自己编写。I2C总线驱动主要是i2c的适配驱动 和platform总线对标同等级别。在dtsi 中可以找到iic的控制器的资源查找到iic控制器的驱动4.2 I2C子系统结构框架图4.3 I2C子系统中重要的结构体总线驱动从属i2c_client类型描述从属关系i2c_adapteri2c适配器(i2c总线)从属i2c_adapteri2c_algorithm描述i2c数据如何收发的设备驱动类型描述从属关系i2c_client一个设备一个这个结构体包含器件的地址中断号设备名.这个结构体包含一个i2c驱动需要的大部分的资源包含i2c_adapter一个指针i2c_drive类型platform_driver,probe,remove,driver(设备树中匹配设备资源)需要自己编写的地方使用 i2c_client,作为函数的参数staticinti2c_device_match(structdevice*dev,structdevice_driver*drv)负责设备和驱动的匹配工作4.4 I2C相关函数函数名功能i2c_add_driver向I2c子系统注册驱动并触发设备匹配流程i2c_del_driver注销I2c驱动释放资源并解除设备绑定i2c_transfer执行单次或多次I2C消息传输适用于复杂时序操作i2c_master_send封装单次I2C消息写操作适用于简单数据发送i2c_master_recv封装单次I2C消息读操作适用于简单数据读取i2c_register_driver自动封装注册与卸载替代i2c_add_driveri2c_del_driver简化驱动代码。module_i2c_driver底层原始注册函数i2c_add_driver内部会调用它五、LM75温度传感器5.1 LM75相关参数购买传感器时厂家一般都会附带相应的参考手册若没有可在立创商城中搜索就可以找到相应手册。参数规格工作温度-55℃ ~ 125℃精度±0.4℃典型值-25℃ ~ 55℃区间分辨率12位0.0625℃供电电压1.4V ~ 5.5V转换时间26ms工作电流15μA 12Hz关断电流0.3μA数字输出接口I2C、SMBus替代型号x755.2 LM75驱动编写5.2.1 设备树节点添加Linux系统中已经有了I2C子系统我们只需要在I2C下添加对应的LM75传感器的相关信息即可注意IMX6ULL中只有两个I2C引出5.2.1 驱动代码编写module_i2c_driver注册LM75传感器设备创建i2c_driver结构体填入module_i2c_driver在probe中 创建杂项设备编写read函数实现温度读取完整代码/** * file led_drv.c * author suaplus * brief iic 温度传感器 * version 0.1 * date 2026-10-16 * * copyright Copyright (c) 2026 * */#includeasm-generic/errno-base.h#includeasm-generic/gpio.h#includeasm/gpio.h#includeasm/uaccess.h#includelinux/export.h#includelinux/fs.h#includelinux/printk.h#includelinux/string.h#includelinux/sysfs.h#includelinux/init.h#includelinux/module.h#includelinux/cdev.h#includeasm/io.h#includelinux/device.h#includelinux/miscdevice.h#includelinux/kdev_t.h#includelinux/kernel.h#includelinux/of.h#includelinux/of_device.h#includelinux/of_address.h#includelinux/of_gpio.h#includelinux/platform_device.h#includelinux/i2c.h/*----------------------设备名------------------------------------------*/#defineDEV_NAMElm75/*------------------------------函数声明-----------------------------------*/staticintmyprob(structi2c_client*,conststructi2c_device_id*);staticintmyremove(structi2c_client*);staticintmyopen(structinode*,structfile*);staticintmyrelease(structinode*,structfile*);staticssize_tmyread(structfile*,char__user*,size_t,loff_t*);/*----------------------------文件操作结构体------------------------------*/staticstructfile_operationsfops{.openmyopen,.readmyread,.releasemyrelease,};staticstructmiscdevicemisc_dev{.nameDEV_NAME,.minorMISC_DYNAMIC_MINOR,.fopsfops,};staticstructi2c_client*pcli;staticstructi2c_device_idid_table[]{{.nameptlm75},{}};staticstructof_device_idof_table[]{{.compatiblept,lm75},{}};staticstructi2c_driverlm75_drv{.probemyprob,.removemyremove,.id_tableid_table,.driver{.nameptlm75,.ownerTHIS_MODULE,.of_match_tableof_table,},};/*---------------------------open函数-----------------------------------------*/staticintmyopen(structinode*pnode,structfile*f){pr_info(open call...\n);return0;}/*---------------------------close函数-----------------------------------------*/staticintmyrelease(structinode*pnode,structfile*f){pr_info(close call...\n);return0;}/*---------------------------read函数-----------------------------------------*/staticssize_tmyread(structfile*f,char__user*data,size_tsize,loff_t*off){//pr_info(open call...\n);charreg_addr0x00;charbuf[2]{0};intret0;i2c_master_send(pcli,reg_addr,1);i2c_master_recv(pcli,buf,2);retcopy_to_user(data,buf,2);if(ret){pr_info(copy_to_user error\n);returnret;}returnsizeof(buf);}/*---------------------------probe函数-----------------------------------------*/staticintmyprob(structi2c_client*cli,conststructi2c_device_id*id){intret0;pclicli;retmisc_register(misc_dev);if(ret0){pr_err(misc_register error\n);returnret;}pr_info(lm75 probe ok\n);return0;}/*---------------------------remove函数-----------------------------------------*/staticintmyremove(structi2c_client*pcli){misc_deregister(misc_dev);return0;}/*---------------------------初始化-----------------------------------------*/module_i2c_driver(lm75_drv);MODULE_LICENSE(GPL);六、BH1750光照传感器6.1 BH1750传感器参数I2C从机地址ADDR引脚决定ADDR引脚电平7位I2C地址8位写地址8位读地址L(接GND)0x230x460x47H(接VCC)0x5C0xB80xB9注意Linux i2c驱动传入7位地址ADDR引脚不可悬空。指令集Opecode直接I2C发送指令名称操作码(十六进制)功能说明Power Down0x00掉电模式无测量芯片休眠Power On0x01上电等待测量命令Reset0x07重置数据寄存器掉电模式下无效连续高分辨率模式0x101lx分辨率典型120ms测量时间连续采集连续高分辨率模式20x110.5lx分辨率典型120ms测量时间连续采集连续低分辨率模式0x134lx分辨率典型16ms测量时间连续采集单次高分辨率模式0x201lx测量完成自动进入掉电单次高分辨率模式20x210.5lx测量完成自动进入掉电单次低分辨率模式0x234lx测量完成自动进入掉电修改测量时间高字节0x40~0x47MTreg高3位修改测量时间低字节0x60~0x7FMTreg低5位测量模式参数测量模式分辨率典型测量时间特点H‑Resolution Mode1 lx120ms推荐使用抑制50/60Hz灯光噪声适合暗光10lxH‑Resolution Mode20.5 lx120ms适合暗光小于10lx最低可测0.11lxL‑Resolution Mode4 lx16ms速度快噪声抑制弱I2C读写时序要点写命令格式起始信号 → 从机地址写位 → ACK → 指令字节 → ACK → 停止信号⚠️每发送1条指令必须发送STOP不支持一条传输多条指令。读数据格式起始信号 → 从机地址读位 → ACK →高8字节→ ACK →低8字节→ NACK → STOP光照数据是16bit大端数据高字节在前低字节在后。照度计算公式H‑Resolution Mode照度(lx) (原始16bit数值) / 1.2H‑Resolution Mode2照度(lx) (原始16bit数值) / 1.2 / 2原始数值 (高字节 8) | 低字节芯片上电状态与状态流转VCC、DVI上电后默认处于Power Down掉电模式。单次测量模式测量结束自动回到Power Down连续模式持续测量。Reset(0x07)指令只清除数据寄存器Power Down模式下Reset指令无效。DVI引脚I2C逻辑参考电压同时是异步复位引脚上电必须保证DVI拉低≥1μs完成内部复位。等待时间代码延时必须遵守模式最大等待时间H‑Resolution / H‑Resolution Mode2180 msL‑Resolution Mode24 ms发送测量指令后必须等待对应最大时间再读取I2C数据否则读出旧值。驱动代码编写注意DVI引脚上电需要至少1μs低电平复位DVI电压范围1.65 ~ VCC。单次模式读完数据芯片自动休眠下一次测量必须重新发送测量命令。Reset命令在Power Down模式下不起作用需要先Power On。I2C最高时钟400KHz。ADDR引脚禁止悬空必须接VCC或者GND。不允许连续发送多条指令而不发送STOP停止信号。读取返回两字节高字节在前不要颠倒大小端。6.2 BH1750驱动编写6.2.1 设备树节点添加6.2.1 驱动代码编写流程与lm75整体是极其相似的这里就不赘述了。#includeasm/delay.h#includeasm/uaccess.h#includelinux/cdev.h#includelinux/device.h#includelinux/fs.h#includelinux/i2c.h#includelinux/init.h#includelinux/miscdevice.h#includelinux/module.h#includelinux/of_gpio.h#includelinux/platform_device.h#includelinux/time.hexternvoidmsleep(unsignedintmsecs);structi2c_client*pbh1750_client;// 驱动层read函数staticssize_tbh1750_read(structfile*fp,char__user*puser,size_tn,loff_t*off){structi2c_msgmsg;unsignedcharbuff[2]{0};// buff[0]high buff[1]:lowshortdata0;intret0;unsignedlongnret0;// 1. 发送通电指令buff[0]0x01;msg.addrpbh1750_client-addr;msg.flags0;msg.bufbuff;msg.len1;retpbh1750_client-adapter-algo-master_xfer(pbh1750_client-adapter,msg,1);if(ret0){pr_info([driver]:master_xfer_power_on failed: %d\n,ret);returnret;}// 2. 发送连续高分辨率测量模式buff[0]0x10;msg.addrpbh1750_client-addr;msg.flags0;msg.bufbuff;msg.len1;retpbh1750_client-adapter-algo-master_xfer(pbh1750_client-adapter,msg,1);if(ret0){pr_info([driver]:master_xfer_mode failed: %d\n,ret);returnret;}// 3. 等待测量完成msleep(180);// 4. 读取2个字节光照强度msg.addrpbh1750_client-addr;msg.flags|I2C_M_RD;msg.bufbuff;msg.len2;retpbh1750_client-adapter-algo-master_xfer(pbh1750_client-adapter,msg,1);if(ret0){pr_info([driver]:master_xfer_read failed: %d\n,ret);returnret;}// 5. 光强原始数据data(buff[0]8)|buff[1];// 6. 拷贝给用户层nretcopy_to_user(puser,data,sizeof(data));if(nret){pr_info([driver]:copy_to_user failed!\n);returnnret;}returnsizeof(data);}// 驱动层操作函数集合staticstructfile_operationsfops{.readbh1750_read,};// 杂项设备结构体 /dev/misc_bh1750staticstructmiscdevicemisc_bh1750{.minorMISC_DYNAMIC_MINOR,.namemisc_bh1750,.fopsfops,};// probe函数staticintbh1750_probe(structi2c_client*pclient,conststructi2c_device_id*pdevice){intret0;// 1. 注册杂项设备(10 自动生成设备节点)retmisc_register(misc_bh1750);if(ret){pr_info([driver]:misc_register failed!\n);returnret;}// 2. i2c_client赋值pbh1750_clientpclient;// 3. 打印pr_info(bh1750_probe successful!\n);return0;}// remove函数staticintbh1750_remove(structi2c_client*pclient){// 1. 注销杂项设备misc_deregister(misc_bh1750);// 2. 打印pr_info(bh1750_remove successful!\n);return0;}// idtable匹配structi2c_device_iddevice_table[]{{.nameputebh1750},{},};// 设备树匹配structof_device_idbh1750_table[]{{.compatiblepute,bh1750},{},};// i2c_driver结构体staticstructi2c_driverbh1750_driver{.probebh1750_probe,.removebh1750_remove,.driver{.nameputebh1750,.ownerTHIS_MODULE,.of_match_tablebh1750_table,},.id_tabledevice_table,};module_i2c_driver(bh1750_driver);// i2c_add_driver i2c_del_driverMODULE_LICENSE(GPL);MODULE_AUTHOR(PUTE);