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

[ZSSC3230] 전체 측정 전 NVM 설정 절차

by linuxgo 2025. 8. 15.
반응형

 

ZSSC3230의 전체 측정(AAHEX 명령)은 센서 신호 컨디셔닝(SSC)을 적용하여 보정된 센서 데이터(24비트)와 온도 데이터(24비트)를 반환합니다. 이 과정은 NVM에 저장된 센서 설정과 보정 계수를 사용하므로, 측정 전에 관련 레지스터를 올바르게 설정해야 합니다. 아래는 필요한 NVM 레지스터와 설정 절차입니다.

1. 전체 측정 개요

  • AAHEX 명령: SSC 보정을 적용한 센서 데이터와 온도 데이터를 반환.
  • I2C 프레임: [START] [48HEX + W] [AAHEX] [STOP]
  • 응답: 상태 바이트(1바이트) + 센서 데이터(3바이트) + 온도 데이터(3바이트).
  • 사용되는 NVM 레지스터: 센서 설정(12HEX, 19HEX), 보정 계수(03HEX ~ 18HEX), 출력 설정(00HEX, 01HEX, 1AHEX 등).
  • 요구사항: 센서 특성(커패시턴스 범위, 유형)과 애플리케이션(출력 범위, 해상도)에 맞는 설정.

2. 설정해야 할 NVM 레지스터

전체 측정에 영향을 미치는 주요 NVM 레지스터는 다음과 같습니다(데이터시트 표 16, 28~32페이지):

NVM 주소 비트 필드 이름 설명 권장 설정
00HEX 3:0 Out_res 출력 해상도: 0000(12비트) ~ 1111(24비트). 24비트(1111) 권장.
5:4 Out_mode 출력 모드: 00(센서+온도), 01(센서), 10(온도), 11(예약). 센서+온도(00).
9:6 Update_rate 업데이트 속도: 0000(최대, ~1.3ms) ~ 1111(최소, ~21ms). 애플리케이션에 따라 설정, 예: 0000.
10 SSC_off SSC 보정 비활성화: 0(활성화), 1(비활성화). 활성화(0).
01HEX 15:0 Output_min 출력 최소값(16비트, 부호 있음). 출력 범위에 따라 설정, 예: -32768(0x8000).
02HEX 15:0 Output_max 출력 최대값(16비트, 부호 있음). 출력 범위에 따라 설정, 예: 32767(0x7FFF).
03HEX 15:0 Offset_S[15:0] 센서 오프셋(24비트 하위). 캘리브레이션 후 설정, 예: 0x0000.
04HEX 15:0 Gain_S[15:0] 센서 게인(24비트 하위). 캘리브레이션 후 설정, 예: 0x4000.
05HEX 15:0 Tcg[15:0] 온도 게인 보정(24비트 하위). 캘리브레이션 후 설정, 예: 0x0000.
06HEX 15:0 Tco[15:0] 온도 오프셋 보정(24비트 하위). 캘리브레이션 후 설정, 예: 0x0000.
0DHEX 7:0 Offset_S[23:16] 센서 오프셋 상위. 캘리브레이션 후 설정.
15:8 Gain_S[23:16] 센서 게인 상위. 캘리브레이션 후 설정.
0EHEX 7:0 Tco[23:16] 온도 오프셋 상위. 캘리브레이션 후 설정.
15:8 Tcg[23:16] 온도 게인 상위. 캘리브레이션 후 설정.
12HEX 5:0 shift_cap 제로-시프트 커패시턴스: 0.0pF(00 0000) ~ 15.75pF(11 1111). 센서 범위 이내, 예: 0.25pF(00 0001).
7:6 adc_bits ADC 해상도: 00(12비트), 01(14비트), 10(16비트), 11(18비트). 14비트(01) 권장.
8 noise_mode 노이즈 품질: 0(저전류), 1(저노이즈). 저노이즈(1).
13:9 cap_range 최대 커패시턴스: 0.5pF(00000) ~ 16.0pF(11111). 센서에 맞게, 예: 2.0pF(00010).
14 sensor_leakage 누설 전류 보상: 0(비활성화), 1(활성화). 필요 시 1, 기본 0.
19HEX 1:0 CC_pin_selection 측정 핀: 00(없음), 01(CC), 10(CC’), 11(CC+CC’). 예: CC(01).
2 Dither 디지털 디더링: 0(비활성화), 1(활성화). EMI 우려 시 1, 기본 0.
3 En_sh2 감산 모드: 0(비활성화), 1(활성화). 단일 끝 모드 시 1, 기본 0.
4 En_shlddrv 액티브 쉴드: 0(비활성화), 1(활성화). 쉴드 필요 시 1, 기본 0.
5 Dyn_imp 고전류 구동: 0(비활성화), 1(활성화). 작은 센서 시 1, 기본 0.
6 Test_cap 내부 커패시터(~2pF): 0(비활성화), 1(활성화). 테스트용 1, 기본 0.
1AHEX 2:0 SSC_freq SSC 업데이트 주파수: 000(최대) ~ 111(최소). 애플리케이션에 따라, 예: 000.

