十九、I2C驱动及应用

2023-05-16

一、概述

1、Linux主机驱动和外设驱动分离思想

        外设驱动→API→主机驱动→板级逻辑--具体的i2c设备(camera,ts,eeprom等等)
        主机驱动:根据控制器硬件手册,操作具体的寄存器,产生波形。(单片机工程师肯定有强烈的冲动去配置i2c寄存器,产生波形!)。
        linux应用工程师:屏蔽了驱动和硬件。
        linux驱动工程师:屏蔽硬件!提供标准的主机驱动,驱动工程师需要完成“外设驱动”
       内核函数接口:(API)。主机驱动提供给外设驱动的函数接口。
       注册i2c设备:i2c_board_info
       驱动注册和卸载函数以及结构体:i2c_del_driver/i2c_add_driver,i2c_driver
       读写函数和结构体:i2c_transfer,i2c_msg
       这几个函数是放之四海而皆准!
       外设驱动:针对具体的外部器件的代码。
       摄像头以及声卡中i2c用来配置外部设备(声卡和摄像头)→地址和配置的内容都不一样!
板级逻辑:描述主机和外部设备是怎么连接的。

教程中介绍:I2C函数接口(API):
    设备注册:i2c_board_info
    驱动注册函数和结构体:i2c_del_driver/i2c_add_driver,i2c_driver
    读写函数和结构体:i2c_transfer,i2c_msg 

2.设备-i2c设备注册以及设备注册之后的查询方法

        查询i2c设备地址:

ls /sys/bus/i2c/devices/

        怎么和原理图以及外部设备对应:3-0038→I2C_3_SCL(addr:datasheet中查0x38)
        查询i2c设备名称:

cat /sys/bus/i2c/devices/3-0038/name

        menuconfig中去掉触摸的驱动
        Device Drivers  ---> 
        Input device support  --->
        Touchscreens  ---> 
        FT5X0X based touchscreens(去掉)
        

        在arch\arm\mach-exynos\mach-itop4412.c
        添加i2c设备:i2c_devs3[]中添加
        {
                I2C_BOARD_INFO("i2c_test", 0x70>>1),
        },
       cat /sys/bus/i2c/devices/3-0038/name结果是i2c_test    

3.驱动-i2c驱动注册和卸载。

        i2c设备驱动初始化完成-进入probe函数。

        i2c_del_driver/i2c_add_driver:i2c_driver

        module_initlate_initcall:module_init先运行,late_initcall后运行

4.驱动-i2c数据的传输

        i2c_transfer,i2c_msg

struct i2c_msg {
    __u16 addr;                       /* slave address  */
    __u16 flags;
    #define I2C_M_RD        0x0001    /* read data, from slave to master */
    __u16 len;                        /* msg length                */
    __u8  *buf;                       /* pointer to msg data            */
};

        要完成i2c的读,必须要先写再读!写的时候,你要通知从机,你要读哪个寄存器!

二、驱动代码

1、late_initcall(i2c_test_init);

模块延后执行i2c_test_init,在函数中调用i2c_add_driver(&i2c_test_driver);

2、i2c_add_driver(&i2c_test_driver);

将i2c_test_driver驱动添加到I2C总线上。开始调用i2c_test_driver中的i2c_test_probe。

3、i2c_test_probe

匹配我们前面注册的设备:i2c_test,i2c_test_driver驱动开始运行,

4、misc_register(&i2c_dev);

创建成杂项设备。设备名"i2c_control", // 可以在/dev目录下看到

5、i2c_ops:

定义设备操作函数

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/regulator/consumer.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <asm/uaccess.h> 
#include <linux/miscdevice.h>

static struct i2c_client *this_client;

