<作用>

电子设备中有很多IIC设备之间需要进行相互通信,这样就产生了IIC总线,常用来实现设备之间的数据通信。

 
<IIC总线结构>
IIC总线只有两条线,一条是串行数据线(SDA),另外一条是串行时钟线(SCL).
注:每一个连接到总线上的设备都有一个唯一的地址可以访问(这一点有点像USB设备)
 
<IIC总线信号类型>

a:开始信号(S):当SCL信号为高电平,SDA的电平由高电平变为低电平表示开始传输数据。
b:结束信号(P):当SCL信号为高电平,SDA的电平由低电平变为高电平表示结束传输数据。
c:相应信号(ACK):从机接收到8位数据后,在第9个时钟周期,拉低SDA电平,表示接受到数据,这个信号称为应答信号。
 
<IIC数据传输方式>
主机(IIC控制器)主要通过SDA向从机发送数据,当总线处于空闲的时候SDA和SCL都处于高电平。
a:当主机检测到总线处于空闲状态时,主机发送给开始信号(S)
b:主机发出8位数据,8位数据中,前7位中表示从机地址,第8位表示数据的传输方向。
c:和地址匹配的从机发出相应信号。
d:从机传输一系列响应序列
e:主机接收到这些数据后,发出结束信号P,该次数据传输完成。
 
<IIC控制器结构图>
a:主要涉及的寄存器
IICCON:控制寄存器
IICSTAT:状态寄存器
IICADD:地址寄存器

 
IICCDS:接收和发送的数据存储寄存器
 
<linux 驱动之IIC>
a:背景
对于IIC设备的控制,但是IIC设备太多了,为了很好的管理这些设备(不同等的设备名,管理各种设备,不同设备的地址,不同设备对应的驱动),Linux内核开发了如下结构体:
b:IIC设备(i2c_client)
struct i2c_client {
unsigned short flags;
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; //通过该指针可以找到适配器,然后通过适配器找到                       //适配器算法中的发送消息和接受函数完成同IIC设备                          //的通信              
struct i2c_driver *driver;
struct device dev; //用于建立设备模型
int irq;
struct list_head detected;
};
 
c:IIC驱动(i2c_driver)
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;//用于建立设备模型
const struct i2c_device_id *id_table;//是一个数组,保存该驱动支持的所有的 的                               //设备信息 
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
d:IIC适配器(i2c_adapter)//其本质表示适配器这个设备
-----i2c_adapter is the structure used to identify a physical i2c bus along with the access algorithms necessary to access it.内核中可以有很多总线适配器,其中内核中有一个静态指针数组adapters记录了所有已经注册的总线适配器。
 
struct i2c_adapter {
struct module *owner;
unsigned int class;  /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
 
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
 
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
 
int nr;
char name[48];
struct completion dev_released;
 
struct mutex userspace_clients_lock;
struct list_head userspace_clients;//用于连接所有连接到这个适配器上的i2c设备
};
d:IIC适配器驱动(i2c_algorithm)
 
/*
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 */
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
  to NULL. If an adapter algorithm can do SMBus access, set
  smbus_xfer. If set to NULL, the SMBus protocol is simulated
  using common I2C messages */
/* master_xfer should return the number of messages successfully
  processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
  int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
  unsigned short flags, char read_write,
  u8 command, int size, union i2c_smbus_data *data);
 
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
e:注意:i2c_driver和i2c_client属于设备层,i2c_adapter和i2c_algorithm属于总线层。他们之间的关系如图所示:
f:IIC设备之间的通信方式
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
填该结构体,用于同IIC设备通信。
 
<IIC设备驱动流程>
a:流程图
 
(1)xxx_init()和i2c_add_driver()实现对IIC子系统的初始化,这项工作由内核完成。
(2)xxx_attach_adapter()用来将IIC设备驱动和适配器挂接在一起,这样才能IIC设备驱动才能利用适配器驱动控制适配器和IIC设备通信。(注意:在这两步中间需要根据具体适配器实现相应的驱动,以及相应struct algorithm{}的实现)
(3)i2c_probe()函数是在检测到IIC设备后调用的函数,进一步实现IIC设备的各数据结构的初始化。包括下面的函数都为了实现相应的功能。
 
b:IIC子系统初始化
(1)初始化函数

(2)卸载函数

 
c:适配器驱动程序
(1)IIC适配器内核结构
注意:
"struct i2c_adapter adap"
这是内核提供的IIC适配器的结构体,特定类型的适配器结构体需要在这个结构上进行扩充(面向对象的思想)。
"void __iomem  *regs"
指向IIC适配器的控制寄存器地址,注意这个地址是物理地址
"struct resource *ioarea"
指向适配器申请的资源,这里应该是内存资源,先进行申请,然后进行映射
"struct i2c_msg *msg"
用来实现IIC设备驱动于设备之间的通信
 
(2)适配器加载函数

(3)适配器卸载函数

注意:这个适配器驱动跟字符设备驱动很相似,因为这里不需要同用户空间进行交互,所以这里没有相应的fops,但是有相应的类似fops——struct i2c_algorithm{}。和字符设备驱动相似,
重点就是要实现这个类似fops,struct i2c_algorithm{}中的相应的函数通过操作"void __iomem  *regs"中的相应的寄存器。
 
<IIC适配器驱动模块的加载与卸载>
a:这里将适配器驱动定性为平台设备
加载函数
b:卸载函数

c:注意其中的"s3c2410_i2c_driver"

d:对于一个平台设备来"s3c2410_i2c_probe()"函数是很重要的

(1)"s3c2410_i2c_probe()"中的"s3c2410_i2c_init()"函数

 
e:与"s3c2410_i2c_probe()"相反的函数是s3c2410_i2c_remove()。

 
<IIC设备驱动实例>
a:设备文件操作函数集

static struct file_operations eep_fops = {
.owner = THIS_MODULE,
.llseek = eep_llseek,
.read = eep_read,
.ioctl = eep_ioctl,
.open = eep_open,
.release = eep_release,
.write = eep_write,
};
static dev_t dev_number; /* Allotted Device Number */
static struct class *eep_class; /* Device class */
/* Per-device client data structure for each
* memory bank supported by the driver
*/
 