3. 설정 절차

  1. 센서 및 애플리케이션 특성 확인:
    •    센서: 커패시턴스 범위(예: 2pF), 유형(차동/단일 끝), 핀(CC/CC’).
    •    출력: 해상도(예: 24비트), 범위(예: -32768 ~ 32767).
    •    예: 차동 센서, 2pF, CC 핀, 24비트 출력, 빠른 업데이트.
  2. 커맨드 모드 진입:
    •    A9HEX 명령으로 커맨드 모드 진입.
    •    I2C 프레임: [START] [48HEX + W] [A9HEX] [STOP], 상태 바이트 확인.
  3. PDM 비활성화:
    •    주기적 측정 모드 비활성화(B4HEX).
    •    I2C 프레임: [START] [48HEX + W] [B4HEX] [STOP].
  4. NVM 설정 쓰기:
    •    00HEX: 출력 해상도(24비트, 1111), 모드(센서+온도, 00), 빠른 업데이트(0000), SSC 활성화(0).
      •    예: 0x0F00 (Out_res=1111, Out_mode=00, Update_rate=0000, SSC_off=0).
      •    프레임: [START] [48HEX + W] [20HEX] [0FHEX] [00HEX] [STOP].
    •    01HEX: 출력 최소값, 예: -32768(0x8000).
      •    프레임: [START] [48HEX + W] [21HEX] [80HEX] [00HEX] [STOP].
    •    02HEX: 출력 최대값, 예: 32767(0x7FFF).
      •    프레임: [START] [48HEX + W] [22HEX] [7FHEX] [FFHEX] [STOP].
    •    03HEX ~ 0EHEX: 보정 계수(Offset_S, Gain_S, Tcg, Tco 등).
      •    캘리브레이션 데이터로 설정, 예: Offset_S=0x000000, Gain_S=0x004000.
      •    프레임: [START] [48HEX + W] [23HEX] [00HEX] [00HEX] [STOP] (03HEX).
    •    12HEX: 차동(0), cap_range=2pF(00010), 14비트(01), 저노이즈(1), shift_cap=0.25pF(000001).
      •    예: 0x4102.
      •    프레임: [START] [48HEX + W] [32HEX] [41HEX] [02HEX] [STOP].
    •    19HEX: CC 핀(01), 기타 비활성화.
      •    예: 0x0001.
      •    프레임: [START] [48HEX + W] [39HEX] [00HEX] [01HEX] [STOP].
    •    1AHEX: SSC 주파수(000).
      •    예: 0x0000.
      •    프레임: [START] [48HEX + W] [3AHEX] [00HEX] [00HEX] [STOP].
  5. 체크섬 계산:
    •    90HEX로 체크섬 계산(1CHEX에 저장).
    •    프레임: [START] [48HEX + W] [90HEX] [STOP].
  6. 설정 검증:
    •    00HEX, 01HEX, 02HEX, 03HEX~0EHEX, 12HEX, 19HEX, 1AHEX 읽기.
    •    CRC 검증(1CHEX).
  7. 전체 측정 수행:
    •    AAHEX 명령으로 측정.
    •     프레임: [START] [48HEX + W] [AAHEX] [STOP], 응답: 상태 + 24비트 센서 + 24비트 온도.