static int i2c_tes_read_reg(struct i2c_client *client,u8 addr, u8 *pdata) {
	u8 buf1[4] = { 0 };
	u8 buf2[4] = { 0 };
	struct i2c_msg msgs[] = {
		{
			.addr	= client->addr,	//0x38
			.flags	= 0,	//写
			.len	= 1,	//要写的数据的长度
			.buf	= buf1,
		},
		{
			.addr	= client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= buf2,
		},
	};
	int ret;
	buf1[0] = addr;
	ret = i2c_transfer(client->adapter, msgs, 2);
	if (ret < 0) {
		pr_err("read reg (0x%02x) error, %d\n", addr, ret);
	} else {
		*pdata = buf2[0];
	}
	return ret;
}
static int i2c_tes_read_fw_reg(struct i2c_client *client,unsigned char *val)
{
	int ret;
	*val = 0xff;
	ret = i2c_tes_read_reg(client,0xa6, val);
	printk("ts reg 0xa6 val is %d\n",*val);
	return ret;
}
static int i2c_open_func(struct file *filp)
{
	return 0;
}

static int i2c_release_func(struct file *filp)
{
	return 0;
}

static ssize_t i2c_read_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos){
	int ret;
	u8 reg_data;
	
	ret = copy_from_user(&reg_data,buffer,1);
	
	struct i2c_msg msgs[] = {
		{
			.addr	= this_client->addr,	//0x38
			.flags	= 0,	//写
			.len	= 1,	//要写的数据的长度
			.buf	= &reg_data,
		},
		{
			.addr	= this_client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= &reg_data,
		},
	};
	ret = i2c_transfer(this_client->adapter, msgs, 2);
	if (ret < 0) {
		pr_err("read reg error!\n");
	}
	ret = copy_to_user(buffer,&reg_data,1);
	
	return ret;
}

static ssize_t i2c_write_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos){
	int ret;
	u8 buf[2];
	struct i2c_msg msgs[1];
	
	ret = copy_from_user(&buf,buffer,2);
	
	msgs[0].addr	= this_client->addr;	//0x38
	msgs[0].flags	= 0;	//写
	msgs[0].len	= 2;	//第一个是要写的寄存器地址,第二个是要写的内容
	msgs[0].buf	= buf;

	ret = i2c_transfer(this_client->adapter, msgs, 1);
	if (ret < 0) {
		pr_err("write reg 0x%02x error!\n",buf[0]);
	}
	ret = copy_to_user(buffer,buf,1);
	
	return ret;
}


static struct file_operations i2c_ops = {
	.owner 	= THIS_MODULE,
	.open 	= i2c_open_func,
	.release= i2c_release_func,
	.write  = i2c_write_func,
	.read 	= i2c_read_func,
};


static struct miscdevice i2c_dev = {
	.minor	= MISC_DYNAMIC_MINOR,
	.fops	= &i2c_ops,
	.name	= "i2c_control", // 可以在/dev目录下看到
};

static int i2c_test_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	unsigned char val;
	printk("==%s:\n", __FUNCTION__);
	
	i2c_tes_read_fw_reg(client,&val);
	
	this_client = client;
	
	misc_register(&i2c_dev);
	
	return 0;
}

static int __devexit i2c_test_remove(struct i2c_client *client)
{
	i2c_set_clientdata(client, NULL);
	misc_deregister(&i2c_dev);
	printk("==%s:\n", __FUNCTION__);
	return 0;
}

static const struct i2c_device_id i2c_test_id[] = {
	{ "i2c_test", 0 },
	{ }
};

static struct i2c_driver i2c_test_driver = {
	.probe		= i2c_test_probe,
	.remove		= __devexit_p(i2c_test_remove),
	.id_table	= i2c_test_id,    // 驱动能匹配的设备
	.driver	= {
		.name	= "i2c_test",     // 可以在sys/bus/i2c/drivers/目录下看到驱动名称
		.owner	= THIS_MODULE,
	},
};

static void i2c_io_init(void)
{
	int ret;
	ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN");
	if (ret) {
		printk(KERN_ERR "failed to request TP1_EN for "
				"I2C control\n");
		//return err;
	}
	gpio_direction_output(EXYNOS4_GPL0(2), 1);
	s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT);
	gpio_free(EXYNOS4_GPL0(2));
	mdelay(5);
	
	ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
	if (ret) {
		gpio_free(EXYNOS4_GPX0(3));
		ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
	if(ret)
	{
		printk("ft5xox: Failed to request GPX0_3 \n");
	}
	}
	gpio_direction_output(EXYNOS4_GPX0(3), 0);
	mdelay(200);
	gpio_direction_output(EXYNOS4_GPX0(3), 1);
	s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT);
	gpio_free(EXYNOS4_GPX0(3));
	msleep(300);	
}

