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

Linux驱动学习笔记之I2C通信(观b站讯为电子有感)

1 I2C通信

1.1 I2C通信的介绍

 1.1.1 I2C通信的定义

I2C 是很常用的一个串行通信接口,用于连接各种外设、传感器等。I2C 总线仅仅使用时钟线SCL(Serial Clock)、数据线SDA (Serial Data)这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和 PCB 板布线空间的占用。本文主要介绍中在 Linux 内核中如何使用 I2C 的驱动框架。

1.1.2 I2C通信的特点

I2C通信是一种同步、半双工的通信方式,支持总线挂载多设备(一主多从、多主多从)。如下图所示,所有I2C设备的SCL连在一起,SDA连在一起;设备的SCL和SDA均要配置成开漏输出模式;SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右。从设备下拉,总线处于低电平;从设备放开,从设备相当于浮空,但由于上拉电阻作用,总线处于高电平。只要有一个从设备下拉,总线就处于低电平。

I2C总线框图

1.2 Linux里面的I2C通信

1.2.1 Linux中的I2C节点

不同于单片机使用I2C,使用Linux的I2C通信不需要去利用管脚模拟时序,Linux提供I2C控制器,我们只需要关注如何读写数据就行了。Linux的原则是一切皆文件,所以开发板上的I2C外设也以文件的形式存在,我们打开相应的I2C设备节点。

imx6ull开发板I2C节点

 1.2.2 使用I2C读取触摸芯片FT5X06数据

我们这里使用I2C与触摸芯片FT5X06进行通信,查找原理图FT5X06对应的是I2C2,对应节点i2c-1。我们与FTX06芯片进行通信只需要操作/dev/i2c-1节点就行。我们这里对其状态寄存器TD_STATUS进行读取数据。

FT5X06寄存器

应用层操作I2C是以数据包进行交流的,对应的结构体是i2c_rdwr_ioctl_data,其中

struct i2c_rdwr_ioctl_data

{

    struct i2c_msg __user *msgs; /*要发送的数据包ic_msg指针 */

    __u32 nmsgs; /* 要发送的数据包i2c_msg个数 */

};

struct i2c_msg

{

    __u16 addr; /* 从机地址*/

    __u16 flags; /* flags 为读写标志位,如果 flags 为 1,则为读,反之为 0,则为写*/

    __u16 len; /* buf的大小单位为字节*/

    __u8 *buf; /* 发送或接收缓冲区*/
    
};

以下是应用层实现I2C通信的完整代码及注释:

#include <stdio.h>
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
int fd;
int ret;

/** 
* @description: i2c_read_data i2c 读数据 
* @param {unsignedint} slave_addr:从机设备的地址 
* @param {unsignedchar} reg_addr:寄存器的地址 
* @return {*} 
*/ 

int i2c_read_data(unsigned int slave_addr,unsigned char reg_addr)
{

	unsigned char data; //用于存取读到的值
	
	//定义一个要发送的数据包 i2c_read_lcd 
	struct i2c_rdwr_ioctl_data i2c_read_lcd; 
	//定义初始化 i2c_msg 结构体 
	//定义两个数据包,先写是为了找到具体的从机的哪个寄存器,以便从这个寄存器里面读取数据
	struct i2c_msg msg[2] = { 
		[0] = { 
			.addr = slave_addr, //设置从机额地址 
			.flags = 0, //设置为写 
			.buf = &reg_addr, //设置寄存器的地址 
			.len = sizeof(reg_addr)//设置寄存器的地址的长度
			}, 
		[1] = {
			.addr = slave_addr, //设置从机额地址 
			.flags = 1, //设置为读 
			.buf = &data, //设置寄存器的地址 
			.len = sizeof(data)}, //设置寄存器的地址 
	}; 
	//初始化数据包的数据 
	i2c_read_lcd.msgs = msg; 
	//初始化数据包的个数 
	i2c_read_lcd.nmsgs = 2; 

	//操作读写数据包 
	ret = ioctl(fd, I2C_RDWR, &i2c_read_lcd); 
	if (ret < 0) 
	{ 
		perror("ioctl error "); 
		return ret; 
	} 

	return data;
	/*
	这里data为什么一会char,一会int
	C 语言中存在整数提升的规则,即任何小于 int 的整数类型在表达式中会被自动提升为 int 类型。
	int 类型的范围通常能够容纳 unsigned char 类型的所有可能取值。
	char 和 unsigned char 类型通常用于表示小范围的整数或字符
	*/
}

int main(int argc,char const* argv[])
{
	int TD_STATUS; 	
	struct input_event test_event; 
	fd = open("/dev/i2c-1",O_RDWR);//查看原理图触摸芯片FT5X06对应的I2C的i2c-1 
	if(fd < 0) 
	{ 
		perror("open error \n"); 
		return fd; 
	} 
	while(1){
		//i2C 读从机地址为 0x38,寄存器地址为 0x02 的数据 
		//我们从数据手册中得知 TD_STATUS 的地址为 0x02 
		TD_STATUS = i2c_read_data(0x38, 0x02); 
		// 打印 TD_STATUS 的值 
		printf("TD_STATUS value is %d \n", TD_STATUS); 
		sleep(1); 
	}
	close(fd); 
	return 0;
}

2 Linux I2C驱动

Linux中的I2C也是按照平台总线模型设计的,之前的platform总线模型驱动包括device.c和driver.c。但是I2C总线这里的device不叫device而是叫client。

platform是虚拟出的一条总线,而i2c是真实的,直接使用i2c总线即可。

所以,I2C总线模型的驱动编写需要client.c和driver.c。在这里我们去操作触摸芯片FT5X06的驱动程序。

2.1 Linux I2C驱动(非设备树实现)

开始之前注意在设备的文件中注释掉ft5x06的相关节点,还要去出内核里面编译进去的FT5X06的驱动,重新烧录不带有FT5X06驱动模块的内核。完整的client.c直接看代码,大致记一下,具体的


http://www.kler.cn/a/576550.html

相关文章:

  • HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect
  • 2025-03-07 electron无法打包的小问题
  • 20242817李臻《Linux⾼级编程实践》第二周
  • WordPress报502错误问题解决-php-fpm-84.service loaded failed failed LSB: starts php-fpm
  • 为AI聊天工具添加一个知识系统 之133 详细设计之74通用编程语言 之4 架构及其核心
  • 【SegRNN 源码理解】self.revIN可逆实例标准化
  • Elasticsearch:“Your trial license is expired”
  • 乐鑫打造全球首款 PSA Certified Level 2 RISC-V 芯片
  • Qt 实现绘图板(支持橡皮擦与 Ctrl+Z 撤销功能)[特殊字符]
  • React Native 0.76 升级后 APK 体积增大的原因及优化方案
  • linux的文件系统及文件类型
  • 护照阅读器在旅游景区流程中的应用
  • 深度学习网格搜索实战
  • GPO 配置的 4 种常见安全错误及安全优化策略
  • 机器学习(李宏毅)——Life-Long Learning
  • 在 Docker 中安装并配置 Nginx
  • Service 无法访问后端 Pod,如何逐步定位问题
  • C++ `bitset` 入门指南
  • 蓝桥杯P17153-班级活动 题解
  • Elastic如何获取当前系统时间