본문 바로가기
아날로그회로(Analog Circuit)/ADC관련

ZSC31014 Sensor Signal Conditioner IC 아두이노 라이브러리 구현

by linuxgo 2025. 8. 2.

ZSC31014는 Renesas Electronics의 고정밀 센서 신호 컨디셔너로, 차동/하프 브리지 센서의 신호를 증폭하고 디지털로 변환합니다 . 이 글에서는 Arduino를 사용하여 ZSC31014의 EEPROM 데이터를 읽고 쓰며, Normal Operation Mode에서 센서(브리지) 데이터와 온도 데이터를 I²C로 읽는 방법을 설명합니다. 코드와 설정은 ZSC31014 데이터시(REN_ZSC31014_DST_20160120_1.pdf)를 기반으로 합니다 .

ZSC31014

하드웨어 설정 

필요 부품

  • Arduino 보드 (Uno, Nano 등, 3.3V 권장) 
  • ZSC31014 센서 모듈 
  • 차동/하프 브리지 센서 (예: 압력 센서) 
  • 100nF~470nF 커패시터 (VDD-GND 간) 
  • 1kΩ~10kΩ 풀업 저항 (I²C 라인) 

연결 

  • ZSC31014 VDD: Arduino 3.3V (또는 5V, 2.7V~5.5V 지원) 
  • VSS: GND
  • SCL: Arduino A5
  • SDA: Arduino A4
  • VBP/VBN: 차동 센서 연결
  • 커패시터: VDD-GND 간 100nF~470nF 
  • 풀업 저항: SCL/SDA에 1kΩ~10kΩ 

참고 (Note): Arduino 5V 보드 사용 시 ZSC31014 VDD가 5.5V 이하인지 확인하세요 . 데이터시트 페이지 6 참조 .

소프트웨어 설정 

Arduino IDE 설정 

  1. Arduino IDE를 설치합니다 .
  2. 새 스케치를 만들고 아래 코드를 붙여넣습니다 .
  3. 보드 선택: Arduino Uno/Nano 등 
  4. 시리얼 모니터: 9600 보드레이트 .

라이브러리

  • Wire.h: I²C 통신용, Arduino에 기본 포함 

Arduino 코드 

아래 코드는 ZSC31014의 EEPROM 읽기/쓰기와 센서/온도 데이터 읽기를 Wire 라이브러리를 사용하여 구현하며, I²C 주소는 0x28로 설정됩니다.

#include <Wire.h>

// ZSC31014 I2C 슬레이브 주소 (7비트 주소: 0x28, 데이터시트 페이지 20)
#define ZSC31014_I2C_ADDR 0x28

// 명령 코드 (데이터시트 페이지 37, Table 3.6)
#define CMD_START_CM  0xA0 // Command Mode 진입
#define CMD_START_NOM 0x80 // Normal Operation Mode 복귀
#define CMD_EEPROM_READ_BASE  0x00 // EEPROM 읽기 명령 기본값 
#define CMD_EEPROM_WRITE_BASE 0x40 // EEPROM 쓰기 명령 기본값 

// 타임아웃: 100ms (I2C 통신 안정성)
#define I2C_TIMEOUT 100

// VDD 제어 핀: 가정 - D2, HIGH=ON, LOW=OFF
#define ZSC_VDD_PIN 2

// 함수 프로토타입
bool zsc31014_power_cycle();  // 전원 사이클링 
bool zsc31014_start_cm();     // Command Mode 진입 
bool zsc31014_start_nom();    // Normal Operation Mode 복귀 
bool zsc31014_read_sensor_data(uint16_t *bridge_data, uint16_t *temp_data, bool temp_res_11bit);  // 센서 데이터 읽기 
bool zsc31014_trigger_measurement();  // Sleep Mode에서 측정 트리거 
bool zsc31014_read_eeprom(uint8_t word_addr, uint16_t *data);  // 단일 EEPROM 워드 읽기 
bool zsc31014_write_eeprom(uint8_t word_addr, uint16_t data);  // 단일 EEPROM 워드 쓰기 
bool zsc31014_read_all_eeprom(uint16_t *data_array);  // 전체 EEPROM 읽기
bool zsc31014_write_all_eeprom(uint16_t *data_array); // 전체 EEPROM 쓰기 

