이문서는 Renesas의 ZSSC3241 센서 신호 컨디셔너 IC에 대해 깊이 파고들어 보겠습니다. 이 IC는 저항성 센서(브리지, 하프 브리지, Pt100, NTC/PTC 등)의 신호를 증폭, 디지털화, 보정하는 데 특화된 제품으로, 산업 자동화, 압력/유량 센싱, 의료 기기 등 다양한 응용 분야에서 활용됩니다. 최근에 ZSSC3241 데이터시트(REN_zssc3241-datasheet_DST_20240202.pdf)를 분석하며 STM32L432KC 마이크로컨트롤러와 I2C 인터페이스를 이용한 구현 코드입니다.
이 글은 데이터시트의 주요 기능을 바탕으로 한 분석과, 모든 기능을 포괄하는 I2C 코드 구현을 중심으로 구성했습니다. 데이터시트를 다시 확인하여 내용의 완전성을 보강하였으며, 코드에 상세한 주석을 추가했습니다. 초보자도 따라할 수 있도록 단계별로 설명하겠습니다. 코드 예시는 STM32 HAL 라이브러리를 사용하며, 실제 프로젝트에 적용할 수 있도록 세심하게 작성했습니다.
ZSSC3241 IC 개요와 데이터시트 분석
ZSSC3241은 센서 신호를 고정밀도로 처리하는 IC로, 주요 기능은 다음과 같습니다:
- 아날로그 프론트엔드 (AFE): 프로그래머블 게인 증폭기(PGA), ADC(12~24비트 해상도), 온도 센서(내부/외부)를 포함. 센서 공급 방식(비율 전압, 절대 전압, 전류 루프)을 선택할 수 있습니다. 데이터시트 섹션 6.2에서 상세히 설명되며, 센서 유형에 따라 설정이 필요합니다.
- 디지털 인터페이스: I2C, SPI, OWI 지원. I2C는 최대 3.4MHz 속도, 슬레이브 주소 기본값 0x00. 인터페이스 선택은 전원 인가 후 첫 번째 유효 신호로 결정됩니다(섹션 6.4).
- 작동 모드:
- 슬립 모드: 저전력 대기, 디지털 출력 전용.
- 명령 모드: 사용자가 명령으로 측정 실행, 테스트/캘리브레이션에 적합.
- 사이클릭 모드: 자동 주기적 측정, 연속 출력에 최적화.
- DAC 및 아날로그 출력: 13~16비트 DAC로 전압(0~1V, 0~5V, 비율) 또는 전류 루프(4~20mA) 출력. SSF2 레지스터에서 설정(섹션 6.5.3).
- 진단 기능: 센서 연결 체크, 메모리 무결성, 칩 크랙 검출 등. UPDATE_DIAG(0xB2)와 CHECK_DIAG(0xB0) 명령 사용(섹션 6.3).
- NVM (Non-Volatile Memory): 보정 계수와 설정 저장, 주소 0x00~0x35 (고객용). 잠금 기능(lock 비트) 지원(Table 35, 페이지 51).
- 기타 기능: 인터럽트(EOC 핀), 외부 LDO(5V~48V), 보정 계수 쓰기, 온칩 온도 센서, 외부 온도 센서 지원. 데이터시트 전체 기능은 섹션 6에서 상세히 설명됩니다.
이 IC는 센서 신호 보정(SSC) 수학 코어를 통해 센서 오프셋, 감도, 온도 드리프트, 비선형성을 보정합니다. 보정 계수는 다항식 형태로 NVM에 저장되며, 쓰기 전에 NVM 잠금 해제가 필수입니다.
STM32L432KC와 I2C를 이용한 구현
STM32L432KC는 저전력 MCU로, I2C1(PB6: SCL, PB7: SDA)을 사용하여 ZSSC3241과 통신할 수 있습니다. 아래 코드는 데이터시트의 모든 기능을 I2C로 구현한 것입니다.
주요 기능:
- 작동 모드 전환 (명령, 사이클릭, 슬립).
- NVM 읽기/쓰기, 잠금 해제 (RESO 핀 사용).
- 아날로그 프론트엔드 설정.
- 원시 데이터 및 SSC 보정 데이터 읽기.
- DAC 설정.
- 사이클릭 모드 스케줄 설정.
- 인터럽트 및 진단 기능.
- 외부 LDO 설정.
- 보정 계수 쓰기.
코드는 HAL 라이브러리를 사용하며, 오류 처리와 상태 확인을 포함했습니다. 실제 사용 시 STM32CubeIDE에서 프로젝트를 생성하고, 핀맵과 클럭 설정을 확인하세요
전체 코드
// ZSSC3241 데이터시트 기반 STM32L432KC I2C 구현 코드
// 모든 기능 커버: 모드 전환, NVM 읽기/쓰기, AFE 설정, 데이터 읽기, DAC, 진단, 인터럽트, 외부 LDO, 보정 계수
// 모든 기능 통합 설정: 데이터시트 섹션 6 기반으로 작성, 전체 NVM 읽기, 진단 리셋 등
// 상세 주석 추가: 각 함수와 주요 라인에 데이터시트 참조 (e.g., Table 번호, 섹션 번호)와 동작 설명
// I2C 읽기 프로토콜 수정: 모든 데이터 읽기 시 첫 바이트가 Status Byte임을 고려 (데이터시트 Figure 12, 13; Table 19)
// Status Byte 확인: Bit 5 (Busy), Bit 6 (Memory Error) 등 오류 체크 추가
// 데이터시트 버전: REN_zssc3241-datasheet_DST_20240202.pdf (2024.02.02)
#include "stm32l4xx_hal.h"
// ZSSC3241 기본 I2C 슬레이브 주소 (7비트) - 데이터시트 섹션 6.4.2, 기본 0x00 (필요 시 NVM로 설정 가능)
#define ZSSC3241_SLAVE_ADDR 0x00
// RESQ 핀 설정 (예: PB8) - 데이터시트 핀 7 (RESQ: Digital IC reset, low active, internal pull-up)
// NVM 잠금 해제 및 리셋 용도 (데이터시트 섹션 6.6.2: NVM 잠금 해제 시 RESQ low 펄스 필요)
#define RESQ_PIN GPIO_PIN_8
#define RESQ_PORT GPIOB
// 데이터 유형 정의 - 센서/온도 구분 (데이터시트 섹션 6.5.1: Raw ADC Readings for Sensor/Temperature)
typedef enum { SENSOR, TEMPERATURE } DataType;
// 오류 처리 함수 - 실제 프로젝트에서 로그나 LED로 확장 (e.g., UART 로그 출력)
// 데이터시트 권장: Status Byte 오류 시 (e.g., Memory Error) 적절한 처리 필요
void Error_Handler(void) {
while (1); // 무한 루프, 실제로는 디버깅 코드 추가 (e.g., LED 깜빡임 또는 UART 에러 메시지)
}
// 상태 바이트 읽기 (NOP 명령) - 데이터시트 Table 19 (General Status Byte), 섹션 6.4.1
// NOP (0xFF)으로 상태 바이트(Busy, Mode, Memory Error 등) 확인
// 반환: 1바이트 Status (Bit 7: Power OK, Bit 6: Memory Error, Bit 5: Busy 등)
uint8_t get_status(I2C_HandleTypeDef *hi2c) {
uint8_t nop_cmd = 0xFF; // NOP 명령 코드 (데이터시트 Table 34: Command List)
uint8_t status;
if (HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &nop_cmd, 1, 100) != HAL_OK) {
Error_Handler(); // I2C 전송 실패 시 오류 처리
}
if (HAL_I2C_Master_Receive(hi2c, (ZSSC3241_SLAVE_ADDR << 1) | 0x01, &status, 1, 100) != HAL_OK) {
Error_Handler(); // I2C 수신 실패 시 오류 처리
}
return status;
}
// 장치가 바쁜지 확인 - 상태 바이트 비트 5 (Busy?) 확인 (데이터시트 Table 19)
// 반환: true (Busy) / false (Ready)
bool is_device_busy(I2C_HandleTypeDef *hi2c) {
return (get_status(hi2c) & 0x20) != 0; // Busy 비트(비트 5) 확인
}
// 명령 모드로 전환 - START_CM (0xA9) 명령, 데이터시트 섹션 6.6.1 및 Figure 3 (Main Operating Modes)
// Command Mode: 모든 명령 가능, 디지털/아날로그 출력 지원, 최소 지연
HAL_StatusTypeDef enter_command_mode(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0xA9; // START_CM 명령 코드 (데이터시트 Table 34)
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
if (status == HAL_OK) {
HAL_Delay(10); // 모드 전환 후 안정화 대기 시간 (데이터시트 섹션 5: tSTA1/tSTA2 ~2ms, 여유롭게 10ms)
}
return status;
}
// 사이클릭 모드로 전환 - START_CYC (0xAB) 명령, 데이터시트 섹션 6.6.1 및 Figure 3
// Cyclic Mode: 자율 반복 측정, 디지털/아날로그 출력 지원, 제한된 명령만 가능
HAL_StatusTypeDef enter_cyclic_mode(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0xAB; // START_CYC 명령 코드 (데이터시트 Table 34)
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
if (status == HAL_OK) {
HAL_Delay(10); // 모드 전환 후 안정화 대기
}
return status;
}
// 슬립 모드로 전환 - START_SLEEP (0xA8) 명령, 데이터시트 섹션 6.6.1 및 Figure 3
// Sleep Mode: 최소 전력 소비, 명령 수신 시 깨어남, 아날로그 출력 불가
HAL_StatusTypeDef enter_sleep_mode(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0xA8; // START_SLEEP 명령 코드 (데이터시트 Table 34)
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
if (status == HAL_OK) {
HAL_Delay(10); // 모드 전환 후 안정화 대기
}
return status;
}
// NVM 잠금 상태 확인 - SSF1 (0x03) 레지스터 비트 14 (lock) 확인, 데이터시트 Table 35 (Memory Content Assignments)
// 반환: true (잠김) / false (잠금 해제)
bool is_nvm_locked(I2C_HandleTypeDef *hi2c) {
uint8_t ssf1[2];
HAL_StatusTypeDef status = HAL_I2C_Mem_Read(hi2c, ZSSC3241_SLAVE_ADDR, 0x03, I2C_MEMADD_SIZE_8BIT, ssf1, 2, 100);
if (status != HAL_OK) {
return true; // 읽기 실패 시 잠긴 것으로 가정 (안전 측면)
}
return (ssf1[0] & 0x40) != 0; // 비트 14 (lock) 확인 (데이터시트 Table 35: SSF1 레지스터)
}
// RESQ 핀으로 NVM 잠금 해제 및 리셋 - 데이터시트 핀 7 설명, 저전위로 리셋 (데이터시트 섹션 6.6.2: NVM 재프로그래밍 시 RESQ 사용)
// RESQ low 펄스로 IC 리셋 및 NVM 잠금 해제
void unlock_nvm(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE(); // GPIO 클럭 활성화 (STM32 HAL)
GPIO_InitStruct.Pin = RESQ_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(RESQ_PORT, &GPIO_InitStruct);
// RESQ 핀을 VSS(0V)로 설정하여 리셋 실행 (데이터시트 Table 2: Absolute Maximum Ratings 고려)
HAL_GPIO_WritePin(RESQ_PORT, RESQ_PIN, GPIO_PIN_RESET);
HAL_Delay(10); // 리셋 유지 시간 (데이터시트 권장 최소 시간 ~ms 단위, 여유롭게 10ms)
HAL_GPIO_WritePin(RESQ_PORT, RESQ_PIN, GPIO_PIN_SET); // 리셋 해제
HAL_Delay(10); // 안정화 대기 시간 (데이터시트 섹션 5: tSTA1/tSTA2 ~2ms)
}
// 체크섬 갱신 - Calculate NVM Checksum (0x90) 명령, 데이터시트 섹션 6.6.2 (NVM Integrity)
// NVM 쓰기 후 반드시 호출하여 체크섬 업데이트
HAL_StatusTypeDef update_checksum(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0x90; // 체크섬 갱신 명령 (데이터시트 Table 34)
return HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
}
// NVM에 데이터 쓰기 (일반화된 함수) - Memory Write (0x40~0x75), 데이터시트 Table 34
// 주소 + 0x40으로 쓰기, 잠금 확인 후 해제, 쓰기 후 체크섬 업데이트
HAL_StatusTypeDef write_nvm_data(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t *data, uint8_t size) {
if (is_nvm_locked(hi2c)) {
unlock_nvm(); // NVM 잠금 해제 시도 (RESQ 사용)
if (is_nvm_locked(hi2c)) {
return HAL_ERROR; // 여전히 잠겨 있으면 실패
}
}
enter_command_mode(hi2c); // 쓰기 전에 명령 모드로 전환 (Command Mode에서만 NVM 쓰기 가능)
uint8_t write_addr = address + 0x40; // 쓰기 주소 = NVM 주소 + 0x40 (데이터시트 Table 34: Memory Write)
HAL_StatusTypeDef status = HAL_I2C_Mem_Write(hi2c, ZSSC3241_SLAVE_ADDR, write_addr, I2C_MEMADD_SIZE_8BIT, data, size, 100);
if (status == HAL_OK) {
status = update_checksum(hi2c); // 쓰기 후 체크섬 갱신 (데이터시트 섹션 6.6.2 권장)
}
return status;
}
// NVM 데이터 읽기 (일반화된 함수) - Memory Read (0x00~0x3F), 데이터시트 Table 34
// 읽기 후 Status 확인 추가 (Memory Error 등 체크)
HAL_StatusTypeDef read_nvm_data(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t *data, uint8_t size) {
enter_command_mode(hi2c); // 읽기 전에 명령 모드로 전환 (Command Mode에서만 NVM 읽기 가능)
HAL_StatusTypeDef hal_status = HAL_I2C_Mem_Read(hi2c, ZSSC3241_SLAVE_ADDR, address, I2C_MEMADD_SIZE_8BIT, data, size, 100);
if (hal_status == HAL_OK) {
uint8_t device_status = get_status(hi2c); // 읽기 후 Status 확인 (데이터시트 Table 19)
if (device_status & 0x40) { // Bit 6: Memory Error 체크 예시
return HAL_ERROR; // 오류 발생 시 HAL_ERROR 반환
}
}
return hal_status;
}
// 전체 NVM 읽기 (0x00~0x35, 고객용) - 데이터시트 Table 35 (Memory Content Assignments)
// data 배열: 0x36 * 2 = 72바이트 크기 필요 (각 주소 16비트)
HAL_StatusTypeDef read_all_nvm(I2C_HandleTypeDef *hi2c, uint8_t *data) {
enter_command_mode(hi2c); // 전체 읽기 전에 Command Mode 전환
for (uint8_t i = 0; i <= 0x35; i++) { // 0x00 ~ 0x35 순회 (데이터시트 Table 35: 고객 NVM 범위)
HAL_StatusTypeDef status = read_nvm_data(hi2c, i, &data[i * 2], 2);
if (status != HAL_OK) return status;
}
return HAL_OK;
}
// 전체 NVM 쓰기 (0x00~0x35) - 데이터시트 Table 35
// data 배열: 72바이트 (각 16비트 레지스터)
HAL_StatusTypeDef write_all_nvm(I2C_HandleTypeDef *hi2c, uint8_t *data) {
if (is_nvm_locked(hi2c)) {
unlock_nvm(); // 잠금 해제
if (is_nvm_locked(hi2c)) {
return HAL_ERROR;
}
}
enter_command_mode(hi2c); // Command Mode 전환
for (uint8_t i = 0; i <= 0x35; i++) { // 0x00 ~ 0x35 순회
HAL_StatusTypeDef status = write_nvm_data(hi2c, i, &data[i * 2], 2);
if (status != HAL_OK) return status;
}
return HAL_OK;
}
// 아날로그 프론트엔드 설정 - 데이터시트 섹션 6.2 (Analog Front-End), Table 5~13 (PGA Gain, Sensor Supply 등)
// SSF1, SM_config1/2, extTemp_config1/2 레지스터 설정 예시
HAL_StatusTypeDef configure_analog_frontend(I2C_HandleTypeDef *hi2c) {
// SSF1 설정: sensor_sup = 00 (ratiometric voltage), temp_source = 000 (internal), default_mode = 00 (Command Mode)
// 데이터시트 Table 35: SSF1 (0x03), Table 12 (Sensor Supply Options)
uint8_t ssf1[2] = {0x00, 0x00};
HAL_StatusTypeDef status = write_nvm_data(hi2c, 0x03, ssf1, 2);
if (status != HAL_OK) return status;
// SM_config1 설정: Gain_stage1 = 40 V/V (0111BIN), ioffsc = 0 (0000BIN), adc_bits = 16-bit (0100BIN)
// 데이터시트 Table 5 (PGA Gain Stage1), Table 7 (Absolute Offset Shift), Table 10 (ADC Conversion)
uint8_t sm_config1[2] = {0x40, 0x70};
status = write_nvm_data(hi2c, 0x14, sm_config1, 2);
if (status != HAL_OK) return status;
// SM_config2 설정: Gain_stage2 = 1.2 V/V (001BIN)
// 데이터시트 Table 6 (PGA Gain Stage2)
uint8_t sm_config2[2] = {0x00, 0x01};
status = write_nvm_data(hi2c, 0x15, sm_config2, 2);
if (status != HAL_OK) return status;
// extTemp_config1 설정: 외부 온도 센서 기본값 (사용 시 수정)
// 데이터시트 섹션 6.2.4 (External Temperature Sensing)
uint8_t ext_temp_config1[2] = {0x00, 0x00};
status = write_nvm_data(hi2c, 0x16, ext_temp_config1, 2);
if (status != HAL_OK) return status;
// extTemp_config2 설정: 외부 온도 센서 추가 기본값
uint8_t ext_temp_config2[2] = {0x00, 0x00};
status = write_nvm_data(hi2c, 0x17, ext_temp_config2, 2);
if (status != HAL_OK) return status;
return HAL_OK;
}
// 온칩 진단 수행 - UPDATE_DIAG (0xB2) 및 CHECK_DIAG (0xB0), 데이터시트 섹션 6.3 (On-Chip Diagnostics)
// UPDATE_DIAG: 진단 실행, CHECK_DIAG: 결과 읽기 (Table 18: diagnosticreg [15:0])
HAL_StatusTypeDef perform_diagnostics(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0xB2; // UPDATE_DIAG 명령: 모든 진단 체크 수행 (Sensor Connection, Memory Integrity 등)
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
if (status != HAL_OK) return status;
while (is_device_busy(hi2c)) {
HAL_Delay(10); // 진단 완료 대기 (데이터시트 Table 28: Conversion Times 고려)
}
cmd = 0xB0; // CHECK_DIAG 명령: 진단 결과 읽기
if (HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100) != HAL_OK) {
return HAL_ERROR;
}
uint8_t diag_data[2];
if (HAL_I2C_Master_Receive(hi2c, (ZSSC3241_SLAVE_ADDR << 1) | 0x01, diag_data, 2, 100) != HAL_OK) {
return HAL_ERROR;
}
// diag_data 분석: 데이터시트 Table 18 참조 (e.g., 비트 15~0으로 오류 유형 확인, 실제 프로젝트에서 로그 추가)
return HAL_OK;
}
// 원시 데이터 읽기 - Raw Sensor (0xA2) / Temperature (0xA4), 데이터시트 섹션 6.5.1 및 Table 25 (Data Format of Raw ADC Readings)
// 수정: 4바이트 읽기 (Status 1 + Data 3), Status 확인 후 데이터 추출
int32_t read_raw_data(I2C_HandleTypeDef *hi2c, DataType type) {
enter_command_mode(hi2c); // 명령 모드 확인 및 전환 (Command Mode에서만 단일 측정 가능)
uint8_t cmd = (type == SENSOR) ? 0xA2 : 0xA4; // 명령 선택 (데이터시트 Table 34)
if (HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100) != HAL_OK) {
Error_Handler();
}
while (is_device_busy(hi2c)) {
HAL_Delay(10); // 변환 완료 대기 (데이터시트 Table 27: Conversion Times ~ms 단위)
}
uint8_t rx_data[4]; // 수정: Status(1) + Data(3) (데이터시트 Figure 12: I2C Read Data)
if (HAL_I2C_Master_Receive(hi2c, (ZSSC3241_SLAVE_ADDR << 1) | 0x01, rx_data, 4, 100) != HAL_OK) {
Error_Handler();
}
uint8_t status = rx_data[0]; // 첫 바이트: Status (Table 19 확인)
if (status & 0x40) { // 예: Memory Error 비트 체크 (Bit 6)
Error_Handler(); // 오류 처리 (실제 프로젝트에서 로그 추가)
}
int32_t raw_value = (rx_data[1] << 16) | (rx_data[2] << 8) | rx_data[3]; // 데이터: rx_data[1~3] (24-bit, Table 25)
if (raw_value & 0x800000) {
raw_value |= 0xFF000000; // 두 개의 보수 부호 확장 (signed 24-bit)
}
return raw_value;
}
// SSC 보정 데이터 읽기 - Measure (0xAA), 데이터시트 섹션 6.5.1 및 Table 26 (Data Format of Corrected SSC Results)
// 수정: 7바이트 읽기 (Status 1 + Sensor 3 + Temp 3), Status 확인 후 데이터 추출
HAL_StatusTypeDef read_ssc_data(I2C_HandleTypeDef *hi2c, int32_t *sensor_data, int32_t *temp_data) {
uint8_t cmd = 0xAA; // SSC 보정 측정 명령 (데이터시트 Table 34: Complete SSC Measurement)
if (HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100) != HAL_OK) {
return HAL_ERROR;
}
while (is_device_busy(hi2c)) {
HAL_Delay(10); // 변환 완료 대기
}
uint8_t rx_data[7]; // 수정: Status(1) + Sensor(3) + Temp(3) (데이터시트 Figure 12)
if (HAL_I2C_Master_Receive(hi2c, (ZSSC3241_SLAVE_ADDR << 1) | 0x01, rx_data, 7, 100) != HAL_OK) {
return HAL_ERROR;
}
uint8_t status = rx_data[0]; // 첫 바이트: Status
if (status & 0x40) { // Memory Error 체크 예시 (Table 19)
return HAL_ERROR;
}
*sensor_data = (rx_data[1] << 16) | (rx_data[2] << 8) | rx_data[3]; // rx_data[1~3]: Sensor (Table 26)
if (*sensor_data & 0x800000) *sensor_data |= 0xFF000000; // 부호 확장
*temp_data = (rx_data[4] << 16) | (rx_data[5] << 8) | rx_data[6]; // rx_data[4~6]: Temp
if (*temp_data & 0x800000) *temp_data |= 0xFF000000; // 부호 확장
return HAL_OK;
}
// 사이클릭 모드에서 최신 데이터 읽기 - 데이터시트 섹션 6.5.2 (Cyclic Measurements) 및 Figure 20
// 수정: 7바이트 읽기 (Status 1 + Data 6), 폴링 시 Status 포함
HAL_StatusTypeDef read_latest_data_cyclic(I2C_HandleTypeDef *hi2c, int32_t *sensor_data, int32_t *temp_data) {
uint8_t rx_data[7]; // Status + SSC Data (S + T)
HAL_StatusTypeDef status = HAL_I2C_Master_Receive(hi2c, (ZSSC3241_SLAVE_ADDR << 1) | 0x01, rx_data, 7, 100);
if (status != HAL_OK) return status;
uint8_t rx_status = rx_data[0]; // Status 확인
if (rx_status & 0x40) { // 오류 체크 예시
return HAL_ERROR;
}
*sensor_data = (rx_data[1] << 16) | (rx_data[2] << 8) | rx_data[3];
if (*sensor_data & 0x800000) *sensor_data |= 0xFF000000;
*temp_data = (rx_data[4] << 16) | (rx_data[5] << 8) | rx_data[6];
if (*temp_data & 0x800000) *temp_data |= 0xFF000000;
return HAL_OK;
}
// DAC 설정 (전압 출력 예: 0-5V) - 데이터시트 Table 29 (DAC Configurations), 섹션 6.5.3 (Analog Outputs)
// SSF2 레지스터 (0x04) 설정: Aout_setup 등
HAL_StatusTypeDef configure_dac(I2C_HandleTypeDef *hi2c) {
uint8_t ssf2[2] = {0x00, 0x60}; // Aout_setup = 011 (0~5V absolute), dacouttype = 0 (센서 데이터 기반, Table 29)
return write_nvm_data(hi2c, 0x04, ssf2, 2);
}
// 사이클릭 모드 스케줄 설정 - 데이터시트 섹션 6.5.2 (Measurement Scheduler), 레지스터 0x1E~0x20 (Scheduler0~2)
// 예시: Scheduler0 = S (센서), Scheduler1 = T (온도), Scheduler2 = 비활성화
HAL_StatusTypeDef configure_cyclic_scheduler(I2C_HandleTypeDef *hi2c) {
uint8_t scheduler0[2] = {0x00, 0x00}; // S (센서 측정), pause = 0 (데이터시트 Figure 19)
HAL_StatusTypeDef status = write_nvm_data(hi2c, 0x1E, scheduler0, 2);
if (status != HAL_OK) return status;
uint8_t scheduler1[2] = {0x00, 0x01}; // T (온도 측정), pause = 0
status = write_nvm_data(hi2c, 0x1F, scheduler1, 2);
if (status != HAL_OK) return status;
uint8_t scheduler2[2] = {0x00, 0xFF}; // 비활성화 (0xFF)
status = write_nvm_data(hi2c, 0x20, scheduler2, 2);
if (status != HAL_OK) return status;
return HAL_OK;
}
// 인터럽트 설정 - 데이터시트 섹션 6.5.4 (Output Interrupt Signaling), 레지스터 0x21~0x24 (TRSH1a 등, Table 33)
// 예시: 기본값 설정, 실제 임계값은 Table 33에 따라 조정
HAL_StatusTypeDef configure_interrupt(I2C_HandleTypeDef *hi2c) {
uint8_t int_data[2] = {0x00, 0x00}; // 예시: INT_setup = 00, eoc_hyst_on = 0 등 (데이터시트 Figure 23~26 참조)
return write_nvm_data(hi2c, 0x21, int_data, 2); // INT_config (0x21) 예시
}
// 진단 리셋 - RESET_DIAG (0xB1), 데이터시트 섹션 6.3
// 진단 플래그 초기화
HAL_StatusTypeDef reset_diagnostics(I2C_HandleTypeDef *hi2c) {
uint8_t cmd = 0xB1; // RESET_DIAG 명령 (데이터시트 Table 34)
return HAL_I2C_Master_Transmit(hi2c, ZSSC3241_SLAVE_ADDR << 1, &cmd, 1, 100);
}
// 외부 LDO 설정 (5V~48V) - 데이터시트 섹션 6.7 (External LDOctrl), 레지스터 0x22 (VDD_ldoctrl_target, Table 38)
// 예시: 기본값, 실제로 VDD 타겟 레벨 선택
HAL_StatusTypeDef configure_external_ldo(I2C_HandleTypeDef *hi2c) {
uint8_t ldo_data[2] = {0x00, 0x00}; // 예시: VDD_ldoctrl_target = 5.2V (데이터시트 Table 38)
return write_nvm_data(hi2c, 0x22, ldo_data, 2);
}
// 모든 기능 통합 설정 - 데이터시트 전체 기능 커버 (AFE, Calibration, DAC, Scheduler 등)
// 순차적으로 호출, 오류 시 즉시 반환
HAL_StatusTypeDef configure_all(I2C_HandleTypeDef *hi2c) {
HAL_StatusTypeDef status = configure_analog_frontend(hi2c);
if (status != HAL_OK) return status;
status = configure_calibration_coefficients(hi2c);
if (status != HAL_OK) return status;
status = configure_dac(hi2c);
if (status != HAL_OK) return status;
status = configure_cyclic_scheduler(hi2c);
if (status != HAL_OK) return status;
status = configure_interrupt(hi2c);
if (status != HAL_OK) return status;
status = configure_external_ldo(hi2c);
if (status != HAL_OK) return status;
return HAL_OK;
}
// 보정 계수 쓰기 - 데이터시트 섹션 7 (Calibration), Table 36 (Data Format of 24-bit SSC Coefficients)
// 단일 계수 쓰기 (16비트 레지스터지만 24-bit 형식 고려, MSB/LSB 분리)
HAL_StatusTypeDef write_calibration_coefficient(I2C_HandleTypeDef *hi2c, uint8_t address, uint16_t coefficient) {
uint8_t data[2] = {(coefficient >> 8) & 0xFF, coefficient & 0xFF}; // 16비트로 분리 (데이터시트 Table 36: 24-bit but NVM 16-bit words)
return write_nvm_data(hi2c, address, data, 2);
}
// 보정 계수 설정 예시 - 센서/온도 계수 배열 쓰기, 데이터시트 섹션 6.6.3 (SSC Mathematics)
// 예시 값 사용, 실제 캘리브레이션 데이터로 대체 (섹션 7: Calibration 절차 참조)
HAL_StatusTypeDef configure_calibration_coefficients(I2C_HandleTypeDef *hi2c) {
// 센서 보정 계수 (0x06 ~ 0x0D: S_offset, S_gain 등, 데이터시트 Table 35)
// 예시 값, 실제 캘리브레이션 데이터로 대체
uint16_t sensor_coeffs[8] = {0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000};
for (uint8_t i = 0; i < 8; i++) {
HAL_StatusTypeDef status = write_calibration_coefficient(hi2c, 0x06 + i, sensor_coeffs[i]);
if (status != HAL_OK) return status;
}
// 온도 보정 계수 (0x18 ~ 0x1D: T_offset, T_gain 등)
uint16_t temp_coeffs[6] = {0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000};
for (uint8_t i = 0; i < 6; i++) {
HAL_StatusTypeDef status = write_calibration_coefficient(hi2c, 0x18 + i, temp_coeffs[i]);
if (status != HAL_OK) return status;
}
return update_checksum(hi2c); // 계수 쓰기 후 체크섬 업데이트
}
// I2C 초기화 - 데이터시트 섹션 6.4.2 (I2C Interface), Table 22 (I2C Parameters: fC,I2C up to 3.4MHz)
// STM32 HAL 기반, 100kHz 예시 (Timing 값은 시스템 클럭에 따라 조정)
void I2C1_Init(void) {
I2C_HandleTypeDef *hi2c = &hi2c1;
hi2c->Instance = I2C1;
hi2c->Init.Timing = 0x00702991; // 100kHz, 시스템 클럭에 따라 조정 (데이터시트 Table 22: Standard/Fast/High-Speed Mode)
hi2c->Init.OwnAddress1 = 0;
hi2c->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c->Init.OwnAddress2 = 0;
hi2c->Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(hi2c) != HAL_OK) {
Error_Handler();
}
}
// GPIO 초기화 (I2C 및 RESQ 핀 설정) - 데이터시트 핀 설명 (Pin 11: SCLK/SCL, Pin 9: MOSI/SDA)
// STM32 HAL MSP Init: PB6 (SCL), PB7 (SDA)
void HAL_I2C_MspInit(I2C_HandleTypeDef *i2cHandle) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (i2cHandle->Instance == I2C1) {
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; // PB6: SCL, PB7: SDA (데이터시트 Pin 11, 9)
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 풀업 저항 활성화 (데이터시트 섹션 6.4.2: SDA/SCL Pull-up to VDD)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_RCC_I2C1_CLK_ENABLE();
}
}
// 메인 함수 예시 - 모든 기능 테스트 (초기화, 설정, 데이터 읽기, 모드 전환 등)
// 실제 응용에서 HAL_Delay 대신 타이머나 인터럽트 사용 권장
int main(void) {
HAL_Init();
I2C1_Init();
// 모든 설정 실행 (AFE, Calibration, DAC 등)
configure_all(&hi2c1);
// 진단 수행 및 리셋
perform_diagnostics(&hi2c1);
reset_diagnostics(&hi2c1);
// 원시 데이터 읽기 (Command Mode)
int32_t sensor_data = read_raw_data(&hi2c1, SENSOR);
int32_t temp_data = read_raw_data(&hi2c1, TEMPERATURE);
// SSC 데이터 읽기 (Command Mode)
int32_t ssc_sensor, ssc_temp;
read_ssc_data(&hi2c1, &ssc_sensor, &ssc_temp);
// Cyclic Mode 전환 및 데이터 읽기
enter_cyclic_mode(&hi2c1);
int32_t cyclic_sensor, cyclic_temp;
read_latest_data_cyclic(&hi2c1, &cyclic_sensor, &cyclic_temp);
// 슬립 모드 전환 (최소 전력 모드)
enter_sleep_mode(&hi2c1);
while (1) {
// 메인 루프 - 실제 응용에서 데이터 처리 추가 (e.g., UART 출력 또는 LCD 표시)
}
}
코드 설명
위 코드에서 각 함수는 데이터시트의 특정 섹션과 연계됩니다:
함수 | 데이터시트 섹션 | 설명 |
---|---|---|
get_status | 6.4.1, Table 19 | NOP 명령으로 상태 바이트(Busy, Mode, Error) 확인 |
enter_command_mode | 6.6.1, Table 34 | START_CM (0xA9)으로 명령 모드 전환 |
enter_cyclic_mode | 6.6.1, Table 34 | START_CYC (0xAB)으로 사이클릭 모드 전환 |
enter_sleep_mode | 6.6.1, Table 34 | START_SLEEP (0xA8)으로 슬립 모드 전환 |
is_nvm_locked | Table 35, SSF1 | SSF1 비트 14 (lock) 확인 |
unlock_nvm | 핀 7 (RESO) | RESO 핀으로 리셋 및 잠금 해제 |
update_checksum | 6.6.2, Table 34 | Calculate NVM Checksum (0x90) |
write_nvm_data | Table 34 | Memory Write (0x40~0x75) |
read_nvm_data | Table 34 | Memory Read (0x00~0x3F) |
read_all_nvm | Table 35 | 전체 NVM (0x00~0x35) 읽기 보강 |
write_all_nvm | Table 35 | 전체 NVM (0x00~0x35) 쓰기 보강 |
configure_analog_frontend | 6.2, Table 5~13 | AFE 설정 (SSF1, SM_config1/2, extTemp_config) |
perform_diagnostics | 6.3, Table 17/18 | UPDATE_DIAG (0xB2) 및 CHECK_DIAG (0xB0) |
read_raw_data | 6.5.1, Table 34 | Raw Sensor/Temperature (0xA2/0xA4) |
read_ssc_data | 6.5.1, Table 34 | Measure (0xAA)으로 SSC 데이터 읽기 |
read_latest_data_cyclic | 6.5.2 | 사이클릭 모드 최신 데이터 읽기 |
configure_dac | 6.5.3, Table 29 | DAC 및 아날로그 출력 설정 (SSF2) |
configure_cyclic_scheduler | 6.5.2 | Measurement Scheduler (0x1E~0x20) |
configure_interrupt | 6.5.4 | 인터럽트 설정 (0x21) |
reset_diagnostics | 6.3 | RESET_DIAG (0xB1) |
configure_external_ldo | 6.7 | 외부 LDO 설정 (0x22) |
configure_all | 전체 | 모든 기능 통합 설정 |
write_calibration_coefficient | 7, Table 36 | 보정 계수 쓰기 |
configure_calibration_coefficients | 7 | 보정 계수 배열 설정 |
결론
이 가이드는 ZSSC3241의 데이터시트를 기반으로 모든 기능을 I2C 코드로 구현한 것입니다. 실제 프로젝트에서 센서 연결, 캘리브레이션 데이터 계산 등을 추가해 보세요.
참고: ZSSC3241 데이터시트 (Renesas), STM32L432KC 문서 (STMicroelectronics)