ZSC31014는 Renesas Electronics의 고정밀 센서 신호 컨디셔너로, 차동/하프 브리지 센서의 신호를 증폭하고 디지털로 변환합니다 . 이 글에서는 Arduino를 사용하여 ZSC31014의 EEPROM 데이터를 읽고 쓰며, Normal Operation Mode에서 센서(브리지) 데이터와 온도 데이터를 I²C로 읽는 방법을 설명합니다. 코드와 설정은 ZSC31014 데이터시(REN_ZSC31014_DST_20160120_1.pdf)를 기반으로 합니다 .
하드웨어 설정
필요 부품
- 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 설정
- Arduino IDE를 설치합니다 .
- 새 스케치를 만들고 아래 코드를 붙여넣습니다 .
- 보드 선택: Arduino Uno/Nano 등
- 시리얼 모니터: 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)
}
사용 방법
- 하드웨어 연결
- ZSC31014를 Arduino에 연결합니다.
- VDD: Arduino 3.3V (권장) 또는 5V.
- VSS: GND.
- SCL: A5, SDA: A4.
- VDD-GND 간 100nF~470nF 커패시터.
- I²C 라인에 1kΩ~10kΩ 풀업 저항.
- VBP/VBN에 차동 센서 연결 .
- 코드 업로드
- Arduino IDE에서 코드를 복사하여 새 스케치에 붙여넣습니다.
- 보드를 연결하고 업로드합니다.
- 시리얼 모니터를 열어 결과를 확인합니다 (9600 보드레이트) .
- 동작 확인
- 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 요청 .