C언어를 활용한 I2C 센서 데이터 읽기 가이드

I2C(Inter-Integrated Circuit) 통신은 마스터-슬레이브 구조를 통해 간단하고 효율적으로 데이터를 주고받을 수 있는 직렬 통신 프로토콜입니다. 임베디드 시스템에서 센서 데이터를 읽거나 디바이스 간의 통신을 구현할 때 널리 사용됩니다. 본 기사에서는 C언어를 이용해 I2C 통신을 설정하고, 센서 데이터를 읽는 방법을 단계별로 알아봅니다. 또한, 이를 기반으로 실시간 데이터 로깅과 같은 실용적인 응용 사례를 제공합니다.

I2C 통신의 개요


I2C(Inter-Integrated Circuit)는 필립스(현 NXP)에 의해 개발된 직렬 통신 프로토콜로, 마스터-슬레이브 구조를 기반으로 동작합니다.

I2C의 동작 원리


I2C는 두 개의 주요 신호 선로를 사용하여 데이터를 전송합니다:

  • SCL(Serial Clock Line): 마스터 장치가 생성하는 클럭 신호로, 데이터 전송의 타이밍을 결정합니다.
  • SDA(Serial Data Line): 마스터와 슬레이브 간 데이터를 주고받는 선로입니다.

I2C는 한 번에 하나의 마스터가 여러 슬레이브와 통신할 수 있도록 설계되었습니다. 각 슬레이브 장치는 고유의 7비트 또는 10비트 주소를 가지며, 이를 통해 마스터는 특정 장치와 통신할 수 있습니다.

I2C 통신의 주요 특징

  • 단순한 배선: 두 개의 신호 선로(SCL, SDA)만으로 다수의 장치를 연결할 수 있습니다.
  • 멀티마스터 지원: 필요에 따라 여러 마스터가 하나의 버스를 공유할 수 있습니다.
  • 주소 기반 통신: 슬레이브 주소를 통해 특정 장치만 선택적으로 제어 가능.
  • 속도 조정 가능: 일반적으로 100kHz(표준 모드)에서 3.4MHz(고속 모드)까지 다양한 속도를 지원합니다.

왜 I2C가 많이 사용되는가

  • 하드웨어 리소스가 제한된 임베디드 시스템에서 효율적인 통신을 제공합니다.
  • 다양한 센서 및 주변 장치에서 표준으로 채택되어 호환성이 뛰어납니다.
  • 설계 및 구현이 비교적 간단하여 초보자도 쉽게 다룰 수 있습니다.

I2C는 그 유연성과 효율성 덕분에 센서 데이터 수집, 디스플레이 제어, 메모리 장치와의 통신 등 다양한 용도로 사용되고 있습니다.

I2C 통신의 기본 구성

I2C 통신은 두 개의 주요 선로(SCL, SDA)를 기반으로 마스터와 슬레이브 간 데이터를 전송하는 구조를 가지고 있습니다.

마스터와 슬레이브의 역할

  • 마스터:
  • 통신을 주도하며 클럭 신호(SCL)를 생성합니다.
  • 슬레이브의 주소를 지정하고 데이터를 송수신합니다.
  • 슬레이브:
  • 마스터가 보내는 신호를 수신하고, 요청에 따라 데이터를 송신합니다.
  • 각 슬레이브는 고유한 주소를 가지며, 이를 통해 마스터가 특정 장치와 통신할 수 있습니다.

통신 신호와 흐름

  • SCL(Serial Clock Line):
  • 데이터 전송의 타이밍을 조율하며, 마스터가 이 신호를 생성합니다.
  • SDA(Serial Data Line):
  • 데이터가 실제로 송수신되는 선로입니다.

