dnx RTOS 2.2.0 "Eagle"
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
I2C Driver

Detailed Description

Description

Driver handles I2C peripheral.

Supported architectures

Details

Meaning of major and minor numbers

Some manufactures enumerate devices starting from 1 instead of 0 (e.g. ST). In this case major number starts from 0 and is connected to the first device e.g. I2C1.

Numeration restrictions

Number of peripherals determines how big the major number can be. If there is only one I2C peripheral then the major number is always 0. Number of devices (minor number) can be theoretically up to 256 or to limits of memory capacity. Minor number concerns communication that dnx RTOS is a master. When dnx RTOS is a slave then only one minor device should be used.

Driver initialization

To initialize driver the following code can be used:

driver_init("I2C", 0, 0, "/dev/I2C0-0"); // device path can be more descriptive
driver_init("I2C", 0, 1, "/dev/I2C0-1"); // next I2C device on I2C0 bus

Driver release

To release driver the following code can be used:

driver_release("I2C", 0, 0);
driver_release("I2C", 0, 1);

Driver configuration

Driver configuration should be done before usage and after initialization. The best place to do this is user application or initialization daemon.

Master mode

To configure particular I2C device as master the ioctl() function shall be used as follow (EEPROM example):

#include <stdio.h>
#include <stdbool.h>
#include <sys/ioctl.h>
static const I2C_config_t cfg = {
.address = 0xA0, // EEPROM address
.sub_addr_mode = I2C_SUB_ADDR_MODE__2_BYTES, // EEPROM up to 64KiB
.addr_10bit = false, // 7-bit address
.slave_mode = false // master mode
};
static const char *dev_path = "/dev/I2C0-0";
FILE *dev = fopen(dev_path, "r+");
if (dev) {
if (ioctl(fileno(dev), IOCTL_I2C__CONFIGURE, &cfg) != 0) {
perror(dev_path);
}
fclose(dev);
} else {
perror(dev_path);
}
...

Slave mode

To configure particular I2C device as slave the ioctl() function shall be used as follow:

#include <stdio.h>
#include <stdbool.h>
#include <sys/ioctl.h>
static const I2C_config_t cfg = {
.address = 0xA0, // microcontroller address
.sub_addr_mode = I2C_SUB_ADDR_MODE__DISABLED, // don't care in slave mode
.addr_10bit = false, // 7-bit address
.slave_mode = true // slave mode
};
static const char *dev_path = "/dev/I2C0-0";
FILE *dev = fopen(dev_path, "r+");
if (dev) {
if (ioctl(fileno(dev), IOCTL_I2C__CONFIGURE, &cfg) != 0) {
perror(dev_path);
}
fclose(dev);
} else {
perror(dev_path);
}
...

Meaning of Sub-Address Mode

Some I2C devices need to set internal address (called pointer) in purpose of selection of register or memory address. Small devices e.g. RTC or sensors uses mostly 1-byte sub-address to select particular register to write or read. EEPROM devices uses also 1-byte sub-address but there is many devices that uses 2-byte sub-address mode. Some devices do not use any sub-addressing mode. Sub-address mode can be selected in configuration by using particular enumeration value. Sub-address value is controlled by fseek() function. The sub-address functionality works only in master mode.

Sub-addressing disabled

When this selection is used then I2C driver does not send any sub-address bytes. Write and read operations are presented below:

Write: [S][ADDR+W][DATA0][DATA1][DATAn]...[P]

Read: [S][ADDR+R][DATA0][DATA1][DATAn]...[P]

1-byte sub-addressing

When this selection is used then I2C driver send 1 byte of sub-address. In case of read sequence, the I2C driver first send POINTER according to fseek() value and next read data sequence.

Write: [S][ADDR+W][POINTER][DATA1][DATAn]...[P]

Read: [S][ADDR+W][POINTER][Sr][ADDR+R][DATA1][DATAn]...[P]

2-byte sub-addressing

When this selection is used then I2C driver send 2 bytes of sub-address. In case of read sequence, the I2C driver first send POINTER according to fseek() value and next read data sequence.

Write: [S][ADDR+W][POINTER:1][POINTER:0][DATA1][DATAn]...[P]

Read: [S][ADDR+W][POINTER:1][POINTER:0][Sr][ADDR+R][DATA1][DATAn]...[P]

3-byte sub-addressing

