十九、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/SPI的温湿度采集与OLED显示

    一 I2c通讯协议 I2C通讯协议 Inter xff0d Integrated Circuit 引脚少 xff0c 硬件实现简单 xff0c 可扩展性强 xff0c 不需要USART CAN等通讯协议的外部收发设备 xff0c 现在被广泛
  • camera调试:i2c不通如何排查?

    这篇文章介绍一下在调试camera驱动的过程 xff0c 最常见的一个问题 xff0c i2c不通导致驱动注册不上 xff0c 应该如何排查 常见的报错log如下 xff1a 1 973566 T1 gc8034 4 0037 driver
  • [STM32学习]——一文搞懂I2C总线

    目录 I2C总线的概念 I2C最重要的功能包括 xff1a I2C的物理层 I2C主要特点 xff1a I2C的高阻态 I2C物理层总结 xff1a I2C的协议层 初始 xff08 空闲 xff09 状态 开始信号 xff1a 停止信号
  • mtk6765上i2c-tools的使用

    1 下载i2c tools 从开源网站http dl lm sensors org i2c tools releases 下载i2c tools 几个版本都可以用 xff0c 可以选择下载i2c tools 4 3 xff12 到官网下载交
  • UART、I2C、SPI接口常见面试问题总结

    UART 定义 xff1a Universal Asynchronous Receiver Transmitter 通用异步收发传输器 特点 xff1a 速率不快 可全双工 结构上一般由波特率产生器 UART发送器 UART接收器组成 xf
  • I2C驱动体系结构一:驱动软件概念与对应硬件的关系

    一 概念 xff1a 1 设备 xff1a struct device xff1a 该数据结构是对物理设备的软件抽象 xff0c 比如I2C slave xff08 对应i2c client xff09 和I2C 适配器 xff08 对应i
  • 基于I2C/SPI的温湿度采集与OLED显示

    基于I2C SPI的温湿度采集与OLED显示 一 AHT20温湿度采集1 I2C2 温湿度采集代码效果 二 OLED显示1 显示学号姓名2 诗句显示 三 总结四 参考 一 AHT20温湿度采集 1 I2C 解释什么是 软件I2C 和 硬件I
  • stm32的HAL库i2c从机实现

    stm32的i2c默认就是slave模式 xff0c 本文基于HAL库实现中断方式的接收和发送 xff0c 首先是初始化gpio和i2c xff0c 代码如下 xff1a I2C HandleTypeDef I2cHandle void H
  • I2C调试工具

    1 I2C调试工具 i2c tools工具是开源I2C调试工具 xff0c 具有获取I2C总线挂载的设备列表及设备地址 xff0c 可对指定设备指定寄存器进行读写的功能 ubuntu安装 xff1a apt get install libi
  • linux内核I2C子系统详解——看这一篇就够了

    1 I2C通信协议 参考博客 I2C通信协议详解和通信流程分析 2 通过KXTF9 2050芯片分析I2C协议 参考博客 通过KXTF9 2050芯片分析I2C协议 3 I2C子系统框架 1 I2C子系统分为三层 I2C核心层 I2C适配器
  • STM32设置为I2C从机模式(HAL库版本)

    STM32设置为I2C从机模式 HAL库版本 目录 STM32设置为I2C从机模式 HAL库版本 前言 1 硬件连接 2 软件编程 2 1 步骤分解 2 2 测试用例 3 运行测试 3 1 I2C连续写入 3 2 I2C连续读取 3 3 I
  • 【总线】I2C 通信协议

    目录 I2C 总线协议概述 参数总结 I2C 的工作原理 寻址 读 写位 数据帧 I2C数据传输的步骤 具有多个从机的单个主机 具有多个从机的多个主机 I2C的优缺点 优点 缺点 文章参考 I2C 总线协议概述 I2C 总线广泛应用在 OL
  • 外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

    移动设备的广泛应用增加对移动过程中各种参数的检测需求 ADXL345三轴数字加速度计可以用来检测加速度 进而测量倾斜角度等 在这一篇中 我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现 1 功能概述 ADXL345是一款小而薄的
  • Linux:从用户空间实例化:eeprom new_device

    环境 x86 Ubuntu 14 04 我想获得类似的东西 i2c0 eeprom eeprom 50 compatible at 24c32 reg lt 0x50 gt 但因为在 x86 中没有可用的设备树 所以我遵循i2c insta
  • docker 容器内的 I2C

    我正在尝试在 docker 容器内的树莓派上使用 i2c 引脚 我使用 RUN 安装所有模块 但是当我使用 CMD 运行我的 python 程序时 我收到一条错误消息 Trackback most recent call last file
  • ADXL345 与 ESP32 I2C 垃圾值问题

    我已根据以下教程使用 I2C 接口将 ESP32 与 ADXL345 连接起来Tutorial http www esp32learning com code esp32 and adxl345 sensor example php 但是
  • 如何将 I2C 地址传递给 Adafruit CircuitPython 代码? (运行 ADS1115)

    我正在尝试使用两个 I2C 地址 0x48 0x49 在一个 Raspberry Pi 上运行两个 Adafruit ADS1115 每个设备的地址可以通过将 ADDR 引脚连接为高电平 0x49 或使其悬空 默认为 0x48 来设置 我已
  • MCP23017 I2C 设备驱动程序探测函数未调用

    我正在使用以下 I2C GPIO设备驱动 https github com torvalds linux blob master drivers gpio gpio mcp23s08 c访问 MCP23017 GPIO 使用 insmod
  • ESP8266 I2C从机不确认数据

    我有一个 TM4C123 处理器作为 I2C 主处理器 一个 ESP8266 作为从处理器 对于 ESP 我使用的是 Arduino IDE 并在 2 5 2 版安装了 ESP8266 支持 它应该支持 I2C 从模式 但是 我无法让它工作
  • 为什么 i2c_smbus 函数不可用? (I2C——嵌入式Linux)

    有很多参考使用i2c smbus 开发嵌入式 Linux 软件时在 I2C 总线上进行通信的函数 什么时候i2c smbus函数如i2c smbus read word data在软件项目中引用了 ARM8 处理器错误 例如 i2c smb

随机推荐