static int __init i2c_test_init(void)
{
	printk("==%s:\n", __FUNCTION__);
	i2c_io_init();
	printk("==%s:\n", __FUNCTION__);
	return i2c_add_driver(&i2c_test_driver);
}
static void __exit i2c_test_exit(void)
{
	printk("==%s:\n", __FUNCTION__);
	i2c_del_driver(&i2c_test_driver);
}

late_initcall(i2c_test_init);
module_exit(i2c_test_exit);

MODULE_AUTHOR("xunwei_rty");
MODULE_DESCRIPTION("TsI2CTest");
MODULE_LICENSE("GPL");

模块运行结果

 三、应用程序代码

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc,char **argv)
{
	int fd,ret;
	char *i2c_device = "/dev/i2c_control";
	unsigned char buffer[2];
	
	printf("open %s!\n",i2c_device);
	if((fd = open(i2c_device,O_RDWR|O_NDELAY))<0)
		printf("APP open %s failed",i2c_device);
	else{
		printf("APP open %s success!\n",i2c_device);
	}
	
//读一个数据0xa6
	buffer[0] = 0xa6;
	ret = read(fd,buffer,1);
	if(ret<0)
		printf("i2c read failed!\n");
	else{
		printf("i2c read reg 0xa6 data is 0x%02x!\n",buffer[0]);
	}
	
//01先从0x00读出一个数据,02写一个数据到0x00,03再读出来对比
	//01
	buffer[0] = 0x00;
	read(fd,buffer,1);
	printf("i2c read reg 0x00 data is 0x%02x!\n",buffer[0]);
	//02
	buffer[0] = 0x00;
	buffer[1] = 0x40;
	ret = write(fd,buffer,2);
	if(ret<0){
		printf("i2c write failed!\n");
		goto exit;
	}
	//03
	buffer[0] = 0x00;
	read(fd,buffer,1);
	printf("i2c read reg 0x00 data is 0x%02x!\n",buffer[0]);
	
	close(fd);
	
exit:
	close(fd);
	return -1;
}

运行应用程序结果

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