When this selection is used then I2C driver send 3 bytes of sub-address. In case of read sequence, the I2C driver first send POINTER according to fseek() value and next read data sequence.

Write: [S][ADDR+W][POINTER:2][POINTER:1][POINTER:0][DATA1][DATAn]...[P]

Read: [S][ADDR+W][POINTER:2][POINTER:1][POINTER:0][Sr][ADDR+R][DATA1][DATAn]...[P]

Data write

Master mode

Data to the I2C device can be written as regular file. Example assumptions:

#include <stdio.h>
#include <stdbool.h>
#include <dnx/misc.h>
#include <sys/ioctl.h>
static const char *dev_path = "/dev/ee";
static const char *blk1 = "Data block 1 at 0x0";
static const char *blk2 = "Data block 2 at 0x100";
static const I2C_config_t cfg = {
.address = 0xA0, // EEPROM address
.sub_addr_mode = I2C_SUB_ADDR_MODE__2_BYTES, // EEPROM up to 64KiB
.addr_10bit = false, // 7-bit address
.slave_mode = false // master mode
};
int_main(ee_ex, STACK_DEPTH_MEDIUM, int argc, char *argv[])
{
FILE *dev = fopen(dev_path, "r+");
if (dev) {
// set I2C device configuration
// write blk1 at address 0x0
fseek(dev, 0x0, SEEK_SET);
fwrite(blk1, sizeof(char), strsize(blk1), dev);
// write blk2 at adress 0x100
fseek(dev, 0x100, SEEK_SET);
fwrite(blk2, sizeof(char), strsize(blk2), dev);
// close I2C device
fclose(dev);
} else {
perror(dev_path);
}
return 0;
}

Slave mode

On slave mode, data can be written to the peripheral only when ADDR+RD request is received from master device. For master is a read operation but for slave device write. In this case the ioctl() function should be used to obtain the data direction and selection event.

#include <stdio.h>
#include <stdbool.h>
#include <dnx/misc.h>
#include <sys/ioctl.h>
static const char *dev_path = "/dev/I2C0";
static const I2C_config_t cfg = {
.address = 0xA0, // microcontroller address
.sub_addr_mode = I2C_SUB_ADDR_MODE__DISABLED, // don't care in slave mode
.addr_10bit = false, // 7-bit address
.slave_mode = true // slave mode
};
int_main(slave, STACK_DEPTH_MEDIUM, int argc, char *argv[])
{
FILE *dev = fopen(dev_path, "r+");
if (dev) {
// set I2C device configuration
while (true) {
I2C_selection_t sel = {.timeout_ms = MAX_DELAY_MS};
u8_t buf[64];
memset(buf, 0, sizeof(buf));
if (sel.RD_addr) {
// slave write data to master device (master read
// request).
strncpy(buf, "FROM-SLAVE", sizeof(buf)-1);
size_t n = fwrite(buf, 1, strlen(buf), i2c);
printf("Written: %d bytes\n", n);
} else {
// slave device read data from peripheral sent
// by master device.
size_t n = fread(buf, 1, sizeof(buf) - 1, i2c);
printf("Received: %d bytes\n", n);
// print received data
for (uint i = 0; i < n; i++) {
printf("%02X ", buf[i]);
}
puts("");
}
} else {
perror(dev_path);
}
}
// close I2C device
fclose(dev);
} else {
perror(dev_path);
}
return 0;
}

Data read

Master mode

Data to the I2C device can be read as regular file. Example assumptions:

#include <stdio.h>
#include <stdbool.h>
#include <dnx/misc.h>
#include <sys/ioctl.h>
static const char *dev_path = "/dev/ee";
static const I2C_config_t cfg = {
.address = 0xA0, // EEPROM address
.sub_addr_mode = I2C_SUB_ADDR_MODE__2_BYTES, // EEPROM up to 64KiB
.addr_10bit = false // 7-bit address
};
GLOBAL_VARIABLES_SECTION {
char blk1[100];
char blk2[100];
};
int_main(ee_ex, STACK_DEPTH_MEDIUM, int argc, char *argv[])
{
FILE *dev = fopen(dev_path, "r+");
if (dev) {
// set I2C device configuration
// read blk1 at address 0x0
fseek(dev, 0x0, SEEK_SET);
fread(global->blk1, ARRAY_ITEM_SIZE(global->blk1), ARRAY_SIZE(global->blk1), dev);
// write blk2 at adress 0x100
fseek(f, 0x100, SEEK_SET);
fread(global->blk2, ARRAY_ITEM_SIZE(global->blk2), ARRAY_SIZE(global->blk2), dev);
// close I2C device
fclose(dev);
} else {
perror(dev_path);
}
return 0;
}