4. STM32L432KC 예제 코드

아래는 STM32L432KC에서 전체 측정 전 NVM 설정과 모든 명령어를 구현한 코드입니다(STMCube HAL 사용, 차동 센서, 2pF, 14비트, CC 핀, 24비트 출력, 빠른 업데이트).

/* STM32L432KC I2C를 사용한 ZSSC3230 전체 명령어 및 전체 측정 설정
 * - STM32Cube HAL 라이브러리 사용
 * - ZSSC3230 I2C 주소: 0x48 (7비트)
 * - NVM 설정: 차동 센서, 2pF, 14비트, CC 핀, 24비트 출력, 빠른 업데이트
 * - 기능: 모든 명령어, 24비트 계수, CRC 검증, PDM 비활성화, 전체 측정 전 설정
 */

#include "stm32l4xx_hal.h"
#include <string.h>
#include <stdio.h>

/* I2C 핸들 */
extern I2C_HandleTypeDef hi2c1;

/* ZSSC3230 정의 */
#define ZSSC3230_I2C_ADDR    (0x48 << 1)
#define NVM_START_ADDR       0x00
#define NVM_END_ADDR         0x1C
#define CRC_ADDR             0x1C
#define DEFAULT_TIMEOUT_MS   100

/* 24비트 계수 정의 */
typedef struct {
    uint8_t lsb_addr;
    uint8_t msb_addr;
    uint8_t msb_shift;
} CoefficientMap;

static const CoefficientMap coeff_map[] = {
    {0x03, 0x0D, 8}, {0x04, 0x0D, 0}, {0x05, 0x0E, 8}, {0x06, 0x0E, 0},
    {0x07, 0x0F, 8}, {0x08, 0x0F, 0}, {0x09, 0x10, 8}, {0x0A, 0x10, 0},
    {0x0B, 0x11, 8}, {0x0C, 0x11, 0}, {0x16, 0x18, 0}, {0x17, 0x18, 8},
    {0x13, 0x15, 0}, {0x14, 0x15, 8}
};

/* 상태 바이트 확인 */
HAL_StatusTypeDef check_status_byte(uint8_t status) {
    if (status == 0x00) return HAL_OK;
    printf("Error: Status byte 0x%02X\n", status);
    return HAL_ERROR;
}

/* 커맨드 모드 진입 (A9HEX) */
HAL_StatusTypeDef zssc3230_start_command_mode(uint32_t timeout_ms) {
    uint8_t cmd = 0xA9;
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to enter command mode: HAL error %d\n", ret);
        return ret;
    }
    uint8_t status;
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, &status, 1, timeout_ms);
    return (ret == HAL_OK) ? check_status_byte(status) : ret;
}

/* 슬립 모드 진입 (A8HEX) */
HAL_StatusTypeDef zssc3230_start_sleep(uint32_t timeout_ms) {
    uint8_t cmd = 0xA8;
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to enter sleep mode: HAL error %d\n", ret);
        return ret;
    }
    uint8_t status;
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, &status, 1, timeout_ms);
    return (ret == HAL_OK) ? check_status_byte(status) : ret;
}

/* 주기적 측정 모드 (ABHEX) */
HAL_StatusTypeDef zssc3230_start_cyclic_mode(uint32_t *sensor_data, uint32_t *temp_data, uint32_t timeout_ms) {
    uint8_t cmd = 0xAB;
    uint8_t rx_buffer[7];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to start cyclic mode: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 7, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after ABHEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *sensor_data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    *temp_data = (rx_buffer[4] << 16) | (rx_buffer[5] << 8) | rx_buffer[6];
    return HAL_OK;
}