I2C 통신은 비트 단위로 데이터가 전송되며, 다음과 같은 주요 흐름을 따릅니다:

  1. 시작 신호(Start Condition): SDA가 SCL이 HIGH 상태일 때 HIGH에서 LOW로 전환됩니다.
  2. 주소 전송(Address Frame): 마스터는 통신하려는 슬레이브의 주소를 전송합니다.
  3. 읽기/쓰기 결정(Read/Write Bit): 데이터 전송 방향(읽기/쓰기)을 결정합니다.
  4. 데이터 전송(Data Frames): 데이터를 비트 단위로 송수신하며, 각 데이터 프레임 뒤에는 ACK(인증 신호)가 전송됩니다.
  5. 종료 신호(Stop Condition): SDA가 SCL이 HIGH 상태일 때 LOW에서 HIGH로 전환됩니다.

풀업 저항의 역할


I2C 통신의 SCL 및 SDA 선로는 오픈 드레인(Open-Drain) 방식으로 동작하므로, 각 선로에는 풀업 저항이 필요합니다. 풀업 저항은 기본 상태에서 선로를 HIGH로 유지하며, 안정적인 신호 전송에 기여합니다.

장치 간 연결


I2C 통신에서는 마스터와 여러 슬레이브를 동일한 SCL, SDA 선로에 병렬로 연결할 수 있습니다. 이때, 각 슬레이브는 서로 다른 주소를 가져야 충돌 없이 통신할 수 있습니다.

이와 같은 기본 구성을 이해하면, 다양한 I2C 기반 센서나 장치와 효과적으로 통신할 수 있습니다.

C언어에서 I2C 통신 구현

C언어를 사용하여 I2C 통신을 구현하려면 하드웨어와 소프트웨어 설정을 적절히 조합해야 합니다. 아래는 I2C 통신의 기본 코드 구조와 주요 단계에 대한 설명입니다.

필수 헤더 파일 및 설정


I2C 통신을 구현하기 위해 사용할 하드웨어 플랫폼에 따라 적절한 헤더 파일을 포함해야 합니다. 예를 들어, AVR 마이크로컨트롤러를 사용할 경우 avr/io.h와 같은 헤더 파일을 포함합니다.

#include <stdio.h>
#include <avr/io.h> // 하드웨어 레지스터 정의
#include <util/twi.h> // I2C(TWI) 관련 유틸리티

I2C 초기화


I2C 버스를 설정하고 클럭 속도를 지정하기 위해 초기화 함수를 작성합니다.

void I2C_Init(void) {
    // 클럭 속도 설정 (예: 100kHz)
    TWSR = 0x00; // 프리스케일러 설정
    TWBR = ((F_CPU / 100000UL) - 16) / 2; // 비트 속도 계산
    TWCR = (1 << TWEN); // I2C 활성화
}

시작 및 종료 신호


I2C 통신 시작과 종료를 위한 함수를 정의합니다.

void I2C_Start(void) {
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT); // 시작 신호 전송
    while (!(TWCR & (1 << TWINT))); // 완료 대기
}

void I2C_Stop(void) {
    TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT); // 종료 신호 전송
}

데이터 전송


슬레이브 주소나 데이터를 송신하는 함수입니다.

void I2C_Write(uint8_t data) {
    TWDR = data; // 데이터 레지스터에 데이터 쓰기
    TWCR = (1 << TWEN) | (1 << TWINT); // 송신 시작
    while (!(TWCR & (1 << TWINT))); // 완료 대기
}

데이터 수신


슬레이브에서 데이터를 읽어오는 함수입니다.

uint8_t I2C_Read_ACK(void) {
    TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWEA); // ACK 전송 후 데이터 읽기
    while (!(TWCR & (1 << TWINT))); // 완료 대기
    return TWDR;
}

uint8_t I2C_Read_NACK(void) {
    TWCR = (1 << TWEN) | (1 << TWINT); // NACK 전송 후 데이터 읽기
    while (!(TWCR & (1 << TWINT))); // 완료 대기
    return TWDR;
}

기본 통신 예제


센서에서 데이터를 읽기 위한 I2C 통신의 간단한 흐름은 다음과 같습니다.

I2C_Start();                  // 시작 신호
I2C_Write(slave_address);     // 슬레이브 주소 송신
I2C_Write(register_address); // 레지스터 주소 송신
I2C_Start();                  // 반복 시작 신호 (Repeated Start)
I2C_Write(slave_address | 0x01); // 읽기 모드 설정
uint8_t data = I2C_Read_NACK();  // 데이터 수신
I2C_Stop();                   // 종료 신호

