尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Linux驱动-I2C通信-FT5X06驱动程序部分编写

Linux驱动-I2C通信-FT5X06驱动程序部分编写
📅 发布时间:2026/7/22 14:25:54

提示:基于之前I2C 基础知识了解:设备树配置、配置GPIO、I2C驱动框架的了解,这里在此基础上进行I2C 通信。

文章目录

  • 前言
  • 一、参考资料
  • 二、知识点分析
    • 搞清楚需求-IIC驱动程序
    • IIC 用到的api
      • i2c_master_send / i2c_master_recv
      • i2c_transfer
      • 三大函数核心区别对照
    • 结构体-i2c_msg-i2c_client
    • IIC 读操作
    • IIC 写操作
    • IIC 调用
  • 三、驱动源码实现-实现IIC读写
  • 四、驱动验证
  • 总结

前言

在之前I2C基础上进行I2C 通信,打通通信知识点。

一、参考资料

之前基础笔记:
驱动-I2C-客户端代码编写-编写设备树

Linux驱动-i2c 驱动框架编写

Linux驱动-IIC完善FT5X06设备节点和驱动

关联内容:
Linux驱动开发—内核I2C驱动详解

IIC驱动

二、知识点分析

搞清楚需求-IIC驱动程序

如上,我们最终目的是什么,就是在之前IIC 驱动框架的基础上进行IIC通讯,让IIC工作起来。
那么如何验证,在之前程序基础上进行了IIC 读写操作并调用读写来验证IIC 。

IIC 用到的api

这里仅从IIC 通讯案例中涉及到的api 来进行讲解。

i2c_master_send / i2c_master_recv

路径:kernel/include/linux/i2c.h

具体源码如下:

/** * i2c_master_send - issue a single I2C message in master transmit mode * @client: Handle to slave device * @buf: Data that will be written to the slave * @count: How many bytes to write, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes written. */staticinlineinti2c_master_send(conststruct i2c_client*client,constchar*buf,intcount){returni2c_transfer_buffer_flags(client,(char*)buf,count,0);};/** * i2c_master_recv - issue a single I2C message in master receive mode * @client: Handle to slave device * @buf: Where to store data read from slave * @count: How many bytes to read, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes read. */staticinlineinti2c_master_recv(conststruct i2c_client*client,char*buf,intcount){returni2c_transfer_buffer_flags(client,buf,count,I2C_M_RD);};

其实就是单收发报文,然后都调用了i2c_transfer_buffer_flags

i2c_transfer

如上分析i2c_master_send / i2c_master_recv都是指向i2c_transfer_buffer_flags,那么我们看看i2c_transfer_buffer_flags方法函数源码,如下:

路径:kernel/drivers/i2c/i2c-core-base.c

/** * i2c_transfer_buffer_flags - issue a single I2C message transferring data * to/from a buffer * @client: Handle to slave device * @buf: Where the data is stored * @count: How many bytes to transfer, must be less than 64k since msg.len is u16 * @flags: The flags to be used for the message, e.g. I2C_M_RD for reads * * Returns negative errno, or else the number of bytes transferred. */inti2c_transfer_buffer_flags(conststruct i2c_client*client,char*buf,intcount,u16 flags){intret;struct i2c_msg msg={.addr=client->addr,.flags=flags|(client->flags&I2C_M_TEN),.len=count,.buf=buf,};ret=i2c_transfer(client->adapter,&msg,1);/* * If everything went ok (i.e. 1 msg transferred), return #bytes * transferred, else error code. */return(ret==1)?count:ret;}EXPORT_SYMBOL(i2c_transfer_buffer_flags);

继续看i2c_transfer源码,如下

