Embedded Linux Kernel: I2C 드라이버 작성 및 사용법
안녕하세요, 오늘은 Rockchip RK3399을 기준으로 Linux Kernel에서 I2C 인터페이스를 사용하는 방법과 I2C 드라이버를 작성하는 방법에 대해 알아보겠습니다. I2C는 많은 임베디드 시스템에서 사용되는 직렬 통신 프로토콜로, 다양한 센서 및 주변 장치와의 통신에 유용합니다. 이번 포스팅에서는 I2C의 기본 개념부터 드라이버 작성 및 예제 구현까지 설명하겠습니다.
1. I2C 기본 개념
I2C(Inter-Integrated Circuit)는 두 개의 신호 선(SCL, SDA)을 통해 마스터-슬레이브 방식으로 통신하는 프로토콜입니다.
주요 특징
- SCL: 클록 신호
- SDA: 데이터 신호
- 다중 슬레이브 장치를 하나의 I2C 버스에서 제어 가능
- 슬레이브 장치는 고유한 7비트 또는 10비트 주소를 가짐
I2C는 간단하면서도 효율적이기 때문에 센서, EEPROM, RTC, 디스플레이와 같은 장치에서 많이 사용됩니다.
2. I2C 드라이버 작성 개요
Linux 커널에서 I2C 드라이버는 크게 다음과 같은 단계로 작성됩니다:
- I2C 어댑터 등록: I2C 컨트롤러의 하드웨어를 제어.
- I2C 클라이언트 등록: 특정 슬레이브 장치와 통신.
- I2C 메시지 전송: 데이터를 읽거나 씀.
주요 커널 구성 요소
- i2c_adapter: I2C 버스를 나타냄.
- i2c_client: I2C 버스에 연결된 장치를 나타냄.
- i2c_algorithm: 데이터 전송을 위한 함수 집합.
3. Rockchip RK3399에서의 I2C 설정
RK3399 플랫폼에서 I2C 컨트롤러를 사용하려면 Device Tree 설정이 필요합니다. 아래는 예제 Device Tree 설정입니다:
&i2c1 {
status = "okay";
clock-frequency = <400000>;
touch_device: touch@38 {
compatible = "mycompany,my-touch";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
};
};
위 설정은 I2C1 버스에서 주소가 0x38인 터치 장치를 활성화하고 GPIO1의 12번 핀을 인터럽트로 사용합니다.
4. I2C Touch Input Device 드라이버 작성 예제
다음은 I2C 기반 터치 입력 장치 드라이버의 수정된 예제입니다. 이 드라이버는 GPIO 인터럽트를 통해 터치 이벤트를 처리합니다.
헤더 파일 포함
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
드라이버 데이터 구조체
struct touch_device {
struct i2c_client *client;
struct input_dev *input;
int irq;
};
인터럽트 핸들러
static irqreturn_t touch_irq_handler(int irq, void *dev_id) {
struct touch_device *touch = dev_id;
u8 data[4];
int ret;
/* 터치 컨트롤러에서 데이터 읽기 */
ret = i2c_smbus_read_i2c_block_data(touch->client, 0x00, 4, data);
if (ret < 0) {
dev_err(&touch->client->dev, "Failed to read touch data\n");
return IRQ_HANDLED;
}
/* 입력 이벤트 전송 */
input_report_abs(touch->input, ABS_X, data[0] | (data[1] << 8));
input_report_abs(touch->input, ABS_Y, data[2] | (data[3] << 8));
input_sync(touch->input);
return IRQ_HANDLED;
}
프로브 함수
static int touch_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct touch_device *touch;
int ret;
touch = devm_kzalloc(&client->dev, sizeof(struct touch_device), GFP_KERNEL);
if (!touch) {
dev_err(&client->dev, "Failed to allocate memory\n");
return -ENOMEM;
}
touch->client = client;
/* 입력 장치 할당 */
touch->input = devm_input_allocate_device(&client->dev);
if (!touch->input) {
dev_err(&client->dev, "Failed to allocate input device\n");
return -ENOMEM;
}
touch->input->name = "I2C Touch Device";
touch->input->id.bustype = BUS_I2C;
input_set_capability(touch->input, EV_ABS, ABS_X);
input_set_capability(touch->input, EV_ABS, ABS_Y);
input_set_abs_params(touch->input, ABS_X, 0, 800, 0, 0);
input_set_abs_params(touch->input, ABS_Y, 0, 480, 0, 0);
ret = input_register_device(touch->input);
if (ret) {
dev_err(&client->dev, "Failed to register input device\n");
return ret;
}
/* GPIO 인터럽트 설정 */
touch->irq = client->irq;
ret = devm_request_threaded_irq(&client->dev, touch->irq, NULL,
touch_irq_handler, IRQF_ONESHOT,
"touch_irq", touch);
if (ret) {
dev_err(&client->dev, "Failed to request IRQ\n");
return ret;
}
i2c_set_clientdata(client, touch);
dev_info(&client->dev, "Touch Input Device Initialized\n");
return 0;
}
static int touch_remove(struct i2c_client *client) {
dev_info(&client->dev, "Touch Input Device Removed\n");
return 0;
}
장치 ID 테이블 및 OF 매치 테이블
static const struct i2c_device_id touch_id[] = {
{ "my-touch", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, touch_id);
static const struct of_device_id touch_of_match[] = {
{ .compatible = "mycompany,my-touch" },
{}
};
MODULE_DEVICE_TABLE(of, touch_of_match);
I2C 드라이버 구조체 등록
static struct i2c_driver touch_driver = {
.driver = {
.name = "my-touch",
.of_match_table = touch_of_match,
},
.probe = touch_probe,
.remove = touch_remove,
.id_table = touch_id,
};
module_i2c_driver(touch_driver);
모듈 정보
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("I2C Touch Input Device Driver with Interrupt");
MODULE_LICENSE("GPL");
5. 드라이버 빌드 및 로드
Makefile
obj-m := touch_driver.o
빌드
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
드라이버 로드
sudo insmod touch_driver.ko
dmesg 확인
dmesg | grep "Touch Input Device"
6. 결론
이 포스팅에서는 RK3399 플랫폼에서 I2C 기반 터치 입력 장치 드라이버를 작성하고 GPIO 인터럽트를 통해 터치 데이터를 처리하는 방법을 소개했습니다. 인터럽트를 활용하면 이벤트 기반으로 효율적인 데이터 처리가 가능합니다. 실제 환경에서 이 코드를 테스트하며 더 많은 경험을 쌓아보시길 바랍니다.
'Linux > Kernel Driver' 카테고리의 다른 글
USART Driver 작성 및 Linux Kernel에서의 USART 인터페이스 사용 방법 (0) | 2025.03.22 |
---|---|
Embedded Linux에서 SPI Driver 작성하기 (Rockchip RK3399 기준) (0) | 2025.03.21 |
Embedded Linux Kernel: Platform Driver 작성하기 (0) | 2025.03.19 |
Device Driver에서 Sysfs 등록 (0) | 2025.03.18 |
Embedded Linux: Character Device Driver 작성 (0) | 2025.03.17 |