이 코드를 기반으로 다양한 I2C 장치와 통신하는 프로그램을 작성할 수 있습니다. 향후 섹션에서 센서와의 실제 데이터 읽기 예제를 다룹니다.

I2C 통신 설정과 초기화

I2C 통신을 시작하기 위해 하드웨어 설정과 초기화 과정을 올바르게 수행해야 합니다. 초기화 과정은 장치 간 안정적인 통신을 보장하는 중요한 단계입니다.

I2C 클럭 속도 설정


I2C 통신 속도는 SCL(Serial Clock Line)의 주파수로 결정됩니다. 일반적으로 사용되는 속도는 다음과 같습니다:

  • 표준 모드: 100kHz
  • 고속 모드: 400kHz
  • 고속 플러스 모드: 1MHz
    클럭 속도는 마스터 장치에서 설정하며, 슬레이브 장치는 이를 기반으로 통신을 동기화합니다.
#define F_CPU 16000000UL // CPU 주파수
#define I2C_CLOCK 100000UL // I2C 클럭 속도

void I2C_Init(void) {
    TWSR = 0x00; // 프리스케일러 설정 (기본값 1)
    TWBR = ((F_CPU / I2C_CLOCK) - 16) / 2; // 클럭 속도 계산
    TWCR = (1 << TWEN); // I2C 활성화
}

슬레이브 주소 정의


통신할 슬레이브 장치의 주소를 정의해야 합니다. I2C 슬레이브 주소는 7비트 또는 10비트로 구성됩니다.
예를 들어, 온도 센서의 I2C 주소가 0x48일 경우, 해당 주소를 상수로 정의합니다.

#define SLAVE_ADDRESS 0x48 // 센서 I2C 주소

시작 신호 및 종료 신호


I2C 시작 및 종료 신호는 통신의 시작과 끝을 표시합니다. 이를 통해 슬레이브 장치는 마스터의 통신 요청을 인식합니다.

  • 시작 신호:
  • SDA가 HIGH에서 LOW로 전환되고, SCL은 HIGH 상태를 유지합니다.
  • 종료 신호:
  • SDA가 LOW에서 HIGH로 전환되고, SCL은 HIGH 상태를 유지합니다.
void I2C_Start(void) {
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT); // 시작 신호 전송
    while (!(TWCR & (1 << TWINT))); // 완료 대기
}

void I2C_Stop(void) {
    TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT); // 종료 신호 전송
}

슬레이브 장치와의 통신 준비


마스터 장치가 특정 슬레이브 장치와 통신을 시작하려면 슬레이브 주소를 전송해야 합니다. 이 과정은 슬레이브 장치가 마스터의 요청을 인식하는 데 필수적입니다.

void I2C_SendAddress(uint8_t address) {
    I2C_Start();          // 시작 신호 전송
    I2C_Write(address);   // 슬레이브 주소 전송
}

통신 상태 확인


I2C 통신 중 오류를 방지하려면 통신 상태를 지속적으로 확인해야 합니다. 이를 위해 TWI 상태 레지스터를 검사합니다.

uint8_t I2C_GetStatus(void) {
    uint8_t status;
    status = TWSR & 0xF8; // 상태 레지스터의 상위 5비트 읽기
    return status;
}

초기화 완료


위 과정을 통해 I2C 마스터 장치를 초기화하고, 슬레이브 장치와 통신을 준비할 수 있습니다. 이 초기화 단계는 이후의 센서 데이터 읽기와 같은 고급 기능 구현의 기초가 됩니다.

센서 데이터 읽기 예제

C언어를 활용하여 I2C 통신으로 센서 데이터를 읽는 과정을 단계별로 설명합니다. 이 예제에서는 온도 센서를 사용하여 데이터를 읽는 방법을 다룹니다.

