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

ZSC31014 Sensor Signal Conditioner IC STM32L432KC에서 I2C통신으로 EEPROM 데이터 읽기와 쓰기 코드 구현

by linuxgo 2025. 8. 2.

아래는 STM32L432KC 마이크로컨트롤러에서 ZSC31014의 EEPROM 데이터를 I²C 인터페이스를 통해 전체 읽기/쓰기 및 특정 번지 읽기/쓰기를 수행하는 C 코드를 제공합니다. 코드 작성은 STM32 HAL 라이브러리를 기반으로 하며, ZSC31014 데이터시트(REN_ZSC31014_DST_20160120_1.pdf, 페이지 33~37, 38~43)를 참조하여 구현했습니다.

ZSC31014

가정 및 설정

  • MCU: STM32L432KC, HAL 라이브러리 사용.
  • I²C 설정:
    • ZSC31014 기본 슬레이브 주소: 0x28 (EEPROM 워드 02HEX, 비트 [9:3], 왼쪽 시프트 후 WRITE 비트 포함).
    • I²C 클럭 속도: 100kHz (ZSC31014는 100kHz 또는 400kHz 지원, 페이지 20).
  • EEPROM 워드: 00HEX ~ 13HEX (총 20개 워드, 16비트, 페이지 38~43, Table 3.7).
  • 명령:
    • Command Mode 진입: Start_CM (A0HEX, 페이지 37).
    • EEPROM 읽기: 00HEX ~ 13HEX (페이지 37, Table 3.6).
    • EEPROM 쓰기: 40HEX ~ 53HEX.
    • Normal Operation Mode 복귀: Start_NOM (80HEX).
  • 주의사항:
    • T_Config (워드 10HEX), Osc_Trim (워드 11HEX)은 공장 설정 유지 권장(페이지 41).
    • 쓰기 후 Start_NOM으로 서명 업데이트 필수(페이지 37).
    • 전원 켜짐 후 6ms 이내 Start_CM 실행(페이지 37).
    • STM32L432KC의 I²C HAL 함수(HAL_I2C_Master_Transmit, HAL_I2C_Master_Receive) 사용.

코드

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

// I2C 핸들러: STM32CubeMX에서 설정된 I2C1 사용 가정
extern I2C_HandleTypeDef hi2c1;

// ZSC31014 I2C 슬레이브 주소: 7비트 주소 0x28, 왼쪽 시프트로 8비트 변환 (데이터시트 페이지 20)
#define ZSC31014_I2C_ADDR (0x28 << 1)

// 명령 코드: 데이터시트 페이지 37, Table 3.6
#define CMD_START_CM  0xA0 // Command Mode 진입
#define CMD_START_NOM 0x80 // Normal Operation Mode 복귀
#define CMD_EEPROM_READ_BASE  0x00 // EEPROM 읽기 명령 베이스 (0x00 ~ 0x13)
#define CMD_EEPROM_WRITE_BASE 0x40 // EEPROM 쓰기 명령 베이스 (0x40 ~ 0x53)

// 타임아웃: 100ms (I2C 통신 안정성, 데이터시트 페이지 21 Table 2.7)
#define I2C_TIMEOUT 100

// GPIO for VDD control: 가정 - GPIOA Pin 0, HIGH=ON, LOW=OFF
#define ZSC_VDD_GPIO_PORT GPIOA
#define ZSC_VDD_GPIO_PIN GPIO_PIN_0

// 함수 프로토타입
HAL_StatusTypeDef zsc31014_power_cycle(void);  // 전원 사이클링
HAL_StatusTypeDef zsc31014_start_cm(void);     // Command Mode 진입
HAL_StatusTypeDef zsc31014_start_nom(void);    // Normal Operation Mode 복귀
HAL_StatusTypeDef zsc31014_read_eeprom(uint8_t word_addr, uint16_t *data);  // 단일 EEPROM 워드 읽기
HAL_StatusTypeDef zsc31014_write_eeprom(uint8_t word_addr, uint16_t data);  // 단일 EEPROM 워드 쓰기
HAL_StatusTypeDef zsc31014_read_all_eeprom(uint16_t *data_array);  // 전체 EEPROM 읽기
HAL_StatusTypeDef zsc31014_write_all_eeprom(uint16_t *data_array); // 전체 EEPROM 쓰기