十九、I2C驱动及应用 的相关文章

  • I2C通信之EEPROM-第1季第15部分-朱有鹏-专题视频课程

    I2C通信之EEPROM 第1季第15部分 3173人已学习 课程介绍 本课程是 朱有鹏老师单片机完全学习系列课程 第1季第15个课程 xff0c 主要讲解了EEPROM的编程和使用 xff0c 其中重点是I2C接口 xff0c I2C是物
  • I2C读取IST8310

    文章目录 I2C简介I2C传输过程I2C仲裁IST8310磁力计Cube配置相关函数HAL I2C Mem Read函数HAL I2C Mem Write函数IST8310的初始化 程序流程代码编写 I2C简介 2C是一种半双工双向二线制的
  • 模拟I2C

    I2C具体内容参考资料 https blog csdn net sternlycore article details 85600668 https blog csdn net qq 43460068 article details 122
  • Jetson nano i2c教程(MPU6050 + PCA9685)

    首先介绍nano板子上的i2c相关的硬件信息 xff1a 安装所需要的i2c库 sudo apt get install l y i2c tools 完成nano中io与i2c设备的硬件接线 本次案例使用的是PCA9685和MPU6050
  • [STM32学习]——一文搞懂I2C总线

    目录 I2C总线的概念 I2C最重要的功能包括 xff1a I2C的物理层 I2C主要特点 xff1a I2C的高阻态 I2C物理层总结 xff1a I2C的协议层 初始 xff08 空闲 xff09 状态 开始信号 xff1a 停止信号
  • PX4通过I2C方式添加自定义传感器(2)

    PX4 I2C通信方式传感器驱动分析 xff08 以ets airspeed为例 xff09 1 说明 这篇文章我们就来看看I2C传感器的驱动过程 xff0c 当然里面也有很多东西我不是很理解 xff0c 所以仅谈我领悟的一些东西 我就以e
  • 【技术分享】GD32硬件I2C调试中的问题与解决过程-续

    使用GD32303C EVAL开发板和MPL3115A2模块测量气压或高度数据 xff0c 两者间使用硬件I2C进行通讯 上次调试发现官方例程 xff08 单一I2C读写功能 xff09 可以正常读写MPL芯片的寄存器 xff0c 而我建立
  • I2C通讯协议介绍

    2019独角兽企业重金招聘Python工程师标准 gt gt gt I2C总线是PHLIPS公司在20世纪80年代推出的一种串行总线 具有引脚少 xff0c 硬件实现简单 xff0c 可扩展性强的优点 I2C总线的另一优点是支持多主控 xf
  • I2C接口及时序

    1 I2C学习要点 1 有2条双向串行线 xff0c 一条数据线SDA xff0c 一条时钟线SCL 如果只做master SCL可以只是输出 SDA在PAD上一定是inout pin xff0c 当然转为数字信号时可以分为两组 inout
  • i2c那些坑

    origin http bbs ntpcb com simple t126695 html I2C 的那些坑 一般情况下 xff0c i2c 设备焊接没什么问题 xff0c 按照设备手册一步步来 xff0c 基本上就顺风顺水能够用起来 如果
  • Linux控制I2C/SMBus设备

    平台 xff1a 树莓派 bcm2835 Raspberry Pi 3 Model B Rev 1 2 I2C是Philips开发的一种两线通信协议 xff0c 常用于一些对速度要求不高的小型器件上 SMBus是系统管理总线 xff0c 基
  • I2C驱动App

    1 查看eeprog c源代码 copyright C by 2009 Guangzhou FriendlyaRM in China email capbily 64 163 com website arm9 net include lt
  • 串口通信协议 UART+I2C+SPI

    UART 异步 串行 全双工 I2C SPI 不同通信协议比较 UART UART协议详解 UART通信 xff0c 接收与发送 xff08 详细版 xff0c 附代码 xff09 UART串行通信详解 待整理 UART是Universal
  • DAC MCP4725 i2c 驱动(linux)

    mcp4725是一款低功耗 高精度 单通道 拥有EEPROM的12位的dac 由于最近项目中使用到了该芯片所以贴出来给大家参考 步进电机电机芯片半流锁定 本贴呢非项目中使用的平台 主要是想在linux 下实现对该器件的使用 实现一个简单的i
  • CORE-ESP32C3

    目录 参考博文 源于网友oled eink aht10项目 源代码修改及复现说明 主要修改 显示效果 编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 日期温度显示屏 正常初始化LOG 示例代
  • 如何为连接到 I2C gpio 扩展器的虚拟 mdio-gpio 设备配置 ACPI *.asl

    我正在使用 Q7 模块 x86 并尝试在 Linux 上使用 ACPI SSDT Overlay 配置我们的外设 但我对此很挣扎 我想我误解了ACPI的一些核心概念 Problem CPU gt I2C gt PCA9575 GPIO Ex
  • docker 容器内的 I2C

    我正在尝试在 docker 容器内的树莓派上使用 i2c 引脚 我使用 RUN 安装所有模块 但是当我使用 CMD 运行我的 python 程序时 我收到一条错误消息 Trackback most recent call last file
  • 在 Raspberry Pi 上使用 Python smbus - 与语法混淆

    我正在尝试在 Raspberry Pi 上使用 python smbus 使用 I2C 与 MMA7660 加速计芯片进行通信 在下面的代码中 我正在读取芯片的寄存器 0x 00 0x01 0x02 和 0x03 并且我得到的值完全相同 查
  • 为什么 i2c_smbus 函数不可用? (I2C——嵌入式Linux)

    有很多参考使用i2c smbus 开发嵌入式 Linux 软件时在 I2C 总线上进行通信的函数 什么时候i2c smbus函数如i2c smbus read word data在软件项目中引用了 ARM8 处理器错误 例如 i2c smb
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些

随机推荐