b:IIC设备结构体
struct eep_bank {
struct i2c_client *client; /* I2C client for this bank */
unsigned int addr; /* Slave address of this bank */
unsigned short current_pointer; /* File pointer */
int bank_number; /* Actual memory bank number */
/* ... */ /* Spinlocks, data cache for slow devices,.. */
};
#define NUM_BANKS 2 /* Two supported banks */
#define BANK_SIZE 2048 /* Size of each bank */
struct ee_bank *ee_bank_list; /* List of private data
structures, one per bank */
c:初始化函数
int __init
eep_init(void)
{
int err, i;
/* Allocate the per-device data structure, ee_bank */
ee_bank_list = kmalloc(sizeof(struct ee_bank)*NUM_BANKS, GFP_KERNEL);
memset(ee_bank_list, 0, sizeof(struct ee_bank)*NUM_BANKS);
/* Register and create the /dev interfaces to access the EEPROM
banks. Refer back to Chapter 5, "Character Drivers" for more details */
if (alloc_chrdev_region(&dev_number, 0,
NUM_BANKS, "eep") < 0) {
printk(KERN_DEBUG "Can't register device\n");
return -1;
}
eep_class = class_create(THIS_MODULE, DEVICE_NAME);
for (i=0; i < NUM_BANKS;i++) {
/* Connect the file operations with cdev */
cdev_init(&ee_bank[i].cdev, &ee_fops);
/* Connect the major/minor number to the cdev */
if (cdev_add(&ee_bank[i].cdev, (dev_number + i), 1)) {
printk("Bad kmalloc\n");
return 1;
}
device_create(eep_class, NULL, MKDEV (MAJOR) (dev_number),i),
"eeprom%d", i);
}
/* Inform the I2C core about our existence. See the section
"Probing the Device" for the definition of eep_driver */
err = i2c_add_driver(&eep_driver);
if (err) {
printk("Registering I2C driver failed, errno is %d\n", err);
return err;
}
printk("EEPROM Driver Initialized.\n");

return 0;
}

(1) eep_driver结构体如下所示:
eep_init()在设备初始化的时调用i2c_add_driver()将注册eep_probe()
static struct i2c_driver eep_driver =

{
.driver = {
.name = "EEP", /* Name */
},
.id = I2C_DRIVERID_EEP, //设备标志符I2C_DRIVERID_EEP对于每个设备应该是唯一的

.attach_adapter = eep_probe, /* Probe Method */
.detach_client = eep_detach, /* Detach Method */
};

(2)当IIC核心调用表明主机适配器已经存在的客户驱动程序的probe()方法时,其会发过来调用i2c_probe(),该函数的参数是驱动程序所关联的设备地址以及具体的探测函数attach()。
 
#include <linux/i2c.h>
/* The EEPROM has two memory banks having addresses SLAVE_ADDR1
* and SLAVE_ADDR2, respectively
*/
static unsigned short normal_i2c[] = {
SLAVE_ADDR1, SLAVE_ADDR2, I2C_CLIENT_END
};
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.probe = ignore,
.ignore = ignore,
.forces = ignore,
};
static int eep_probe(struct i2c_adapter *adapter)
{
/* The callback function eep_attach(), is shown in Listing 8.5 */
return i2c_probe(adapter, &addr_data, eep_attach);
}