/**
 * @brief ZSC31014 VDD 전원 OFF/ON (리셋 용도)
 *        데이터시트 페이지 23 (2.3.6 POR): IC 락업 방지를 위해 전원 사이클링
 * @retval HAL_OK 성공
 */
HAL_StatusTypeDef zsc31014_power_cycle(void) {
    // VDD OFF: GPIO를 LOW로 설정 (외부 스위치 OFF)
    HAL_GPIO_WritePin(ZSC_VDD_GPIO_PORT, ZSC_VDD_GPIO_PIN, GPIO_PIN_RESET);
    HAL_Delay(10);  // 최소 10ms OFF 유지 (POR 안정화, 데이터시트 페이지 7 Table 1.3)

    // VDD ON: GPIO를 HIGH로 설정 (외부 스위치 ON)
    HAL_GPIO_WritePin(ZSC_VDD_GPIO_PORT, ZSC_VDD_GPIO_PIN, GPIO_PIN_SET);
    HAL_Delay(10);  // ON 후 안정화 대기 (데이터시트 페이지 28 Figure 3.2: Power-Up ~3ms)

    printf("ZSC31014 전원 사이클링 완료\n");
    return HAL_OK;
}

/**
 * @brief Command Mode 진입 (전원 사이클링 후)
 *        데이터시트 페이지 37: Start_CM (0xA0) + don't care 데이터 2바이트
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_start_cm(void) {
    HAL_StatusTypeDef status;

    // 전원 사이클링: IC 상태 클리어 (데이터시트 페이지 26 3.1)
    status = zsc31014_power_cycle();
    if (status != HAL_OK) {
        printf("전원 사이클링 실패\n");
        return status;
    }

    // Start_CM 명령 + don't care 데이터 (0x0000)
    uint8_t tx_data[3] = {CMD_START_CM, 0x00, 0x00};
    status = HAL_I2C_Master_Transmit(&hi2c1, ZSC31014_I2C_ADDR, tx_data, 3, I2C_TIMEOUT);
    if (status != HAL_OK) {
        printf("Start_CM 실패: %d\n", status);
        return status;
    }

    HAL_Delay(1);  // 명령 완료 대기: 데이터시트 페이지 37 (10us)
    return HAL_OK;
}

/**
 * @brief Normal Operation Mode 복귀 및 EEPROM 서명 업데이트
 *        데이터시트 페이지 37: Start_NOM (0x80) + don't care 데이터 2바이트
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_start_nom(void) {
    uint8_t tx_data[3] = {CMD_START_NOM, 0x00, 0x00};
    HAL_StatusTypeDef status;

    status = HAL_I2C_Master_Transmit(&hi2c1, ZSC31014_I2C_ADDR, tx_data, 3, I2C_TIMEOUT);
    if (status != HAL_OK) {
        printf("Start_NOM 실패: %d\n", status);
        return status;
    }

    HAL_Delay(15);  // 서명 업데이트 포함 최대 15ms 대기 (데이터시트 페이지 37)
    return HAL_OK;
}

/**
 * @brief 특정 EEPROM 워드 읽기 (Command Mode에서 호출 가정)
 *        데이터시트 페이지 37: 읽기 명령 (0x00 + word_addr) + 데이터 수신 (0x5A + 16비트)
 * @param word_addr 워드 주소 (0x00 ~ 0x13)
 * @param data 읽은 16비트 데이터 저장 포인터
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_read_eeprom(uint8_t word_addr, uint16_t *data) {
    if (word_addr > 0x13) {
        printf("잘못된 워드 주소: 0x%02X\n", word_addr);
        return HAL_ERROR;
    }

    uint8_t tx_data[3] = {CMD_EEPROM_READ_BASE + word_addr, 0x00, 0x00};
    uint8_t rx_data[3];
    HAL_StatusTypeDef status;

    // 읽기 명령 전송
    status = HAL_I2C_Master_Transmit(&hi2c1, ZSC31014_I2C_ADDR, tx_data, 3, I2C_TIMEOUT);
    if (status != HAL_OK) {
        printf("EEPROM 읽기 명령 전송 실패 (워드 0x%02X): %d\n", word_addr, status);
        return status;
    }

    // 데이터 수신 (0x5A + 16비트 데이터)
    status = HAL_I2C_Master_Receive(&hi2c1, ZSC31014_I2C_ADDR, rx_data, 3, I2C_TIMEOUT);
    if (status != HAL_OK) {
        printf("EEPROM 데이터 수신 실패 (워드 0x%02X): %d\n", word_addr, status);
        return status;
    }

    // 첫 바이트 확인: 0x5A (데이터시트 페이지 37)
    if (rx_data[0] != 0x5A) {
        printf("EEPROM 읽기 응답 오류 (워드 0x%02X): 0x%02X\n", word_addr, rx_data[0]);
        return HAL_ERROR;
    }

    // 16비트 데이터 조합
    *data = (rx_data[1] << 8) | rx_data[2];
    printf("워드 0x%02X: 0x%04X\n", word_addr, *data);
    return HAL_OK;
}

/**
 * @brief 특정 EEPROM 워드 쓰기 (Command Mode에서 호출 가정)
 *        데이터시트 페이지 37: 쓰기 명령 (0x40 + word_addr) + 16비트 데이터
 * @param word_addr 워드 주소 (0x00 ~ 0x13)
 * @param data 쓸 16비트 데이터
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_write_eeprom(uint8_t word_addr, uint16_t data) {
    if (word_addr > 0x13) {
        printf("잘못된 워드 주소: 0x%02X\n", word_addr);
        return HAL_ERROR;
    }

    // T_Config(0x10), Osc_Trim(0x11) 쓰기 금지: 데이터시트 페이지 38
    if (word_addr == 0x10 || word_addr == 0x11) {
        printf("오류: 워드 0x%02X는 공장 설정으로 쓰기 금지\n", word_addr);
        return HAL_ERROR;
    }

    uint8_t tx_data[3] = {
        CMD_EEPROM_WRITE_BASE + word_addr,
        (data >> 8) & 0xFF,
        data & 0xFF
    };
    HAL_StatusTypeDef status;

    status = HAL_I2C_Master_Transmit(&hi2c1, ZSC31014_I2C_ADDR, tx_data, 3, I2C_TIMEOUT);
    if (status != HAL_OK) {
        printf("EEPROM 쓰기 실패 (워드 0x%02X): %d\n", word_addr, status);
        return status;
    }

    HAL_Delay(10);  // 쓰기 완료 대기 (데이터시트 페이지 37)
    printf("워드 0x%02X에 0x%04X 쓰기 완료\n", word_addr, data);
    return HAL_OK;
}

/**
 * @brief 전체 EEPROM 읽기 (0x00 ~ 0x13)
 *        Command Mode에서 모든 워드 읽기, 중복 Mode 진입 방지
 * @param data_array 20개 워드(16비트) 저장 배열
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_read_all_eeprom(uint16_t *data_array) {
    HAL_StatusTypeDef status;

    // Command Mode 진입: 루프 밖에서 한 번만 호출
    status = zsc31014_start_cm();
    if (status != HAL_OK) {
        printf("Command Mode 진입 실패\n");
        return status;
    }

    // 워드 0x00 ~ 0x13 읽기
    for (uint8_t word_addr = 0x00; word_addr <= 0x13; word_addr++) {
        status = zsc31014_read_eeprom(word_addr, &data_array[word_addr]);
        if (status != HAL_OK) {
            zsc31014_start_nom();  // 에러 시 NOM 복귀
            return status;
        }
    }

    // Normal Operation Mode 복귀
    status = zsc31014_start_nom();
    if (status != HAL_OK) {
        printf("Normal Operation Mode 복귀 실패\n");
        return status;
    }

    printf("EEPROM 전체 읽기 완료\n");
    return HAL_OK;
}

/**
 * @brief 전체 EEPROM 쓰기 (0x00 ~ 0x13)
 *        Command Mode에서 모든 워드 쓰기, 중복 Mode 진입 방지
 * @param data_array 쓸 20개 워드(16비트) 데이터 배열
 * @retval HAL_OK 성공, 그 외 에러
 */