/* PDM 비활성화 (B4HEX) */
HAL_StatusTypeDef zssc3230_stop_pdm(uint32_t timeout_ms) {
    uint8_t cmd = 0xB4;
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to stop PDM: HAL error %d\n", ret);
    }
    return ret;
}

/* NVM 읽기 (00HEX ~ 1FHEX) */
HAL_StatusTypeDef zssc3230_nvm_read(uint8_t addr, uint16_t *data, uint32_t timeout_ms) {
    uint8_t cmd = addr;
    uint8_t rx_buffer[3];
    HAL_StatusTypeDef ret;

    if (addr > NVM_END_ADDR) {
        printf("Invalid NVM address: 0x%02X\n", addr);
        return HAL_ERROR;
    }

    ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send read command 0x%02X: HAL error %d\n", cmd, ret);
        return ret;
    }

    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data for address 0x%02X: HAL error %d\n", addr, ret);
        return ret;
    }

    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;

    *data = (rx_buffer[1] << 8) | rx_buffer[2];
    return HAL_OK;
}

/* NVM 쓰기 (20HEX ~ 3CHEX) */
HAL_StatusTypeDef zssc3230_nvm_write(uint8_t addr, uint16_t data, uint32_t timeout_ms) {
    uint8_t tx_buffer[3] = {0x20 + addr, (data >> 8) & 0xFF, data & 0xFF};
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, tx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to write to address 0x%02X: HAL error %d\n", addr, ret);
        return ret;
    }
    uint8_t status;
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, &status, 1, timeout_ms);
    return (ret == HAL_OK) ? check_status_byte(status) : ret;
}

/* NVM 체크섬 계산 (90HEX) */
HAL_StatusTypeDef zssc3230_calculate_nvm_checksum(uint32_t timeout_ms) {
    uint8_t cmd = 0x90;
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to calculate checksum: HAL error %d\n", ret);
        return ret;
    }
    uint8_t status;
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, &status, 1, timeout_ms);
    return (ret == HAL_OK) ? check_status_byte(status) : ret;
}

/* 원시 센서 측정 (A2HEX) */
HAL_StatusTypeDef zssc3230_raw_sensor_measure(uint32_t *data, uint32_t timeout_ms) {
    uint8_t tx_buffer[3] = {0xA2, 0x00, 0x00};
    uint8_t rx_buffer[4];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, tx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send A2HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 4, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after A2HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    return HAL_OK;
}

/* 사용자 설정 원시 센서 측정 (A3HEX) */
HAL_StatusTypeDef zssc3230_raw_sensor_measure_custom(uint16_t config, uint32_t *data, uint32_t timeout_ms) {
    uint8_t tx_buffer[3] = {0xA3, (config >> 8) & 0xFF, config & 0xFF};
    uint8_t rx_buffer[4];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, tx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send A3HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 4, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after A3HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    return HAL_OK;
}

/* 원시 온도 측정 (A6HEX) */
HAL_StatusTypeDef zssc3230_raw_temp_measure(uint32_t *data, uint32_t timeout_ms) {
    uint8_t tx_buffer[3] = {0xA6, 0x00, 0x00};
    uint8_t rx_buffer[4];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, tx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send A6HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 4, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after A6HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    return HAL_OK;
}

/* 전체 측정 (AAHEX) */
HAL_StatusTypeDef zssc3230_measure(uint32_t *sensor_data, uint32_t *temp_data, uint32_t timeout_ms) {
    uint8_t cmd = 0xAA;
    uint8_t rx_buffer[7];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send AAHEX: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 7, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after AAHEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *sensor_data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    *temp_data = (rx_buffer[4] << 16) | (rx_buffer[5] << 8) | rx_buffer[6];
    return HAL_OK;
}