/**
 * @brief ZSC31014 VDD 전원 OFF/ON (리셋 용도)
 *        데이터시트 페이지 23 (2.3.6 POR): IC 락업 방지
 * @return true 성공 
 */
bool zsc31014_power_cycle() 
{
  // VDD OFF: 디지털 핀 LOW로 설정
  digitalWrite(ZSC_VDD_PIN, LOW);
  delay(10);  // 최소 10ms OFF 유지 (POR 안정화, 데이터시트 페이지 7 Table 1.3)

  // VDD ON: 디지털 핀 HIGH로 설정
  digitalWrite(ZSC_VDD_PIN, HIGH);
  delay(10);  // ON 후 안정화 대기 (데이터시트 페이지 28 Figure 3.2: Power-Up ~3ms)
             

  Serial.println("ZSC31014 전원 사이클링 완료 ");
  return true;
}

/**
 * @brief Command Mode 진입 
 *        데이터시트 페이지 37: Start_CM (0xA0) + don't care 데이터 2바이트  
 * @return true 성공, false 실패 
 */
bool zsc31014_start_cm() 
{
  // 전원 사이클링: IC 상태 클리어 (데이터시트 페이지 26 3.1)
  if (!zsc31014_power_cycle()) 
  {
    Serial.println("전원 사이클링 실패 ");
    return false;
  }

  Wire.beginTransmission(ZSC31014_I2C_ADDR);
  Wire.write(CMD_START_CM);
  Wire.write(0x00); // 상위 바이트 
  Wire.write(0x00); // 하위 바이트 
  if (Wire.endTransmission() != 0) 
  {
    Serial.println("Start_CM 실패");
    return false;
  }

  delay(1);  // 명령 완료 대기 (데이터시트: 10us, 페이지 37)
            
  return true;
}

/**
 * @brief Normal Operation Mode 복귀 및 EEPROM 서명 업데이트
 *        데이터시트 페이지 37: Start_NOM (0x80) + don't care 데이터 2바이트
 * @return true 성공, false 실패
 */
bool zsc31014_start_nom() 
{
  Wire.beginTransmission(ZSC31014_I2C_ADDR);
  Wire.write(CMD_START_NOM);
  Wire.write(0x00); // 상위 바이트 (Upper byte)
  Wire.write(0x00); // 하위 바이트 (Lower byte)
  if (Wire.endTransmission() != 0) 
  {
    Serial.println("Start_NOM 실패");
    return false;
  }

  delay(15);  // 서명 업데이트 포함 최대 15ms 대기 (데이터시트 페이지 37)
  return true;
}

/**
 * @brief 센서(브리지) 데이터와 온도 데이터 읽기
 *        데이터시트 페이지 33 Figure 3.6: Read_DF3 (8비트) 또는 Read_DF4 (11비트)
 * @param bridge_data 브리지 데이터(14비트) 저장 포인터 
 * @param temp_data 온도 데이터(8비트 또는 11비트) 저장 포인터 
 * @param temp_res_11bit 온도 해상도 (false: 8비트, true: 11비트) 
 * @return true 성공, false 실패
 */