HAL_StatusTypeDef zsc31014_write_all_eeprom(uint16_t *data_array) {
    HAL_StatusTypeDef status;

    // Command Mode 진입: 루프 밖에서 한 번만 호출
    status = zsc31014_start_cm();
    if (status != HAL_OK) {
        printf("Command Mode 진입 실패\n");
        return status;
    }

    // 워드 0x00 ~ 0x13 쓰기
    for (uint8_t word_addr = 0x00; word_addr <= 0x13; word_addr++) {
        status = zsc31014_write_eeprom(word_addr, data_array[word_addr]);
        if (status != HAL_OK) {
            zsc31014_start_nom();  // 에러 시 NOM 복귀
            return status;
        }
    }

    // Normal Operation Mode 복귀
    status = zsc31014_start_nom();
    if (status != HAL_OK) {
        printf("Normal Operation Mode 복귀 실패\n");
        return status;
    }

    printf("EEPROM 전체 쓰기 완료\n");
    return HAL_OK;
}

/**
 * @brief 메인 함수 예시 (사용 예시)
 */
void zsc31014_example(void) {
    HAL_StatusTypeDef status;
    uint16_t eeprom_data[20];  // 0x00 ~ 0x13 워드 저장 배열

    // 초기 전원 사이클링
    status = zsc31014_power_cycle();
    if (status != HAL_OK) {
        printf("초기 전원 사이클링 실패\n");
        return;
    }

    // 전체 EEPROM 읽기
    printf("EEPROM 전체 읽기 시작\n");
    status = zsc31014_read_all_eeprom(eeprom_data);
    if (status != HAL_OK) {
        printf("EEPROM 전체 읽기 실패\n");
        return;
    }

    // 특정 워드 읽기 (예: 워드 0x01)
    uint16_t single_data;
    printf("워드 0x01 읽기 시작\n");
    status = zsc31014_start_cm();
    if (status == HAL_OK) {
        status = zsc31014_read_eeprom(0x01, &single_data);
        zsc31014_start_nom();
        if (status == HAL_OK) {
            printf("워드 0x01: 0x%04X\n", single_data);
        } else {
            printf("워드 0x01 읽기 실패\n");
        }
    }

    // 특정 워드 쓰기 (예: 워드 0x01에 0x0000 쓰기)
    printf("워드 0x01에 0x0000 쓰기 시작\n");
    status = zsc31014_start_cm();
    if (status == HAL_OK) {
        status = zsc31014_write_eeprom(0x01, 0x0000);
        zsc31014_start_nom();
        if (status == HAL_OK) {
            printf("워드 0x01 쓰기 완료\n");
        } else {
            printf("워드 0x01 쓰기 실패\n");
        }
    }

    // 전체 EEPROM 쓰기 (샘플 데이터)
    uint16_t sample_data[20];
    memset(sample_data, 0, sizeof(sample_data));  // 0으로 초기화
    sample_data[0x01] = 0x0004;  // I2C, 1MHz, 11비트 온도, Update_Rate=25ms (페이지 38 Table 3.7)
    sample_data[0x02] = 0x0280;  // 슬레이브 주소 0x28
    sample_data[0x0F] = 0x0A80;  // PreAmp_Gain=6, A2D_Offset=-1/2 ~ 1/2 (페이지 16 Table 2.4)
    printf("EEPROM 전체 쓰기 시작\n");
    status = zsc31014_write_all_eeprom(sample_data);
    if (status == HAL_OK) {
        printf("EEPROM 전체 쓰기 완료\n");
    } else {
        printf("EEPROM 전체 쓰기 실패\n");
    }
}