/* 오버샘플링 측정 (ACHEX, ADHEX, AEHEX, AFHEX) */
HAL_StatusTypeDef zssc3230_oversample_measure(uint8_t oversample_cmd, uint32_t *sensor_data, uint32_t *temp_data, uint32_t timeout_ms) {
    if (oversample_cmd != 0xAC && oversample_cmd != 0xAD && oversample_cmd != 0xAE && oversample_cmd != 0xAF) {
        printf("Invalid oversample command: 0x%02X\n", oversample_cmd);
        return HAL_ERROR;
    }
    uint8_t cmd = oversample_cmd;
    uint8_t rx_buffer[7];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send 0x%02X: HAL error %d\n", cmd, ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 7, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after 0x%02X: HAL error %d\n", cmd, ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *sensor_data = (rx_buffer[1] << 16) | (rx_buffer[2] << 8) | rx_buffer[3];
    *temp_data = (rx_buffer[4] << 16) | (rx_buffer[5] << 8) | rx_buffer[6];
    return HAL_OK;
}

/* 브로큰 칩 테스트 (B0HEX) */
HAL_StatusTypeDef zssc3230_broken_chip_test(uint16_t *result, uint32_t timeout_ms) {
    uint8_t cmd = 0xB0;
    uint8_t rx_buffer[3];
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, &cmd, 1, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to send B0HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = HAL_I2C_Master_Receive(&hi2c1, ZSSC3230_I2C_ADDR, rx_buffer, 3, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to receive data after B0HEX: HAL error %d\n", ret);
        return ret;
    }
    ret = check_status_byte(rx_buffer[0]);
    if (ret != HAL_OK) return ret;
    *result = (rx_buffer[1] << 8) | rx_buffer[2];
    return HAL_OK;
}

/* 소프트 리셋 (FXHEX) */
HAL_StatusTypeDef zssc3230_soft_reset(uint32_t timeout_ms) {
    uint8_t tx_buffer[2] = {0xF0, 0x00};
    HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, ZSSC3230_I2C_ADDR, tx_buffer, 2, timeout_ms);
    if (ret != HAL_OK) {
        printf("Soft reset failed: HAL error %d\n", ret);
    }
    return ret;
}

/* 24비트 계수 읽기 */
HAL_StatusTypeDef zssc3230_nvm_read_24bit(uint8_t coeff_idx, int32_t *value, uint32_t timeout_ms) {
    if (coeff_idx >= sizeof(coeff_map) / sizeof(coeff_map[0])) {
        printf("Invalid coefficient index: %d\n", coeff_idx);
        return HAL_ERROR;
    }
    uint16_t lsb_data, msb_data;
    HAL_StatusTypeDef ret = zssc3230_nvm_read(coeff_map[coeff_idx].lsb_addr, &lsb_data, timeout_ms);
    if (ret != HAL_OK) return ret;
    ret = zssc3230_nvm_read(coeff_map[coeff_idx].msb_addr, &msb_data, timeout_ms);
    if (ret != HAL_OK) return ret;
    uint32_t raw = ((msb_data >> coeff_map[coeff_idx].msb_shift) & 0xFF) << 16 | lsb_data;
    *value = (raw & 0x800000) ? (int32_t)(raw | 0xFF000000) : (int32_t)raw;
    return HAL_OK;
}

/* 24비트 계수 쓰기 */
HAL_StatusTypeDef zssc3230_nvm_write_24bit(uint8_t coeff_idx, int32_t value, uint32_t timeout_ms) {
    if (coeff_idx >= sizeof(coeff_map) / sizeof(coeff_map[0])) {
        printf("Invalid coefficient index: %d\n", coeff_idx);
        return HAL_ERROR;
    }
    HAL_StatusTypeDef ret;
    uint16_t lsb_data = value & 0xFFFF;
    uint16_t msb_data;
    ret = zssc3230_nvm_read(coeff_map[coeff_idx].msb_addr, &msb_data, timeout_ms);
    if (ret != HAL_OK) return ret;
    uint8_t shift = coeff_map[coeff_idx].msb_shift;
    uint8_t mask = (shift == 0) ? 0xFF00 : 0x00FF;
    msb_data = (msb_data & mask) | (((value >> 16) & 0xFF) << shift);
    ret = zssc3230_nvm_write(coeff_map[coeff_idx].lsb_addr, lsb_data, timeout_ms);
    if (ret != HAL_OK) return ret;
    ret = zssc3230_nvm_write(coeff_map[coeff_idx].msb_addr, msb_data, timeout_ms);
    return ret;
}