센서와의 통신 기본 흐름

  1. I2C 시작 신호 전송
  2. 슬레이브 주소와 읽기/쓰기 모드 전송
  3. 센서 레지스터 주소 전송
  4. 데이터 읽기 요청 및 수신
  5. 종료 신호 전송

센서 데이터 읽기 코드


아래 예제는 I2C 통신으로 온도 데이터를 읽는 코드를 보여줍니다.

#include <avr/io.h>
#include <util/delay.h>
#include <util/twi.h>

#define F_CPU 16000000UL  // CPU 주파수
#define I2C_CLOCK 100000UL  // I2C 클럭 속도
#define SLAVE_ADDRESS 0x48  // 온도 센서 I2C 주소

void I2C_Init(void) {
    TWSR = 0x00; // 프리스케일러 설정
    TWBR = ((F_CPU / I2C_CLOCK) - 16) / 2; // 비트 속도 계산
    TWCR = (1 << TWEN); // I2C 활성화
}

void I2C_Start(void) {
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
}

void I2C_Stop(void) {
    TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT);
}

void I2C_Write(uint8_t data) {
    TWDR = data;
    TWCR = (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
}

uint8_t I2C_Read_ACK(void) {
    TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWEA);
    while (!(TWCR & (1 << TWINT)));
    return TWDR;
}