코드 설명

  1. I²C 설정:
    •    ZSC31014_I2C_ADDR는 7비트 주소 0x28을 왼쪽으로 1비트 시프트하여 HAL 라이브러리 형식에 맞춤.
    •    STM32CubeMX에서 I²C1을 100kHz로 설정했다고 가정.
    •    hi2c1은 STM32CubeMX에서 생성된 I²C 핸들러.
  2. 함수:
    •    zsc31014_start_cm(): Command Mode 진입 (A0HEX), POR 후 6ms 이내 호출 필요.
    •    zsc31014_start_nom(): Normal Operation Mode 복귀 및 서명 업데이트 (80HEX).
    •    zsc31014_read_eeprom(): 특정 워드(0x00 ~ 0x13) 읽기, 명령 코드 00HEX ~ 13HEX.
    •    zsc31014_write_eeprom(): 특정 워드 쓰기, 명령 코드 40HEX ~ 53HEX.
    •    zsc31014_read_all_eeprom(): 전체 워드(0x00 ~ 0x13) 읽기, 결과를 배열에 저장.
    •    zsc31014_write_all_eeprom(): 전체 워드에 데이터 쓰기, 공장 설정 워드(0x10, 0x11)에 대한 경고 포함.
    •    zsc31014_example(): 사용 예시로 전체 읽기/쓰기, 특정 워드 읽기/쓰기 포함.
  3. 타이밍:
    •    Start_CM: 10μs 대기.
    •    Start_NOM: 최대 15ms 대기 (서명 업데이트 포함).
    •    EEPROM 읽기: 100μs 대기.
    •    EEPROM 쓰기: 10ms 대기 (페이지 37, Table 3.6).
    •    HAL_Delay를 사용하여 충분한 대기 시간 확보.
  4. 에러 처리:
    •    각 함수는 HAL 상태를 반환하며, 에러 시 콘솔에 메시지 출력 (디버깅용 printf 가정).
    •    잘못된 워드 주소(0x13 초과) 또는 공장 설정 워드(0x10, 0x11) 쓰기 시 경고.
  5. 사용 예시:
    •    zsc31014_example()는 전체 EEPROM 읽기, 특정 워드(0x01) 읽기/쓰기, 전체 쓰기를 테스트.
    •    샘플 데이터는 워드 0x01 (예: I²C, 1MHz, 8비트 온도)과 0x02 (슬레이브 주소 0x28)만 설정, 나머지는 0으로 초기화.

