Linux uses the I2C bus to read and write EEPROM (read and write I2C from Device general program) __linux

Source: Internet
Author: User
Tags goto strcmp usleep dmesg
Use IIC bus to read and write i2c slave device register under Linux

by Han David @Jilin Normal University

handawei@jusontech.com

                  Reprint please be sure to indicate the source
******************* ******************************* ***************

2012.7.16

1. This article gives the implementation program of reading and writing i2c slave device using IIC bus under linux.
2. This article gives solutions to several very subtle errors encountered in programming.
3. The reading and writing program in this article is very general:

i2c -d / dev / i2c-1 -s 0x51 0x05 18 ----- Write 18 to the register: 0x05 of the i2c-slave address: 0x51

i2c -d / dev / i2c-10 0x57 0x05 ------ Read the register: 0x05 of the i2c-slave address: 0x57

i2c 0x40 0x0f ----- read in the default path i2c slave device address 0x40 0x0f address (or register address)

4. This program takes the EEPROM as an example, and finally shows how to read and write the general program of the slave device under i2c, not for the EEPROM chip. There is also an article in "Communication with E2PROM under Linux using i2c bus"

Some operations of EEPROM. But the principle is the same as this article.

 The E2PROM in our embedded system is 24C02. Let ’s briefly understand this chip:

The storage capacity of AT24C02 is 2Kb, the content is divided into 32 pages, each page 8B, a total of 256B, there are two addressing modes during operation: chip addressing and on-chip sub-address addressing.
(1) Chip addressing: The chip address of AT24C02 is 1010, and the address control word format is 1010A2A1A0R / W. Among them A2, A1, A0 programmable address selection bits. A2, A1, A0 pins are connected high,
After the low level, the determined three-bit code is formed, and the 7-bit code is formed with 1010, which is the address code of the device. R / W is the chip read-write control bit, this bit is 0, indicating that the chip performs a write operation.
(2) On-chip sub-address addressing: chip addressing can read / write any of the internal 256B, and its addressing range is 00 ~ FF, a total of 256 addressing units.

We use the second addressing method.


In addition, there is a question that needs to be understood, that is, EEPROM and flash, when to use EEPROM, when to use FLASH is appropriate.
********************

 From Www.baidu.com:

Flash memory, also known as flash memory, combines the advantages of ROM and RAM, not only has the performance of electronically erasable and programmable (EEPROM), but also can quickly read data (NVRAM advantage), so that the data will not be powered off
Lost. This kind of memory is used in U disk and MP3. Used to store Bootloader and operating system or program code, or directly used as a hard disk (U disk).

One, EEPROM reads and writes with single byte, some chips of FLASH can only be erased in block mode (whole chip erase), some chips can be written (programmed) in single byte, generally block write mode is needed;


Second, FLASH has faster read and write speeds and higher reliability than EEPROM.

Third, in terms of price, FLASH is more expensive than EEPROM.

So, our version card parameter information, wait for some fixed, small amount, do not need to modify the data information frequently in the EEPROM. The flash is used as a memory for storing programs, storing operating system codes, etc. that need to be read and written quickly,
Frequently accessed data.


************** ************************************ ******************


**** ********************************************** *********

First introduce some problems encountered:

Question 1: Bad address

Ioctl is used to send the packaged data to the kernel at the user layer, but the results show that:

 error = Bad address
I originally thought it was the wrong address for me, but the address was correct.

Later, I saw the location of the error:

In the kernel code driver / i2c / i2c-dev.c, the function i2cdev_ioctl_rdrw ()

if (copy_from_user (& rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *) arg,
sizeof (rdwr_arg)))
 return -EFAULT;

This statement returned the error message bad address.
After checking the information, the very cause of the error is concealed and very simple:
  The definition of copy_to_user is:
  copy_to_user (void __user * to, const void * from, unsigned long n);
   
 But this unsigned long is equal to unsigned int on a 32bit processor, both are 4 bytes, 32bit.