Slave mode

On slave mode, data can be read from the peripheral only when ADDR+WR request is received from master device. For master is a write operation but for slave device read. In this case the ioctl() function should be used to obtain the data direction and selection event.

#include <stdio.h>
#include <stdbool.h>
#include <dnx/misc.h>
#include <sys/ioctl.h>
static const char *dev_path = "/dev/I2C0";
static const I2C_config_t cfg = {
.address = 0xA0, // microcontroller address
.sub_addr_mode = I2C_SUB_ADDR_MODE__DISABLED, // don't care in slave mode
.addr_10bit = false, // 7-bit address
.slave_mode = true // slave mode
};
int_main(slave, STACK_DEPTH_MEDIUM, int argc, char *argv[])
{
FILE *dev = fopen(dev_path, "r+");
if (dev) {
// set I2C device configuration
while (true) {
I2C_selection_t sel = {.timeout_ms = MAX_DELAY_MS};
u8_t buf[64];
memset(buf, 0, sizeof(buf));
if (sel.RD_addr) {
// slave write data to master device (master read
// request).
strncpy(buf, "FROM-SLAVE", sizeof(buf)-1);
size_t n = fwrite(buf, 1, strlen(buf), i2c);
printf("Written: %d bytes\n", n);
} else {
// slave device read data from peripheral sent
// by master device.
size_t n = fread(buf, 1, sizeof(buf) - 1, i2c);
printf("Received: %d bytes\n", n);
// print received data
for (uint i = 0; i < n; i++) {
printf("%02X ", buf[i]);
}
puts("");
}
} else {
perror(dev_path);
}
}
// close I2C device
fclose(dev);
} else {
perror(dev_path);
}
return 0;
}

Data Structures

struct  I2C_config_t
 
struct  I2C_selection_t
 

Macros

#define IOCTL_I2C__CONFIGURE   _IOW(I2C, 0, const I2C_config_t*)
 Configure device. More...
 
#define IOCTL_I2C__SLAVE_WAIT_FOR_SELECTION   _IOWR(I2C, 1, I2C_selection_t*)
 Wait for I2C address event. More...
 
#define IOCTL_I2C__CONFIGURE_STR   IOCTL_DEVICE__CONFIGURE_STR
 Configure device by using string. More...
 

Enumerations

enum  I2C_sub_addr_mode_t
 

Data Structure Documentation

struct I2C_config_t

Type represents I2C peripheral configuration.

Data Fields
bool addr_10bit

true: 10 bit addressing enabled.

u16_t address

Device address 8 or 10 bit.

bool slave_mode

true: slave moce enabled.

I2C_sub_addr_mode_t sub_addr_mode

Number of bytes of sub-address (EEPROM, RTC).

struct I2C_selection_t

Type used to check I2C slave mode event.

Data Fields
bool RD_addr

ADDR+RD received.

u32_t timeout_ms

Timeout of waiting for event.

Macro Definition Documentation

#define IOCTL_I2C__CONFIGURE   _IOW(I2C, 0, const I2C_config_t*)
Parameters
[WR]I2C_config_t* device configuration
Returns
On success 0 is returned, otherwise -1 and errno code is set.
#define IOCTL_I2C__CONFIGURE_STR   IOCTL_DEVICE__CONFIGURE_STR
Parameters
[WR]const char* configuration string
Returns
On success 0 is returned, otherwise -1 and errno code is set.
#define IOCTL_I2C__SLAVE_WAIT_FOR_SELECTION   _IOWR(I2C, 1, I2C_selection_t*)
Parameters
[WR]I2C_selection_t* I2C address event (device selection)
Returns
On success 0 is returned, otherwise -1 and errno code is set.

Enumeration Type Documentation

Type defines possible modes of sub-addressing sequence (used e.g. in EEPROM).

Enumerator
I2C_SUB_ADDR_MODE__DISABLED 

Sub-addressing disabled.

I2C_SUB_ADDR_MODE__1_BYTE 

Sub-address is 1 byte long.

I2C_SUB_ADDR_MODE__2_BYTES 

Sub-address is 2 byte long.

I2C_SUB_ADDR_MODE__3_BYTES 

Sub-address is 3 byte long.