bool zsc31014_read_sensor_data(uint16_t *bridge_data, uint16_t *temp_data, bool temp_res_11bit) 
{
  uint8_t byte_count = temp_res_11bit ? 4 : 3;  // Read_DF4 (11비트) 또는 Read_DF3 (8비트)
                                          
  Wire.requestFrom(ZSC31014_I2C_ADDR, byte_count);
  if (Wire.available() != byte_count) 
  {
    Serial.println("센서 데이터 수신 실패 ");
    return false;
  }

  uint8_t rx_data[4];
  for (uint8_t i = 0; i < byte_count; i++) 
  {
    rx_data[i] = Wire.read();
  }

  // 상태 비트 확인 (S[1:0], 데이터시트 페이지 24, Table 2.9)
  uint8_t status_bits = (rx_data[0] >> 6) & 0x03;
  if (status_bits == 0x03) 
  {
    Serial.println("진단 오류: EEPROM 무결성 또는 센서 오류");
    return false;
  } else if (status_bits == 0x02) 
  {
    Serial.println("경고: Stale 데이터 ");
  }

  // 브리지 데이터 (14비트) 조합 
  *bridge_data = ((rx_data[0] & 0x3F) << 8) | rx_data[1];

  // 온도 데이터 조합
  if (byte_count >= 3) 
  {
    if (temp_res_11bit) 
    {
      // 11비트 온도 데이터
      *temp_data = (rx_data[2] << 3) | ((rx_data[3] >> 5) & 0x07);
    } else 
    {
      // 8비트 온도 데이터 
      *temp_data = rx_data[2];
    }
  } else 
  {
    *temp_data = 0;  // 온도 데이터 요청되지 않음
  }

  Serial.print(" (Bridge Data: 0x");
  Serial.print(*bridge_data, HEX);
  Serial.print(", Temperature Data: 0x");
  Serial.print(*temp_data, HEX);
  Serial.println(")");
  return true;
}

/**
 * @brief Sleep Mode에서 측정 요청 (Write_MR)
 *        데이터시트 페이지 36 Figure 3.9: 빈 쓰기로 깨우기
 * @return true 성공, false 실패 
 */
bool zsc31014_trigger_measurement() 
{
  uint16_t config_reg;

  // Config_Reg 읽기: 클럭 설정 확인 (워드 0x00, 비트 [10], 데이터시트 페이지 38)
  if (!zsc31014_start_cm() || !zsc31014_read_eeprom(0x00, &config_reg) || !zsc31014_start_nom()) 
  {
    Serial.println("Config_Reg 읽기 실패 ");
    return false;
  }
  uint8_t clk_4mhz = (config_reg & (1 << 10)) ? 1 : 0;  // 비트 10: 1=4MHz, 0=1MHz

  // Write_MR: 빈 데이터 전송
  Wire.beginTransmission(ZSC31014_I2C_ADDR);
  if (Wire.endTransmission() != 0) 
  {
    Serial.println("Write_MR 실패");
    return false;
  }

  // 측정 완료 대기: 데이터시트 페이지 31 Table 3.4-3.5 (4MHz: ~1.5ms, 1MHz: ~6ms)
  delay(clk_4mhz ? 2 : 6);
  return true;
}

/**
 * @brief 특정 EEPROM 워드 읽기 (Command Mode에서 호출 가정)
 *        데이터시트 페이지 37: 읽기 명령 (0x00 + word_addr) + 데이터 수신 (0x5A + 16비트)
 * @param word_addr 워드 주소 (0x00 ~ 0x13) 
 * @param data 읽은 16비트 데이터 저장 포인터 
 * @return true 성공, false 실패
 */
bool zsc31014_read_eeprom(uint8_t word_addr, uint16_t *data) 
{
  if (word_addr > 0x13) 
  {
    Serial.print("(Invalid word address: 0x");
    Serial.print(word_addr, HEX);
    Serial.println(")");
    return false;
  }

  // 읽기 명령 전송 
  Wire.beginTransmission(ZSC31014_I2C_ADDR);
  Wire.write(CMD_EEPROM_READ_BASE + word_addr);
  Wire.write(0x00); // 상위 바이트 
  Wire.write(0x00); // 하위 바이트
  if (Wire.endTransmission() != 0)
  {
    Serial.print("(EEPROM read command failed, word 0x");
    Serial.print(word_addr, HEX);
    Serial.println(")");
    return false;
  }

  // 데이터 수신 (5AHEX + 16비트 데이터)
  Wire.requestFrom(ZSC31014_I2C_ADDR, 3);
  if (Wire.available() != 3) 
  {
    Serial.print("(EEPROM data receive failed, word 0x");
    Serial.print(word_addr, HEX);
    Serial.println(")");
    return false;
  }

  uint8_t rx_data[3];
  rx_data[0] = Wire.read(); // 0x5A
  rx_data[1] = Wire.read(); // 데이터 상위 바이트 
  rx_data[2] = Wire.read(); // 데이터 하위 바이트 

  // 첫 바이트가 0x5A인지 확인
  if (rx_data[0] != 0x5A) 
  {
    Serial.print("(EEPROM read response error, word 0x");
    Serial.print(word_addr, HEX);
    Serial.print("): 0x");
    Serial.print(rx_data[0], HEX);
    Serial.println(")");
    return false;
  }

  // 16비트 데이터 조합
  *data = (rx_data[1] << 8) | rx_data[2];
  Serial.print("(Word 0x");
  Serial.print(word_addr, HEX);
  Serial.print(": 0x");
  Serial.print(*data, HEX);
  Serial.println(")");
  return true;
}