/* NVM 전체 읽기 */
HAL_StatusTypeDef zssc3230_nvm_read_all(uint16_t *data_buffer, uint32_t timeout_ms) {
    HAL_StatusTypeDef ret = zssc3230_start_command_mode(timeout_ms);
    if (ret != HAL_OK) return ret;
    for (uint8_t addr = NVM_START_ADDR; addr <= NVM_END_ADDR; addr++) {
        ret = zssc3230_nvm_read(addr, &data_buffer[addr], timeout_ms);
        if (ret != HAL_OK) return ret;
    }
    return HAL_OK;
}

/* NVM 전체 쓰기 */
HAL_StatusTypeDef zssc3230_nvm_write_all(uint16_t *data_buffer, uint32_t timeout_ms) {
    HAL_StatusTypeDef ret = zssc3230_start_command_mode(timeout_ms);
    if (ret != HAL_OK) return ret;
    ret = zssc3230_stop_pdm(timeout_ms);
    if (ret != HAL_OK) return ret;
    for (uint8_t addr = NVM_START_ADDR; addr <= NVM_END_ADDR; addr++) {
        ret = zssc3230_nvm_write(addr, data_buffer[addr], timeout_ms);
        if (ret != HAL_OK) return ret;
    }
    ret = zssc3230_calculate_nvm_checksum(timeout_ms);
    return ret;
}

/* CRC 계산 */
uint16_t zssc3230_calculate_crc(uint16_t *data_buffer) {
    uint32_t crc = 0;
    uint16_t poly = 0x8005;
    for (uint8_t addr = NVM_START_ADDR; addr < NVM_END_ADDR; addr++) {
        uint16_t data = data_buffer[addr];
        for (int i = 15; i >= 0; i--) {
            uint32_t bit = (crc >> 15) ^ ((data >> i) & 1);
            crc = (crc << 1) | bit;
            if (bit) crc ^= poly;
        }
    }
    return (uint16_t)(crc & 0xFFFF);
}

/* CRC 검증 */
HAL_StatusTypeDef zssc3230_verify_crc(uint16_t *data_buffer, uint32_t timeout_ms) {
    uint16_t calc_crc = zssc3230_calculate_crc(data_buffer);
    uint16_t stored_crc;
    HAL_StatusTypeDef ret = zssc3230_nvm_read(CRC_ADDR, &stored_crc, timeout_ms);
    if (ret != HAL_OK) {
        printf("Failed to read stored CRC\n");
        return ret;
    }
    if (calc_crc == stored_crc) {
        printf("CRC verification passed (0x%04X)\n", calc_crc);
        return HAL_OK;
    } else {
        printf("CRC verification failed (Calculated: 0x%04X, Stored: 0x%04X)\n", calc_crc, stored_crc);
        return HAL_ERROR;
    }
}

