Chinaunix首页 | 论坛 | 博客
  • 博客访问: 515280
  • 博文数量: 87
  • 博客积分: 4086
  • 博客等级: 上校
  • 技术积分: 900
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-23 15:55
文章分类

全部博文(87)

文章存档

2012年(3)

2010年(13)

2009年(7)

2008年(64)

我的朋友

分类: LINUX

2008-10-31 18:59:03

Distribution Guide
Device drivers
Inter-IC (I2C)
ST Logo
   


Inter-IC (I2C)

The I2C bus is a simple interconnect for low data rate communications in embedded applications. Only two signals are required, and multiple devices can be attached to a single bus as shown in Figure 2: Multiple devices on a single bus.

Figure 2: Multiple devices on a single bus

More recent variants support operation up to 2 MHz. It is now used by a large number of manufacturers, and parts are available for numerous monitoring, control and audio/visual applications. For more details on the bus and protocols, see the site.

A variant of I2C, called , is now frequently used in x86 based PC systems for system management and monitoring. It is frequently possible to mix and match I2C and SMBus devices, although there are some differences. The differences are discussed in a Maxim .

Linux support for I2C grew out of the project to support the hardware health monitors such as the LM78 and LM75, but has now been extended to other classes of hardware. The site has some documentation on how to use the various I2C software interfaces, both from user and kernel mode.

The ST distribution 2.0 supports two different methods for interfacing with I2C devices:

  • a software which drives the PIO pins to generate the clock and data signals (Bit-Banging),

  • a hardware implementation which use the Synchronous Serial Controller (SSC) peripheral to generate the signals.

I2C protocol

I2C devices communicate over the physical bus using a standard protocol. Each I2C operation (called a transaction) consists of a "Start Condition" followed by the bus master writing a "Slave Address" to select a device. The type of operation (read or write) is written and then, if the slave responds, the master writes/reads data byte-by-byte to/from the bus until the transaction is closed with a "Stop Condition".

Note: After a stop condition, it is mandatory for the bus to be idle for a number of microseconds.

An I2C operation is shown in .

Figure 3: An I2C operation

When there are repeated transactions, the I2C standard supports a "Repeat Start Condition". This avoids too much time being spent on the "Stop Condition - Idle - Start Condition" phase.

This "RepStart" mode is shown in .

Figure 4: The RepStart mode

This mode is supported by the SSC driver in Linux.

Configuring the kernel

By default, Linux kernel distributions 2.0 and later, include I2C "Bit-Banging" support. This support must be configured as before (see ). Bring up the Linux Kernel Configuration screen and enter the following sections in the order given:

  1. Device Drivers ---> I2C Support ---> I2C Support

  2. Device Drivers ---> I2C Support ---> I2C device interface

  3. Either:

    • Device Drivers ---> I2C Hardware Bus Support ---> STMicroelectronics I2C Support ---> I2C bit-banging PIO driver, or

    • Device Drivers ---> I2C Hardware Bus Support ---> STMicroelectronics I2C Support ---> I2C hardware SSC driver.

The I2C based on the SSC is supported on the following platforms:

  • STi5528 Espresso (Silicon cuts 1.3 and 2.0),

  • STi5528 Evaluation board (mb376),

  • STb7100 (mb411),

  • ST220 Evaluation board (mb392 - STm8000).

The Linux I2C device model



Figure 5: The Linux I2C device model

The I2C algorithm and adapter provide the low-level hardware control which drive the bus. It is these functions that are implemented by the ST Linux drivers in order to drive the I2C on ST hardware. For further technical details, see the at .

The I2C driver is a kernel driver which controls a particular type of device. Each instance of a device (for example, the same type of device on different buses) is handled as a separate client.

More information can be found in the linux kernel source code directory under Documentation/i2c/summary.

Linux I2C user-space driver

I2C bus user-mode interface

The Linux kernel I2C "I2C device interface" provides an I2C driver where the clients are associated with all devices on a particular I2C bus. A standard character device interface for selecting a particular device on a particular bus, setting its properties and reading/writing to the bus and device from user-space is then provided.

Each STMicroelectronics' SoC can be equipped with a number of SSC devices, or a number of I2C buses can be set up on different sets of PIO pins. In both cases, the software drivers assume that the buses are independent.

Each I2C bus is assigned a device node of /dev/i2c-n (where n is the number of the bus).

The device nodes are character devices with permission, device name, and major and minor numbers:

crw-rw-rw- /dev/i2c-n 89 n

More information can be found in the Linux kernel source code directory under Documentation/i2c/dev-interface. Example code for using this interface is given below.

I2C driver standard ioctl calls

  • I2C_RETRIES
    This sets the number of times a device address should be polled when not acknowledging. The default value is 1.

  • I2C_TIMEOUT
    This sets the timeout. The default value is 4 seconds (for each I2C message).

  • I2C_SLAVE
    This sets the slave address.

  • I2C_RDWR
    This is the combined R/W transfer (one stop bit only).
     
    During a normal read or write operation the I2C kernel subsystem builds an internal struct i2c_msg object. This object generates a single I2C transaction (with stop bit).
     
    If the user application has to generate more I2C transactions or it needs the "Repeat Start Condition" then it has to use the I2C_RDWR ioctl.
     
    With the I2C_RDWR ioctl the user application releases to the kernel, a struct i2c_rdwr_ioctl_data object which holds a pointer to the struct i2c_msg and the array size. When the user application uses the I2C_RDWR ioctl it does not need the I2C_SLAVE ioctl because it has to specify the following for each struct i2c_msg object:

    • the slave address in the addr field,

    • the operation type (read or write) in the flags field,

    • the buffer length in the len field,

    • the pointer to the data buffer in the buf field.

    The i2c_msg and i2c_rdwr_ioctl_data struct definitions are shown below:

    /*
    * In ./include/linux/i2c.h
    */
    struct i2c_msg {
    /* slave address */
    __u16 addr;
    /* flags == 0 means write operations
    flags == 1 means read operations */
    __u16 flags;
    /* msg length */
    __u16 len;
    /* pointer to msg data */
    __u8 *buf;
    };
     
    /*
    * In ./include/linux/i2c-dev.h
    */
    struct i2c_rdwr_ioctl_data {
    /* pointers to i2c_msgs */
    struct i2c_msg __user *msgs;
    /* number of i2c_msgs */
    __u32 nmsgs;
    };