uint8_t I2C_Read_NACK(void) {
    TWCR = (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
    return TWDR;
}

int main(void) {
    uint8_t temperature = 0;

    I2C_Init(); // I2C 초기화

    // 센서에서 온도 데이터 읽기
    I2C_Start(); // 시작 신호 전송
    I2C_Write(SLAVE_ADDRESS << 1); // 슬레이브 주소(쓰기 모드)
    I2C_Write(0x00); // 온도 데이터 레지스터 주소 전송
    I2C_Start(); // 반복 시작 신호
    I2C_Write((SLAVE_ADDRESS << 1) | 1); // 슬레이브 주소(읽기 모드)
    temperature = I2C_Read_NACK(); // 데이터 읽기
    I2C_Stop(); // 종료 신호 전송

    // 온도 데이터 출력
    while (1) {
        // 실제 시스템에서는 데이터를 처리하거나 디스플레이
        _delay_ms(1000);
    }

    return 0;
}

코드 설명

  1. 초기화: I2C_Init() 함수로 I2C 통신 설정.
  2. 레지스터 주소 전송: 센서의 특정 데이터 레지스터(예: 온도 데이터)를 지정.
  3. 데이터 읽기: 슬레이브에서 데이터를 수신.
  4. 종료 신호: 통신 완료 후 종료 신호 전송.

예제 실행 결과


위 코드를 실행하면, 센서에서 읽은 온도 데이터가 temperature 변수에 저장됩니다. 이를 이용해 디스플레이 출력, 데이터 로깅 등의 작업을 추가로 구현할 수 있습니다.

유용한 팁

  • 센서 데이터 시트에서 레지스터 주소와 데이터 형식을 확인하세요.
  • 통신 중 오류가 발생할 경우, 디버깅을 위해 상태 코드를 확인하는 로직을 추가하세요.

I2C 통신 디버깅 방법

I2C 통신은 신뢰성이 높은 프로토콜이지만, 하드웨어 설정 오류나 소프트웨어 버그로 인해 예상치 못한 문제가 발생할 수 있습니다. 아래는 I2C 통신 문제를 디버깅하고 해결하는 주요 방법을 소개합니다.

1. 하드웨어 연결 확인

  • 풀업 저항: I2C 통신에서 SCL 및 SDA 선로는 풀업 저항이 필요합니다. 일반적으로 4.7kΩ 또는 10kΩ 저항을 사용합니다.
  • 핀 배치 확인: 마스터와 슬레이브의 SCL 및 SDA 핀이 정확히 연결되었는지 확인하세요.
  • 전원 공급: 슬레이브 장치가 올바른 전원 공급을 받고 있는지 확인하세요.

2. 통신 상태 코드 확인


I2C 통신 상태 코드는 문제의 원인을 파악하는 데 유용합니다. AVR의 경우, TWSR 레지스터의 상위 5비트를 사용하여 상태를 확인합니다.

uint8_t I2C_GetStatus(void) {
    return TWSR & 0xF8; // 상위 5비트를 추출하여 상태 코드 반환
}

주요 상태 코드:

  • 0x08: 시작 신호가 성공적으로 전송됨
  • 0x18: 슬레이브 주소(쓰기)가 성공적으로 전송됨
  • 0x40: 슬레이브 주소(읽기)가 성공적으로 전송됨
  • 0x28: 데이터가 성공적으로 송신됨
  • 0x50: 데이터가 성공적으로 수신되고 ACK가 반환됨

상태 코드가 예상과 다를 경우, 해당 단계에서 문제가 발생했음을 의미합니다.

3. 클럭 속도 및 레지스터 설정 확인

  • 클럭 속도 설정(TWBR)과 프리스케일러 설정(TWSR)이 올바른지 확인하세요.
  • 슬레이브 장치가 지원하는 클럭 속도(예: 100kHz 또는 400kHz)를 초과하지 않도록 설정합니다.

4. 슬레이브 주소 확인

  • 슬레이브 장치의 I2C 주소가 정확히 설정되었는지 데이터 시트를 참고하세요.
  • 7비트 주소를 사용할 경우, 읽기/쓰기 모드에 따라 마지막 비트(R/W 비트)를 적절히 설정해야 합니다.
#define SLAVE_WRITE_ADDRESS (SLAVE_ADDRESS << 1)
#define SLAVE_READ_ADDRESS ((SLAVE_ADDRESS << 1) | 1)

5. 논리 분석기 또는 오실로스코프 사용


I2C 신호를 직접 분석하기 위해 논리 분석기나 오실로스코프를 사용할 수 있습니다. 이를 통해 SCL과 SDA 신호를 관찰하고 신호 왜곡, 노이즈, 타이밍 문제를 파악할 수 있습니다.

6. 통신 타임아웃 처리


I2C 통신이 중단될 경우 무한 대기 상태에 빠질 수 있습니다. 이를 방지하기 위해 타임아웃 처리를 추가합니다.

uint8_t I2C_WaitForComplete(uint16_t timeout) {
    uint16_t count = 0;
    while (!(TWCR & (1 << TWINT))) {
        if (++count > timeout) return 0; // 타임아웃 발생
    }
    return 1; // 완료 신호 수신
}

7. 디버깅 출력 추가

  • UART 시리얼 통신을 사용하여 디버깅 정보를 출력합니다.
  • 예를 들어, 상태 코드나 슬레이브 응답을 시리얼 모니터에 출력하여 문제를 추적합니다.
printf("I2C Status: 0x%02X\n", I2C_GetStatus());

8. 소프트웨어 버그 수정

  • 반복 시작 신호(Repeated Start) 사용 여부를 확인하세요.
  • 데이터 시트에 명시된 통신 순서를 정확히 구현했는지 확인하세요.

정리


I2C 통신 디버깅은 하드웨어와 소프트웨어 모두에서 발생할 수 있는 문제를 체계적으로 해결하는 과정입니다. 위의 방법을 활용하면 I2C 통신 오류를 효과적으로 진단하고 수정할 수 있습니다.

응용 예시: 실시간 데이터 로깅

I2C 통신을 사용해 센서 데이터를 읽는 시스템은 다양한 응용 사례로 확장될 수 있습니다. 이번 예제에서는 I2C 기반 센서 데이터를 읽어 실시간으로 로깅하는 방법을 설명합니다. 이 응용은 환경 모니터링, IoT 디바이스 개발 등에서 활용할 수 있습니다.

프로젝트 목표

  • I2C 온도 센서에서 데이터를 읽어 주기적으로 로깅
  • 데이터를 시리얼 출력 또는 파일에 저장하여 분석 가능

설계 구성

  1. 센서 연결: I2C 온도 센서를 마스터 장치와 연결합니다.
  2. 데이터 수집 주기 설정: 주기적으로 데이터를 읽어옵니다.
  3. 데이터 저장: 시리얼 출력 또는 메모리에 데이터를 저장합니다.

코드 구현

아래는 실시간 데이터 로깅을 위한 코드 예제입니다.

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <util/twi.h>

#define F_CPU 16000000UL  // CPU 주파수
#define I2C_CLOCK 100000UL  // I2C 클럭 속도
#define SLAVE_ADDRESS 0x48  // 센서 I2C 주소

void I2C_Init(void) {
    TWSR = 0x00; // 프리스케일러 설정
    TWBR = ((F_CPU / I2C_CLOCK) - 16) / 2; // 비트 속도 계산
    TWCR = (1 << TWEN); // I2C 활성화
}

void I2C_Start(void) {
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
}

void I2C_Stop(void) {
    TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT);
}

