IoT (4) - I2C 프로토콜 예제: ADT7420 제어

Updated:

4. I2C 프로토콜 예제: ADT7420 온도 센서 제어

이번 포스트에서는 임베디드 프로세서가 탑재된 하드웨어를 이용하여 ADT7420 온도 센서를 제어하는 예제를 소개한다. I2C 프로토콜에 관한 이론적인 내용을 토대로, 실제 개발 과정의 예를 소개할 예정이다. ADT7420 에 대한 자세한 특징은 맨 아래 데이터 시트를 참조하였고, 본 예제에서는 Nordic Semiconductor 사의 nRF52832 BLE 칩이 탑재된 nRF52-DK 를 사용하였다.

Notice: 본 포스트는 I2C 프로토콜에 대한 기본적인 내용을 숙지하고 있다는 가정하에 작성되었습니다.

4.1 하드웨어 연결

임베디드 보드를 이용해 ADT7420 온도 센서로부터 현재 실내 공간의 온도를 측정하는 프로그램을 작성한다고 가정해보자. 이를 위해서는 프로그래밍을 하기에 앞서, 보드와 온도 센서를 연결해줘야 한다. 실제 통신에 사용되는 핀은 SCL, SDA 두 개이지만, 일반적으로 SlaveMaster(보드) 사이의 전압 레벨을 맞추고 전원을 공급하기 위해 VDD, GND 핀도 연결한다.

대부분 보드에서 SCL, SDA로 사용할 디지털 핀은 사용자가 임의로 선택할 수 있기 때문에, I2C 통신으로 사용 가능한 핀 중에 원하는 핀으로 설정하면 된다. 주의사항으로는 별도의 언급이 없을 경우에는 해당 핀에는 아래 그림과 같이 Pull-up 저항을 연결해줘야 한다.


4.2 데이터 시트 참조

하드웨어 준비가 다 되었으면, 다음으로는 Slave의 데이터 시트로부터 필요한 정보를 확인해야한다. 가장 기본적으로 I2C 프로토콜을 이용하기 위해서는 Slave의 주소 값을 확인해야 할 것이다. 또한, 본 예제와 같이 온도 센서를 제어하는 경우에는 어떤 레지스터에 온도 값이 저장되어 있는지를 알아야 하고, 어떤 포맷으로 데이터가 저장되어 있는지를 알아야 실제 온도 단위(e.g. Celsius)로 변환할 수 있다.

ADT7420 온도 센서의 경우 7 bits 주소 값을 가지며, 센서의 A1, A0 핀 값에 따라서 주소 값을 설정할 수 있다. 예를 들어 위의 그림에서처럼 A1, A0가 모두 GND에 연결된 경우, 아래의 데이터 시트에서 볼 수 있듯이 Slave 주소 값은 0x48로 결정된다.

Slave 주소를 알았으면, 다음으로는 온도 센서를 제어하기 위한 주요 레지스터 주소를 알아야한다. 레지스터에 대한 정보는 데이터 시트에 포함되어 있거나, 레지스터 맵이라는 별도의 문서가 존재나는 경우도 있다. ADT7420 의 경우 아래와 같이 데이터 시트 내에 레지스터 정보가 포함되어 있다.

위 그림에 표시한 것처럼, 기본 기능을 사용하는데 있어서는 0x00-0x03 레지스터에 접근하는 것만으로 충분하다. ADT7420 출력 값은 sign-bit를 포함해 13 bits 또는 16 bits 크기를 갖기 때문에, 해당 정보는 두 레지스터에 걸쳐 저장된다(0x00, 0x01). 0x02는 센서의 내부 상태를 나타내는 레지스터이고, 0x03 레지스터를 이용해 온도 센서의 정밀도 (resolution)나 측정 패턴 등을 설정할 수 있다.


4.3 예제 코드

I2C Write 예제

twi_init();

m_xfer_done = false;
uint8_t reg[2] = {0x03, 0x40};
    
