이 내용는 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관련' 카테고리의 다른 글
[ZSC31050] I²C 인터페이스 설정 및 데이터 처리 코드 구현 (0) | 2025.08.16 |
---|---|
[ZSSC3230] 보정 절차 및 Calibration Math 분석 (1) | 2025.08.15 |
[ZSSC3230] 원시 센서(Raw Sensor) 측정 설정 절차 (0) | 2025.08.15 |
[ZSSC3230] 전체 측정 전 NVM 설정 절차 (0) | 2025.08.15 |
[ZSSC3230]정전용량 센서 신호 컨디셔너 요약 (1) | 2025.08.14 |
[AD2S1210] Resolver-to-Digital Converter with STM32G474 SPI Driver (0) | 2025.08.14 |
[AD7747]정전용량-디지털 변환기 디바이스 드라이버 코드를 STM32용으로 구현 (Guide to implementing AD7747 Capacitance-to-Digital Converter driver for STM32 microcontroller) (0) | 2025.08.14 |
[ZSSC3123]Command List and Encodings 상세 분석 (0) | 2025.08.13 |