/**
 * @brief 특정 EEPROM 워드 쓰기 (Command Mode에서 호출 가정)
 *        데이터시트 페이지 37: 쓰기 명령 (0x40 + word_addr) + 16비트 데이터
 * @param word_addr 워드 주소 (0x00 ~ 0x13) 
 * @param data 쓸 16비트 데이터
 * @return true 성공, false 실패 
 */
bool zsc31014_write_eeprom(uint8_t word_addr, uint16_t data) 
{
  if (word_addr > 0x13) 
  {
    Serial.print("(Invalid word address: 0x");
    Serial.print(word_addr, HEX);
    Serial.println(")");
    return false;
  }

  // T_Config(0x10), Osc_Trim(0x11) 쓰기 금지 (데이터시트 페이지 38)
  if (word_addr == 0x10 || word_addr == 0x11) 
  {
    Serial.print("(Error: Word 0x");
    Serial.print(word_addr, HEX);
    Serial.println(" is factory-set and write-protected)");
    return false;
  }

  Wire.beginTransmission(ZSC31014_I2C_ADDR);
  Wire.write(CMD_EEPROM_WRITE_BASE + word_addr);
  Wire.write((data >> 8) & 0xFF); // 상위 바이트
  Wire.write(data & 0xFF);        // 하위 바이트
  if (Wire.endTransmission() != 0) 
  {
    Serial.print("(EEPROM write failed, word 0x");
    Serial.print(word_addr, HEX);
    Serial.println(")");
    return false;
  }

  delay(10);  // 쓰기 완료 대기 (데이터시트: 10ms, 페이지 37)
  Serial.print("(Wrote 0x");
  Serial.print(data, HEX);
  Serial.print(" to word 0x");
  Serial.print(word_addr, HEX);
  Serial.println(")");
  return true;
}

/**
 * @brief 전체 EEPROM 읽기 (0x00 ~ 0x13)
 *        Command Mode에서 모든 워드 읽기, 중복 진입 방지
 * @param data_array 20개 워드(16비트)를 저장할 배열
 * @return true 성공, false 실패 
 */
bool zsc31014_read_all_eeprom(uint16_t *data_array) 
{
  // Command Mode 진입
  if (!zsc31014_start_cm()) 
  {
    Serial.println("Command Mode 진입 실패");
    return false;
  }

  // 워드 0x00 ~ 0x13 읽기
  for (uint8_t word_addr = 0x00; word_addr <= 0x13; word_addr++) 
  {
    if (!zsc31014_read_eeprom(word_addr, &data_array[word_addr])) 
    {
      zsc31014_start_nom();  // 에러 시 NOM 복귀
      return false;
    }
  }

  // Normal Operation Mode 복귀
  if (!zsc31014_start_nom()) 
  {
    Serial.println("Normal Operation Mode 복귀 실패");
    return false;
  }

  Serial.println("EEPROM 전체 읽기 완료");
  return true;
}

/**
 * @brief 전체 EEPROM 쓰기 (0x00 ~ 0x13)
 *        Command Mode에서 모든 워드 쓰기, 중복 진입 방지
 * @param data_array 쓸 20개 워드(16비트) 데이터 배열 
 * @return true 성공, false 실패
 */
