-
리눅스 어플리케이션에서 I2C 사용하기프로그래밍/리눅스 2021. 3. 23. 22:05728x90
I2C나 SPI로 통신되는 칩들은 대부분 리눅스 디바이스 드라이버로 핸들링하는게 보통이지만 테스트나 간단한 기능을 하는 칩의 경우에는 어플리케이션에서 핸들링하기도 하지요.
이번에는 리눅스 어플리케이션에서 I2C 칩을 Read/Write 하는 방법을 설명해 볼까 합니다.
아마도 대부분은 I2C 노드가 /dev 디렉터리에 i2c/0, i2c/1 ... i2c/N 이렇게 존재하거나 i2c-0, i2c-1 ... i2cN 이렇게 존재할 것입니다. 이 노드들을 open, close, ioctl, read, write와 같은 I/O 함수들로 I2C 통신을 할 수 있습니다.
읽어오기
MCU의 I2C 0번 버스에 Slave address가 A0h인 칩이 있고, Sub address가 16비트이고 데이터는 8비트로 통신되는 칩과 가정하겠습니다.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <errno.h> #include <linux/i2c-dev.h> #include <linux/i2c.h> const int bus = 0; const unsigned long slave_addr = 0xA0; const unsigned short sub_addr = 0x10; int main() { int fd; int rval; char path[20]; unsigned char buf[2]; unsigned char rxd[1]; sprintf(path, "/dev/i2c/%d", bus); fd = open(path, O_RDWR); if ((fd < 0) && (errno == ENOENT)) { sprintf(path, "/dev/i2c-%d", bus); fd = open(path, O_RDWR); } if (fd < 0) { fprintf(stderr, "Can't open file '/dev/i2c-%d' or '/dev/i2c/%d': %s\r\n", bus, bus, strerror(errno)); exit(EXIT_FAILURE); } // Read 1byte ioctl(fd, I2C_SLAVE, slave_addr); buf[0] = (sub_addr & 0xFF00) >> 8; buf[1] = sub_addr & 0xFF; if ((rval = write(fd, buf, 2)) < 0) { fprintf(stderr, "Writing error! (%lX): %s\r\n", slave_addr, strerror(errno)); exit(EXIT_FAILURE); } if ((rval = read(fd, rxd, sizeof(unsigned char))) < 0) { fprintf(stderr, "Reading error! (%lX): %s\r\n", slave_addr, strerror(errno)); exit(EXIT_FAILURE); } fprintf(stdout, "Read 1byte = 0x%02X\r\n", rxd[0]); close(fd); return 0; }
위와 같이 I2C 노드를 열고 I2C 통신 표준에 따라 Slave address를 전송한 뒤 Sub address를 전송하면 read 함수로 데이터를 읽어 올 수 있습니다.
한꺼번에 여러 바이트를 전송할 수 있는 칩이라면 여러 바이트를 읽어 올 수 있습니다.
전송하기
읽어 오는 걸 하셨으니 전송하는 것은 바로 하실 수 있으실 겁니다. Sub address와 함께 데이터를 전송하시면 됩니다.
// Write unsigned char buf[3]; ioctl(fd, I2C_SLAVE_FORCE, slave_addr); buf[0] = (sub_addr & 0xFF00) >> 8; buf[1] = sub_addr & 0xFF; buf[2] = 0xAA; if ((rval = write(fd, buf, 3)) < 0) { fprintf(stderr, "Writing error! (%lX): %s\r\n", slave_addr, strerror(errno)); exit(EXIT_FAILURE); }
read와 마찬가지로 한꺼번에 여러 바이트를 전송받을 수 있는 칩이라면 버퍼 크기와 전송 크기를 수정해서 여러 바이트를 전송하실 수 있습니다.
읽어오기 2
다음과 같이 데이터를 읽어 올 수도 있습니다.
// Read multi-bytes struct i2c_msg msg[2]; struct i2c_rdwr_ioctl_data idata; unsigned char rxd[10]; msg[0].addr = (__u16)slave_addr; msg[0].flags = 0; msg[0].len = 2; msg[0].buf = (__u8 *)&sub_addr; msg[1].addr = (__u16)slave_addr; msg[1].flags = I2C_M_RD; msg[1].len = (__u16)10; msg[1].buf = (__u8 *)rxd; idata.msgs = msg; idata.nmsgs = 2; if ((rval = ioctl(fd, I2C_RDWR, &idata)) < 0) { fprintf(stderr, "IOCTL error! (%lX): %s\r\n", slave_addr, strerror(errno)); exit(EXIT_FAILURE); }
기능(Function) 조사하기
그리고, I2C 칩이 블록 전송이 가능한지 등 지원되는 기능이 무엇인지 조사할 수 도 있습니다.
// Check funcs unsigned long func_flags; if ((rval = ioctl(fd, I2C_FUNCS, &func_flags)) < 0) { fprintf(stderr, "Could not get the adapter functionality matrix: %s\r\n", strerror(errno)); exit(EXIT_FAILURE); }
조사한 결과는 각 비트 플레그를 조사하시면 됩니다. 각 비트 플레그의 의미는 (링크)를 참조하세요.
728x90'프로그래밍 > 리눅스' 카테고리의 다른 글
리눅스 어플리케이션에서 RTC 사용하기 (0) 2021.03.26 리눅스 어플리케이션에서 SPI 사용하기 (0) 2021.03.24 리눅스 어플리케이션에서 GPIO 사용하기 (0) 2013.03.13