IIC设备驱动程序.docx
- 文档编号:10825418
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:48
- 大小:226.28KB
IIC设备驱动程序.docx
《IIC设备驱动程序.docx》由会员分享,可在线阅读,更多相关《IIC设备驱动程序.docx(48页珍藏版)》请在冰豆网上搜索。
IIC设备驱动程序
IIC设备驱动程序
IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中。
在现代电子系统中,有很多的IIC设备需要进行相互之间通信
IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接微处理器和外部IIC设备。
IIC设备产生于20世纪80年代,最初专用与音频和视频设备,现在在各种电子设备中都广泛应用
IIC总线有两条总线线路,一条是串行数据线(SDA),一条是串行时钟线(SCL)。
SDA负责数据传输,SCL负责数据传输的时钟同步。
IIC设备通过这两条总线连接到处理器的IIC总线控制器上。
一种典型的设备连接如图:
与其他总线相比,IIC总线有很多重要的特点。
在选择一种设备来完成特定功能时,这些特点是选择IIC设备的重要依据。
主要特点:
1,每一个连接到总线的设备都可以通过唯一的设备地址单独访问
2,串行的8位双向数据传输,位速率在标准模式下可达到100kb/s;快速模式下可以达到400kb/s;告诉模式下可以达到3.4Mb/s
3,总线长度最长7.6m左右
4,片上滤波器可以增加抗干扰能力,保证数据的完成传输
5,连接到一条IIC总线上的设备数量只受到最大电容400pF的限制
6,它是一个多主机系统,在一条总线上可以同时有多个主机存在,通过冲突检测方式和延时等待防止数据不被破坏。
同一时间只能有一个主机占用总线
IIC总线在传输数据的过程中有3种类型的信号:
开始信号、结束信号、和应答信号
>>开始信号(S):
当SCL为高电平时,SDA由高电平向低电平跳变,表示将要开始传输数据
>>结束信号(P):
当SCL为高电平时,SDA由低电平向高电平跳变,表示结束传输数据
>>响应信号(ACK):
从机接收到8位数据后,在第9个周期,拉低SDA电平,表示已经收到数据。
这个信号称为应答信号
开始信号和结束信号的波形如下图:
主机:
IIC总线中发送命令的设备,对于ARM处理器来说,主机就是IIC控制器
从机:
接受命令的设备
主机向从机发送数据:
主机通过数据线SDA向从机发送数据。
当总线空闲时,SDA和SCL信号都处于高电平。
主机向从机发送数据的过程:
1,当主机检测到总线空闲时,主机发出开始信号
2,主机发送8位数据。
这8位数据的前7位表示从机地址,第8位表示数据的传输方向。
这时,第8位为0,表示向从机发送数据
3,被选中的从机发出响应信号ACK
4,从机传输一系列的字节和响应位
5,主机接受这些数据,并发出结束信号P,完成本次数据传输
由上图可知,IIC控制器主要是由4个寄存器来完成所有的IIC操作的。
IICCON:
控制是否发出ACK信号,是否开启IIC中断
IICSTAT:
IICADD:
挂载到总线上的从机地址。
该寄存器的[7:
1]表示从机地址。
IICADD寄存器在串行输出使能位IICSTAT[4]为0时,才可以写入;在任何时候可以读出
IICDS:
保存将要发送或者接收到的数据。
IICCDS在串行输出使能IICSTAT[4]为1时,才可以写入;在任何时间都可以读出
因为IIC设备种类太多,如果每一个IIC设备写一个驱动程序,那么显得内核非常大。
不符合软件工程代码复用,所以对其层次话:
这里简单的将IIC设备驱动分为设备层、总线层。
理解这两个层次的重点是理解4个数据结构,这4个数据结构是i2c_driver、i2c_client、i2c_algorithm、i2c_adapter。
i2c_driver、i2c_client属于设备层;i2c_algorithm、i2c_adapter属于总线型。
如下图:
设备层关系到实际的IIC设备,如芯片AT24C08就是一个IIC设备。
总线层包括CPU中的IIC总线控制器和控制总线通信的方法。
值得注意的是:
一个系统中可能有很多个总线层,也就是包含多个总线控制器;也可能有多个设备层,包含不同的IIC设备
由IIC总线规范可知,IIC总线由两条物理线路组成,这两条物理线路是SDA和SCL。
只要连接到SDA和SCL总线上的设备都可以叫做IIC设备。
一个IIC设备由i2c_client数据结构进行描述:
struct i2c_client
{
unsignedshort flags; //标志位
unsignedshort addr; //设备的地址,低7位为芯片地址
charname[I2C_NAME_SIZE]; //设备的名称,最大为20个字节
struct i2c_adapter*adapter; //依附的适配器i2c_adapter,适配器指明所属的总线
struct i2c_driver*driver; //指向设备对应的驱动程序
structdevice dev; //设备结构体
intirq; //设备申请的中断号
structlist_head list; //连接到总线上的所有设备
structlist_head detected; //已经被发现的设备链表
structcompletion released; //是否已经释放的完成量
};
设备结构体i2c_client中addr的低8位表示设备地址。
设备地址由读写位、器件类型和自定义地址组成,如下图:
第7位是R/W位,0表示写,2表示读,所以I2C设备通常有两个地址,即读地址和写地址
类型器件由中间4位组成,这是由半导体公司生产的时候就已经固化了。
自定义类型由低3位组成。
由用户自己设置,通常的做法如EEPROM这些器件是由外部I芯片的3个引脚所组合电平决定的(A0,A1,A2)。
A0,A1,A2就是自定义的地址码。
自定义的地址码只能表示8个地址,所以同一IIC总线上同一型号的芯片最多只能挂载8个。
AT24C08的自定义地址码如图:
A0,A1,A2接低电平,所以自定义地址码为0;
如果在两个不同IIC总线上挂接了两块类型和地址相同的芯片,那么这两块芯片的地址相同。
这显然是地址冲突,解决的办法是为总线适配器指定一个ID号,那么新的芯片地址就由总线适配器的ID和设备地址组成
除了地址之外,IIC设备还有一些重要的注意事项:
1,i2c_client数据结构是描述IIC设备的“模板”,驱动程序的设备结构中应包含该结构
2,adapter指向设备连接的总线适配器,系统可能有多个总线适配器。
内核中静态指针数组adapters记录所有已经注册的总线适配器设备
3,driver是指向设备驱动程序,这个驱动程序是在系统检测到设备存在时赋值的
IIC设备驱动 i2c_driver:
struct i2c_driver
{
intid; //驱动标识ID
unsignedintclass; //驱动的类型
int(*attach_adapter)(structi2c_adapter*); //当检测到适配器时调用的函数
int(*detach_adapter)(structi2c_adapter*); //卸载适配器时调用的函数
int(*detach_client)(structi2c_client*) __deprecated; //卸载设备时调用的函数
//以下是一种新类型驱动需要的函数,这些函数支持IIC设备动态插入和拔出。
如果不想支持只实现上面3个。
要不实现上面3个。
要么实现下面5个。
不能同时定义
int (*probe)(structi2c_client*,conststruct i2c_device_id*); //新类型设备探测函数
int(*remove)(structi2c_client*); //新类型设备的移除函数
void(*shutdown)(structi2c_client*); //关闭IIC设备
int(*suspend)(struct i2c_client*,pm_messge_tmesg); //挂起IIC设备
int(*resume)(struct i2c_client*); //恢复IIC设备
int(*command)(structi2c_client*client,unsignedintcmd,void*arg); //使用命令使设备完成特殊的功能。
类似ioctl()函数
structdevcie_driver driver; //设备驱动结构体
conststruct i2c_device_id*id_table; //设备ID表
int(*detect)(structi2c_client*,int kind,struct i2c_board_info*); //自动探测设备的回调函数
const structi2c_client_address_data *address_data; //设备所在的地址范围
struct list_head clients; //指向驱动支持的设备
};
结构体i2c_driver和i2c_client的关系较为简单,其中i2c_driver表示一个IIC设备驱动,i2c_client表示一个IIC设备。
关系如下图:
IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。
IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:
structi2c_adapter
{
structmodule*owner; //模块计数
unsigned intid; //alogorithm的类型,定义于i2c_id.h中
unsigned int class; //允许探测的驱动类型
conststructi2c_algorithm*algo; //指向适配器的驱动程序
void*algo_data; //指向适配器的私有数据,根据不同的情况使用方法不同
int(*client_register)(struct i2c_client*); //设备client注册时调用
int(*client_unregister(struct i2c_client*); //设备client注销时调用
u8level;
struct mutex bus_lock; //对总线进行操作时,将获得总线锁
struct mutex clist_lock; //链表操作的互斥锁
inttimeout; //超时
intretries; //重试次数
structdevicedev; //指向适配器的设备结构体
int nr;
struct list_head clients; //连接总线上的设备的链表
charname[48]; //适配器名称
structcompletion dev_released; //用于同步的完成量
};
每一个适配器对应一个驱动程序,该驱动程序描述了适配器与设备之间的通信方法:
struct i2c_algorithm
{
int (*master_xfer)(struct i2c_adapter*adap, struct i2c_msg*msg,intnum); //传输函数指针,指向实现IIC总线通信协议的函数,用来确定适配器支持那些传输类型
int (*smbus_xfer)(struct i2c_adapter*adap,u16 addr,unsigned shortflags,char read_write,u8command,intsize,union i2c_smbus_data *data); //smbus方式传输函数指针,指向实现SMBus总线通信协议的函数。
SMBus和IIC之间可以通过软件方式兼容,所以这里提供了一个函数,但是一般都赋值为NULL
u32 (*functionality)(struct i2c_adapter*); //返回适配器支持的功能
};
IIC设备驱动程序大致可以分为设备层和总线层。
设备层包括一个重要的数据结构,i2c_client。
总线层包括两个重要的数据结构,分别是i2c_adapter和i2c_algorithm。
一个i2c_algorithm结构表示适配器对应的传输数据方法。
3个数据结构关系:
IIC设备层次结构较为简单,但是写IIC设备驱动程序却相当复杂。
IIC设备驱动程序的步骤:
IIC子系统:
IIC子系统是作为模块加载到系统中的。
初始化函数:
staticint__initi2c_init(void)
{
intretval; //返回值,成功0,错误返回负值
retval=bus_register(&i2c_bus_type); //注册一条IIC的BUS总线
if(retval)
returnretval;
retval=class_register(&i2c_adapter_class); //注册适配器类,用于实现sys文件系统的部分功能
if(retval)
gotobus_err;
retval=i2c_add_driver(&dummy_driver); //将一个空驱动程序注册到IIC总线中
if(retval)
gotoclass_err;
return0;
class_err:
class_unregister(&i2c_adapter_class); //类注销
bus_err:
bus_unregister(&i2c_bus_type); //总线注销
returnretval;
}
structbus_typei2c_bus_type={
.name ="i2c",
.dev_attrs =i2c_dev_attrs,
.match =i2c_device_match,
.uevent =i2c_device_uevent,
.probe =i2c_device_probe,
.remove =i2c_device_remove,
.shutdown =i2c_device_shutdown,
.suspend =i2c_device_suspend,
.resume =i2c_device_resume,
};
staticstructclassi2c_adapter_class={
.owner =THIS_MODULE,
.name ="i2c-adapter",
.dev_attrs =i2c_adapter_attrs,
};
staticstructi2c_driverdummy_driver={
.driver.name ="dummy",
.probe =dummy_probe,
.remove =dummy_remove,
.id_table =dummy_id,
};
IIC子系统退出函数:
staticvoid__exiti2c_exit(void)
{
i2c_del_driver(&dummy_driver); //注销IIC设备驱动程序,主要功能是去掉总线中的该设备驱动程序
class_unregister(&i2c_adapter_class); //注销适配器类
bus_unregister(&i2c_bus_type); //注销I2C总线
}
适配器驱动程序是IIC设备驱动程序需要实现的主要驱动程序,这个驱动程序需要根据具体的适配器硬件来编写。
I2c_adapter结构体为描述各种IIC适配器提供了“模板",它定义了注册总线上所有设备的clients链表、指向具体IIC适配器的总线通信方法I2c_algorithm的algo指针、实现i2c总线的操作原子性的lock信号量。
但i2c_adapter结构体只是所有适配器的共有属性,并不能代表所有类型的适配器
s3c2440对应的适配器为:
structs3c24xx_i2c{
spinlock_t lock; //lock自旋锁
wait_queue_head_t wait; //等待队列头。
由于IIC设备是低速设备,所以可以采取“阻塞-中断”的驱动模型,即读写i2c设备的用户程序在IIC设备操作期间进入阻塞状态,待IIC操作完成后,总线适配器将引发中断,再将相应的中断处理函数中唤醒受阻的用户进程。
该队列用来放阻塞的进程
unsignedint suspended:
1; //设备是否挂起
structi2c_msg *msg; //从适配器到设备一次传输的单位,用这个结构体将数据包装起来便于操作,
unsignedint msg_num; //表示消息的个数
unsignedint msg_idx; //表示第几个消息。
当完成一个消息后,该值增加
unsignedint msg_ptr; //总是指向当前交互中要传送、接受的下一个字节,在i2c_msg.buf中的偏移量位置
unsignedint tx_setup; //表示写IIC设备寄存器的一个时间,这里被设置为50ms
unsignedint irq; //适配器申请的中断号
enums3c24xx_i2c_state state; //表示IIC设备目前的状态
unsignedlong clkrate; //时钟速率
void__iomem *regs; //IIC设备寄存器地址
structclk *clk; //对应的时钟
structdevice *dev; //适配器对应的设备结构体
structresource *ioarea; //适配器的资源
structi2c_adapter adap; //适配器主体结构体
#ifdefCONFIG_CPU_FREQ
structnotifier_block freq_transition;
#endif
};
enums3c24xx_i2c_state{
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
};
struct i2c_msg
{
__u16 addr; //IIC设备地址。
这个字段说明一个适配器在获得总线控
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- IIC 设备 驱动程序