bool zsc31014_write_all_eeprom(uint16_t *data_array) 
{
  // Command Mode 진입 
  if (!zsc31014_start_cm()) 
  {
    Serial.println("Command Mode 진입 실패");
    return false;
  }

  // 워드 0x00 ~ 0x13 쓰기
  for (uint8_t word_addr = 0x00; word_addr <= 0x13; word_addr++) 
  {
    if (!zsc31014_write_eeprom(word_addr, data_array[word_addr])) 
    {
      zsc31014_start_nom();  // 에러 시 NOM 복귀 
      return false;
    }
  }

  // Normal Operation Mode 복귀 및 서명 업데이트
  if (!zsc31014_start_nom()) 
  {
    Serial.println("Normal Operation Mode 복귀 실패 ");
    return false;
  }

  Serial.println("EEPROM 전체 쓰기 완료");
  return true;
}

/**
 * @brief 테스트 함수
 */
void zsc31014_example() 
{
  uint16_t eeprom_data[20];  // 0x00 ~ 0x13 워드 저장 배열
  uint16_t bridge_data, temp_data;

  // 초기 전원 사이클링
  if (!zsc31014_power_cycle()) 
  {
    Serial.println("초기 전원 사이클링 실패");
    return;
  }

  // 전체 EEPROM 읽기 
  Serial.println("EEPROM 전체 읽기 시작 ");
  if (zsc31014_read_all_eeprom(eeprom_data)) 
  {
    Serial.println("EEPROM 전체 읽기 완료 ");
  } else 
  {
    Serial.println("EEPROM 전체 읽기 실패");
  }

  // 특정 워드 읽기 (예: 워드 0x01) 
  uint16_t single_data;
  Serial.println("워드 0x01 읽기 시작");
  if (zsc31014_start_cm() && zsc31014_read_eeprom(0x01, &single_data) && zsc31014_start_nom()) 
  {
    Serial.print("(Word 0x01: 0x");
    Serial.print(single_data, HEX);
    Serial.println(")");
  } else 
  {
    Serial.println("워드 0x01 읽기 실패");
  }

  // 특정 워드 쓰기 (예: 워드 0x01에 0x0000 쓰기) 
  Serial.println("워드 0x01에 0x0000 쓰기 시작");
  if (zsc31014_start_cm() && zsc31014_write_eeprom(0x01, 0x0000) && zsc31014_start_nom()) 
  {
    Serial.println("워드 0x01 쓰기 완료");
  } else 
  {
    Serial.println("워드 0x01 쓰기 실패");
  }

  // 전체 EEPROM 쓰기 예시 (샘플 데이터)
  uint16_t sample_data[20];
  memset(sample_data, 0, sizeof(sample_data)); // 0으로 초기화 
  sample_data[0x01] = 0x0004; // I2C, 1MHz, 11비트 온도, Update_Rate=25ms (I2C, 1MHz, 11-bit temp, Update_Rate=25ms)
  sample_data[0x02] = 0x0280; // 슬레이브 주소 0x28 
  sample_data[0x0F] = 0x0A80; // PreAmp_Gain=6, A2D_Offset=-1/2 ~ 1/2 (datasheet page 16 Table 2.4)
  Serial.println("EEPROM 전체 쓰기 시작 ");
  if (zsc31014_write_all_eeprom(sample_data)) 
  {
    Serial.println("EEPROM 전체 쓰기 완료 ");
  } else 
  {
    Serial.println("EEPROM 전체 쓰기 실패 ");
  }

  // Sleep Mode에서 센서 데이터와 온도 데이터 읽기 
  Serial.println("Sleep Mode에서 센서/온도 데이터 읽기 시작");
  if (zsc31014_trigger_measurement()) 
  {
    if (zsc31014_read_sensor_data(&bridge_data, &temp_data, true)) 
    {
      Serial.println("Sleep Mode 데이터 읽기 완료 ");
    } else 
    {
      Serial.println("Sleep Mode 데이터 읽기 실패 ");
    }
  } else 
  {
    Serial.println("Write_MR 실패 (Write_MR Failed)");
  }

  // Update Mode에서 센서 데이터와 온도 데이터 읽기 
  Serial.println("Update Mode에서 센서/온도 데이터 읽기 시작");
  if (zsc31014_read_sensor_data(&bridge_data, &temp_data, true)) 
  {
    Serial.println("Update Mode 데이터 읽기 완료 ");
  } else 
  {
    Serial.println("Update Mode 데이터 읽기 실패");
  }
}

/**
 * @brief Arduino setup 함수
 */
