I trying to read and write an Atmel 24C256 EEPROM with a Raspberry Pi B+ over I2C, but I'm having trouble getting it all to work right.
Here is the code I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <linux/i2c.h>
#define DEVICE_PATH "/dev/i2c-1"
#define PAGE_SIZE 64
#define DEVICE_ADDR 0x50 // 0b1010xxxx
int file_desc;
char buffer[PAGE_SIZE + 2]; // 64 bytes + 2 for the address
void teardownI2C()
{
int result = close(file_desc);
}
void setupI2C()
{
file_desc = open(DEVICE_PATH, O_RDWR);
if(file_desc < 0)
{
printf("%s\n", strerror(errno));
exit(1);
}
if(ioctl(file_desc, I2C_SLAVE, DEVICE_ADDR) < 0)
{
printf("%s\n", strerror(errno));
teardownI2C();
exit(1);
}
}
int write_to_device(char addr_hi, char addr_lo, char * buf, int len)
{
struct i2c_rdwr_ioctl_data msg_rdwr;
struct i2c_msg i2cmsg;
char my_buf[PAGE_SIZE + 2];
if(len > PAGE_SIZE + 2)
{
printf("Can't write more than %d bytes at a time.\n", PAGE_SIZE);
return -1;
}
int i;
my_buf[0] = addr_hi;
my_buf[1] = addr_lo;
for(i= 0; i < len; i++)
{
my_buf[2+i] = buf[i];
}
msg_rdwr.msgs = &i2cmsg;
msg_rdwr.nmsgs = 1;
i2cmsg.addr = DEVICE_ADDR;
i2cmsg.flags = 0;
i2cmsg.len = 2+len;
i2cmsg.buf = my_buf;
if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
{
printf("write_to_device(): %s\n", strerror(errno));
return -1;
}
return 0;
}
int read_from_device(char addr_hi, char addr_lo, char * buf, int len)
{
struct i2c_rdwr_ioctl_data msg_rdwr;
struct i2c_msg i2cmsg;
if(write_to_device(addr_hi, addr_lo ,NULL,0)<0)
{
printf("read_from_device(): address reset did not work\n");
return -1;
}
msg_rdwr.msgs = &i2cmsg;
msg_rdwr.nmsgs = 1;
i2cmsg.addr = DEVICE_ADDR;
i2cmsg.flags = I2C_M_RD;
i2cmsg.len = len;
i2cmsg.buf = buf;
if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
{
printf("read_from_device(): %s\n", strerror(errno));
return -1;
}
return 0;
}
void fill_buffer(char *buf)
{
int i = 0;
while(i < PAGE_SIZE && *buf)
{
buffer[i+2] = *buf++;
}
while(i++ < PAGE_SIZE-1)
{
buffer[i+2] = '*'; // fill the buffer with something
}
}
int main()
{
setupI2C(); //setup
fill_buffer("Here are some words.");
write_to_device(0x01, 0x00, buffer, PAGE_SIZE);
char newbuf[PAGE_SIZE];
if(read_from_device(0x01, 0x00, newbuf, PAGE_SIZE)>0)
{
printf("%s\n", newbuf);
}
teardownI2C(); //cleanup
return EXIT_SUCCESS;
}
Writing to the device like in the line write_to_device(0x01, 0x00, buffer, PAGE_SIZE); doesn't generate any errors but when I try to read from the device, I have to write a "dummy" byte according to the spec sheet and then try to read from the device but for some reason writing the dummy byte results in an error "Input/output error". I can't figure out how this works. I am using two resources to guide me, the Linux I2C-Dev documentation and an example from a similar EEPROM device. I'm sort of stuck here and don't know what to try. Any suggestions or pointers are greatly appreciated!
Alternatively, you could access it via the kernel at24.c driver, if you're able to compile and install a different kernel device tree for your Raspberry Pi.
The kernel device tree needs to specify the EEPROM's type and address, and which I²C bus it's connected to. I'm not sure about Raspberry Pi, but for the BeagleBone Black EEPROM it goes like this:
&i2c0 {
eeprom: eeprom#50 {
compatible = "at,24c32";
reg = <0x50>;
};
};
For your device you'd specify compatible = "at,24c256";
Ensure the kernel config specifies CONFIG_EEPROM_AT24=y (or =m).
Then you should be able to access the EEPROM memory from userspace at something like /sys/bus/i2c/devices/0-0050/eeprom or /sys/bus/i2c/drivers/at24/0-0050/eeprom.
maybe this here might help. http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program since it handles apparently the device you are trying to program and also explains some caveats of addressing 24c256
Craig McQueen's answer got me on the right track, but it is not easy to figure the whole thing out on your own.
Here is a AT24C256 device tree overlay that works for me on the Raspberry Pi:
/dts-v1/;
/plugin/;
/ {
fragment#0 {
target = <&i2c1>;
overlay {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
at24#50 {
compatible = "atmel,24c256","at24";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x50>;
pagesize = <64>;
size = <32768>;
address-width = <16>;
};
};
};
};
Save it to "at24c256.dts", compile (might need to install the device tree compiler) it using:
dtc -O dtb -o at24c256.dtbo -b 0 -# at24c256.dts
and save it in "/boot/overlays". Then activate the overlay by adding:
dtparam=i2c_arm=on
dtoverlay=at24c256
to "/boot/config.txt" and reboot. You should now have a device file "/sys/class/i2c-dev/i2c-1/device/1-0050/eeprom" (if your I2C bus number is 1) which you can write to like a normal file.
Write to it using e.g.:
echo 'Hello World' | sudo tee /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom
Read from it using e.g.:
sudo more /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom
Not sure how you can get around the su-rights for accessing the device though. Adding the user to the i2c-group does not help...
Small and simple program to understand the easy management of an eeprom
/*
Simple program to write / read the eeprom AT24C32.
Developed and tested on the Raspberry pi3B jessie
To create the executable use the following command:
gcc -Wall -o thisprogram.exe thisprogram.c
*/
#include <stdio.h>
#include <sys/ioctl.h> // ioctl
#include <fcntl.h> // open
#include <unistd.h> // read/write usleep
#include <time.h>
#include <netinet/in.h> // htons
#include <linux/i2c-dev.h>
#pragma pack(1)
#define PAGESIZE 32
#define NPAGES 128
#define NBYTES (NPAGES*PAGESIZE)
#define ADDRESS 0x57 // AT24C32's address on I2C bus
typedef struct {
ushort AW;
char buf[PAGESIZE+2];
}WRITE;
static WRITE AT = {0};
int main() {
int fd;
char bufIN[180] = {0};
time_t clock=time(NULL);
snprintf(AT.buf, PAGESIZE+1, "%s: my first attempt to write", ctime(&clock)); // the buffer to write, cut to 32 bytes
if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) { printf("Couldn't open device! %d\n", fd); return 1; }
if (ioctl(fd, I2C_SLAVE, ADDRESS) < 0) { printf("Couldn't find device on address!\n"); return 1; }
AT.AW = htons(32); // I will write to start from byte 0 of page 1 ( 32nd byte of eeprom )
if (write(fd, &AT, PAGESIZE+2) != (PAGESIZE+2)) { perror("Write error !"); return 1; }
while (1) { char ap[4]; if (read(fd,&ap,1) != 1) usleep(500); else break; } // wait on write's end
if (write(fd, &AT, 2) != 2) { perror("Error in sending the reading address"); return 1; }
if (read(fd,bufIN,PAGESIZE) != PAGESIZE) { perror("reading error\n"); return 1;}
printf ("\n%s\n", bufIN);
close(fd);
return 0;
}
My code:
enter code here
__s32 write_eeprom(__s32 fd,__u32 offset,__u32 len,__u8 *buf)
{
__s32 ret;
struct i2c_rdwr_ioctl_data msg_set;
struct i2c_msg iomsgs;
__u32 sended, sending;
__u8 temp[ONE_PAGE + 1];
if((offset + len) > BYTES_MAX || len == 0)
{
printf("write too long than BYTES_MAX\n");
return -1;
}
sended = 0;
iomsgs.addr = DEVICE_ADDR;
iomsgs.flags = 0; //write
iomsgs.buf = temp;
msg_set.msgs = &iomsgs;
msg_set.nmsgs = 1;
while(len > sended)
{
if(len - sended > ONE_PAGE)
sending = ONE_PAGE;
else
sending = len - sended;
iomsgs.len = sending + 1;
temp[0] = offset + sended;
memcpy(&temp[1], buf + sended, sending);
//printf("sending:%d sended:%d len:%d offset:%d \n", sending, sended, len, offset);
ret = ioctl(fd, I2C_RDWR, (unsigned long)&msg_set);
if(ret < 0)
{
printf("Error dring I2C_RDWR ioctl with error code: %d\n", ret);
return ret;
}
sended += sending;
usleep(5000);
}
return sended;
}
Related
I need to read an eeprom in an embedded device.
So far this "almost" worked:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#define READ_SIZE (256)
#define NB_PAGES (128)
void dump_to_file(const char *output_file_path,
const uint8_t *buffer, const int buffer_length)
{
int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
if (output_file < 0) {
printf("Failed opening output file %s\n", output_file_path);
return;
}
write(output_file, buffer, buffer_length);
}
int main(int argc, char *argv[])
{
/* got these values from i2cdetect */
const char *i2c_device = "/dev/i2c-4";
const int device_address = 0x50;
/* open the i2c device file */
int file = open(i2c_device, O_RDWR);
if (file < 0) {
printf("Failed opening %s\n", i2c_device);
return 1;
}
if (ioctl(file, I2C_SLAVE, device_address) < 0) {
printf("Failed addressing device at %02X\n", device_address);
close(file);
return 1;
}
int i = 0;
for (i = 0; i < NB_PAGES; i++) {
char buf[READ_SIZE] = {0};
if (read(file, buf, READ_SIZE) != READ_SIZE) {
printf("Failed reading\n");
close(file);
return 1;
}
dump_to_file(argv[1], buf, READ_SIZE);
}
close(file);
return 0;
}
By "almost" I mean that it dumps the full eeprom but the START depends on the last block read..
It's not always the same.
If I read 10 blocks. then run the program again I read the next ones and not the first 10.
How to set the starting address?
Update:
if I do:
i2cset -y 4 0x50 0x00 0x00
and the run the above code, it works.
so how can I put the equivalent of the i2cset command in the code?
Done!
It wasn't easy because I could not find documentations anywhere.. but I thought that since the eeprom is 32K, maybe it was "like" a 24c256. But even in that case I found nothing in userspace until I decided to go by instinct.
I studied i2cset source, understood what it did and put it in the code.
Here is the result, which dumps a full i2c 32k eprom from userspace.
Note a full backup and restore utility can be found here:
https://gist.github.com/Zibri/cf8ac0b311301aeeaa8910c7da824bff
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#define READ_SIZE (256)
#define NB_PAGES (128)
void dump_to_file(const char *output_file_path,
const uint8_t *buffer, const int buffer_length)
{
int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
if (output_file < 0) {
printf("Failed opening output file %s\n", output_file_path);
return;
}
write(output_file, buffer, buffer_length);
}
int main(int argc, char *argv[])
{
const char *i2c_device = "/dev/i2c-4";
const int device_address = 0x50;
int file = open(i2c_device, O_RDWR);
if (file < 0) {
printf("Failed opening %s\n", i2c_device);
return 1;
}
if (ioctl(file, I2C_SLAVE, device_address) < 0) {
printf("Failed addressing device at %02X\n", device_address);
close(file);
return 1;
}
int i = 0;
write(file,'\x00\x00',2); // ADDRESS
for (i = 0; i < NB_PAGES; i++) {
char buf[READ_SIZE] = {0};
if (read(file, buf, READ_SIZE) != READ_SIZE) {
printf("Failed reading\n");
close(file);
return 1;
}
dump_to_file(argv[1], buf, READ_SIZE);
}
close(file);
return 0;
}
Hey I am trying to write a user space application to move some data to an I2C for an embedded system running PetaLinux, an operating system for embedded Linux, although I do not think that is what is affecting the issue. I am getting a Connection timeout and a segmentation fault.
The function has macros that direct it to write to the first I2C bus. I specify the data that I want to write in main and pass it to i2c_write, which then passes it to i2c_ioctl_write.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
#define REG_ADDR 0x00
int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
printf("\tBuffer Allocation Successful...\n");
buf[j ++] = regaddr;
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
printf("\tBuffer Setup Successful...\n");
struct i2c_msg messages[] = {
{
.addr = dev,
.buf = buf,
.len = sizeof(buf) / sizeof(buf[0]),
},
};
printf("\tSetup I2C Messages...\n");
struct i2c_rdwr_ioctl_data payload = {
.msgs = messages,
.nmsgs = sizeof(messages) / sizeof(messages[0]),
};
printf("\tSetup I2C IOCTL Payload...\n");
ret = ioctl(fd, I2C_RDWR, &payload);
printf("\tWrote with IOCTL...\n");
if (ret < 0) {
ret = -errno;
}
free (buf);
return ret;
}
int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_smbus_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
struct i2c_smbus_ioctl_data payload = {
.read_write = I2C_SMBUS_WRITE,
.size = I2C_SMBUS_WORD_DATA,
.command = regaddr,
.data = (void *) buf,
};
ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
if (ret < 0)
{
ret = -errno;
goto exit;
}
ret = ioctl (fd, I2C_SMBUS, &payload);
if (ret < 0)
{
ret = -errno;
goto exit;
}
exit:
free(buf);
return ret;
}
int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2x_write\n");
uint64_t funcs;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
return -errno;
}
if (funcs & I2C_FUNC_I2C) {
return i2c_ioctl_write (fd, dev, regaddr, data);
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
return i2c_ioctl_smbus_write (fd, dev, regaddr, data);
} else {
return -ENOSYS;
}
}
int main (int argc, char *argv[])
{
printf("main\n");
uint8_t regaddr;
int fd;
int ret = 0;
uint16_t data[] = {1, 2, 4};
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
ret = i2c_write(fd, I2C_DEVICE, REG_ADDR, data);
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
free(data);
return ret;
}
When I run the program on QEMU I get the following output:
main
i2x_write
i2c_ioctl_write
Buffer Allocation Successful...
Buffer Setup Successful...
Setup I2C Messages
Setup I2C IOCTL Payload
cdns-i2c e0004000.i2c: timeout waiting on completion
Wrote with IOCTL
Connection timed out.
Segmentation fault
I assume it is failing on the line
ret = ioctl(fd, I2C_RDWR, &payload);
but I am not sure why. Was the payload constructed improperly?
Update: Here is the current code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int fd;
int ret = 0;
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
uint64_t funcs;
int addr = 0X00;
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
printf("Cannot setup as slave");
exit(1);
}
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
printf("ioctl failed");
return -errno;
}
printf("funcs & I2C_FUNC_I2C: %llu\n", funcs & I2C_FUNC_I2C);
printf("funcs & I2C_FUNC_SMBUS_WORD_DATA: %llu\n", funcs & I2C_FUNC_SMBUS_WORD_DATA);
__u8 reg = 0x10;
__s32 res;
if (funcs & I2C_FUNC_I2C) {
char buf[10];
printf("Attempting to write to I2C bus via I2C protocol...\n");
buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
int bytes_written = write(fd, buf, 3);
if(bytes_written != 3) {
printf("Wrote %d bytes", bytes_written);
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
char buf2[10];
printf("Attempting to read from I2C bus via I2C protocol...\n");
if(read(fd, buf2, 1) != 1) {
printf("\tFailed to do I2C read from Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", buf[0]);
printf("\t\tRead value: %c", buf2[0]);
}
return 0;
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
printf("Attempting to write to I2C bus via SMBus protocol...\n");
//res = i2c_smbus_write_word_data(fd, REG_ADDR, 0x6543);
res = 1;
if(res < 0) {
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
//res = i2c_smbus_read_word_data(fd, REG_ADDR);
if(res < 0) {
printf("\tFailed to read from I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", 0x6543);
printf("\t\tRead value: %c", res);
}
} else {
printf("Cannot write to I2C");
return -ENOSYS;
}
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
return ret;
}
I was able to get rid of the seg fault by removing free(), so thanks there. I have pinpointed the exact issue of the timeout which occurs in the Cadence I2C Driver here:
https://github.com/Xilinx/linux-xlnx/blob/3f3c7b60919d56119a68813998d3005bca501a40/drivers/i2c/busses/i2c-cadence.c#L825
which is still occurring.
As mentioned, there is probably some issue with the way I am writing to slave causing the slave to not send ACK, resulting in a timeout. I am not sure which registers I will need to write what to. I have a feeling the I2C_DEVICE macro and addr and reg variables will need to be changed.
cdns-i2c e0004000.i2c: timeout waiting on completion
It seems that i2c driver (cdns-i2s) doesnt recieves the acknowledgment from the slave. It may occur as you are using I2C-slave address as 0x00 which is a general call address. While using general call address the second byte that is sent has a special purpose which is mentioned in the i2c-specification (section 3.1.13).
If you use general call address you need to follow the specification or else Try using the exact i2c slave address instead of general call address(0x00).
I am trying to read one value from a memory location on the I2C bus after writing to it. I am getting strange output when I run it in the terminal.
Here is my program
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
int addr = 0X00; /* XGPIOPS_DATA_LOW_OFFSET */
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave %s", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = 0x10;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf(buf);
printf("\n\n");
}
return 0;
}
The output from the program looks like this
Successful write
Successful read ��
On my terminal those blocks look more like question marks inside of diamonds. I am not sure what that corresponds to in ASCII.
Why am I not reading back that 0x10 which is the second byte after the address byte that I originally write?
Based on the first set of answers, here is the updated code:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
long addr, reg_addr;
char *end;
if(argc == 3) {
addr = strtol(argv[1], &end, 16);
printf("Value of addr is: %ld\n", addr);
reg_addr = strtol(argv[2], &end, 16);
printf("Value of reg_addr is: %ld\n", reg_addr);
}
else {
printf("arg failed\n\n.");
addr = 0x00;
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = reg_addr;
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 3) != 3) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
At this point, whenever I use 0x00 as the addr, I get FF, FF, FF as the output, no matter what argv[2] is. Here is the applicable part of the device tree file. Note that this is being emulated, so I cannot probe the physical device.
&i2c0 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
i2cswitch#74 {
compatible = "nxp,pca9548";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x74>;
i2c#0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
si570: clock-generator#5d {
#clock-cells = <0>;
compatible = "silabs,si570";
temperature-stability = <50>;
reg = <0x5d>;
factory-fout = <156250000>;
clock-frequency = <148500000>;
};
};
i2c#2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
eeprom#54 {
compatible = "at,24c08";
reg = <0x54>;
};
};
i2c#3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
gpio#21 {
compatible = "ti,tca6416";
reg = <0x21>;
gpio-controller;
#gpio-cells = <2>;
};
};
i2c#4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <4>;
rtc#51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
i2c#7 {
#address-cells = <1>;
#size-cells = <0>;
reg = <7>;
hwmon#52 {
compatible = "ti,ucd9248";
reg = <52>;
};
hwmon#53 {
compatible = "ti,ucd9248";
reg = <53>;
};
hwmon#54 {
compatible = "ti,ucd9248";
reg = <54>;
};
};
};
};
Here are a couple of example tests
Try to test the SiLabs clock generator
root#plnx_arm:~# /usr/bin/i2c-test-mem-location 0x54 0x00
Value of addr is: 84
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
Try to test the eeprom device
root#plnx_arm:~# /usr/bin/i2c-test-mem-location 0x5d 0x00
Value of addr is: 93
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
This is my program on the third try. After taking to mind the notes made in the answers, I have this written
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define DEVICE_ADDRESS 0x54
int main (int argc, char *argv[])
{
int file;
uint8_t reg, value;
char *end;
printf("The device address on the bus: %d", DEVICE_ADDRESS);
if(argc == 3) {
reg = strtol(argv[1], &end, 16);
printf("Value of register address: %d\n", reg);
value = strtol(argv[2], &end, 16);
printf("value to write is: %d\n", value);
}
else {
printf("arg failed\n\n.");
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, DEVICE_ADDRESS) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = reg;
buf[1] = value;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
Unfortunately, even still, I am getting the same error.
root#plnx_arm:~# /usr/bin/i2c-test-mem-location 0x00 0x10
The device address on the bus: 84Value of register address: 0
value to write is: 16
Unable to open device as slave
Device or resource busy
root#plnx_arm:~# /usr/bin/i2c-test-mem-location 0x30 0x10
The device address on the bus: 84Value of register address: 48
value to write is: 16
Unable to open device as slave
Device or resource busy
EDIT 2: I think you might not be setting your I2C device address correctly. What you have as your I2C_ADAPTER ("/dev/i2c-0") indicates which I2C bus the device is on. You aren't even using your I2C_DEVICE macro, but that's what you should be passing to your ioctl call (e.g. ioctl(file, I2C_SLAVE, I2C_DEVICE);) and it should be the I2C address of the device you want to access (e.g. 0x5D for the Clock Generator) instead of 0x00.
I also think your reads/writes are incorrect. Once you've specified the bus and device via open() and ioctl() you don't need to worry about those anymore. You only need to worry about the register you want to access (if your I2C device uses registers) and the actual data.
To write to your I2C device, assuming it uses a one-byte register, write a buffer of two bytes: The first is the register, the second is the value you want to write:
bool i2cdev_byte_write(int file, uint8_t reg, uint8_t val)
{
uint8_t bytes[2];
bytes[0] = reg;
bytes[1] = val;
/* Write the register followed by the value */
if (write(file, bytes, 2) != 2)
return false;
return true;
}
To read from your I2C device, assuming it uses a one-byte register, write a buffer of one byte (the register address) then read a buffer of one or more bytes (the value at that register and subsequent registers):
bool i2cdev_bytes_read(int file, uint8_t reg, unsigned int count, uint8_t *out_buf)
{
if (!out_buf)
return false;
/* Write the register */
if (write(file, ®, 1) != 1)
{
printf("Failed to write register value\n");
return false;
}
/* Read the specified number of bytes */
if (read(file, out_buf, count) != count)
{
printf("Failed to read from the i2c bus\n");
return false;
}
return true;
}
Again, note that all the above comments depend on it being an I2C device that uses a single-byte register address and that it supports auto-incrementing the register address when reading multiple bytes at a time. You'll need to check the datasheet for your I2C device to determine exactly how it needs to be accessed.
EDIT: This is a printf() newbie failure. You can't just try to printf an array of bytes. That's not how printf() works.
Try this:
printf("Buf = [%02X,%02X]\n", buf[0], buf[1]);
Also, as I wrote in my original response, you likely need to write the register address back out again prior to reading the register contents.
The i2c protocol requires that you specify a device address (i.e. 0x00) and a register address. You can then write the value (in your case, 0x10) to that register address. Try this instead:
char buf[10];
buf[0] = addr;
buf[1] = [REGISTER ADDRESS];
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf("Addr: %02x Subaddr: %02x Value: %02x\n\n", buf[0], buf[1], buf[2]);
}
Once this write has been completed, you should be able to read with:
if(read(file, buf, 1) != 1) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Value: %02x\n\n", buf[0]);
}
Based on the device tree, the address that needs to be fed to IOCtl is the i2cswitch mux address. This address is 0x74, which can be seen in the device tree. Opening the i2c-0 device file corresponds to the i2c0 entry in the device tree which is parent to the mux. When writing to EEPROM, the first byte in the buffer should be the device address, as mentioned by #AndrewCottrell. This address is 0x54. The second byte should be the data you want to write
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_SWITHC_MUX_ADDRESS 0x74
#define DEVICE_ADDRESS 0x54
...
file = open(I2C_ADAPTER, O_RDWR); /* Check for error */
ioctl(file, I2C_SLAVE_FORCE, I2C_SWITHC_MUX_ADDRESS); /* Check for error */
uint8_t reg, value;
reg = DEVICE_ADDRESS;
buf[0] = reg;
buf[1] = value;
write(file, buf, 2); /* Check for error */
read(file, buf, 1); /* Check for error */
/* buf[0] should be value*/
I also have problems reading data from the memory of an NTAG 5 boost component and I found this thread while looking for the reason for this problem. Indeed, for each I2C reading I do on the component, I receive the "FF" value. I didn't find the answer in this thread but maybe you answer my question in this thread.
I am attempting to read gyroscopic data using a raspberry pi and am having a lot of problems. I think I understand a lot of the code and it is written properly.
The problem comes in when printing it onto raspberry screen, all I get is constant numbers that don't change at all.
Everything is based off of http://ozzmaker.com/berryimu/ I know the code is provided but I wanted to learn and possibly optimize it for my purposes. I am using https://www.adafruit.com/datasheets/L3GD20H.pdf gyroscope.
The program to read values is:
#include </home/pi/gyroSetup.c>
#include <stdio.h>
int main() {
setup();
int i;
int G[3];
for(i = 0;i<=1000;i++){
readGyro(G);
printf("%d ", G[0]);
printf("%d ", G[1]);
printf("%d\n", G[2]);
}
}
The gyroSetup.c is here:
#include <stdio.h>
#include <stdint.h>
#include "fcntl.h"
#include "linux/i2c-dev.h"
#define Gyr_adress 0x6b
#define Gyr_ctrl1 0b1110100
#define Gyr_ctrl4 0b00000000
#define Gyr_odr 0b00000000
int file;
void readBlock(uint8_t command, uint8_t size, uint8_t *data) {
int result = i2c_smbus_read_i2c_block_data(file, command, size, data);
if(result != size) {
printf("Failed to read block from i2c!");
{
}
void readGyro(int *T) {
uint8_t block[6];
if(ioctl(file,I2C_SLAVE, Gyr_adress) < 0) {
printf("failed to select device!");
}
readBlock(0x80 | 0x28, sizeof(block), block);
*T = (int16_t)(block[0] | block[1] << 8);
*(T+1) = (int16_t)(block[2] | block[3] << 8);
*(T+2) = (int16_t)(block[4] | block[4] << 8);
}
void writeGyro(uint8_t location, uint8_t value, int file) {
if(ioctl(file, I2C_SLAVE, Gyr_adress) < 0) {
printf("Failed to select i2c device");
}
int result = i2c_smbus_write_byte_data(file, location, value);
if(result == -1) {
printf("Failed to write byte!");
}
}
void setup(){
__u16 block[I2C_SMBUS_BLOCK_MAX];
file = open("/dev/i2c-1", O_RDWR);
if( file < 0) {
printf("Unable to open i2c bus!");
}
writeGyro(0x20, Gyr_ctrl1, file);
writeGyro(0x23, Gyr_ctrl4, file);
writeGyro(0x39, Gyr_odr, file);
}
I have no idea why when reading the values don't change so please help. When compiling there are no errors.
I'm having a requirement to create a file in the externally mounted hard disk .created file should contain the serial no of the harddisk and that file can be used by other process.
I tried to use the following code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
int main(int argc, char *argv[])
{
static struct hd_driveid hd;
int fd;
if (geteuid() > 0) {
printf("ERROR: Must be root to use\n");
exit(1);
}
if ((fd = open(argv[1], O_RDONLY|O_NONBLOCK)) < 0) {
printf("ERROR: Cannot open device %s\n", argv[1]);
exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
printf("Hard Disk Model: %.40s\n", hd.model);
printf(" Serial Number: %.20s\n", hd.serial_no);
} else if (errno == -ENOMSG) {
printf("No hard disk identification information available\n");
} else {
perror("ERROR: HDIO_GET_IDENTITY");
exit(1);
}
exit(0);
}
this is working fine for internal hard disk but when i do this for external hard disk(usb) it is giving me the following error
ERROR: HDIO_GET_IDENTITY: Invalid argument
Because the device is connected to a USB bridge, you can't send the HDIO_GET_IDENTITY command.
You can try hdparm to query the identity of the device. With the default options, hdparm fails to identify the device so you have to specify the type of the device with -d (see USB devices and smartmontools).
Without the -d option, I get:
$ sudo smartctl /dev/sdc
/dev/sdc: Unknown USB bridge [0x059f:0x1011 (0x000)]
Please specify device type with the -d option.
With -d sat,auto, hdparm manages to display some information about the device:
$ sudo smartctl -d sat,auto -i /dev/sdc
/dev/sdc [SCSI]: Device open changed type from 'sat,auto' to 'scsi'
=== START OF INFORMATION SECTION ===
Vendor: ST2000VN
Product: 000-1H3164
User Capacity: 2 000 398 934 016 bytes [2,00 TB]
Logical block size: 512 bytes
Device type: disk
Local Time is: Thu Mar 13 09:41:32 2014 CET
SMART support is: Unavailable - device lacks SMART capability.
You can try to do the same thing as smartctl in your C program, but it's probably easier to write a script that invokes smartctl.
Thanks for the explanation and i got the below to identify the serial no of a external hardisk
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
int scsi_get_serial(int fd, void *buf, size_t buf_len) {
// we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
unsigned char sense[32];
struct sg_io_hdr io_hdr;
int result;
memset(&io_hdr, 0, sizeof (io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = inq_cmd;
io_hdr.cmd_len = sizeof (inq_cmd);
io_hdr.dxferp = buf;
io_hdr.dxfer_len = buf_len;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.sbp = sense;
io_hdr.mx_sb_len = sizeof (sense);
io_hdr.timeout = 5000;
result = ioctl(fd, SG_IO, &io_hdr);
if (result < 0)
return result;
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
return 1;
return 0;
}
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
int storeData (char *filepath, char *data) {
int rc = 0;
FILE *fOut = fopen (filepath, "a");
if (fOut != NULL) {
if (fputs (data, fOut) != EOF) {
rc = 1;
}
fclose (fOut); // or for the paranoid: if (fclose (fOut) == EOF) rc = 0;
}
return rc;
}
int main(int argc, char** argv) {
if(argc>1){
char *dev = (char *)argv[1];
char outStr[1024];
printf("\nEntered Serial no : %s\n",argv[1]);
char scsi_serial[255];
int rc;
int fd;
fd = open(dev, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror(dev);
}
memset(scsi_serial, 0, sizeof (scsi_serial));
rc = scsi_get_serial(fd, scsi_serial, 255);
// scsi_serial[3] is the length of the serial number
// scsi_serial[4] is serial number (raw, NOT null terminated)
if (rc < 0) {
printf("FAIL, rc=%d, errno=%d\n", rc, errno);
} else
if (rc == 1) {
printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
} else {
if (!scsi_serial[3]) {
printf("Failed to retrieve serial for %s\n", dev);
return -1;
}
printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
scsi_serial[4+scsi_serial[3]]='\0';
trim(&scsi_serial[4]);
sprintf(outStr,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> \n<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\"> \n<properties>\n<comment/>\n<entry key=\"SerialNo\">%s</entry>\n</properties>\n", (char *) & scsi_serial[4]);
//strcat((char *)argv[2],(char *)"/hdd.xml");
printf("\n%s",outStr);
// printf("\n%s",(char *)argv[2]);
//storeData((char *)argv[1],(char *) outStr);
}
close(fd);
}else{
printf("\nInsufficient no of arguments \n");
}
return (EXIT_SUCCESS);
}