QNX----I2C驱动框架

                                         QNX----I2C驱动框架

   I2C (interintegrated Circuit)是一种简单的串行协议,它以主从关系连接多个设备。多个主设备可以共享一个总线。在不同的事务中,同一设备可以同时充当主从设备。I2C规范定义了这些传输速度范围:

  • ≤ 100 Kbit/s
  • ≤ 400 Kbit/s
  • ≤ 3.4 Mbit/s

I2C框架由以下部分组成:

 

hardware/i2c/*   硬件接口

lib/i2c   资源管理器层

<hw/i2c.h> 定义硬件和应用程序接口的公共头文件

 

I2C总线最常见的应用是对从设备寄存器的低带宽、低速率访问,例如:编写音频编解码器、编程一个RTC程序、读取温度传感器数据等等。 通常,总线上只交换几个字节。可以将I2C主机实现为单线程资源管理器或专用应用程序。资源管理器接口的主要优点是:

1、它为应用程序开发人员提供了一个清晰、易于理解的思路。

2、它作为一个中介,在多个应用程序对一个或多个从设备之间进行访问,强制不同I2C接口之间的一致性。

3、对于专用的i2c总线应用程序,硬件访问库更有效;硬件接口定义了这个库的接口,有助于维护和代码可移植性。

 

1、硬件管理接口

typedef struct {
    size_t size;    /* size of this structure */
    int (*version_info)(i2c_libversion_t *version);
    void *(*init)(int argc, char *argv[]);
    void (*fini)(void *hdl);
    i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    int (*abort)(void *hdl, int rcvid);
    int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
    int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
    int (*driver_info)(void *hdl, i2c_driver_info_t *info);
    int (*ctl)(void *hdl, int cmd, void *msg, int msglen, 
               int *nbytes, int *info);
} i2c_master_funcs_t;

这是整个硬件接口函数,提供了10个接口函数,分别对10个接口函数进行介绍:

 

1、version_info函数来获取关于库版本的信息。这个函数的原型是:

   int version_info( i2c_libversion_t *version );

version参数是指向这个函数必须填充的i2c_libversion_t结构的指针。该结构定义如下:

typedef struct {
    unsigned char   major;
    unsigned char   minor;
    unsigned char   revision;
} i2c_libversion_t;

这个函数应该设置这个结构的成员如下:

version->major = I2CLIB_VERSION_MAJOR;
version->minor = I2CLIB_VERSION_MINOR;
version->revision = I2CLIB_REVISION;

 

2、init函数初始化主接口。该函数的原型是:

void *init( int argc, char *argv[]);

参数是在命令行上传递的。函数返回传递给所有其他函数的句柄,如果发生错误则返回NULL。这个函数主要根据命令行解析来进行I2C初始化。

 

3、fini函数清理驱动程序并释放与给定句柄关联的所有内存。该函数的原型是:

void fini(void *hdl);  

hdl init函数返回的句柄。

 

4、发送函数启动主发送。通信完成时将发送一个可选事件(可以释放数据缓冲区)。如果此函数失败,则未启动任何通信。

i2c_status_t send(
               void *hdl,
               void *buf,
               unsigned int len,
               unsigned int stop );

hdl :init函数返回的句柄;

buf:指向要发送的数据缓冲区的指针;

len:要发送的数据的长度(以字节为单位);

stop:如果这是非零的,函数将在发送完成时设置停止条件;

返回状态为:

I2C_STATUS_DONE:传输完成,并且没有错误。

I2C_STATUS_ERROR:传输错误

I2C_STATUS_NACK:没有ACK

I2C_STATUS_ARBL:失去了仲裁。

I2C_STATUS_BUSY:传输超时

I2C_STATUS_ABORT:传输终止

 

5、recv函数启动主接收。传输完成时将发送一个可选事件(可以使用数据缓冲区)。如果此函数失败,则未启动任何传输。

该函数的原型是:

i2c_status_t recv(
               void *hdl,
               void *buf,
               unsigned int len,
               unsigned int stop );

hdl :init函数返回的句柄;

buf:指向要接收的数据缓冲区的指针;

len:要接收的数据的长度(以字节为单位);

stop:如果这是非零的,函数将在发送完成时设置停止条件;

返回状态为:

I2C_STATUS_DONE:传输完成,并且没有错误。

I2C_STATUS_ERROR:传输错误

I2C_STATUS_NACK:没有ACK

I2C_STATUS_ARBL:失去了仲裁。

I2C_STATUS_BUSY:传输超时

I2C_STATUS_ABORT:传输终止

 

6、中止功能迫使主程序释放总线。当停止条件被发送时,它返回。该函数的原型是:

int abort(
      void *hdl,
      int rcvid );

hdl:  init函数返回的句柄。

rcvid:客户端的接收ID。

 

7、set_slave_addr函数指定目标从地址。该函数的原型是:

int set_slave_addr(
      void *hdl,
      unsigned int addr,
      i2c_addrfmt_t fmt );