/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. */inti2c_transfer(struct i2c_adapter*adap,struct i2c_msg*msgs,intnum){intret;/* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that's the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it's unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */if(adap->algo->master_xfer){#ifdefDEBUGfor(ret=0;ret<num;ret++){dev_dbg(&adap->dev,"master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",ret,(msgs[ret].flags&I2C_M_RD)?'R':'W',msgs[ret].addr,msgs[ret].len,(msgs[ret].flags&I2C_M_RECV_LEN)?"+":"");}#endifif(in_atomic()||irqs_disabled()){ret=i2c_trylock_bus(adap,I2C_LOCK_SEGMENT);if(!ret)/* I2C activity is ongoing. */return-EAGAIN;}else{i2c_lock_bus(adap,I2C_LOCK_SEGMENT);}ret=__i2c_transfer(adap,msgs,num);i2c_unlock_bus(adap,I2C_LOCK_SEGMENT);returnret;}else{dev_dbg(&adap->dev,"I2C level transfers not supported\n");return-EOPNOTSUPP;}}EXPORT_SYMBOL(i2c_transfer);

三大函数核心区别对照

综合上面源码,看注释就可以明白区别,这里直接对照表如下:

对比维度i2c_master_send / i2c_master_recvi2c_transfer
支持消息数量仅单条 i2c_msg支持多条连续 msg 数组
总线时序单次传输结束必发 STOP,断开总线多条消息中间无 STOP,连续占用总线
传入句柄struct i2c_client(设备)struct i2c_adapter(硬件总线)
读写控制固定纯写 / 纯读,无法混合每条 msg 可独立配置读写标志
使用门槛简单,新手友好稍复杂,需要手动构造 i2c_msg
寄存器读取❌ 无法实现✅ 唯一能实现先写后读时序
典型场景简单单发、单纯只读触摸 / 传感器寄存器读写、多段连续 I2C 报文

所以 大多数场景我们用的是i2c_transfer方法,只是需要自己去拼接i2c_msg结构体。

struct i2c_msg msg={.addr=client->addr,.flags=flags|(client->flags&I2C_M_TEN),.len=count,.buf=buf,};

结构体-i2c_msg-i2c_client

路径:include/uapi/linux/i2c.h,i2c_msg这个是永远传输i2c 数据,读写中会用到,可以理解为通信介质。

struct i2c_msg{__u16 addr;/* slave address */__u16 flags;#defineI2C_M_RD0x0001/* read data, from slave to master *//* I2C_M_RD is guaranteed to be 0x0001! */#defineI2C_M_TEN0x0010/* this is a ten bit chip address */#defineI2C_M_DMA_SAFE0x0200/* the buffer of this message is DMA safe *//* makes only sense in kernelspace *//* userspace buffers are copied anyway */#defineI2C_M_RECV_LEN0x0400/* length will be first received byte */#defineI2C_M_NO_RD_ACK0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */#defineI2C_M_IGNORE_NAK0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */#defineI2C_M_REV_DIR_ADDR0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */#defineI2C_M_NOSTART0x4000/* if I2C_FUNC_NOSTART */#defineI2C_M_STOP0x8000/* if I2C_FUNC_PROTOCOL_MANGLING */__u16 len;/* msg length */__u8*buf;/* pointer to msg data */};

具体核心参数说明如下:

struct i2c_msg{__u16 addr;// I2C从设备7位地址__u16 flags;// 传输控制标志位(多个宏按位或组合)__u16 len;// 当前这条消息要传输的字节数量__u8*buf;// 数据缓冲区指针,存放收发的字节数据};

i2c_client结构体定义如下:

struct i2c_client{unsignedshortflags;/* div., see below */unsignedshortaddr;/* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */charname[I2C_NAME_SIZE];struct i2c_adapter*adapter;/* the adapter we sit on */struct device dev;/* the device structure */intinit_irq;/* irq set at initialization */intirq;/* irq issued by device */struct list_head detected;#ifIS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb;/* callback for slave mode */#endif};

IIC 读操作

//i2c 读函数intft5x06_read_reg(u8 reg_addr){u8 data;//i2c 通讯,以最小8位为最小单位// i2c_transfer标准读写流程:先写寄存器地址,再读数据struct i2c_msg msgs[2]={[0]={.addr=ft5x06_client->addr,// I2C从机地址0x38.flags=0,// 标志0 = I2C写操作.len=sizeof(reg_addr),// 长度1字节(寄存器地址).buf=&reg_addr,// 缓冲区:要读取的寄存器号},[1]={.addr=ft5x06_client->addr,.flags=I2C_M_RD,// I2C_M_RD = 读操作标志.len=sizeof(data),// 读取1字节返回值.buf=&data,// 读到的数据存入data},};// i2c_transfer:发起一组I2C消息,返回成功执行的msg数量// ARRAY_SIZE(msgs)=2,必须两条消息都执行成功才算读取正常if(i2c_transfer(ft5x06_client->adapter,msgs,ARRAY_SIZE(msgs))!=ARRAY_SIZE(msgs)){return-EIO;// 读写失败,返回IO错误码}returndata;// 返回寄存器读到的值}

那么构造从机地址 addr从哪里来? 在 方法int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)也就是 probe 函数中的i2c_client指针变量中去取

所以在程序中定义全局变量:struct i2c_client *ft5x06_client;,然后赋值

intft5x06_probe(struct i2c_client*client,conststruct i2c_device_id*id){.....ft5x06_client=client;.....

IIC 写操作

写操作其实跟简单,对比读操作来说:

voidft5x06_write_reg(u8 reg_addr,u8 data,u16 len){u8 buff[256];// 本地缓冲区,最大255字节数据+1字节寄存器地址struct i2c_msg msgs[]={[0]={.addr=ft5x06_client->addr,.flags=0,// 纯写操作.len=len+1,// 总长度 = 寄存器地址1字节 + 有效数据长度.buf=buff,},};buff[0]=reg_addr;// 缓冲区首字节:寄存器地址memcpy(&buff[1],&data,len);// 后续空间拷贝待写入数据// 发起I2C写传输if(i2c_transfer(ft5x06_client->adapter,msgs,ARRAY_SIZE(msgs))!=ARRAY_SIZE(msgs)){return;// 写入失败直接退出,无错误返回、无打印}}

IIC 调用

接下来就开始调用了,如下:

intft5x06_probe(struct i2c_client*client,conststruct i2c_device_id*id){intret=0;intvalue=0;ft5x06_client=client;....................ft5x06_write_reg(0x80,0x4b,1);value=ft5x06_read_reg(0x80);printk("reg 0x80 is %#x\n",value);return0;}

有人会问,未删除传递寄存器地址0x80,哪里来的??? 那当然是数据手册里面找的,不是瞎写的。

三、驱动源码实现-实现IIC读写

#include<linux/init.h>#include<linux/module.h>#include<linux/i2c.h>#include<linux/of_device.h>#include<linux/gpio/consumer.h>#include<linux/delay.h>#include<linux/interrupt.h>struct gpio_desc*reset_gpio;struct gpio_desc*irq_gpio;struct i2c_client*ft5x06_client;//i2c 读函数intft5x06_read_reg(u8 reg_addr){u8 data;//i2c 通讯,以最小8位为最小单位// i2c_transfer标准读写流程:先写寄存器地址,再读数据struct i2c_msg msgs[2]={[0]={.addr=ft5x06_client->addr,// I2C从机地址0x38.flags=0,// 标志0 = I2C写操作.len=sizeof(reg_addr),// 长度1字节(寄存器地址).buf=&reg_addr,// 缓冲区:要读取的寄存器号},[1]={.addr=ft5x06_client->addr,.flags=I2C_M_RD,// I2C_M_RD = 读操作标志.len=sizeof(data),// 读取1字节返回值.buf=&data,// 读到的数据存入data},};// i2c_transfer:发起一组I2C消息,返回成功执行的msg数量// ARRAY_SIZE(msgs)=2,必须两条消息都执行成功才算读取正常if(i2c_transfer(ft5x06_client->adapter,msgs,ARRAY_SIZE(msgs))!=ARRAY_SIZE(msgs)){return-EIO;// 读写失败,返回IO错误码}returndata;// 返回寄存器读到的值}// // 为什么一个是u8 类型,一个是u16类型:因为u8是用来i2c通讯的,u16是用來賦值給i2c_msg 的voidft5x06_write_reg(u8 reg_addr,u8 data,u16 len){u8 buff[256];// 本地缓冲区,最大255字节数据+1字节寄存器地址:就是要struct i2c_msg msgs[]={[0]={.addr=ft5x06_client->addr,.flags=0,// 纯写操作.len=len+1,// 总长度 = 寄存器地址1字节 + 有效数据长度.buf=buff,},};buff[0]=reg_addr;// 缓冲区首字节:寄存器地址memcpy(&buff[1],&data,len);// 后续空间拷贝待写入数据// 发起I2C写传输if(i2c_transfer(ft5x06_client->adapter,msgs,ARRAY_SIZE(msgs))!=ARRAY_SIZE(msgs)){return;// 写入失败直接退出,无错误返回、无打印}}irqreturn_tft5x06_handler(intirq,void*args){printk("==========ft5x06_handler==========\n");returnIRQ_RETVAL(IRQ_HANDLED);}intft5x06_probe(struct i2c_client*client,conststruct i2c_device_id*id){intret=0;intvalue=0;ft5x06_client=client;printk(KERN_INFO"###### This is ft5x06 probe ######\n");// 获取复位GPIO描述符reset_gpio=gpiod_get_optional(&client->dev,"reset",0);// 第二个参数是属性(reset-gpios)前缀resetif(reset_gpio==NULL){printk("gpiod_get_optional reset_gpio error\n");return-1;}// 获取中断GPIO描述符irq_gpio=gpiod_get_optional(&client->dev,"interrupts",0);// 第二个参数是属性(interrupts-gpio)前缀interruptsif(irq_gpio==NULL){printk("gpiod_get_optional irq_gpio error\n");return-1;}gpiod_direction_output(reset_gpio,0);ssleep(5);gpiod_direction_output(reset_gpio,1);ret=request_irq(client->irq,ft5x06_handler,IRQF_TRIGGER_FALLING|IRQF_ONESHOT,"ft5x06_irq",NULL);if(ret<0){printk("request_irq request error\n");return-1;}ft5x06_write_reg(0x80,0x4b,1);value=ft5x06_read_reg(0x80);printk("reg 0x80 is %#x\n",value);return0;}intft5x06_remove(struct i2c_client*client){printk(KERN_INFO"###### This is ft5x06_remove ######\n");return0;}staticconststruct of_device_id ft5x06_id[]={{.compatible="my-ft5x06"},{/* Sentinel */},};// 关键补充MODULE_DEVICE_TABLE(of,ft5x06_id);staticstruct i2c_driver ft5x06_driver={.driver={.owner=THIS_MODULE,.name="my-ft5x06",.of_match_table=ft5x06_id,},.probe=ft5x06_probe,.remove=ft5x06_remove,};staticint__initft5x06_driver_init(void){intret;ret=i2c_add_driver(&ft5x06_driver);if(ret<0){printk(KERN_ERR"i2c_add_driver failed ret=%d\n",ret);returnret;}printk(KERN_INFO"###### ft5x06 driver register success ######\n");return0;}staticvoid__exitft5x06_driver_exit(void){i2c_del_driver(&ft5x06_driver);printk(KERN_INFO"###### ft5x06 driver unregister ######\n");}module_init(ft5x06_driver_init);module_exit(ft5x06_driver_exit);MODULE_LICENSE("GPL");

四、驱动验证

编译后,make 编译.ko 文件,然后加载驱动验证,结果如下:

总结

  • 这里其实就是一个简单的IIC通讯,还没有涉及到实际的功能,就是验证IIC通不通,读写寄存器
  • 这里重点还是理解思路、对于读写操作和api 操作要熟悉。

相关新闻

  • 无线麦克风一开多就断信号?
  • 宇舶最新发布官方售后服务热线、线下网点地址及其收费体系全解析 - 亨得利售后服务官网
  • 基于原生Socket实现C++邮件客户端:POP3/SMTP协议与MIME解析实战

最新新闻

  • 从ChatGPT写Hello World到生产环境API上线:一个需求的12小时AI开发实录(含完整日志与耗时拆解)
  • 【会议征稿通知 | 深圳理工大学主办 | ACM出版 | EI 、Scopus稳定检索】第三届智能计算与数据分析国际学术会议(ICDA 2026)
  • TI TMS320TCI6484/C6457 DSP硬件设计:电源、时钟与配置实战指南
  • 2026英国老牌留学机构排名盘点,梳理主流中介优缺点、避雷要点与申请服务特点 - 速递信息
  • Unity 2D雨夜场景实现:粒子系统与音频同步技术详解
  • 个人AI模型上手即用:无需GPU、不装Docker、5分钟完成本地部署(含Ollama+LM Studio双路径实操录屏要点)

日新闻

  • AI云原生实战05-金融AI上云最难的不是技术,是“不出事“——TCE银行风控架构拆解
  • 2026年GEOSEO优化公司选型深度测评:五大硬核标准严选,这六家重塑搜索增长新格局 - 品牌前沿专家
  • **核验!2026年7月卡地亚香港**售后网点地址及服务电话公告 - 卡地亚服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号