void setup() 
{
  // 시리얼 초기화 (디버깅용) 
  Serial.begin(9600);
  while (!Serial); // 시리얼 포트 연결 대기

  // I2C 초기화 
  Wire.begin();
  Wire.setClock(400000); // 400kHz (데이터시트 페이지 21 Table 2.7 지원)
  Wire.setWireTimeout(I2C_TIMEOUT * 1000, true);  // 타임아웃 100ms 설정 (us 단위)
                                        

  // VDD 제어 핀 설정 
  pinMode(ZSC_VDD_PIN, OUTPUT);
  digitalWrite(ZSC_VDD_PIN, HIGH);  // 초기 전원 ON 

  Serial.println("ZSC31014 Arduino 테스트 시작 ");
  zsc31014_example();  // 테스트 실행 
}

/**
 * @brief Arduino loop 함수
 */
void loop() 
{
  // 주기적으로 센서 데이터 읽기 (예: 1초마다)
  uint16_t bridge_data, temp_data;
  Serial.println("주기적 센서/온도 데이터 읽기 ");
  if (zsc31014_trigger_measurement()) 
  {
    if (zsc31014_read_sensor_data(&bridge_data, &temp_data, true))
    {
      Serial.print("(Bridge Data: 0x");
      Serial.print(bridge_data, HEX);
      Serial.print(", Temperature Data: 0x");
      Serial.print(temp_data, HEX);
      Serial.println(")");
    } else 
    {
      Serial.println("센서 데이터 읽기 실패");
    }
  } else 
  {
    Serial.println("Write_MR 실패 (Write_MR Failed)");
  }
  delay(1000); // 1초 대기 (1-second delay)
}

 

사용 방법 

  1. 하드웨어 연결 
    •   ZSC31014를 Arduino에 연결합니다.
    •   VDD: Arduino 3.3V (권장) 또는 5V.
    •   VSS: GND.
    •   SCL: A5, SDA: A4.
    •   VDD-GND 간 100nF~470nF 커패시터.
    •   I²C 라인에 1kΩ~10kΩ 풀업 저항.
    •   VBP/VBN에 차동 센서 연결 .
  2. 코드 업로드 
    •   Arduino IDE에서 코드를 복사하여 새 스케치에 붙여넣습니다.
    •   보드를 연결하고 업로드합니다.
    •   시리얼 모니터를 열어 결과를 확인합니다 (9600 보드레이트) .
  3. 동작 확인 
    •   setup()에서 zsc31014_example()이 EEPROM 읽기/쓰기와 센서/온도 데이터 읽기를 실행합니다.
    •   loop()에서 1초마다 Sleep Mode로 센서 데이터를 읽습니다 .
    •   시리얼 모니터에서 출력 확인:
      •   EEPROM 워드 값.
      •   브리지 데이터 (14비트)와 온도 데이터 (11비트) .

주의사항 

  •   전원 : Arduino 5V 사용 시 ZSC31014 VDD가 5.5V 이하인지 확인하세요 .
  •   POR 후 6ms: Start_CM은 전원 켜짐 후 6ms 이내 실행해야 합니다 .
  •   공장 설정: 워드 0x10 (T_Config), 0x11 (Osc_Trim)은 공장 설정 유지 권장 .
  •   EEPROM 서명: 쓰기 후 Start_NOM 필수, 그렇지 않으면 진단 오류 발생 (S[1:0] = 0x03, 페이지 24) .
  •   Sleep Mode 첫 데이터: 첫 번째 Write_MR은 부정확할 수 있으므로 두 번째 요청 확인 .
  •   I²C 주소: 워드 02HEX 비트 [12:10] = 011이면 주소 고정.
  •   디버깅: 시리얼 모니터로 상태 비트와 통신 오류 확인.

추가 참고 

  •   캘리브레이션: 센서 데이터가 부정확하면 워드 03HEX~09HEX (Gain_B, Offset_B 등)를 재설정하세요 .
  •   IDT 소프트웨어: 캘리브레이션 계수 계산에 사용 .
  •   문서 : ZSC31014 Technical Note-Detailed Equations for Calibration Math 요청 .