hdl:  init函数返回的句柄。

addr: 目标从地址。

fmt:地址的格式; I2C_ADDRFMT_7BIT 和I2C_ADDRFMT_10BIT

 

 8、set_bus_speed函数指定总线速度。如果请求的总线速度无效,该函数将返回一个失败,并保持总线速度不变。该函数的原型是:

int set_bus_speed(
      void *hdl,
      unsigned int speed,
      unsigned int *ospeed );

hdl

 init函数返回的句柄。

speed

总线的速度。

ospeed

NULL或指向函数应该存储实际总线速度的位置的指针。

 

9、driver_info函数返回有关驱动程序的信息。该函数的原型是:

int driver_info(
      void *hdl,
      i2c_driver_info_t *info );

hdl

init函数返回的句柄

info

一个指向i2c_driver_info_t结构的指针,函数应该在该结构中存储信息:

typedef struct {

    uint32_t    speed_mode;

    uint32_t    addr_mode;

    uint32_t    reserved[2];

} i2c_driver_info_t;

 

10.ctl函数处理一个驱动程序特定的devctl()命令。该函数的原型是:

int ctl(

      void *hdl,

      int cmd,

      void *msg,

      int msglen,

      int *nbytes,

      int *info );

 

hdlinit函数返回的句柄;

cmd:设备命令;

msg一个指向消息缓冲区的指针。该函数可以更改缓冲区的内容;

msglen:消息缓冲区的长度,以字节为单位;

nbytes:返回的字节数。这个不能大于msglen;

Info指向函数可以存储devctl()返回状态信息的位置指针;

 

2、硬件访问接口

资源管理器接口使用此函数访问特定于硬件的函数。它必须得到执行;

int i2c_master_getfuncs(i2c_master_funcs_t *funcs, int tabsize);

硬件访问接口流程如下:

    #include <hw/i2c.h>
 
    i2c_master_funcs_t  masterf;
    i2c_libversion_t    version;
    i2c_status_t        status;
    void *hdl;
 
    i2c_master_getfuncs(&masterf, sizeof(masterf));//初始化硬件接口
 
    masterf.version_info(&version);
    if ((version.major != I2CLIB_VERSION_MAJOR) ||
        (version.minor > I2CLIB_VERSION_MINOR))
    {
        /* error */
        ...
    }
 
    hdl = masterf.init(...); //调用初始化函数
 
    masterf.set_bus_speed(hdl, ...); //设置总线速度
    masterf.set_slave_addr(hdl, ...); //设置从地址
 
    status = masterf.send(hdl, ...);//发送数据
    if (status != I2C_STATUS_DONE) {
        /* error */
        if (!(status & I2C_STATUS_DONE))
            masterf.abort(hdl);
    }
 
    status = masterf.recv(hdl, ...);//接收数据
    if (status != I2C_STATUS_DONE) {
        /* error */
        ...
    }
 
    masterf.fini(hdl);//完成I2C传输后,清理驱动程序并释放与给定句柄关联的所有内存

3、共享库的接口

当I2C主设备专用于单个应用程序使用时,可以将硬件接口代码编译为库并将其链接到应用程序。

为了提高代码的可移植性,应用程序应该调用i2c_master_getfuncs()来访问特定于硬件的函数。通过功能表访问硬件库,应用程序更容易加载和管理多个硬件库。

资源管理器接口以类似的方式使用硬件库。

4、资源管理器设计

资源管理器层实现为静态链接到硬件库的库,提供了一个单线程管理器,libi2c-master。

在启动时,resmgr层执行以下操作:

i2c_master_getfuncs(&masterf)
masterf.init()
masterf.set_bus_speed()

然后,资源管理器让自己在后台运行,下面是资源管理器处理这些devctl()命令:

  • DCMD_I2C_DRIVER_INFO calls masterf.driver_info().
  • DCMD_I2C_SET_BUS_SPEED and DCMD_I2C_SET_SLAVE_ADDR only update the state of the current connection.
  • DCMD_I2C_SENDDCMD_I2C_RECVDCMD_I2C_SENDRECVDCMD_I2C_MASTER_SEND, and DCMD_I2C_MASTER_RECV result in the following:
  • if (bus_speed has changed)
  •     masterf.set_bus_speed()
  • masterf.set_slave_address()
  • masterf.send() or masterf.recv()

资源管理器线程一直占用,直到传输完成并回复客户端,可以通过向资源管理器发送SIGTERM来终止它。

5、实例程序分析

1、首先在startup 对I2C进行硬件初始化,包括基地址、中断向量、时钟等等,这部分和硬件相关。

 

2、首先驱动从build 文件里进行加载,在dev生成设备节点,/dev/i2c1 等等

 

 

3、编写I2C驱动,其中init.c解析build加载参数,如I2C基地址地址、 中断号等等,并在lib.c 对所有的I2C驱动进行封装,调用i2c_master_getfuncs函数实现。

猜你喜欢

转载自blog.csdn.net/u011996698/article/details/82857146