err_code = nrf_drv_twi_tx(&m_twi, 0x48, reg, sizeof(reg), false);
APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);

Nordic Semiconductor 사에서는 TWI 라는 이름으로 I2C 프로토콜 관련 함수를 제공한다. 위 코드에서 twi_init(); 함수는 보드에서 I2C 모듈을 초기화하는 함수이고, 해당 함수 내에서 어떤 핀을 SCL, SDA로 사용할 것인지, 데이터 전송 속도는 얼마로 설정할 것인지 등에 대한 정보를 정의한다.

nrf_drv_twi_tx(...) 함수는 I2C 프로토콜에 따라서 Slave로 Write 명령을 수행해주는 함수이다. 해당 함수의 파라미터 중 0x48Slave의 주소이고, Write 하고 싶은 정보는 reg라는 unsigned 8 bits 배열에 저장되어 있다. reg[0]에 저장된 0x03는 ADT7420 의 configuration 레지스터 주소이고, 0x40은 해당 레지스터에 쓰고자 하는 값이다.

위의 그림은 configuration 레지스터에 대한 정보이며, 해당 레지스터를 0x40 = 0b01000000 로 설정할 경우, 온도 센서의 출력 값은 13 bits의 정밀도를 가지며, 온도 센서는 1 SPS 모드로 동작할 것이다.

I2C Read 예제

uint8_t regTmp = 0x00;
uint8_t samples[2] = {0};

m_xfer_done = false;

err_code = nrf_drv_twi_tx(&m_twi, 0x48, &regTmp, sizeof(regTmp), false);
APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);

err_code = nrf_drv_twi_rx(&m_twi, 0x48, samples, 2);
APP_ERROR_CHECK(err_code);

위의 코드는 configuration 레지스터 설정 이후, 온도 센서 출력 값을 읽는 과정의 예를 보여준다. 이전 포스트에서 설명했듯이, I2C 에서는 Slave로부터 값을 읽기 전에 읽고자 하는 레지스터의 주소를 Write 하는 과정이 필요하다. ADT7420 의 온도 값은 0x00, 0x01 레지스터에 저장되어있는데, 이러한 경우에는 0x00 레지스터를 기준으로 2 bytes를 Read 하면 되기 때문에, Write 단계에서 0x00 주소를 전송하는 것을 볼 수 있다. nrf_drv_twi_rx(...) 함수는 이용해 이전 시퀀스에서 전송한 0x00 레지스터로부터 데이터를 읽는 과정을 보여주고, 위 코드에서는 2 bytes의 정보를 samples 배열에 저장하고 있다.

0x00, 0x01 레지스터의 데이터를 정상적으로 Read 한 뒤에는, 해당 데이터를 온도 단위로 변환해줘야한다. 위 그림에서 볼 수 있는 것처럼, MSB에 대한 정보는 0x00 레지스터에 저장되어 있고, 0x01 레지스터에는 LSB 값이 저장되어 있다. 한 가지 주의 사항으로는 13 bits 정밀도로 측정한 경우에는 0x01의 하위 3 bits에 온도 센서의 정보가 저장되지 않는다는 것이다.

따라서, 레지스터에서 측정된 데이터와 대응되는 ADC 값은 다음과 같이 계산할 수 있다.

uint16_t Temp16 = (sampes[0] << 8) + samples[1];
// when 16-bits resolution

uint16_t Temp13 = ((sampes[0] << 8) + samples[1]) >> 3;
// when 13-bits resolution

본 예제에서는 13 bits 정밀도로 측정하는 경우를 가정하였음으로, Temp13의 식을 이용해 ADC 코드 값을 계산하면 될 것이다. 마지막으로, 위의 과정으로 계산된 값은 아래의 수식을 이용해 온도 단위로 변환이 가능하다.


Reference

https://www.microchip.com/wwwproducts/en/MCP3428 [PDF]

Comments