int eep_attach(struct i2c_adapter *adapter, int address, int kind)
{
static struct i2c_client *eep_client;
eep_client = kmalloc(sizeof(*eep_client), GFP_KERNEL);
eep_client->driver = &eep_driver; /* Registered in Listing 8.2 */
eep_client->addr = address; /* Detected Address */
eep_client->adapter = adapter; /* Host Adapter */
eep_client->flags = 0;
strlcpy(eep_client->name, "eep", I2C_NAME_SIZE);
/* Populate fields in the associated per-device data structure */
/* ... */
/* Attach */
i2c_attach_client(new_client);
}  

 
<整个程序框架>

 

 

Linux驱动之IIC总线的更多相关文章

  1. Linux驱动之I2C总线设备以及驱动

    [ 导读] 本文通过阅读内核代码,来梳理一下I2C子系统的整体视图.在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,导致没有一个清晰的思路.所以从高层级来分析一下I2C系统的设计思路,将有助于 ...

  2. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  3. Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析

    关于Exynos4412 IIC 裸机开发请看 :Exynos4412 裸机开发 —— IIC总线 ,下面回顾下 IIC 基础概念 一.IIC 基础概念 IIC(Inter-Integrated Ci ...

  4. 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

    本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...

  5. SHT20 IIC总线驱动概述

    SHT20温湿度传感器使用iic总线的驱动方式,以下资料参考SHT20 datasheet总结 1.IIC总线 Start信号 IIC总线的起始信号以SDA由高电平变为低电平,等待5us以上,再由SC ...

  6. 迅为4412开发板Linux驱动教程——总线_设备_驱动注冊流程具体解释

    视频下载地址: 驱动注冊:http://pan.baidu.com/s/1i34HcDB 设备注冊:http://pan.baidu.com/s/1kTlGkcR 总线_设备_驱动注冊流程具体解释 • ...

  7. RT-thread 设备驱动组件之IIC总线设备

    本文主要介绍RT-thread中IIC总线设备驱动,涉及到的主要文件有:驱动框架文件(i2c_core.c,i2c_dev.c,i2c-bit-ops.c,i2c_dev.h,i2c.h):底层硬件驱 ...

  8. Linux I2C核心、总线和设备驱动

    目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...

  9. 【Linux开发】【DSP开发】Linux设备驱动之——PCI 总线

    PCI总线概述  随着通用处理器和嵌入式技术的迅猛发展,越来越多的电子设备需要由处理器控制.目前大多数CPU和外部设备都会提供PCI总线的接口,PCI总线已成为计算机系统中一种应用广泛.通用的总线标准 ...

随机推荐

  1. sql 存储过程导出指定数据到.txt文件(定时)

    需求:每天生成一份txt文件数据,供第三方通过http方式调用 方法: 1.新建存储过程: USE [LocojoyMicroMessage] GO /****** Object: StoredPro ...

  2. 关于 jQuery 中的 $.data() 方法和 jQuery 对象上的data 方法

    参见文章:http://www.it165.net/pro/html/201404/11922.html

  3. TCP报文的最大负载和报文的最小长度

    TCP报文的最大负载和报文的最小长度 MTU:最大传输单元,以太网的MTU为1500Bytes MSS:最大分解大小,为每次TCP数据包每次传输的最大数据的分段大小,由发送端通知接收端,发送大于MTU ...

  4. 1 - django-介绍-MTV-命令-基础配置-admin

    目录 1 什么是web框架 2 WSGI 3 MVC与MTV模式 3.1 MVC框架 3.2 MTV框架 3.3 区别 4 django介绍 4.1 Django处理顺序 4.2 创建django站点 ...

  5. 85.YCbCr与YUV的区别

    yuv色彩模型来源于rgb模型,该模型的特点是将亮度和色度分离开,从而适合于图像处理领域. YCbCr模型来源于yuv模型,应用于数字视频,ITU-R BT.601 recommendation 通过 ...

  6. DevExpress 行事历(Scheduler)的常用属性、事件和方法

    一.TcxScheduler[TcxScheduler常用属性]1.Storage    - 邦定一个Storage为Scheduler显示提供数据 2.DateNavigate.ColCount   ...

  7. 金蝶K3WISE常用数据表

    K3Wise 14.2 清空密码update t_User set FSID=') F ", ,P T #8 *P!D &D 80!N &@ <0 C '+''''+' ...

  8. 从此编写 Bash 脚本不再难【转】

    从此编写 Bash 脚本不再难 原创 Linux技术 2017-05-02 14:30 在这篇文章中,我们会介绍如何通过使用 bash-support vim 插件将 Vim 编辑器安装和配置 为一个 ...

  9. 我所知道的MVVM框架(转 司徒大大 )

    RubyLouvre commented on 6 Sep 2014   avalon http://avalonjs.github.io/ (使用Object.defineProperties. V ...

  10. python 写入execl记录

    记录代码中关于写execl的操作 # 创建execl workbook = xlwt.Workbook(encoding='utf8') # 创建样式实例 style = xlwt.XFStyle() ...