사용 방법

  1. STM32CubeMX 설정:
    •    I²C1 활성화, 속도 100kHz, 주소 7비트 모드.
    •    GPIO 설정: SCL (예: PB6), SDA (예: PB7), 풀업 저항(1kΩ~10kΩ, 데이터시트 페이지 6).
    •    UART 디버깅용 설정(printf 출력용, 선택 사항).
  2. 코드 통합:
    •    위 코드를 STM32 프로젝트의 main.c 또는 별도의 소스 파일에 추가.
    •    hi2c1 핸들러가 STM32CubeMX에서 초기화되었다고 가정.
    •    main()에서 zsc31014_example() 호출.
  3. 하드웨어 연결:
    •    ZSC31014의 VDD: 2.7V~5.5V (예: 3.3V, STM32L432KC와 동일).
    •    VSS: GND.
    •    SCL: PB6, SDA: PB7 (STM32 I²C1 핀).
    •    VDD와 GND 사이에 100nF~470nF 커패시터 연결(페이지 6).
    •    I²C 라인에 1kΩ 풀업 저항 연결.
  4. 빌드 및 테스트:
    •    STM32CubeIDE에서 빌드 및 플래시.
    •    시리얼 터미널(예: PuTTY)로 디버그 출력 확인.
    •    zsc31014_example() 호출로 EEPROM 읽기/쓰기 테스트.

주의사항

  • POR 후 6ms: Start_CM은 전원 켜짐 후 6ms 이내 실행(페이지 37).
  • 공장 설정 워드: T_Config(0x10), Osc_Trim(0x11)은 변경 시 온도 정확도(±2.5K) 손실 가능(페이지 41).
  • EEPROM 서명: 쓰기 후 Start_NOM 호출 필수,진단 오류(S[1:0] = 11, 페이지 24).
  • I²C 주소: 워드 02HEX 비트 [12:10]이 011이면 주소 고정(페이지 40).
  • 디버깅: HAL 에러 코드와 ZSC31014 상태를 확인하여 문제 진단.
  • 샘플 데이터: 전체 쓰기 시 sample_data 배열은 응용에 맞게 수정 필요(예: Gain_B, Offset_B 등).