I2C driver non-standard IOCTL calls

  • I2C_STM_IOCTL_FAST
    This enables I2C fast mode (400 Kbits/s). The ioctl value is defined in ./drivers/i2c/algos/i2c-algo-stm.h. The default value is 0.

User-space example C code I2C software

This simple example demonstrates how to use the I2C character device interface from user space. It reads size bytes at the memory address mem_addr from an eeprom (M24c64 with slave address 0x50) on the /dev/i2c-x bus.

In this example, the user application does not manage an i2c_msg struct and it sets the slave address with the I2C_SLAVE ioctl.

#include 
#include
#include
#include
#include
#include
#include
 
#define I2C_RETRIES 0x0701
#define I2C_TIMEOUT 0x0702
#define I2C_SLAVE 0x0703
 
int main(int argc, char **argv)
{
unsigned int fd;
unsigned short mem_addr;
unsigned short size;
unsigned short idx;
#define BUFF_SIZE 32
char buf[BUFF_SIZE];
char cswap;
union
{
unsigned short addr;
char bytes[2];
} tmp;
 
if ( argc < 3 ) {
printf("Use:\n%s /dev/i2c-x mem_addr size\n",argv[0]);
return 0;
}
sscanf(argv[2],"%d",&mem_addr);
sscanf(argv[3],"%d",&size);
 
if ( size > BUFF_SIZE) size = BUFF_SIZE;
 
fd = open(argv[1], O_RDWR);
 
if (!fd) {
printf("Error on opening the device file\n");
return 0;
}
 
ioctl(fd,I2C_SLAVE, 0x50); /* set the eeprom address */
ioctl(fd,I2C_TIMEOUT,1); /* set the timeout */
ioctl(fd,I2C_RETRIES,1); /* set the retries */
 
for ( idx= 0; idx < size; ++idx, ++mem_addr)
{
tmp.addr = mem_addr;
cswap = tmp.bytes[0];
tmp.bytes[0] = tmp.bytes[1];
tmp.bytes[1] = cswap;
write(fd,&tmp.addr,2);
read(fd,&buf[idx],1);
}
buf[size] = 0;
close(fd);
printf("Read %d char: %s\n",size,buf);
return 0;
}

The next example shows how to use the I2C_RDWR ioctl.

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define MAX_I2C_MSG 2

#define I2C_RETRIES 0x0701
#define I2C_TIMEOUT 0x0702
#define I2C_RDWR 0x0707
 
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_RD 0x01
__u8 *buf; /* pointer to msg data */
};
struct i2c_rdwr_ioctl_data {
struct i2c_msg *msgs; /* pointers to i2c_msgs */
int nmsgs; /* number of i2c_msgs */
};
 
int main(int argc, char **argv)
{
struct i2c_rdwr_ioctl_data work_queue;
unsigned int idx;
unsigned int fd;
unsigned short start_address;
int ret;
 
if ( argc < 4 ) {
printf("Usage:\n%s /dev/i2c-x start_addr\n",argv[0]);
return 0;
}
 
fd = open(argv[1], O_RDWR);
 
if (!fd) {
printf("Error on opening the device file\n");
return 0;
}
sscanf(argv[2],"%x",&start_address);
work_queue.nmsgs = MAX_I2C_MSG;
 
work_queue.msgs = (struct i2c_msg*)malloc(work_queue.nmsgs*sizeof(struct i2c_msg));
if (!work_queue.msgs) {
printf("Memory alloc error\n");
close(fd);
return 0;
}
 
for ( idx = 0; idx < work_queue.nmsgs; ++idx)
{
(work_queue.msgs[idx]).len = 0;
(work_queue.msgs[idx]).addr = start_address + idx;
(work_queue.msgs[idx]).buf = NULL;
}
 
ioctl(fd,I2C_TIMEOUT,2); /* set the timeout */
ioctl(fd,I2C_RETRIES,1); /* set the retries */
 
ret = ioctl(fd,I2C_RDWR,(unsigned long)&work_queue);
 
if ( ret < 0 ) {
printf("Error during I2C_RDWR ioctl with error code: %d\n",ret);
}
 
close(fd);
return;
}

Note: When the user application uses the I2C_RDWR ioctl, the I2C kernel subsystem starts to send the I2C messages, but if during the transaction the kernel meets some problem (for example, a NOTACK) it will stop all the I2C transactions and return an error code to the user application.

Linux I2C kernel drivers

Implementing an I2C driver is relatively straightforward. A good introduction is given in the Linux kernel source code directory under Documentation/i2c/writing-clients.

For further technical details, see the at .

阅读(3760) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~