void I2C_Write(uint8_t data) {
    TWDR = data;
    TWCR = (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
}

uint8_t I2C_Read_NACK(void) {
    TWCR = (1 << TWEN) | (1 << TWINT);
    while (!(TWCR & (1 << TWINT)));
    return TWDR;
}

void UART_Init(unsigned int baud) {
    unsigned int ubrr = F_CPU / 16 / baud - 1;
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;
    UCSR0B = (1 << TXEN0); // UART 송신 활성화
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8비트 데이터
}

void UART_Transmit(char data) {
    while (!(UCSR0A & (1 << UDRE0))); // 송신 버퍼 준비 대기
    UDR0 = data; // 데이터 전송
}

void UART_Print(const char *str) {
    while (*str) {
        UART_Transmit(*str++);
    }
}

int main(void) {
    uint8_t temperature = 0;
    char buffer[16];

    I2C_Init(); // I2C 초기화
    UART_Init(9600); // UART 초기화 (9600bps)

    while (1) {
        // 센서에서 데이터 읽기
        I2C_Start();
        I2C_Write(SLAVE_ADDRESS << 1); // 슬레이브 주소 (쓰기 모드)
        I2C_Write(0x00); // 데이터 레지스터 주소
        I2C_Start();
        I2C_Write((SLAVE_ADDRESS << 1) | 1); // 슬레이브 주소 (읽기 모드)
        temperature = I2C_Read_NACK();
        I2C_Stop();

        // 시리얼 출력
        sprintf(buffer, "Temp: %d°C\n", temperature);
        UART_Print(buffer);

        // 1초 대기
        _delay_ms(1000);
    }
}

코드 설명

  1. I2C 초기화: I2C_Init() 함수로 마스터 장치 설정.
  2. 센서 데이터 읽기: 센서 데이터 레지스터에서 값을 읽어옵니다.
  3. UART 초기화 및 출력: 데이터를 UART로 출력해 실시간 로깅.
  4. 주기적 데이터 수집: _delay_ms(1000)로 1초 간격으로 데이터를 읽습니다.

응용 시나리오

  • 환경 모니터링: 온도, 습도 등의 데이터를 장시간 수집.
  • IoT 디바이스: 센서 데이터를 네트워크로 전송.
  • 데이터 분석: 저장된 데이터를 분석하여 트렌드 파악.

결론


I2C 통신과 UART를 결합해 데이터를 실시간으로 로깅하면 다양한 응용에 활용할 수 있습니다. 이 코드를 바탕으로 데이터를 파일에 저장하거나 네트워크로 전송하는 기능을 추가해 프로젝트를 확장할 수 있습니다.

요약

이번 기사에서는 I2C 통신을 활용해 C언어로 센서 데이터를 읽는 방법을 설명했습니다. I2C 통신의 기본 개념부터 하드웨어 설정, 센서 데이터 읽기 코드, 통신 디버깅 방법, 그리고 실시간 데이터 로깅을 구현하는 응용 사례까지 단계적으로 다뤘습니다.

I2C 통신은 간단한 배선과 높은 확장성으로 임베디드 시스템에서 널리 사용됩니다. 적절한 초기화와 디버깅을 통해 안정적인 데이터 수집 시스템을 구축할 수 있으며, 이를 활용한 다양한 응용 프로젝트를 구현할 수 있습니다.