在Linux环境下I2C总线EEPROM驱动程序应该如何设计

来源:
导读 大家好,我是本期栏目编辑小友,现在为大家讲解在Linux环境下I2C总线EEPROM驱动程序应该如何设计问题。 I2C(Inter-Integrated circuitie

大家好,我是本期栏目编辑小友,现在为大家讲解在Linux环境下I2C总线EEPROM驱动程序应该如何设计问题。

I2C(Inter-Integrated circuities 1 bus)是飞利浦开发的一种双线串行总线,用于连接微控制器及其外围设备。它是一种特殊的同步通信形式,具有接口线路少、控制方式简单、设备封装形式小、通信速率高等优点。在主从通信中,多个I2C总线设备可以同时连接到I2C总线,通信对象可以通过地址来识别。在基于MPC8250开发嵌入式Linux系统的过程中,我发现嵌入式系统中广泛使用的是I2C总线,I2C总线控制器的类型很多,系统提供的操作界面也大不相同。连接到I2C总线的从设备主要包括微控制器、EEPROM、实时时钟、模数转换器等。MPC8250处理器通过其内部的I2C总线控制器与连接到I2C总线的这些设备交换数据。由于I2C总线的特点,Linux I2C总线设备驱动程序的设计者在设计驱动程序时采用了独特的架构。开发I2C总线设备驱动程序的方法与开发通用设备驱动程序的方法有很大的不同。因此,I2C总线设备驱动程序的开发不仅涉及通用Linux内核驱动程序的知识。还有必要对I2C公交司机的架构有深入的了解。在开发过程中,作者使用了带AT24C01A的EEPROM作为器件模型,对I2C总线驱动器进行了测试。

1工作原理概述。

在介绍I2C巴士结构之前。理解两个概念:I2C总线控制器和I2C设备。I2C总线控制器为微控制器或微处理器提供了控制I2C总线的接口,控制I2C总线的所有特殊序列、协议、仲裁和定时。这是指MPC8250提供的I2C总线控制接口。I2C器件是指通过I2C总线与微控制器或微处理器相连的器件,如EEPROM、LCD驱动器等。

在串行数据通道中,I2C总线控制器可以配置为主模式或从模式。在开发过程中,MPC8250的I2C总线控制器作为主控设备工作在主控模式。连接到总线的I2C设备是作为从设备的AT24C01A EEPROM。主设备和从设备都可以在接收和发送状态下工作。总线必须由主设备控制,主设备产生串行时钟来控制总线的传输方向,并产生启动和停止条件。

1.1 I2C总线控制器。

I2C使用由串行数据线SDA和串行时钟线SCL组成的双线结构在外部集成电路和控制器之间交换数据。MPC8250的I2C总线控制器包括发送和接收单元、独立的波特率发生器和控制单元。如果I2C是主设备,发送和接收单元使用相同的时钟信号。然后由I2C波特率发生器产生时钟信号;如果I2C是从设备,则时钟信号由外部提供。

SDA和SCL是双向的,通过外部3.3 V上拉电阻连接到直流电压。当总线处于空闲状态时,SDA和SCL应处于高电平,I2C的一般配置模式如图1所示。

1 I2C配置模式。

I2C的接收和发送单元都是双缓冲器。发送数据时,数据以时钟速率从发送数据寄存器输出到移位寄存器的SDA线。接收数据时,数据从SDA线进入移位寄存器,然后进入接收寄存器。

1.2 I2C总线控制器和EEPROM的基本操作。

I2C总线在数据传输过程中有三种信号,即启动信号、结束信号和应答信号。

启动信号:当SCL为高电平时,SDA从高电平跳至低电平,开始数据传输;

结束信号:当SCL为高电平时,SDA从低电平跳到高电平,数据传输结束;

应答信号:接收一字节数据后,接收数据的设备向发送数据的设备发送特定的低电平脉冲。表示已收到数据。

当MPC8250的I2C总线空闲时,SDA和SCL都处于高电平,主设备通过发送启动信号开始发送过程。该信号的时序要求是,当SCL为高电平时,SDA有一个从高电平到低电平的跳变。初始条件后。必须是从设备的地址字节,其中高4位为设备类型标识(不同芯片类型定义不同,EEPROM一般应为1010),后面3位为芯片选择,最后1位为读写位。当它为1时,是读操作,当它为0时,是写操作,如图2所示。

图2 EEPROM器件的地址字节结构。

如果主设备想将数据写入EEPROM,主设备会在地址字节(R/W=0)中向EEPROM发送写请求,发送的地址字节后面是要发送的数据。发送完每一个字节的数据后,EEPROM将产生一个响应信号,主设备也将监控该响应信号。如果EEPROM在发送一个字节后没有返回响应信号,主设备将停止发送并产生结束信号。写操作的时序如图3所示。

图3 I2C主写操作时序。

要从EEPROM读取数据,设置读/写=1。EEPROM发送一个字节的数据后,主设备产生一个响应信号,通知EEPROM主设备需要更多的数据。

备产生的每个应答信号EEPROM将发送一个字节的数据。当主设备不发送应答信号并随后发送结束信号位时结束此操作。读操作的时序如图4所示。

  

  图4 I2C主设备读操作时序

  2 Linux中I2C总线驱动体系结构

  在Linux系统中,对于一个给定的I2C总线硬件配置系统,I2C总线驱动程序体系结构由I2C总线驱动和I2C设备驱动组成。其中I2C总线驱动包括一个具体的控制器驱动和I2C总线的算法驱动。一个算法驱动适用于一类总线控制器。而一个具体的总线控制器驱动要使用某一种算法。例如,Linux内核中提供的算法i2e-algo-8260可以用在MPC82xx系列处理器提供的I2C总线控制器上。Linux内核中提供了一些常见处理器如MPC82xx系列的算法驱动。对于I2C设备,基本上每种具体设备都有自己的基本特性。其驱动程序一般都需要特别设计。

  在I2C总线驱动程序体系结构中。使用数据结构Driver来表示I2C设备驱动,使用数据结构Client表示一个具体的I2C设备。而对于I2C总线

  控制器,各种总线控制器在进行数据传输时采用的算法有好多种,使用相同算法的控制器提供的控制接口也可能不同。在I2C总线驱动程序体系结构中,用数据结构Algorithm来表示算法,用数据结构Adapter来表示不同的总线控制器。Linux内核的I2C总线驱动程序体系结构如图5所示。

  

  图5 Linux内核I2C总线驱动程序体系结构

  在图5中,一个Client对象对应一个具体的I2C总线设备,而一种I2C设备的Driver可以同时支持多个Client。每个Adapter对应一个具体的I2C总线控制器。不同的I2C总线控制器可以使用相同的算法Algorithm。i2c-core是I2C总线驱动程序体系结构的核心,在这个模块中,除了为总线设备驱动提供了一些统一的调用接口来访问具体的总线驱动程序功能,以进行读写或设置操作外,还提供了将各种支持的总线设备驱动和总线驱动添加到这个体系中的方法,以及当不再使用这些驱动时将其从体系中删除的方法。i2c-core将总线驱动程序体系一分为二,相互独立。可以针对某个I2C总线设备来设计一个I2C设备驱动程序,而不需要关心系统的I2C总线控制器是何种类型,所以提高了其可移植性。另一方面,在设计I2C总线驱动时也可以不要考虑其将用来支持何种设备。因为i2c-core提供了统一的接口,所以也为设计这两类驱动

  提供了方便。

  3 开发实例

  Linux内核已经提供了I2C驱动中所需要的基本模块。i2c-core、i2c-dev和i2c-proc是总线控制器和I2C设备所需要的核心模块。对于MPC8250处理器,内核中还有MPC8260的算法模块i2c-algo-8260,它也适用于MPC8250的I2C控制接口。这些模块程序在默认条件下是不会被编译到内核里的,所以需要在配置Linux内核时把这些模块选中。在笔者的开发中需要实现的是I2C总线控制器驱动和I2C设备EEPROM驱动。

  3.1 I2C总线控制器驱动的设计

  MPC8250的I2C总线驱动程序由i2c-algo-8260算法模块和MPC8250具体的I2C总线控制器驱动组成。其中i2c-algo-8260算法模块已经在内核中实现,所以主要实现FC总线控制器驱动。

  i2c-algo-8260算法模块主要用来描述MPC82xx处理器如何在I2C总线上传输数据。该模块中主要实现了MPC82xx处理器上I2C总线的初始化、读写、ioctl控制和中断请求等功能。另外,还有i2c_8260_add_bus和i2c_8260_del_bus两个函数,它们是使用这个算法的Adapter初始化时和退出时调用的函数,用来注册和注销一个总线控制器,需要从模块导出。这些函数功能都被封装在一个i2c-algorithm结构中,传递给使用这个算法的Adapter。算法模块中这些函数需要调用特定控制器模块中的函数来实现具体的操作。

  在I2C总线控制器驱动模块中主要要实现两个结构体i2c_adapter和i2c_algo_8260_data,定义这两个结构中的函数指针成员。并且用己经初始化好的i2c_algo_826o_data结构来初始化struct i2c_adapter结构的algo_data成员变量。其中,定义i2e_algo_8260_data结构为:

  struct i2c_algo_8260_data rw8250_data={

  seTIsr:rw8250_install_isr

  };

  这里的成员变量rw8250_install__isr提供了MPC8250的I2C总线控制器向内核申请中端请求的功能。结构体i2c_adapter定义如下:

  struct i2c_adapter rw8250_ops={“rw8250”,I2C_HW_

  MPC8250_RW8250,NULL,&rw8250_data,rw8250_inc_use,rw8250_dec_use,rw8250_reg,rw8250_unreg,};

  其中,“rw8250”是该总线控制器的标识名,宏名I2C_HW_MPC8250_RW8250定义了内核中注册该适配器的ID号,而成员函数rw8250_inc_use和rw8250_dec_use用来增加和减少内核使用该模块的次数。

  另外,该模块还要完成一个注册模块时的初始化函数rw8250_iic_init,在该函数中要初始化I2C控制器使用的通用端口号PortD14、PortD15,并在双端口RAM 中为发送和接受数据的缓冲区分配空间。函数rw8250_iic_init在进行模块初始化时将被init_module调用。

  总之。I2C控制器模块中设计的这些函数都是为i2c_algo_8650算法模块服务的。最后需要封装在i2c-adapter结构中。通过i2c_algo_8260_data算法模块中输出的接口函数传递给算法模块。

  3.2 I2C设备驱动的设计

  I2C设备EEPROM 驱动除了要根据EEPROM的具体特性进行设计外。还要考虑I2C总线驱动程序体系结构的特性。在EEPROM设备驱动程序中需要实现一个i2c_driver结构。每个对应于具体设备的Client都从这个结构来构造。在i2c_driver结构中有两个函数attach_adapter和detach_client必须要实现。i2c_driver结构的定义如下:

  struct i2c_driver eeprom_driver = {

  /*name*/ “I2C_EEPROM_DRIVER”,/*id*/I2C_DRIVERID_EEPROM,

  /*flags*/ I2C_DF_NOTIFY,/*attach_ adapter*/&eeprom_attach_adapter,/*detach_client */&eeprom_detach_client,

  /*command*/&eeprom_command,/*inc_use*/ &eeprom_inc_use,/*dec_use*/ &eeprom_dec_use

  };

  在设备驱动中。向EEPROM 写数据通过调用i2c-core提供的i2c_master_send函数来完成。从EEPROM 读取数据通过另一个函数i2c_master_read来完成。与一般设备驱动不同的地方就是在EEPROM驱动模块初始函数中要调用i2c-core提供的i2c_add_driver函数来注册该设备。在模块退出函数中调用i2c_del_driver函数来注销该设备。

  4 结束语

  I2C总线具有控制简单、通信速率高等优点,作为一种2线双向同步串行数据总线,它为嵌入式系统设计提供了一种完善的集成电路间的串行总线扩展技术,大大简化了应用系统的硬件设计,为实现应用系统的模块化设计创造了极为有利的条件。同时,在很多情况下需要对系统中的某些动态信息进行掉电保护。在数据量不太大的场合下,通过I2C总线连接的EEPROM在这方面就比较能发挥作用。而Linux作为一种新的操作系统,目前在嵌入式系统中的应用非常广泛。其发展前景无法估量。由于Linux源码开放,且非常易于移植,为其编写设备驱动程序相对容易。本文介绍了Linux下I2C总线EEPROM驱动程序的一般设计方法。

  I2C (Inter-Integrated Circuit1总线是一种由Philips公司开发的2线式串行总线,用于连接微控制器及其外围设备。它是同步通信的一种特殊形式,具有接口线少、控制方式简单、器件封装形式小、通信速率较高等优点。在主从通信中,可有多个I2C总线器件同时接到I2C总线上,通过地址来识别通信对象。笔者在开发基于MPC8250的嵌入式Linux系统的过程中发现I2C总线在嵌入式系统中应用广泛,I2C总线控制器的类型比较多,对系统提供的操作接口差别也很大。与I2C总线相连的从设备主要有微控制器、EEPROM、实时时钟、A/D转换器等.MPC8250处理器正是通过内部的I2C总线控制器来和这些连接在I2C总线上的设备进行数据交换的。由于I2C总线的特性,Linux的I2C总线设备驱动程序的设计者在设计驱动程序时采用了独特的体系结构。使开发I2C总线设备驱动程序与开发一般设备驱动程序的方法具有很大差别。因此,开发I2C总线设备驱动程序除了要涉及一般Linux内核驱动程序的知识外。还要对I2C总线驱动的体系结构有深入的了解。笔者在开发过程中使用设备型号为AT24C01A的EEPROM 来测试I2C总线驱动。

  1 工作原理概述

  在介绍I2C总线结构之前。要搞清楚两个概念:I2C总线控制器和I2C设备。I2C总线控制器为微控制器或微处理器提供控制I2C总线的接口,它控制所有I2C总线的特殊序列、协议、仲裁、时序,这里指MPC8250提供的I2C总线控制接口。I2C设备是指通过I2C总线与微控制器或微处理器相连的设备,如EEPROM、LCD驱动器等,这里指EEPROM。

  在一个串行数据通道中.I2C总线控制器可以配置成主模式或从模式。开发过程中,MPC8250的I2C总线控制器工作在主模式,作为主设备;与总线相连的I2C设备为AT24C01A型EEPROM,作为从设备。主设备和从设备都可以工作于接收和发送状态。总线必须由主设备控制,主设备产生串行时钟控制总线的传输方向,并产生起始和停止条件。

  1.1 I2C总线控制器

  I2C使用由串行数据线SDA 和串线时钟线SCL组成的两线结构来在外部集成电路与控制器之间交换数据。MPC8250的I2C总线控制器包括发送和接收单元、一个独立的波特率发生器和一个控制单元。发送和接收单元使用相同的时钟信号,如果I2C为主设备。那么时钟信号由I2C的波特率发生器产生;如果I2C为从设备,时钟信号则由外部提供。

  SDA和SCL为双向的,通过外部+3.3 V上拉电阻连接至正向电压。当总线处于空闲状态时,SDA和SCL都应是高电平,I2C通常的配置模式如图1所示。

  

  图1 I2C配置模式

  I2C的接收和发送单元均为双缓存,在数据发送时,数据从发送数据寄存器到移位寄存器,以时钟速率输出到SDA线;在数据接收时,数据从SDA线进入移位寄存器,然后进入接收寄存器。

  1.2 I2C总线控制器和EEPROM 的基本操作

  I2C总线在传送数据过程中共有3种类型的信号,分别是:开始信号、结束信号和应答信号。

  开始信号:SCL为高电平时,SDA 由高电平向低电平跳变,开始传送数据;

  结束信号:SCL为高电平时,SDA由低电平向高电平跳变,传送数据结束;

  应答信号:接收数据的设备在接收到一个字节数据后, 向发送数据的设备发出特定的低电平脉冲。表示已收到数据。

  当MPC8250的I2C总线空闲时,其SDA和SCL均为高电平,主设备通过发送一个开始信号启动发送过程。这个信号的时序要求是当SCL为高时,SDA出现一个由高到低的电平跳变。在起始条件之后。必须是从设备的地址字节,其中高4位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着3位为片选,最后1位为读写位,当为1时为读操作,为0时为写操作,如图2所示。

  

  图2 EEPROM设备地址字节结构

  如果主设备要向EEPROM 中写数据,在地址字节中主设备向EEPROM发出一个写请求(R/W=0),发送的地址字节之后紧跟着要发送的数据。每发送一个字节的数据后EEPROM就会产生一个应答信号,主设备也会监控应答信号,如果在发送一个字节后EEPROM没有返回应答信号,则主设备就会停止发送,并生成一个结束信号。写操作的时序如图3所示。

  

  图3 I2C主设备写操作时序

  要从EEPROM 中读取数据时,应设置R/W=1。在EEPROM发送完一个字节的数据后,主设备产生一个应答信号来响应,告知EEPROM主设备要求更多的数据,对应主设备产生的每个应答信号EEPROM将发送一个字节的数据。当主设备不发送应答信号并随后发送结束信号位时结束此操作。读操作的时序如图4所示。

  

  图4 I2C主设备读操作时序

  2 Linux中I2C总线驱动体系结构

  在Linux系统中,对于一个给定的I2C总线硬件配置系统,I2C总线驱动程序体系结构由I2C总线驱动和I2C设备驱动组成。其中I2C总线驱动包括一个具体的控制器驱动和I2C总线的算法驱动。一个算法驱动适用于一类总线控制器。而一个具体的总线控制器驱动要使用某一种算法。例如,Linux内核中提供的算法i2e-algo-8260可以用在MPC82xx系列处理器提供的I2C总线控制器上。Linux内核中提供了一些常见处理器如MPC82xx系列的算法驱动。对于I2C设备,基本上每种具体设备都有自己的基本特性。其驱动程序一般都需要特别设计。

  在I2C总线驱动程序体系结构中。使用数据结构Driver来表示I2C设备驱动,使用数据结构Client表示一个具体的I2C设备。而对于I2C总线

  控制器,各种总线控制器在进行数据传输时采用的算法有好多种,使用相同算法的控制器提供的控制接口也可能不同。在I2C总线驱动程序体系结构中,用数据结构Algorithm来表示算法,用数据结构Adapter来表示不同的总线控制器。Linux内核的I2C总线驱动程序体系结构如图5所示。

  

  图5 Linux内核I2C总线驱动程序体系结构

  在图5中,一个Client对象对应一个具体的I2C总线设备,而一种I2C设备的Driver可以同时支持多个Client。每个Adapter对应一个具体的I2C总线控制器。不同的I2C总线控制器可以使用相同的算法Algorithm。i2c-core是I2C总线驱动程序体系结构的核心,在这个模块中,除了为总线设备驱动提供了一些统一的调用接口来访问具体的总线驱动程序功能,以进行读写或设置操作外,还提供了将各种支持的总线设备驱动和总线驱动添加到这个体系中的方法,以及当不再使用这些驱动时将其从体系中删除的方法。i2c-core将总线驱动程序体系一分为二,相互独立。可以针对某个I2C总线设备来设计一个I2C设备驱动程序,而不需要关心系统的I2C总线控制器是何种类型,所以提高了其可移植性。另一方面,在设计I2C总线驱动时也可以不要考虑其将用来支持何种设备。因为i2c-core提供了统一的接口,所以也为设计这两类驱动

  提供了方便。

  3 开发实例

  Linux内核已经提供了I2C驱动中所需要的基本模块。i2c-core、i2c-dev和i2c-proc是总线控制器和I2C设备所需要的核心模块。对于MPC8250处理器,内核中还有MPC8260的算法模块i2c-algo-8260,它也适用于MPC8250的I2C控制接口。这些模块程序在默认条件下是不会被编译到内核里的,所以需要在配置Linux内核时把这些模块选中。在笔者的开发中需要实现的是I2C总线控制器驱动和I2C设备EEPROM驱动。

  3.1 I2C总线控制器驱动的设计

  MPC8250的I2C总线驱动程序由i2c-algo-8260算法模块和MPC8250具体的I2C总线控制器驱动组成。其中i2c-algo-8260算法模块已经在内核中实现,所以主要实现FC总线控制器驱动。

  i2c-algo-8260算法模块主要用来描述MPC82xx处理器如何在I2C总线上传输数据。该模块中主要实现了MPC82xx处理器上I2C总线的初始化、读写、ioctl控制和中断请求等功能。另外,还有i2c_8260_add_bus和i2c_8260_del_bus两个函数,它们是使用这个算法的Adapter初始化时和退出时调用的函数,用来注册和注销一个总线控制器,需要从模块导出。这些函数功能都被封装在一个i2c-algorithm结构中,传递给使用这个算法的Adapter。算法模块中这些函数需要调用特定控制器模块中的函数来实现具体的操作。

  在I2C总线控制器驱动模块中主要要实现两个结构体i2c_adapter和i2c_algo_8260_data,定义这两个结构中的函数指针成员。并且用己经初始化好的i2c_algo_826o_data结构来初始化struct i2c_adapter结构的algo_data成员变量。其中,定义i2e_algo_8260_data结构为:

  struct i2c_algo_8260_data rw8250_data={

  seTIsr:rw8250_install_isr

  };

  这里的成员变量rw8250_install__isr提供了MPC8250的I2C总线控制器向内核申请中端请求的功能。结构体i2c_adapter定义如下:

  struct i2c_adapter rw8250_ops={“rw8250”,I2C_HW_

  MPC8250_RW8250,NULL,&rw8250_data,rw8250_inc_use,rw8250_dec_use,rw8250_reg,rw8250_unreg,};

  其中,“rw8250”是该总线控制器的标识名,宏名I2C_HW_MPC8250_RW8250定义了内核中注册该适配器的ID号,而成员函数rw8250_inc_use和rw8250_dec_use用来增加和减少内核使用该模块的次数。

  另外,该模块还要完成一个注册模块时的初始化函数rw8250_iic_init,在该函数中要初始化I2C控制器使用的通用端口号PortD14、PortD15,并在双端口RAM 中为发送和接受数据的缓冲区分配空间。函数rw8250_iic_init在进行模块初始化时将被init_module调用。

  总之。I2C控制器模块中设计的这些函数都是为i2c_algo_8650算法模块服务的。最后需要封装在i2c-adapter结构中。通过i2c_algo_8260_data算法模块中输出的接口函数传递给算法模块。

  3.2 I2C设备驱动的设计

  I2C设备EEPROM 驱动除了要根据EEPROM的具体特性进行设计外。还要考虑I2C总线驱动程序体系结构的特性。在EEPROM设备驱动程序中需要实现一个i2c_driver结构。每个对应于具体设备的Client都从这个结构来构造。在i2c_driver结构中有两个函数attach_adapter和detach_client必须要实现。i2c_driver结构的定义如下:

  struct i2c_driver eeprom_driver = {

  /*name*/ “I2C_EEPROM_DRIVER”,/*id*/I2C_DRIVERID_EEPROM,

  /*flags*/ I2C_DF_NOTIFY,/*attach_ adapter*/&eeprom_attach_adapter,/*detach_client */&eeprom_detach_client,

  /*command*/&eeprom_command,/*inc_use*/ &eeprom_inc_use,/*dec_use*/ &eeprom_dec_use

  };

  在设备驱动中。向EEPROM 写数据通过调用i2c-core提供的i2c_master_send函数来完成。从EEPROM 读取数据通过另一个函数i2c_master_read来完成。与一般设备驱动不同的地方就是在EEPROM驱动模块初始函数中要调用i2c-core提供的i2c_add_driver函数来注册该设备。在模块退出函数中调用i2c_del_driver函数来注销该设备。

  4 结束语

  I2C总线具有控制简单、通信速率高等优点,作为一种2线双向同步串行数据总线,它为嵌入式系统设计提供了一种完善的集成电路间的串行总线扩展技术,大大简化了应用系统的硬件设计,为实现应用系统的模块化设计创造了极为有利的条件。同时,在很多情况下需要对系统中的某些动态信息进行掉电保护。在数据量不太大的场合下,通过I2C总线连接的EEPROM在这方面就比较能发挥作用。而Linux作为一种新的操作系统,目前在嵌入式系统中的应用非常广泛。其发展前景无法估量。由于Linux源码开放,且非常易于移植,为其编写设备驱动程序相对容易。本文介绍了Linux下I2C总线EEPROM驱动程序的一般设计方法。

技术专区 低成本开发系统现在正处于物联网期望膨胀峰值期 因特尔Cyclone 10 LP FPGA评估板电路图集及PCB装配图 采用32位MCU系列对新型无磁水表设计 一个嵌入式或者X86的工业控制板上,少不了CAN口! 以MSP432主机微控制器的软件与硬件集成解决方案

标签:

版权声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢您的支持与理解。