IoT (5) - I2C 프로토콜 예제: MCP3428 제어

Updated:

5. I2C 프로토콜 예제: MCP3428 ADC 제어

이번 포스트에서는 I2C 프로토콜을 이용하는 또 다른 예제를 소개한다. MCP3428 은 Microchip 사의 ADC 모듈로 I2C 프로토콜을 이용해 제어할 수 있지만, 이전에 소개한 ADT7420 센서와 제어 방식에 약간의 차이가 있다.

5.1 Non-Register I2C Slave (NRI2S)

I2C Slave로 동작하는 MCP3428 는 다른 I2C Slave 모듈과 마찬가지로 Slave 주소와 내부 레지스터에 접근하는 방식으로 이용해 제어가 가능하다. 한 가지 특이한 점은 MCP3428 이 Non-Register I2C Slave (NRI2S) 라는 것이다. I2C 에 대해 어느정도 친숙한 사람은 알겠지만, NRI2S 라는 단어는 필자가 임의로 만들어낸 단어이다. NRI2S 라고 언급한 이유에 대해서는 MCP3248 데이터 시트의 I2C Write 시퀀스를 보면 어느정도 납득이 갈 것이다.

위 그림은 MCP3428 의 동작 모드를 설정하기 위한 I2C Write 예시이다. 그림에서 1st ByteSlave 주소와 R/W 비트(0)에 해당되고, 2nd Byte는 MCP3428 ADC의 동작 모드를 설정하는 값에 해당한다. MCP3428 에는 4 개의 ADC 채널을 가지고 있는데, 만약 One-Shot 모드로 0번 채널에서 데이터를 읽고 싶은 경우 2nd Byte 값을 0x80 = 0b10000000으로 설정해주면 된다.

특이한 점으로는 configuration 레지스터의 주소 값을 별도로 Write 하지 않고, Slave 주소 다음에 곧바로 configuration 값을 Write 하고 있다는 점이다. 원래 I2C Write 시에는 Slave 주소 Write 이후, 해당 Slave의 내부 레지스터 주소와 그 주소에 입력하고 싶은 값을 Write 하는데, MCP3428 데이터 시트를 보면 레지스터 주소 값을 입력하는 단계가 생략되어 있다.

위 그림은 MCP3428 동작 모드 설정 이후 ADC 값을 읽는 과정을 보여주며, 이 또한 레지스터 주소를 입력하는 단계가 생략되어 있는 것을 볼 수 있다. 즉, 이러한 방식으로 제어되는 I2C Slave 모듈도 있음을 소개하고자 하였다.


5.2 예제 코드

다음은 Arduino 에서 MCP3428 ADC 측정 값을 읽은 뒤 시리얼 모니터에 값을 출력하는 예제 코드의 일부이다. Arduino의 경우 처음 프로그램 동작 시 setup() 함수를 한 번 실행하고, 이후 loop() 함수를 반복적으로 수행한다. 아래 코드에서 Wire.begin()Arduino의 I2C Master를 활성화 시키는 함수이고, generalCallReset() 함수를 이용해 MCP3428 을 초기화 한다.

void setup() {
    // put your setup code here, to run once:
    // Initialise I2C communication as MASTER
    Wire.begin();
   
    Serial.begin(115200);
    while(!Serial);
    Serial.println("Start serial-interface");

    // reset MCP3428 ADC (general Call Reset)
    generalCallReset();
    delay(10);
}

다음은 2초 마다 4 개의 채널의 ADC 값을 Arudino 시리얼 모니터로 출력하는 코드의 예시를 보여준다. Wire.beginTransmission(0x68) 함수는 I2C Slave와 통신하기 위해 주소 값을 Write 하는 용도로 사용되었고, 본 예제에서는 Slave 주소가 0x68인 경우를 가정하고 있다.

void loop() {
    uint8_t buffer[3];
    uint8_t status;
    int adc_code;
    
    // put your main code here, to run repeatedly:
    for (uint8_t ch = 0; ch < 4; ch++) {
        // Start conversion for a ch in single-sot mode
        Wire.beginTransmission(0x68);
        Wire.write(0x80 | (ch << 5));    // initiate One-shot Mode
        Wire.endTransmission();
        
        delay(5);
        
        Wire.requestFrom(0x68, 2);
        for (int i = 0; i < 2; i++)
            buffer[i] =  Wire.read();
                
        // Convert the data to 12-bits
        adc_code = ((buffer[0] & 0x0F) << 8) + buffer[1];
        
        if(adc_code > 2047)
            adc_code -= 4095;
        
        Serial.print("ADC channel: ");
        Serial.print(ch);
        Serial.print("  ");
        Serial.print(adc_code);
        Serial.print(",  ");
        Serial.println(adc_code/2047.0*2.048, 4);
    }
    delay(2000);
}

Wire.write(0x80|(ch<<5)) 코드는 MCP3428 의 한 채널 (ch)을 One-Shot 모드로 동작하도록 설정해주는 부분이고, Wire.requestFrom(0x68, 2) 코드를 이용해 Slave에 Read 명령을 전송하며, 실제 값은 Wrie.read() 함수를 이용해 1 byte 단위로 읽는다.


Reference

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

Comments