/* 전체 측정 전 NVM 설정 */
HAL_StatusTypeDef zssc3230_setup_full_measurement(uint32_t timeout_ms) {
    HAL_StatusTypeDef ret;

    // 1. 커맨드 모드 진입
    ret = zssc3230_start_command_mode(timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("Command mode entered\n");

    // 2. PDM 비활성화
    ret = zssc3230_stop_pdm(timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("PDM stopped\n");

    // 3. 00HEX 설정 (24비트, 센서+온도, 빠른 업데이트, SSC 활성화)
    uint16_t reg_00 = (0 << 10) | // SSC_off: 0
                      (0 << 6)  | // Update_rate: 0000
                      (0 << 4)  | // Out_mode: 00
                      (15);       // Out_res: 1111 (24비트)
    ret = zssc3230_nvm_write(0x00, reg_00, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x00] set to 0x%04X\n", reg_00);

    // 4. 01HEX 설정 (출력 최소값: -32768)
    ret = zssc3230_nvm_write(0x01, 0x8000, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x01] set to 0x8000\n");

    // 5. 02HEX 설정 (출력 최대값: 32767)
    ret = zssc3230_nvm_write(0x02, 0x7FFF, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x02] set to 0x7FFF\n");

    // 6. 보정 계수 설정 (예: 기본값)
    uint16_t calib_data[] = {0x0000, 0x4000, 0x0000, 0x0000}; // Offset_S, Gain_S, Tcg, Tco
    uint8_t calib_addrs[] = {0x03, 0x04, 0x05, 0x06};
    for (int i = 0; i < 4; i++) {
        ret = zssc3230_nvm_write(calib_addrs[i], calib_data[i], timeout_ms);
        if (ret != HAL_OK) return ret;
        printf("NVM[0x%02X] set to 0x%04X\n", calib_addrs[i], calib_data[i]);
    }
    ret = zssc3230_nvm_write(0x0D, 0x0000, timeout_ms); // Offset_S[23:16], Gain_S[23:16]
    if (ret != HAL_OK) return ret;
    printf("NVM[0x0D] set to 0x0000\n");
    ret = zssc3230_nvm_write(0x0E, 0x0000, timeout_ms); // Tcg[23:16], Tco[23:16]
    if (ret != HAL_OK) return ret;
    printf("NVM[0x0E] set to 0x0000\n");

    // 7. 12HEX 설정 (차동, 2pF, 14비트, 저노이즈, shift_cap=0.25pF)
    uint16_t reg_12 = (0 << 15) | // sensecap_type: 차동
                      (0 << 14) | // sensor_leakage: 비활성화
                      (2 << 9)  | // cap_range: 2.0pF
                      (1 << 8)  | // noise_mode: 저노이즈
                      (1 << 6)  | // adc_bits: 14비트
                      (1);        // shift_cap: 0.25pF
    ret = zssc3230_nvm_write(0x12, reg_12, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x12] set to 0x%04X\n", reg_12);

    // 8. 19HEX 설정 (CC 핀)
    uint16_t reg_19 = (1 << 0); // CC_pin_selection: CC
    ret = zssc3230_nvm_write(0x19, reg_19, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x19] set to 0x%04X\n", reg_19);

    // 9. 1AHEX 설정 (SSC 주파수: 최대)
    ret = zssc3230_nvm_write(0x1A, 0x0000, timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("NVM[0x1A] set to 0x0000\n");

    // 10. 체크섬 계산
    ret = zssc3230_calculate_nvm_checksum(timeout_ms);
    if (ret != HAL_OK) return ret;
    printf("Checksum calculated\n");

    return HAL_OK;
}

/* 메인 함수 예제 */
void zssc3230_command_example(void) {
    HAL_StatusTypeDef ret;
    uint16_t nvm_data[29];
    uint32_t sensor_data, temp_data;
    uint16_t result;
    uint32_t timeout_ms = DEFAULT_TIMEOUT_MS;

    // 1. 전체 측정 전 NVM 설정
    ret = zssc3230_setup_full_measurement(timeout_ms);
    if (ret != HAL_OK) {
        printf("Full measurement setup failed\n");
        return;
    }

    // 2. 전체 측정 (AAHEX)
    ret = zssc3230_measure(&sensor_data, &temp_data, timeout_ms);
    if (ret == HAL_OK) {
        printf("SSC-corrected sensor: 0x%06X, temp: 0x%06X\n", sensor_data, temp_data);
    } else {
        printf("Full measurement failed\n");
    }

    // 3. 원시 센서 측정 (A2HEX)
    ret = zssc3230_raw_sensor_measure(&sensor_data, timeout_ms);
    if (ret == HAL_OK) printf("Raw sensor data: 0x%06X\n", sensor_data);
    else printf("Raw sensor measurement failed\n");

    // 4. 사용자 설정 원시 센서 측정 (A3HEX)
    uint16_t config = 0x4102; // 12HEX 상위 + 19HEX 하위
    ret = zssc3230_raw_sensor_measure_custom(config, &sensor_data, timeout_ms);
    if (ret == HAL_OK) printf("Custom raw sensor data: 0x%06X\n", sensor_data);
    else printf("Custom raw sensor measurement failed\n");

    // 5. NVM 전체 읽기 및 CRC 검증
    ret = zssc3230_nvm_read_all(nvm_data, timeout_ms);
    if (ret == HAL_OK) {
        printf("NVM read all succeeded:\n");
        for (uint8_t i = 0; i <= NVM_END_ADDR; i++) {
            printf("NVM[0x%02X] = 0x%04X\n", i, nvm_data[i]);
        }
        ret = zssc3230_verify_crc(nvm_data, timeout_ms);
    } else {
        printf("NVM read all failed\n");
    }

    // 6. 24비트 계수 쓰기/읽기 (Tcg, 인덱스 2)
    ret = zssc3230_nvm_write_24bit(2, 0x123456, timeout_ms);
    if (ret == HAL_OK) printf("Tcg write (24-bit) succeeded\n");
    else printf("Tcg write failed\n");

    int32_t coeff_value;
    ret = zssc3230_nvm_read_24bit(2, &coeff_value, timeout_ms);
    if (ret == HAL_OK) printf("Tcg (24-bit) = 0x%06X\n", (uint32_t)coeff_value);
    else printf("Tcg read failed\n");

    // 7. 원시 온도 측정 (A6HEX)
    ret = zssc3230_raw_temp_measure(&temp_data, timeout_ms);
    if (ret == HAL_OK) printf("Raw temp data: 0x%06X\n", temp_data);
    else printf("Raw temp measurement failed\n");

    // 8. 오버샘플링 측정 (ACHEX)
    ret = zssc3230_oversample_measure(0xAC, &sensor_data, &temp_data, timeout_ms);
    if (ret == HAL_OK) printf("Oversample-2 sensor: 0x%06X, temp: 0x%06X\n", sensor_data, temp_data);
    else printf("Oversample-2 measurement failed\n");

    // 9. 주기적 측정 모드 (ABHEX)
    ret = zssc3230_start_cyclic_mode(&sensor_data, &temp_data, timeout_ms);
    if (ret == HAL_OK) printf("Cyclic mode started, sensor: 0x%06X, temp: 0x%06X\n", sensor_data, temp_data);
    else printf("Cyclic mode failed\n");

    // 10. PDM 정지 (B4HEX)
    ret = zssc3230_stop_pdm(timeout_ms);
    if (ret == HAL_OK) printf("PDM stopped\n");
    else printf("PDM stop failed\n");

    // 11. 브로큰 칩 테스트 (B0HEX)
    ret = zssc3230_broken_chip_test(&result, timeout_ms);
    if (ret == HAL_OK) printf("Broken chip test: %s (0x%04X)\n", result ? "Fail" : "Pass", result);
    else printf("Broken chip test failed\n");

    // 12. 슬립 모드 (A8HEX)
    ret = zssc3230_start_sleep(timeout_ms);
    if (ret == HAL_OK) printf("Sleep mode entered\n");
    else printf("Sleep mode failed\n");

    // 13. 소프트 리셋 (FXHEX)
    ret = zssc3230_soft_reset(timeout_ms);
    if (ret == HAL_OK) printf("Soft reset succeeded\n");
    else printf("Soft reset failed\n");
}
반응형