So I initially had this definition in eeprom_io.h: typedef unsigned long u32;
In eeprom_io.c: ioctl (fd, I2C_RDWR, (u32) iocs);

The processor of our version card is a 64bit mips series processor, unsigned long is 8 bytes, 64bit. The cross compiler will not report an error. But after running, there will always be Bad address due to the problem of bytes
Error feedback. This misleads me simply to think that the address is wrong.
Later I changed typedef unsigned long u32 to typedef unsigned long u64;
ioctl (fd, I2C_RDWR, (u64) iocs). Only to solve this problem.


************** ************************************ ***************************

Problem two, when the program executes write once and then executes write:


Input / output error

Similarly, there is such an error after executing write once and then reading.

I changed addr to 0x90,0x10. The running results are all reported as error: Input output error

Find the location of the error:
It is the execution function of the next one in XXX_i2c_xfer (): octeon_i2c_simple_write ()


    cmd = __raw_readq (i2c-> twsi_base + SW_TWSI);
        printk (KERN_DEBUG "% s: after readq cmd =% llx \ n", __ func __, cmd);

        if ((cmd & SW_TWSI_R) == 0) {
                if (octeon_i2c_lost_arb (cmd))
                        goto retry;
                ret = -EIO;
              

This EIO is on the user level

 printf ("% s: error =% s \ n", __ FUNCTION __, strerror (errno));
Will display input / output error


Observe dmesg:

[2656.285759] octeon_i2c_xfer: num = 1
[2656.285769] octeon_i2c_xfer: msgs [0] .addr = 57, msgs [0] .flags = 0, msgs [0] .len = 2
[2656.285780] octeon_i2c_xfer: msgs [0] .buf [0] = 0
[2656.285789] octeon_i2c_simple_write:
[2656.285797] octeon_i2c_simple_write: cmd = 8090570000000000
[2656.285808] octeon_i2c_simple_write: msgs [0] .buf [1] = 10, cmd = 8090570000000010
[2656.285820] octeon_i2c_simple_write: msgs [0] .buf [0] = 0, cmd = 8090570000000010
[2656.285948] octeon_i2c_simple_write: after readq cmd = 090570000000020
[2656.285955]


I printed the correctly executed program dmesg as a comparison:

[4312.259857] octeon_i2c_xfer: num = 1
[4312.259866] octeon_i2c_xfer: msgs [0] .addr = 51, msgs [0] .flags = 0, msgs [0] .len = 2
[4312.259878] octeon_i2c_xfer: msgs [0] .buf [0] = 2
[4312.259887] octeon_i2c_simple_write:
[4312.259895] octeon_i2c_simple_write: cmd = 8090510000000000
[4312.259906] octeon_i2c_simple_write: msgs [0] .buf [1] = 30, cmd = 8090510000000030
[4312.259918] octeon_i2c_simple_write: msgs [0] .buf [0] = 02, cmd = 8090510000000230
[4312.260227] octeon_i2c_simple_write: after readq cmd = 01905100ffffffff


The first write execution was successful, which means that there is no problem with the code. Then the second execution fails. There should be other reasons. I checked the 24C02 information on the Internet. It turned out that it was like this:


"After the data is written, it must be delayed by 10MS after giving a stop signal. It takes 24C02 so long to load the data"

This is the electrical characteristic of 24C02.

Use usleep (10000) once in the write function.

To add: Our CPU is a 6 core 500M frequency, and its computing power is extremely strong. If you write a general delay at the user level

The program does not work at all. Generally, two fors are used, and one for is executed 10,000 times. There will be a significant delay on the PC. but

In our embedded system, 8 for statements are used, and each for is executed 10,000 times, with no impact at all

A slight delay. In addition, simply using the for action delay will block the CPU at full load,

Affect other functions, so it is recommended that you use the usleep () function, usleep (10000), which is exactly 10Ms, so
The best use of CPU time.

********* ***************************************** ************

************** ************************************ ************************

Question three, about

 fd = i_open ("/ dev / i2c-2", TIMEOUT, RETRY);

This statement itself is no problem, and it has passed the test on my system, but other systems may have errors.

I observe his dmesg:

[4515.609931] octeon_i2c_xfer: num = 1
[4515.609941] octeon_i2c_xfer: msgs [0] .addr = 57, msgs [0] .flags = 0, msgs [0] .len = 2
[4515.609952] octeon_i2c_xfer: msg
s [0] .buf [0] = 2
[4515.609961] octeon_i2c_simple_write:
[4515.609969] octeon_i2c_simple_write: cmd = 8090570000000000
[4515.609980] octeon_i2c_simple_write: msgs [0] .buf [1] = 01, cmd = 8090570000000001
[4515.609992] octeon_i2c_simple_write: msgs [0] .buf [0] = 02, cmd = 8090570000000201
[4515.610117] octeon_i2c_simple_write: after readq cmd = 0090570000000020


I performed a correct write and printed the results for comparison:

[4312.259857] octeon_i2c_xfer: num = 1
[4312.259866] octeon_i2c_xfer: msgs [0] .addr = 51, msgs [0] .flags = 0, msgs [0] .len = 2
[4312.259878] octeon_i2c_xfer: msgs [0] .buf [0] = 2
[4312.259887] octeon_i2c_simple_write:
[4312.259895] octeon_i2c_simple_write: cmd = 8090510000000000
[4312.259906] octeon_i2c_simple_write: msgs [0] .buf [1] = 30, cmd = 8090510000000030
[4312.259918] octeon_i2c_simple_write: msgs [0] .buf [0] = 02, cmd = 8090510000000230
[4312.260227] octeon_i2c_simple_write: after readq cmd = 01905100ffffffff


Later, after research, there was a problem with the i2c bus I visited.

The reasons are as follows:

On the datasheet of my version card: 24C02 is hung on I2C0, / dev / i2c-0, / dev / i2c2-/ dev / i2c-9

All belong to I2c0, so you can access these buses. But / dev / i2c-1, / devi2c-10 --- / dev / i2c-33 these are all

Belongs to I2C1, so simple fd = i_open ("/ dev / i2c-2", TIMEOUT, RET

RY) Such a program cannot be universal, the only way is to use argv

Pass in the path as a parameter, and then enter the correct / dev / i2c- * bus. At the same time, in order to simplify,

Use / dev / i2c-0 as the default path. Can be used

 i2c -d / dev / i2c-1 0x57 0x10 uses / dev / i2c-1 as the path

i2c 0x57 0x10 uses the default path / dev / i2c-0.


************************************************ *******************************************

Here is the implementation code. Main.c:

***********************************************



#include "i2c.h"

#define TIMEOUT 3
#define RETRY 3

static int fd;
static u16 addr;
static u8 offset;

int i_open (unsigned char * dev, unsigned int timeout, unsigned int retry) {

        return i2c_open (dev, timeout, retry);
}

int read_data (u16 addr, u8 offset, u8 * val) {
int ret;
The
ret = i2c_read_data (addr, offset, val);
if (ret <0) {
printf ("% s error! \ n", __ FUNCTION__);
exit (-1);
}
printf ("read success, val =% 02x \ n", * val);
return 0;
}

int write_data (u16 addr, u8 offset, char * argv) {
The
int ret;
u8 val = (unsigned char) strtoul (argv, 0,16);
The
ret = i2c_write_data (addr, offset, val);
if (ret <0) {
printf ("% s error! \ n", __ FUNCTION__);
exit (-1);
}

printf ("write success, val =% 02x \ n", val);
usleep (10000); // Delay program
return 0;
}

int help_info (void) {
printf ("\ nUsage: i2c [-d PATH] ADDR OFFSET \ n");
printf ("\ nOr: i2c [-d PATH] -s ADDR OFFSET DATA \ n");
        printf ("\ nRead or Write the register of i2c slave \ n");
printf ("\ nFor example \ n");
printf ("\ ti2c 0x51 0x05 \ t \ t \ t \ t \ t \ tRead the register: 0x05 of the address: 0x51 \ n");
printf ("\ ti2c -d / dev / i2c-10 0x51 0x05 \ t \ t \ t \ tRead the register: 0x05 of the address: 0x51 \ n");
printf ("\ ti2c -d / dev / i2c-1 -s 0x51 0x05 18 \ t \ t \ tWrite 18 to the register: 0x05 of the address: 0x51 \ n \ n");

return 0;
}

void i2c_path (char * argv) {

fd = i_open (argv, TIMEOUT, RETRY);
if (fd <0) {
printf ("i2c_open error! \ n");
exit (-1);
}
}

void i2c_addr (char * argv) {
char * s = argv;
addr = bin2bcd (atoi (s + 2));
}

void i2c_offs (char * argv) {
char * s = argv;
offset = bin2bcd (atoi (s + 2));
}

int main (int argc, char * argv []) {
u8 val;

switch (argc) {
The
case 2: {
if (! strcmp (argv [1], "-h") ||! strcmp (argv [1], "-help")) {
help_info ();
} else {
printf ("cmd error! \ n");
exit (-1);
}
} break;
case 3: {
i2c_path ("/ dev / i2c-1");
i2c_addr (argv [1]);
i2c_offs (argv [2]);
read_data (addr, offset, & val);
} break;
case 5: {
if (! strcmp (argv [1], "-d")) {
i2c_path (argv [2]);
i2c_addr (argv [3]);
i2c_offs (argv [4]);
read_data (addr, offset, & val);
} else if (! strcmp (argv [1], "-s")) {
i2c_path ("/ dev / i2c-1");
i2c_addr (argv [2]);
i2c_offs (argv [3]);
write_data (addr, offset, argv [4]);
} else {
printf ("cmd error! \ n");
exit (-1);
}
} break;
case 7: {
if (! strcmp (argv [1], "-d")) {
i2c_path (argv [2]);
if (! strcmp (argv [3], "-s")) {
i2c_addr (argv [4]);
i2c_offs (argv [5]);
write_data (addr, offset, argv [6]);
}
} else {
printf ("cmd error! \ n");
exit (-1);
}
} break;
default:
printf ("Please input --help or -h for help information \ n");
}


The
close (fd);
return 0;
}


******************************* ******************* *******************


i2c.c:

************** ************************************

#include "i2c.h"

static int fd;

int
i2c_read_data (u16 addr, u8 offset, u8 * val)
{
int i, ret = 0;

struct i2c_rdwr_ioctl_data * data;

if ((data = (struct i2c_rdwr_ioctl_data *) malloc (sizeof (struct i2c_rdwr_ioctl_data))) == NULL)
return -1;

data-> nmsgs = 2;
if ((data-> msgs = (struct i2c_msg *) malloc (data-> nmsgs * sizeof (struct i2c_msg))) == NULL) {
ret = -1;
goto errexit3;
}
if ((data-> msgs [0] .buf = (unsigned char *) malloc (sizeof (unsigned char))) == NULL) {
ret = -1;
goto errexit2;
}
if ((data-> msgs [1] .buf = (unsigned char *) malloc (sizeof (unsigned char))) == NULL) {
ret = -1;
goto errexit1;
}

data-> msgs [0] .addr = addr;
data-> msgs [0] .flags = 0;
data-> msgs [0] .len = 1;
data-> msgs [0] .buf [0]
= offset;

data-> msgs [1] .addr = addr;
data-> msgs [1] .flags = I2C_M_RD;
data-> msgs [1] .len = 13; // original data is 1
data-> msgs [1] .buf [0] = 0;

if ((ret = __i2c_send (fd, data)) <0)
goto errexit0;

for (i = 0; i <data-> msgs [1] .len; i ++)
val [i] = data-> msgs [1] .buf [i];

errexit0:
free (data-> msgs [1] .buf);
errexit1:
free (data-> msgs [0] .buf);
errexit2:
free (data-> msgs);
errexit3:
free (data);

return ret;
}

int
i2c_write_data (u16 addr, u8 offset, u8 val)
{
int ret = 0;

struct i2c_rdwr_ioctl_data * data;

if ((data = (struct i2c_rdwr_ioctl_data *) malloc (sizeof (struct i2c_rdwr_ioctl_data))) == NULL)
return -1;

data-> nmsgs = 1;
if ((data-> msgs = (struct i2c_msg *) malloc (data-> nmsgs * sizeof (struct i2c_msg))) == NULL) {
ret = -1;
goto errexit2;
}
if ((data-> msgs [0] .buf = (unsigned char *) malloc (2 * sizeof (unsigned char))) == NULL) {
ret = -1;
goto errexit1;
}

data-> msgs [0] .addr = addr;
data-> msgs [0] .flags = 0;
data-> msgs [0] .len = 2;
data-> msgs [0] .buf [0] = offset;
data-> msgs [0] .buf [1] = val;

if ((ret = __i2c_send (fd, data)) <0)
goto errexit0;

errexit0:
free (data-> msgs [0] .buf);
errexit1:
free (data-> msgs);
errexit2:
free (data);

return ret;
}

int
i2c_open (unsigned char * dev, unsigned int timeout, unsigned int retry)
{
if ((fd = open (dev, O_RDWR)) <0)
return fd;
The
__i2c_set (fd, timeout, retry);

return fd;
}

static int
__i2c_send (int fd, struct i2c_rdwr_ioctl_data * data)
{
if (fd <0)
return -1;

if (data == NULL)
return -1;

if (data-> msgs == NULL || data-> nmsgs == 0)
return -1;
The
return ioctl (fd, I2C_RDWR, (unsigned long) data);
}

static int
__i2c_set (int fd, unsigned int timeout, unsigned int retry)
{
if (fd == 0)
return -1;

ioctl (fd, I2C_TIMEOUT, timeout? timeout: I2C_DEFAULT_TIMEOUT);

ioctl (fd, I2C_RETRIES, retry? retry: I2C_DEFAULT_RETRY);
The
return 0;
}

void
i2c_close (int fd)
{
if (fd <0)
return;

close (fd);
}

unsigned bcd2bin (unsigned char val)
{
        return (val & 0x0f) + (val >> 4) * 10;
}

unsigned char bin2bcd (unsigned val)
{
        return ((val / 10) << 4) + val% 10;
}
i2c.h:

************* ************************************* *******

#ifndef I2C_H
#define I2C_H

#include <linux / types.h>
#include <linux / i2c.h>
#include <linux / i2c-dev.h>
#include <linux / rtc.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys / types.h>
#include <sys / ioctl.h>
#include <errno.h>
#include <string.h>

#define I2C_DEFAULT_TIMEOUT 1
#define I2C_DEFAULT_RETRY 3

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef short s16;
typedef int s32;
typedef long long s64;

unsigned bcd2bin (unsigned char val);

unsigned bcd2bin (unsigned char val);

static int
__i2c_send (int fd, struct i2c_rdwr_ioctl_data * data);

static int
__i2c_set (int fd, unsigned int timeout, unsigned int retry);

int
i2c_read_data (u16 addr, u8 offset, u8 * val);

int
i2c_write_data (u16 addr, u8 offset, u8 val);

int
i2c_open (unsigned char * dev, unsigned int timeout, unsigned int retry);

#endif

************* ************************************* ***********


by Han David @Jilin Normal University

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.