이 내용는 ZSSC3230 센서 신호 컨디셔너의 비휘발성 메모리(NVM) 설정, 센서 데이터 측정을 STM32L432KC 마이크로컨트롤러를 I2C 인터페이스를 사용한 구현 방법을 상세히 설명합니다. 데이터시트를 기반으로 작성되었으며, 원시 센서 측정(A2HEX, A3HEX)과 전체 측정(AAHEX)을 위한 설정 및 코드를 포함합니다.
1. ZSSC3230 개요
ZSSC3230은 저전력, 고정밀 커패시턴스-디지털 컨버터로, 센서 신호 컨디셔닝(SSC)을 통해 커패시턴스 센서 데이터를 처리합니다. 최대 18비트 ADC 해상도와 24비트 출력 해상도를 지원하며, I2C 인터페이스를 통해 설정 및 데이터 전송을 수행합니다. 주요 응용 분야는 HVAC, 의료 기기, 웨어러블 장치 등입니다.
주요 특징: 0.5pF~16pF 커패시턴스 범위, 차동/단일 끝 센서 지원, 온도 보정, 저전력 모드(슬립 모드), 최대 10,000회 NVM 쓰기.
2. I2C 인터페이스
ZSSC3230은 I2C 인터페이스를 통해 마이크로컨트롤러와 통신합니다(데이터시트 5.1절, 21~24페이지). I2C 설정과 동작 방식은 다음과 같습니다.
2.1 I2C 프로토콜 사양
- 주소: 7비트 주소
0x48(쓰기:0x90, 읽기:0x91). - 클럭 속도: 최대 400kHz (표준/고속 모드).
- 프레임 구조:
쓰기: [START] [ADDR+W] [COMMAND] [DATA1] [DATA2] [STOP]
읽기: [START] [ADDR+W] [COMMAND] [RESTART] [ADDR+R] [DATA...] [STOP]
타이밍:
- 명령어 간 최소 100μs 대기.
- NVM 쓰기 후 5ms 대기.
- 최대 응답 시간: ~21ms (최소 업데이트 속도).
- 상태 바이트: 모든 명령 후 반환,
0x00은 성공, 기타 값은 에러(데이터시트 표 11).
2.2 I2C 명령어 처리
ZSSC3230은 명령어 기반으로 동작하며, 명령어는 1~3바이트로 구성됩니다. 예를 들어:
A9HEX: 커맨드 모드 진입, 1바이트, 응답: 상태 바이트.A2HEX: 원시 센서 측정, 3바이트(A2 00 00), 응답: 상태 + 24비트 데이터.20HEX~3CHEX: NVM 쓰기, 3바이트(CMD DATA_H DATA_L), 응답: 상태.
주의: I2C 통신 시 SCL/SDA 풀업 저항(4.7kΩ 권장)과 전원 안정화 확인 필요.
3. 명령어 리스트
ZSSC3230은 다양한 명령어를 지원하며, 각 명령어는 특정 작업(측정, 설정, 진단)을 수행합니다(데이터시트 표 10, 25~27페이지). 아래는 전체 명령어와 상세 설명입니다.
| 명령어 | 코드 | 설명 | 입력 | 출력 |
| NVM 읽기 | 00HEX~1FHEX |
NVM 레지스터(주소 0~31) 읽기 | 1바이트 | 상태(1) + 데이터(2) |
| NVM 쓰기 | 20HEX~3CHEX |
NVM 레지스터 쓰기 | 3바이트 (CMD, DATA_H, DATA_L) | 상태(1) |
| 체크섬 계산 | 90HEX |
NVM 체크섬 계산 및 저장 | 1바이트 | 상태(1) |
| 원시 센서 측정 | A2HEX |
SSC 보정 없는 센서 데이터 | 3바이트 (A2 00 00) |
상태(1) + 데이터(3) |
| 사용자 설정 원시 측정 | A3HEX |
사용자 정의 설정으로 원시 측정 | 3바이트 (CMD, CONFIG_H, CONFIG_L) | 상태(1) + 데이터(3) |
| 원시 온도 측정 | A6HEX |
SSC 보정 없는 온도 데이터 | 3바이트 (A6 00 00) |
상태(1) + 데이터(3) |
| 슬립 모드 | A8HEX |
저전력 모드 진입 | 1바이트 | 상태(1) |
| 커맨드 모드 | A9HEX |
커맨드 모드 진입 | 1바이트 | 상태(1) |
| 전체 측정 | AAHEX |
SSC 보정된 센서+온도 데이터 | 1바이트 | 상태(1) + 센서(3) + 온도(3) |
| 주기적 측정 | ABHEX |
주기적 SSC 측정 시작 | 1바이트 | 상태(1) + 센서(3) + 온도(3) |
| 오버샘플링 측정 | ACHEX~AFHEX |
2x~32x 오버샘플링 측정 | 1바이트 | 상태(1) + 센서(3) + 온도(3) |
| 브로큰 칩 테스트 | B0HEX |
칩 진단 테스트 | 1바이트 | 상태(1) + 결과(2) |
| PDM 정지 | B4HEX |
주기적 측정 모드 비활성화 | 1바이트 | 없음 |
| 소프트 리셋 | F0HEX |
칩 소프트 리셋 | 2바이트 (F0 00) |
없음 |
4. NVM 설정
ZSSC3230의 NVM은 센서 설정, 보정 계수, 출력 설정을 저장합니다. 원시 센서 측정과 전체 측정은 서로 다른 레지스터를 사용합니다.
4.1 원시 센서 측정 NVM 설정
원시 센서 측정(A2HEX, A3HEX)은 12HEX와 19HEX를 참조합니다.
| NVM 주소 | 비트 | 필드 이름 | 설명 | 권장 설정 |
12HEX |
5:0 | shift_cap | 제로-시프트 커패시턴스: 0.0pF~15.75pF | 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~16.0pF | 2.0pF (00010) |
|
| 14 | sensor_leakage | 누설 전류 보상: 0(비활성화), 1(활성화) | 0 | |
| 15 | sensecap_type | 센서 유형: 0(차동), 1(단일 끝) | 차동 (0) |
|
19HEX |
1:0 | CC_pin_selection | 측정 핀: 00(없음), 01(CC), 10(CC’), 11(CC+CC’) | CC (01) |
| 2 | Dither | 디지털 디더링: 0(비활성화), 1(활성화) | 0 | |
| 3 | En_sh2 | 감산 모드: 0(비활성화), 1(활성화) | 0 | |
| 4 | En_shlddrv | 액티브 쉴드: 0(비활성화), 1(활성화) | 0 | |
| 5 | Dyn_imp | 고전류 구동: 0(비활성화), 1(활성화) | 0 |
설정 절차
- 센서 특성 확인: 2pF, 차동, CC 핀.
- 커맨드 모드 진입:
A9HEX. - NVM 설정:
12HEX=0x4102,19HEX=0x0001. - 체크섬 계산:
90HEX. - 검증: NVM 읽기 및 CRC 확인.
4.2 전체 측정 NVM 설정
전체 측정(AAHEX)은 SSC 보정을 적용하며, 추가 레지스터를 설정합니다.
| NVM 주소 | 비트 | 필드 이름 | 설명 | 권장 설정 |
00HEX |
3:0 | Out_res | 출력 해상도: 0000(12비트)~1111(24비트) | 24비트 (1111) |
| 5:4 | Out_mode | 출력 모드: 00(센서+온도), 01(센서), 10(온도) | 센서+온도 (00) |
|
| 9:6 | Update_rate | 업데이트 속도: 0000(최대)~1111(최소) | 최대 (0000) |
|
| 10 | SSC_off | SSC 보정: 0(활성화), 1(비활성화) | 활성화 (0) |
|
01HEX |
15:0 | Output_min | 출력 최소값 | -32768 (0x8000) |
02HEX |
15:0 | Output_max | 출력 최대값 | 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] | 온도 게인 보정 | 캘리브레이션, 예: 0x0000 |
06HEX |
15:0 | Tco[15:0] | 온도 오프셋 보정 | 캘리브레이션, 예: 0x0000 |
0DHEX |
15:0 | Offset_S[23:16], Gain_S[23:16] | 상위 비트 | 캘리브레이션, 예: 0x0000 |
0EHEX |
15:0 | Tcg[23:16], Tco[23:16] | 상위 비트 | 캘리브레이션, 예: 0x0000 |
12HEX |
15:0 | 위와 동일 | 원시 센서 설정 | 0x4102 |
19HEX |
15:0 | 위와 동일 | 원시 센서 설정 | 0x0001 |
1AHEX |
2:0 | SSC_freq | SSC 주파수: 000(최대)~111(최소) | 최대 (000) |
설정 절차
- 센서 및 출력 특성 확인: 2pF, 차동, CC 핀, 24비트 출력.
- 커맨드 모드 진입:
A9HEX. - PDM 비활성화:
B4HEX. - NVM 설정:
00HEX=0x0F00,01HEX=0x8000,02HEX=0x7FFF,03HEX~06HEX, 0DHEX, 0EHEX,12HEX=0x4102,19HEX=0x0001,1AHEX=0x0000. - 체크섬 계산:
90HEX. - 검증: NVM 읽기 및 CRC 확인.
5. STM32L432KC 구현
STM32L432KC를 사용해 ZSSC3230의 I2C 통신, NVM 설정, 모든 명령어를 구현했습니다. STM32Cube HAL 라이브러리를 사용하며, I2C 주소는 0x48(7비트)입니다.
5.1 구현 개요
- 환경: STM32CubeMX, I2C1(400kHz, 표준 모드), UART(디버깅).
- 기능:
- 모든 명령어 지원:
00HEX~FXHEX. - NVM 읽기/쓰기, 24비트 계수 처리, CRC 검증.
- 원시 센서 및 전체 측정 전용 NVM 설정 함수.
- 에러 처리 및 상태 바이트 확인.
- 모든 명령어 지원:
- 설정 예시: 차동 센서, 2pF, 14비트 ADC, 24비트 출력, CC 핀, 저노이즈, 최대 업데이트 속도.
5.2 코드
/* STM32L432KC I2C를 사용한 ZSSC3230 전체 명령어 및 NVM 설정
* - STM32Cube HAL 라이브러리 사용
* - ZSSC3230 I2C 주소: 0x48 (7비트)
* - 설정: 차동 센서, 2pF, 14비트, CC 핀, 24비트 출력, 빠른 업데이트
* - 기능: 모든 명령어(00HEX~FXHEX), 24비트 계수, CRC 검증, PDM 비활성화
*/
#include "stm32l4xx_hal.h"
#include
#include
/* 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_raw_sensor(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. 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);
// 4. 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);
// 5. 체크섬 계산
ret = zssc3230_calculate_nvm_checksum(timeout_ms);
if (ret != HAL_OK) return ret;
printf("Checksum calculated\n");
return HAL_OK;
}
/* 전체 측정 전 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. 원시 센서 측정 설정 및 테스트
ret = zssc3230_setup_raw_sensor(timeout_ms);
if (ret == HAL_OK) {
printf("Raw sensor setup completed\n");
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");
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");
} else {
printf("Raw sensor setup failed\n");
}
// 2. 전체 측정 설정 및 테스트
ret = zssc3230_setup_full_measurement(timeout_ms);
if (ret == HAL_OK) {
printf("Full measurement setup completed\n");
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");
}
} else {
printf("Full measurement setup failed\n");
}
// 3. 기타 명령어 테스트
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");
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");
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");
ret = zssc3230_stop_pdm(timeout_ms);
if (ret == HAL_OK) printf("PDM stopped\n");
else printf("PDM stop failed\n");
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");
ret = zssc3230_start_sleep(timeout_ms);
if (ret == HAL_OK) printf("Sleep mode entered\n");
else printf("Sleep mode failed\n");
ret = zssc3230_soft_reset(timeout_ms);
if (ret == HAL_OK) printf("Soft reset succeeded\n");
else printf("Soft reset failed\n");
// 4. 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");
}
// 5. 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");
}
5.3 구현 상세
코드는 다음과 같은 주요 기능을 제공합니다:
- 모듈화: 각 명령어(
A2HEX,AAHEX등)를 별도 함수로 구현해 재사용성 향상. - NVM 설정 함수:
zssc3230_setup_raw_sensor: 원시 측정용12HEX,19HEX설정.zssc3230_setup_full_measurement: 전체 측정용00HEX~1AHEX설정.
- 24비트 계수 처리:
zssc3230_nvm_read_24bit,zssc3230_nvm_write_24bit로 보정 계수 관리. - CRC 검증:
zssc3230_calculate_crc,zssc3230_verify_crc로 데이터 무결성 확인. - 에러 처리: HAL 상태와 ZSSC3230 상태 바이트를 확인해 디버깅 용이.
- 테스트 루틴:
zssc3230_command_example로 모든 기능 테스트.
설정은 차동 센서(2pF, CC 핀, 14비트 ADC, 24비트 출력, 저노이즈, 최대 업데이트 속도)를 가정하며, 실제 애플리케이션에서는 캘리브레이션 데이터를 사용해야 합니다.
6. 주의사항
- 보정 계수:
03HEX~0EHEX는 실제 센서 캘리브레이션 데이터로 설정해야 하며, 예시 값(0x0000,0x4000)은 테스트용입니다. - 커패시턴스 범위:
shift_cap은cap_range이하로 설정(예: 0.25pF ≤ 2pF). - I2C 타이밍: 명령어 간 100μs, NVM 쓰기 후 5ms 대기 준수.
- NVM 쓰기 제한: 약 10,000회로 제한되므로 빈번한 쓰기 피하기.
- PDM 모드: 설정 변경 전
B4HEX로 비활성화. - 하드웨어: I2C 핀(GPIOB, 4.7kΩ 풀업 저항), VDD 안정화(2.3V~3.6V) 확인.
- 디버깅: UART 출력을 통해 상태 바이트와 에러 로그 확인.
7. 사용 방법
- STM32CubeMX 설정:
- I2C1 활성화(400kHz, 표준 모드, GPIOB 핀).
- UART 활성화(디버깅용, 예: USART2).
- 시스템 클럭 설정(예: 80MHz).
- 코드 통합: 위 코드를
main.c또는 별도 소스 파일에 추가. - 빌드 및 디버깅: STM32CubeIDE로 빌드,
zssc3230_command_example호출. - 캘리브레이션: 실제 센서 데이터로 보정 계수 갱신.
- 테스트: UART 터미널로 출력 확인.
8. 결론
ZSSC3230은 고정밀 커패시턴스 센서 애플리케이션에 적합하며, I2C 인터페이스와 NVM 설정을 통해 유연한 데이터 처리가 가능합니다. 이 글의 코드는 STM32L432KC에서 모든 명령어와 설정을 구현하여 안정적인 측정을 보장합니다. 실제 구현 시 캘리브레이션, 타이밍, 하드웨어 설정에 주의해야 하며, 추가 최적화(예: 인터럽트 기반 I2C 처리)를 고려할 수 있습니다.
'아날로그회로(Analog Circuit) > ADC관련' 카테고리의 다른 글
| PT100/PT1000 RTD 센서용 MAX31865 온도 변환 IC STM32 디바이스 드라이버 구현 (0) | 2025.08.24 |
|---|---|
| PGA302 Sensor Signal Conditioner IC STM32용 I2C 드라이버 구현 (0) | 2025.08.20 |
| ZSC31050 Sensor Signal Conditioner IC STM32용 I2C 드라이버 구현 (0) | 2025.08.16 |
| ZSSC3230 Sensor Signal Conditioner IC 보정 절차 및 Calibration Math 분석 (1) | 2025.08.15 |
| ZSSC3230 Sensor Signal Conditioner IC 센서 Raw Data 측정 설정 절차 및 I2C 코드 (0) | 2025.08.15 |
| ZSSC3230 Sensor Signal Conditioner IC 전체 측정(Measure) 절차 및 I2C 코드 (0) | 2025.08.15 |
| ZSSC3230 Sensor Signal Conditioner IC 사양서 요약 (1) | 2025.08.14 |
| AD2S1210 Resolver-to-Digital Converter with STM32G474 SPI Driver (0) | 2025.08.14 |