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

STM32 HAL 기반 ADS114S08 ADC 드라이버 구현

by linuxgo 2025. 8. 24.
반응형

Texas Instruments의 ADS114S08은 저전력, 고정밀 16비트 Delta-Sigma ADC로, 센서 데이터 수집, 의료 기기, 산업용 계측에 이상적입니다. 이 블로그에서는 STM32L432KC 마이크로컨트롤러와 STM32 HAL을 기반으로 ADS114S08 드라이버를 구현하는 방법을 상세히 다룹니다.데이터시트 기반으로 전체 기능을 다 활용할 수 있는 상세 주석이 포함된 완전한 소스 코드, STM32CubeMX 설정 가이드를 제공하며, 초보자부터 숙련된 개발자까지 활용 가능한 실용적인 내용을 담았습니다.

키워드: ADS114S08, STM32, ADC 드라이버, 고정밀 데이터 수집, STM32CubeMX, SPI 인터페이스, Delta-Sigma ADC

ADS114S08 사양

ADS114S08은 저전력 고정밀 애플리케이션에 최적화된 16비트 Delta-Sigma ADC입니다. Texas Instruments 데이터시트(Rev. A, 2017)에 기반한 주요 사양은 다음과 같습니다:

ADS114S08 function diagram

ADC 성능

  • 해상도: 16비트 (No Missing Codes).
  • 데이터 레이트: 2.5 SPS ~ 4000 SPS (프로그래머블, DATARATE 레지스터).
  • 입력 채널: 12개 아날로그 입력(AIN0~AIN11), 싱글 엔드 또는 디퍼런셜 구성.
  • 입력 범위: ±VREF/게인 (VREF=참조 전압, 게인=1~128).
  • 잡음 성능: 0.75 μV RMS (20 SPS, 게인=1, 저지연 필터).
  • CMRR: 105 dB (typ), PSRR: 100 dB (typ).

입력 멀티플렉서 (INPMUX)

  • 12개 입력 핀(AIN0~AIN11) 및 AINCOM, 플로팅 입력(Open), (AVDD-AVSS)/4, 온도 센서 선택 가능.
  • 디퍼런셜 또는 싱글 엔드 구성 지원.

프로그래머블 게인 앰프 (PGA)

  • 게인: 1, 2, 4, 8, 16, 32, 64, 128 (PGA 레지스터).
  • 옵션: PGA 바이패스(게인=1), 입력 버퍼 활성화.
  • 레일 감지: 입력 및 출력 레일링 플래그(STATUS 레지스터).

참조 전압 (REF)

  • 선택: 외부(REF0, REF1), 내부 2.5V(±0.5%).
  • 내부 참조 제어: 항상 켜짐, 변환 중 켜짐, 꺼짐 (REF 레지스터).
  • 모니터링: 저전압 감지 레벨 1/2 (STATUS 레지스터).

디지털 필터 (DATARATE)

  • 타입: sinc^3 (높은 잡음 억제) 또는 저지연 필터 (빠른 응답).
  • 글로벌 초핑: 오프셋 감소.

IDAC (전류 소스, IDACMAG/IDACMUX)

  • 2개 독립 IDAC: 10 μA, 50 μA, 100 μA, 250 μA, 500 μA, 750 μA, 1000 μA, 1500 μA, 2000 μA.
  • 라우팅: AIN0~AIN11, AINCOM, 또는 비연결.

VBIAS

  • 바이어스 전압: (AVDD+AVSS)/2 또는 (REFP+REFN)/2.
  • AIN0~AIN6, AINCOM에 개별 적용.

GPIO (GPIODAT/GPIOCON)

  • 4개 핀(AIN8~AIN11): 아날로그 입력 또는 GPIO로 구성.
  • 방향: 입력/출력, 데이터 읽기/쓰기 지원.

캘리브레이션 (OFCAL/FSCAL)

  • 시스템 오프셋/게인, 셀프 오프셋 캘리브레이션.
  • 샘플 수: 1, 4, 8, 16 (SYS 레지스터).
  • 24비트 캘리브레이션 레지스터 (16비트 ADC에 맞게 하위 16비트 사용).

SPI 인터페이스

  • 모드: SPI Mode 1 (CPOL=0, CPHA=1).
  • 최대 클럭: 10 MHz.
  • 명령어: WAKEUP, POWERDOWN, RESET, START/SYNC, STOP, RDATA, SY_OCAL, SY_GCAL, SF_OCAL, RREG, WREG.
  • 데이터 읽기: 16비트 ADC 데이터, 옵션 상태 바이트(1), CRC(2).

상태 및 진단 (STATUS)

  • 플래그: POR, 데이터 준비(RDY), PGA 레일링, 참조 저전압.
  • SPI 타임아웃, 레지스터 CRC 옵션 (SYS 레지스터).

전원 및 소비

  • 아날로그 전원: AVDD 2.7V~5.25V, AVSS (그라운드 또는 -2.5V).
  • 디지털 전원: DVDD 2.7V~3.6V.
  • 소비 전류: 280 μA (typ, 내부 참조/PGA 활성), 1 μA (power-down).

기타

  • 내부 온도 센서: ±2°C 정확도.
  • 클럭: 내부 4.096 MHz(±2%) 또는 외부 입력.
  • 패키지: TQFP-32, VQFN-32.
  • 동작 온도: -50°C ~ +125°C.

코드 구현 개요

이 드라이버는 STM32L432KC (Nucleo-32 보드 가정)를 기반으로 ADS114S08의 모든 기능을 지원합니다:

  • 환경:
    • STM32CubeMX로 프로젝트 생성: SPI1, UART2, GPIO, EXTI 설정.
    • SPI1: PA5(SCLK), PA6(MISO), PA7(MOSI), Mode 1, ~1 MHz.
    • CS: PA4 (GPIO 출력, active low).
    • DRDY: PB0 (GPIO 인터럽트, falling edge).
    • UART2: PA2(TX)/PA3(RX), 115200 baud (디버깅 출력).
    • 클럭: 80 MHz.
  • 기능:
    • 전체 명령어 지원: WAKEUP, POWERDOWN, RESET, START/SYNC, STOP, RDATA, 캘리브레이션(SY_OCAL, SY_GCAL, SF_OCAL).
    • 레지스터(0x00~0x11) 읽기/쓰기, 비트 필드 정의.
    • 초기화: POR 대기, 리셋, ID 확인, 기본 구성(AIN0-AIN1, 게인1, 20 SPS, 내부 참조, IDAC/VBIAS/GPIO off).
    • 데이터 읽기: 동적 버퍼 크기(상태/CRC 옵션), DRDY 인터럽트 기반.
    • 캘리브레이션: 시스템/셀프, 샘플 수 및 대기 시간 계산.
    • IDAC, VBIAS, GPIO 설정 및 제어.
    • 상태 점검: STATUS 플래그 확인(POR, RDY, 오류).
  • 타이밍:
    • RESET: 4096 tCLK (~1 ms @4.096 MHz).
    • 내부 참조 안정화: ~5.9 ms (1 μF 기준).
    • 캘리브레이션: 데이터 레이트 및 샴플 수에 따라 동적 대기.
    • VBIAS 안정화: ~1 ms (예상 부하 기준).
  • 에러 처리:
    • ID 확인, STATUS 오류 플래그 점검.
    • HAL 상태 반환 및 타임아웃 처리.
  • main.c:
    • 초기화, 셀프 오프셋 캘리브레이션, IDAC/VBIAS/GPIO 예시 구성.
    • DRDY 인터럽트로 데이터 읽기, UART로 출력.

사용 사례

  • 산업용 센서: 온도, 압력, 스트레인 게이지 데이터를 고정밀으로 수집.
  • 의료 기기: 바이오 센서 신호 측정.
  • IoT 디바이스: 저전력 환경에서 배터리 효율적 데이터 로깅.

코드

ads114s08.h

#ifndef ADS114S08_H
#define ADS114S08_H

#include "stm32l4xx_hal.h"  // STM32L4 HAL 헤더 (L432KC 대상)

// ADS114S08 명령어 opcode 정의 (데이터시트 Section 9.5.3)
// 명령어는 SPI를 통해 ADS114S08 제어에 사용
#define ADS_NOP         0x00  // No operation, 패딩 또는 타임아웃 방지
#define ADS_WAKEUP      0x00  // Power-down에서 깨우기 (0x02도 동일)
#define ADS_POWERDOWN   0x02  // 저전력 모드 진입 (~1uA 소비)
#define ADS_RESET       0x06  // 소프트웨어 리셋, 레지스터 기본값 복원
#define ADS_START       0x08  // 변환 시작 또는 동기화 (0x0A도 동일)
#define ADS_STOP        0x0A  // 변환 중지
#define ADS_RDATA       0x12  // 변환 데이터 읽기 (16-bit, 상태/CRC 옵션)
#define ADS_SY_OCAL     0x16  // 시스템 오프셋 캘리브레이션 (입력 단락 필요)
#define ADS_SY_GCAL     0x18  // 시스템 게인 캘리브레이션 (풀 스케일 입력 필요)
#define ADS_SF_OCAL     0x1A  // 셀프 오프셋 캘리브레이션 (내부 단락)
#define ADS_RREG        0x20  // 레지스터 읽기: 0x20 + (addr << 2) + (num-1)
#define ADS_WREG        0x40  // 레지스터 쓰기: 0x40 + (addr << 2) + (num-1)

// 레지스터 주소 정의 (데이터시트 Table 19, 0x00~0x11)
// ADS114S08의 설정 및 상태를 제어/읽기 위한 레지스터
#define ADS_REG_ID      0x00  // 읽기 전용, 장치 ID (ADS114S08=0x4X)
#define ADS_REG_STATUS  0x01  // 상태 (POR, RDY, 오류 플래그)
#define ADS_REG_INPMUX  0x02  // 입력 멀티플렉서 (MUXP/MUXN)
#define ADS_REG_PGA     0x03  // PGA 설정 (게인, 바이패스, 지연)
#define ADS_REG_DATARATE 0x04 // 데이터 레이트 및 필터 설정
#define ADS_REG_REF     0x05  // 참조 전압 제어
#define ADS_REG_IDACMAG 0x06  // IDAC 크기
#define ADS_REG_IDACMUX 0x07  // IDAC 라우팅
#define ADS_REG_VBIAS   0x08  // VBIAS 설정
#define ADS_REG_SYS     0x09  // 시스템 제어 (CRC, 상태, 타임아웃 등)
#define ADS_REG_OFCAL0  0x0A  // 오프셋 캘리브레이션 LSB
#define ADS_REG_OFCAL1  0x0B  // 오프셋 캘리브레이션 MSB
#define ADS_REG_OFCAL2  0x0C  // 오프셋 캘리브레이션 상위 (16-bit ADC에서는 사용 안 함)
#define ADS_REG_FSCAL0  0x0D  // 풀 스케일 캘리브레이션 LSB
#define ADS_REG_FSCAL1  0x0E  // 풀 스케일 캘리브레이션
#define ADS_REG_FSCAL2  0x0F  // 풀 스케일 캘리브레이션 MSB (기본 0x00400000=1.0)
#define ADS_REG_GPIODAT 0x10  // GPIO 데이터 (방향 및 값)
#define ADS_REG_GPIOCON 0x11  // GPIO 설정 (아날로그 또는 GPIO)

// 비트 필드 정의 (데이터시트 Table 20~36)
// 각 레지스터의 비트별 기능 정의
#define ADS_ID_DEV_ID_MASK   0x07  // Bits 2:0: 장치 ID (0x04=ADS114S08)
#define ADS_STATUS_FL_POR    (1 << 7)  // Bit 7: POR 발생 플래그 (1=발생, 0으로 클리어)
#define ADS_STATUS_RDY       (1 << 6)  // Bit 6: 데이터 준비 (0=준비됨)
#define ADS_STATUS_FL_P_RAILP (1 << 5) // Bit 5: PGA 긍정 입력 상한 레일링
#define ADS_STATUS_FL_P_RAILN (1 << 4) // Bit 4: PGA 긍정 입력 하한 레일링
#define ADS_STATUS_FL_N_RAILP (1 << 3) // Bit 3: PGA 부정 입력 상한 레일링
#define ADS_STATUS_FL_N_RAILN (1 << 2) // Bit 2: PGA 부정 입력 하한 레일링
#define ADS_STATUS_FL_REF_L1 (1 << 1)  // Bit 1: 참조 전압 저전압 레벨 1
#define ADS_STATUS_FL_REF_L0 (1 << 0)  // Bit 0: 참조 전압 저전압 레벨 0
#define ADS_INPMUX_MUXP_SHIFT 4        // Bits 7:4: 긍정 입력 (0x0=AIN0, 0xC=AINCOM, 0xD=Open, 0xE=(AVDD-AVSS)/4, 0xF=온도 센서)
#define ADS_INPMUX_MUXN_MASK  0x0F     // Bits 3:0: 부정 입력 (유사)
#define ADS_PGA_DELAY_MASK   (7 << 5)  // Bits 7:5: 변환 지연 (000=14 tMOD, 111=2048 tMOD)
#define ADS_PGA_GAIN_MASK    (7 << 2)  // Bits 4:2: 게인 (000=1, 001=2, ..., 111=128)
#define ADS_PGA_EN_MASK      0x03      // Bits 1:0: PGA 활성화 (00=바이패스, 01=활성, 10=버퍼 포함)
#define ADS_DATARATE_GLOBCHOP (1 << 7) // Bit 7: 글로벌 초핑 활성화 (오프셋 감소)
#define ADS_DATARATE_CLKSEL  (1 << 6)  // Bit 6: 클럭 소스 (0=내부 4.096MHz, 1=외부)
#define ADS_DATARATE_MODE    (1 << 5)  // Bit 5: 변환 모드 (0=싱글 샷, 1=연속)
#define ADS_DATARATE_FILTER  (1 << 4)  // Bit 4: 필터 타입 (0=sinc3, 1=저지연)
#define ADS_DATARATE_DR_MASK 0x0F      // Bits 3:0: 데이터 레이트 (0000=2.5SPS, 1111=4000SPS)
#define ADS_REF_FL_REF_EN_MASK (3 << 6) // Bits 7:6: 참조 모니터링 (00=꺼짐, 11=레벨 1+2)
#define ADS_REF_REFSEL_MASK    (3 << 3) // Bits 4:3: 참조 선택 (00=REF0, 01=REF1, 10=내부, 11=IDAC용 내부)
#define ADS_REF_REFCON_MASK    0x03     // Bits 1:0: 내부 참조 제어 (00=꺼짐, 01=항상 켜짐, 10=변환 중 켜짐)
#define ADS_IDACMAG_FL_POR_STAT (1 << 7) // Bit 7: POR 상태 (읽기 전용)
#define ADS_IDACMAG_IMAG_MASK   0x0F     // Bits 3:0: IDAC 크기 (0000=꺼짐, 0001=10uA, ..., 1100=2000uA)
#define ADS_IDACMUX_IDAC2_MASK (0xF << 4) // Bits 7:4: IDAC2 라우팅 (0x0=AIN0, ..., 0xF=비연결)
#define ADS_IDACMUX_IDAC1_MASK 0x0F       // Bits 3:0: IDAC1 라우팅 (유사)
#define ADS_VBIAS_VB_LEVEL   (1 << 7)     // Bit 7: VBIAS 레벨 (0=중앙 공급, 1=중앙 참조)
#define ADS_VBIAS_VB_MASK    0x7F         // Bits 6:0: VBIAS 활성화 (AIN0~6, AINCOM)
#define ADS_SYS_CAL_SAMP_MASK (3 << 6)    // Bits 7:6: 캘리브레이션 샘플 수 (00=1, 01=4, 10=8, 11=16)
#define ADS_SYS_CRC          (1 << 5)     // Bit 5: 데이터 읽기 CRC 활성화
#define ADS_SYS_STATUS       (1 << 4)     // Bit 4: 데이터 읽기 상태 바이트 포함
#define ADS_SYS_TIMEOUT      (1 << 3)     // Bit 3: SPI 타임아웃 활성화
#define ADS_SYS_REG_CRC      (1 << 2)     // Bit 2: 레지스터 맵 CRC 활성화
#define ADS_SYS_RESERVED     0x03         // Bits 1:0: 예약됨 (0)
#define ADS_GPIODAT_DIR_MASK (0xF << 4)   // Bits 7:4: GPIO 방향 (1=출력, 0=입력)
#define ADS_GPIODAT_DAT_MASK 0x0F         // Bits 3:0: GPIO 데이터
#define ADS_GPIOCON_CON_MASK 0x0F         // Bits 3:0: GPIO 활성화 (1=GPIO, 0=아날로그 입력)

// 데이터 읽기 버퍼 크기 (SYS 레지스터에 따라 동적)
#define ADS_DATA_BYTES_BASE  2  // 기본: 16-bit 데이터
#define ADS_DATA_BYTES_MAX   5  // 최대: 상태(1) + 데이터(2) + CRC(2)

// 함수 선언
// 초기화: 기본 구성 및 인터럽트 설정
void ADS114S08_Init(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, GPIO_TypeDef *drdy_port, uint16_t drdy_pin);
// 단일 명령어 전송 (예: RESET, START)
void ADS114S08_SendCommand(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t cmd);
// 레지스터 읽기
HAL_StatusTypeDef ADS114S08_ReadRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t reg_addr, uint8_t *data, uint8_t num_bytes);
// 레지스터 쓰기
HAL_StatusTypeDef ADS114S08_WriteRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t reg_addr, uint8_t *data, uint8_t num_bytes);
// 데이터 읽기: 16-bit ADC 값, 옵션 상태/CRC
int32_t ADS114S08_ReadData(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t *status_out, uint16_t *crc_out);
// 변환 시작
void ADS114S08_StartConversion(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// 변환 중지
void ADS114S08_StopConversion(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// 소프트웨어 리셋
void ADS114S08_Reset(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// Wakeup 명령
void ADS114S08_Wakeup(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// Power-down 명령
void ADS114S08_PowerDown(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// 캘리브레이션 수행 (시스템/셀프)
HAL_StatusTypeDef ADS114S08_PerformCalibration(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t cal_type);
// 상태 점검
uint8_t ADS114S08_CheckStatus(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);
// IDAC 구성 (크기 및 라우팅)
void ADS114S08_ConfigIDAC(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t mag, uint8_t mux1, uint8_t mux2);
// VBIAS 구성
void ADS114S08_ConfigVBIAS(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t level, uint8_t enable_mask);
// GPIO 구성 (AIN8~11)
void ADS114S08_ConfigGPIO(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t con, uint8_t dir, uint8_t dat);
// GPIO 데이터 읽기
uint8_t ADS114S08_ReadGPIO(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin);

// DRDY 인터럽트 콜백 (main에서 재정의)
extern void ADS114S08_DataReadyCallback(void);

#endif /* ADS114S08_H */

ads114s08.c

#include "ads114s08.h"

// CS 핀 제어 매크로 (active low)
// SPI 통신 시 칩 선택(CS)을 활성화/비활성화
#define CS_LOW(cs_port, cs_pin) HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET)
#define CS_HIGH(cs_port, cs_pin) HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET)

/**
 * @brief ADS114S08 초기화
 * - 데이터시트 Section 9.5.1 Pseudo Code 기반.
 * - POR 후 2.2ms 대기 (HAL_Init에서 처리 가정).
 * - SPI Mode 1 (CPOL=0, CPHA=1), CubeMX에서 설정.
 * - 소프트웨어 리셋 후 POR 플래그 클리어.
 * - ID 확인 (0x4X for ADS114S08).
 * - 기본 구성:
 *   - 입력: AIN0(긍정)-AIN1(부정).
 *   - PGA: 바이패스, 게인=1, 지연=14 tMOD.
 *   - 데이터 레이트: 20 SPS, 저지연 필터, 연속 모드.
 *   - 참조: 내부 2.5V, 항상 켜짐.
 *   - IDAC: 꺼짐.
 *   - VBIAS: 꺼짐.
 *   - 시스템: CRC/상태 바이트 꺼짐, 타임아웃 꺼짐.
 *   - 캘리브레이션: OFCAL=0, FSCAL=1.0.
 *   - GPIO: 모두 아날로그 입력 (AIN8~11).
 * @param hspi SPI 핸들러 (STM32 SPI 인스턴스)
 * @param cs_port CS GPIO 포트
 * @param cs_pin CS GPIO 핀
 * @param drdy_port DRDY GPIO 포트 (인터럽트)
 * @param drdy_pin DRDY GPIO 핀
 */
void ADS114S08_Init(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, GPIO_TypeDef *drdy_port, uint16_t drdy_pin) {
    // DRDY 인터럽트는 CubeMX에서 설정 (EXTI, falling edge, pull-up)
    // 리셋 명령 전송
    ADS114S08_Reset(hspi, cs_port, cs_pin);
    HAL_Delay(1);  // RESET 후 4096 tCLK (~1ms @4.096MHz) 대기, 데이터시트 Figure 67

    // STATUS: POR 플래그 클리어 (데이터시트 Table 20)
    uint8_t status = 0x00;  // FL_POR=0
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_STATUS, &status, 1);

    // ID 확인 (0x40~0x4F 예상, 데이터시트 Table 20)
    uint8_t id;
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_ID, &id, 1);
    if ((id & ADS_ID_DEV_ID_MASK) != 0x04) {
        // 에러: 잘못된 장치 ID, 디버깅용 무한 루프
        while(1);
    }

    // INPMUX: MUXP=AIN0 (0x0), MUXN=AIN1 (0x1), 데이터시트 Table 21
    uint8_t reg_data = (0x0 << ADS_INPMUX_MUXP_SHIFT) | 0x1;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_INPMUX, &reg_data, 1);

    // PGA: DELAY=000 (14 tMOD), GAIN=000 (1), PGA_EN=00 (바이패스), 데이터시트 Table 22
    reg_data = 0x00;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_PGA, &reg_data, 1);

    // DATARATE: GLOBCHOP=0, CLKSEL=0 (내부), MODE=1 (연속), FILTER=1 (저지연), DR=0100 (20SPS), 데이터시트 Table 23
    reg_data = (0 << 7) | (0 << 6) | (1 << 5) | (1 << 4) | 0x04;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_DATARATE, &reg_data, 1);

    // REF: FL_REF_EN=00 (꺼짐), REFSEL=10 (내부 2.5V), REFCON=01 (항상 켜짐), 데이터시트 Table 24
    reg_data = (0 << 6) | (2 << 3) | 0x01;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_REF, &reg_data, 1);

    // IDACMAG: IMAG=0000 (꺼짐), 데이터시트 Table 25
    reg_data = 0x00;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_IDACMAG, &reg_data, 1);

    // IDACMUX: IDAC1/2=1111 (비연결), 데이터시트 Table 26
    reg_data = 0xFF;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_IDACMUX, &reg_data, 1);

    // VBIAS: VB_LEVEL=0, VB=0000000 (모두 꺼짐), 데이터시트 Table 27
    reg_data = 0x00;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_VBIAS, &reg_data, 1);

    // SYS: CAL_SAMP=00 (1 샘플), CRC=0, STATUS=0, TIMEOUT=0, REG_CRC=0, 데이터시트 Table 28
    reg_data = 0x00;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_SYS, &reg_data, 1);

    // 캘리브레이션: OFCAL=0, FSCAL=0x00400000 (1.0), 데이터시트 Table 29~34
    uint8_t cal_data[6] = {0x00, 0x00, 0x00, 0x00, 0x40, 0x00};
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_OFCAL0, cal_data, 6);

    // GPIO: CON=0000 (모두 아날로그), DIR=0, DAT=0, 데이터시트 Table 35~36
    reg_data = 0x00;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_GPIOCON, &reg_data, 1);
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_GPIODAT, &reg_data, 1);

    // 내부 참조 안정화 대기 (5.9ms for 1uF capacitor, 데이터시트 Section 9.4.6)
    HAL_Delay(6);
}

/**
 * @brief 단일 명령어 전송
 * - 데이터시트 Figure 71: CS low -> 명령어 전송 -> CS high.
 * - SPI 통신의 기본 단위로, ADS114S08 제어 명령 전송.
 * @param cmd 명령어 opcode (예: ADS_RESET, ADS_START)
 */
void ADS114S08_SendCommand(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t cmd) {
    CS_LOW(cs_port, cs_pin);
    HAL_SPI_Transmit(hspi, &cmd, 1, HAL_MAX_DELAY);
    CS_HIGH(cs_port, cs_pin);
}

/**
 * @brief 레지스터 읽기
 * - 데이터시트 Figure 73: RREG 명령 = 0x20 | (addr << 2) | (num-1).
 * - 2바이트 명령어 전송 후 지정된 바이트 수만큼 데이터 수신.
 * @param reg_addr 시작 레지스터 주소 (0x00~0x11)
 * @param data 읽은 데이터 저장 버퍼
 * @param num_bytes 읽을 바이트 수 (1~18)
 * @return HAL 상태 (HAL_OK, HAL_ERROR 등)
 */
HAL_StatusTypeDef ADS114S08_ReadRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t reg_addr, uint8_t *data, uint8_t num_bytes) {
    uint8_t tx_buf[2] = {ADS_RREG | (reg_addr << 2), num_bytes - 1};
    CS_LOW(cs_port, cs_pin);
    HAL_SPI_Transmit(hspi, tx_buf, 2, HAL_MAX_DELAY);
    HAL_StatusTypeDef status = HAL_SPI_Receive(hspi, data, num_bytes, HAL_MAX_DELAY);
    CS_HIGH(cs_port, cs_pin);
    return status;
}

/**
 * @brief 레지스터 쓰기
 * - 데이터시트 Figure 72: WREG 명령 = 0x40 | (addr << 2) | (num-1).
 * - 2바이트 명령어 후 지정된 바이트 수만큼 데이터 전송.
 * @param reg_addr 시작 레지스터 주소
 * @param data 쓸 데이터 버퍼
 * @param num_bytes 쓸 바이트 수
 * @return HAL 상태
 */
HAL_StatusTypeDef ADS114S08_WriteRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t reg_addr, uint8_t *data, uint8_t num_bytes) {
    uint8_t tx_buf[2] = {ADS_WREG | (reg_addr << 2), num_bytes - 1};
    CS_LOW(cs_port, cs_pin);
    HAL_SPI_Transmit(hspi, tx_buf, 2, HAL_MAX_DELAY);
    HAL_StatusTypeDef status = HAL_SPI_Transmit(hspi, data, num_bytes, HAL_MAX_DELAY);
    CS_HIGH(cs_port, cs_pin);
    return status;
}

/**
 * @brief 변환 데이터 읽기
 * - 데이터시트 Section 9.5.3.7, Figure 74: RDATA 명령.
 * - SYS 레지스터 설정에 따라 출력: 상태(1바이트) + 데이터(2바이트) + CRC(2바이트).
 * - 16-bit 데이터 sign-extended로 반환, 상태/CRC은 포인터로 옵션 출력.
 * - DRDY 핀 또는 STATUS RDY=0 확인 후 호출 권장.
 * @param status_out 상태 바이트 출력 (NULL 가능)
 * @param crc_out CRC 출력 (NULL 가능)
 * @return ADC 값 (-32768 ~ 32767, int32_t로 확장)
 */
int32_t ADS114S08_ReadData(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t *status_out, uint16_t *crc_out) {
    // SYS 레지스터 읽어 출력 포맷 확인 (데이터시트 Table 28)
    uint8_t sys_reg;
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_SYS, &sys_reg, 1);
    uint8_t send_status = (sys_reg & ADS_SYS_STATUS) ? 1 : 0;
    uint8_t send_crc = (sys_reg & ADS_SYS_CRC) ? 1 : 0;
    uint8_t rx_bytes = ADS_DATA_BYTES_BASE + send_status + (send_crc * 2);  // 2~5바이트
    uint8_t rx_buf[5] = {0};

    // RDATA 명령 전송 및 데이터 수신
    uint8_t cmd = ADS_RDATA;
    CS_LOW(cs_port, cs_pin);
    HAL_SPI_Transmit(hspi, &cmd, 1, HAL_MAX_DELAY);
    HAL_SPI_Receive(hspi, rx_buf, rx_bytes, HAL_MAX_DELAY);
    CS_HIGH(cs_port, cs_pin);

    // 상태 바이트 처리
    uint8_t offset = 0;
    if (send_status) {
        if (status_out) *status_out = rx_buf[0];
        offset++;
    }

    // 16-bit 데이터 추출 (MSB first, sign extend)
    int16_t adc_data = (int16_t)((rx_buf[offset] << 8) | rx_buf[offset + 1]);
    offset += 2;

    // CRC 처리 (옵션, 데이터시트 Section 9.5.3.7.1)
    if (send_crc && crc_out) {
        *crc_out = (rx_buf[offset] << 8) | rx_buf[offset + 1];
        // CRC 검증은 사용자 애플리케이션에서 구현 (polynomial 0x07)
    }

    return (int32_t)adc_data;
}

/**
 * @brief 변환 시작
 * - 데이터시트 Section 9.5.3.4: START 또는 SYNC 명령.
 * - 연속 또는 싱글 샷 모드 시작 (DATARATE MODE 비트).
 */
void ADS114S08_StartConversion(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, ADS_START);
}

/**
 * @brief 변환 중지
 * - 데이터시트 Section 9.5.3.5: STOP 명령.
 */
void ADS114S08_StopConversion(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, ADS_STOP);
}

/**
 * @brief 소프트웨어 리셋
 * - 데이터시트 Section 9.5.3.3: RESET 명령.
 * - 모든 레지스터 기본값 복원, STATUS FL_POR=1 설정.
 */
void ADS114S08_Reset(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, ADS_RESET);
}

/**
 * @brief Wakeup from power-down
 * - 데이터시트 Section 9.5.3.1: WAKEUP 명령.
 * - 저전력 모드에서 정상 모드로 복귀.
 */
void ADS114S08_Wakeup(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, ADS_WAKEUP);
}

/**
 * @brief Power-down 모드 진입
 * - 데이터시트 Section 9.5.3.2: POWERDOWN 명령.
 * - 소비 전류 ~1uA로 감소.
 */
void ADS114S08_PowerDown(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, ADS_POWERDOWN);
}

/**
 * @brief 캘리브레이션 수행
 * - 데이터시트 Section 9.4.12: SY_OCAL, SY_GCAL, SF_OCAL.
 * - SYS CAL_SAMP(1,4,8,16)에 따라 대기 시간 계산.
 * - 완료 확인: STATUS RDY=0.
 * - SY_OCAL/SY_GCAL은 외부 입력 준비 필요.
 * @param cal_type 캘리브레이션 타입 (ADS_SY_OCAL, ADS_SY_GCAL, ADS_SF_OCAL)
 * @return HAL_OK (성공), HAL_ERROR (실패)
 */
HAL_StatusTypeDef ADS114S08_PerformCalibration(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t cal_type) {
    ADS114S08_SendCommand(hspi, cs_port, cs_pin, cal_type);

    // 대기 시간 계산 (데이터 레이트 및 샘플 수 기반, 데이터시트 Table 7)
    uint8_t sys_reg, datarate_reg;
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_SYS, &sys_reg, 1);
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_DATARATE, &datarate_reg, 1);
    uint8_t samples = 1 << ((sys_reg >> 6) & 0x03);  // 1, 4, 8, 16
    uint16_t dr_code = datarate_reg & ADS_DATARATE_DR_MASK;
    uint32_t dr_hz = (dr_code == 0) ? 3 : (1 << dr_code);  // 근사 데이터 레이트 (Hz)
    uint32_t delay_ms = (samples * 1000 / dr_hz) + 100;  // 여유 마진
    HAL_Delay(delay_ms);

    // 완료 확인 (STATUS RDY 비트 체크)
    uint8_t status = ADS114S08_CheckStatus(hspi, cs_port, cs_pin);
    if (status & ADS_STATUS_RDY) return HAL_ERROR;
    return HAL_OK;
}

/**
 * @brief 상태 레지스터 점검
 * - 데이터시트 Table 20: RDY, POR, 레일링, 참조 오류 확인.
 * @return STATUS 레지스터 값
 */
uint8_t ADS114S08_CheckStatus(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    uint8_t status;
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_STATUS, &status, 1);
    return status;
}

/**
 * @brief IDAC 구성
 * - 데이터시트 Section 9.4.7: 크기 및 출력 핀 설정.
 * - mag: 0x0=꺼짐 ~ 0xC=2000uA.
 * - mux1/mux2: 0x0=AIN0 ~ 0xF=비연결.
 */
void ADS114S08_ConfigIDAC(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t mag, uint8_t mux1, uint8_t mux2) {
    uint8_t reg_data = mag & ADS_IDACMAG_IMAG_MASK;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_IDACMAG, &reg_data, 1);

    reg_data = ((mux2 & 0x0F) << 4) | (mux1 & 0x0F);
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_IDACMUX, &reg_data, 1);
}

/**
 * @brief VBIAS 구성
 * - 데이터시트 Section 9.4.8: 바이어스 전압 활성화.
 * - level: 0=(AVDD+AVSS)/2, 1=(REFP+REFN)/2.
 * - enable_mask: AIN0~AIN6, AINCOM 비트 (0x7F).
 */
void ADS114S08_ConfigVBIAS(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t level, uint8_t enable_mask) {
    uint8_t reg_data = ((level & 0x01) << 7) | (enable_mask & 0x7F);
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_VBIAS, &reg_data, 1);
    HAL_Delay(1);  // VBIAS 안정화 (~280us~28ms, 데이터시트 Section 9.4.8)
}

/**
 * @brief GPIO 구성 (AIN8~AIN11)
 * - 데이터시트 Section 9.4.11: 모드, 방향, 데이터 설정.
 * - con: 1=GPIO, 0=아날로그 (bit per AIN8~11).
 * - dir: 1=출력, 0=입력.
 * - dat: 출력 값 (입력 시 무시).
 */
void ADS114S08_ConfigGPIO(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t con, uint8_t dir, uint8_t dat) {
    uint8_t reg_data = con & ADS_GPIOCON_CON_MASK;
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_GPIOCON, &reg_data, 1);

    reg_data = ((dir & 0x0F) << 4) | (dat & 0x0F);
    ADS114S08_WriteRegister(hspi, cs_port, cs_pin, ADS_REG_GPIODAT, &reg_data, 1);
}

/**
 * @brief GPIO 데이터 읽기
 * - 입력 모드에서 AIN8~AIN11 상태 반환.
 * @return GPIO 데이터 (하위 4비트)
 */
uint8_t ADS114S08_ReadGPIO(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    uint8_t reg_data;
    ADS114S08_ReadRegister(hspi, cs_port, cs_pin, ADS_REG_GPIODAT, &reg_data, 1);
    return reg_data & ADS_GPIODAT_DAT_MASK;
}

// DRDY 콜백 (main.c에서 재정의)
__weak void ADS114S08_DataReadyCallback(void) {
    // 기본 구현: 사용자 코드에서 데이터 처리
}

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : STM32L432KC 기반 ADS114S08 드라이버 테스트 (80MHz 클럭)
  * - STM32CubeMX로 생성: SPI1, UART2, GPIO, EXTI.
  * - ADS114S08 초기화, 셀프 오프셋 캘리브레이션, IDAC/VBIAS/GPIO 예시.
  * - DRDY 인터럽트로 데이터 읽기, UART 출력.
  * - 핀 구성:
  *   - SPI1: PA5(SCLK), PA6(MISO), PA7(MOSI).
  *   - CS: PA4 (GPIO 출력).
  *   - DRDY: PB0 (EXTI, falling edge).
  *   - UART2: PA2(TX), PA3(RX), 115200 baud.
  * - 클럭: PLL 기반 80MHz (MSI 4MHz 소스, PLL M=1, N=40, R=2).
  * - SPI 클럭: ~10MHz (80MHz/8).
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "ads114s08.h"
#include <string.h>
#include <stdio.h>

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef huart2;

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART2_UART_Init(void);

/* ADS114S08 핀 정의 */
#define ADS_CS_PORT GPIOA
#define ADS_CS_PIN GPIO_PIN_4
#define ADS_DRDY_PORT GPIOB
#define ADS_DRDY_PIN GPIO_PIN_0

/* 글로벌 변수 */
volatile uint8_t data_ready = 0;  // DRDY 인터럽트 플래그

/**
  * @brief 메인 함수
  * - HAL 초기화, 시스템 클럭(80MHz), 주변장치 초기화.
  * - ADS114S08 초기화 및 설정 (셀프 캘리브레이션, IDAC, VBIAS, GPIO).
  * - DRDY 인터럽트로 데이터 읽고 UART로 출력.
  */
int main(void)
{
  /* MCU 초기화 */
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART2_UART_Init();

  /* ADS114S08 초기화 (기본 설정 적용) */
  ADS114S08_Init(&hspi1, ADS_CS_PORT, ADS_CS_PIN, ADS_DRDY_PORT, ADS_DRDY_PIN);

  /* 셀프 오프셋 캘리브레이션 (입력 단락 불필요, 데이터시트 Section 9.4.12) */
  if (ADS114S08_PerformCalibration(&hspi1, ADS_CS_PORT, ADS_CS_PIN, ADS_SF_OCAL) != HAL_OK) {
    char msg[] = "Calibration Failed\r\n";
    HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    while(1);  // 캘리브레이션 실패 시 디버깅용 무한 루프
  }

  /* IDAC 설정 예시: 500uA, IDAC1=AIN0, IDAC2=AIN1, 데이터시트 Section 9.4.7 */
  ADS114S08_ConfigIDAC(&hspi1, ADS_CS_PORT, ADS_CS_PIN, 0x05, 0x0, 0x1);  // 0x05=500uA

  /* VBIAS 설정 예시: (AVDD+AVSS)/2, AIN0 활성화, 데이터시트 Section 9.4.8 */
  ADS114S08_ConfigVBIAS(&hspi1, ADS_CS_PORT, ADS_CS_PIN, 0, 0x01);

  /* GPIO 설정 예시: AIN8=GPIO 입력, 나머지 아날로그, 데이터시트 Section 9.4.11 */
  ADS114S08_ConfigGPIO(&hspi1, ADS_CS_PORT, ADS_CS_PIN, 0x01, 0x00, 0x00);

  /* 변환 시작 (연속 모드, 데이터시트 Section 9.5.3.4) */
  ADS114S08_StartConversion(&hspi1, ADS_CS_PORT, ADS_CS_PIN);

  /* 메인 루프: DRDY 인터럽트로 데이터 처리 */
  while (1)
  {
    if (data_ready) {
      data_ready = 0;  // 플래그 리셋
      uint8_t status;
      uint16_t crc;
      int32_t adc_value = ADS114S08_ReadData(&hspi1, ADS_CS_PORT, ADS_CS_PIN, &status, &crc);

      // ADC 값 UART 출력
      char buf[50];
      sprintf(buf, "ADC: %ld, Status: 0x%02X\r\n", adc_value, status);
      HAL_UART_Transmit(&huart2, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);

      // GPIO 데이터 읽기 및 출력
      uint8_t gpio_dat = ADS114S08_ReadGPIO(&hspi1, ADS_CS_PORT, ADS_CS_PIN);
      sprintf(buf, "GPIO: 0x%02X\r\n", gpio_dat);
      HAL_UART_Transmit(&huart2, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);

      // 상태 점검: 오류 플래그 출력 (데이터시트 Table 20)
      if (status & (ADS_STATUS_FL_P_RAILP | ADS_STATUS_FL_P_RAILN | ADS_STATUS_FL_N_RAILP | ADS_STATUS_FL_N_RAILN)) {
        sprintf(buf, "Error: PGA Rail Violation\r\n");
        HAL_UART_Transmit(&huart2, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);
      }
    }
  }
}

/**
  * @brief DRDY 인터럽트 콜백
  * - 데이터 준비 시 플래그 설정.
  * - DRDY 핀 falling edge에서 호출 (데이터시트 Section 9.5.3.7).
  */
void ADS114S08_DataReadyCallback(void) {
  data_ready = 1;
}

/**
  * @brief EXTI 인터럽트 핸들러
  * - PB0(DRDY) falling edge에서 호출.
  * - ADS114S08_DataReadyCallback 호출.
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if (GPIO_Pin == ADS_DRDY_PIN) {
    ADS114S08_DataReadyCallback();
  }
}

/**
  * @brief 시스템 클럭 설정
  * - PLL 기반 80MHz SYSCLK 설정.
  * - MSI 4MHz를 소스로 사용, PLL 설정: M=1, N=40, R=2.
  * - HCLK, PCLK1, PCLK2 = 80MHz (1분주).
  * - STM32L432KC 데이터시트 및 참조 매뉴얼(RM0394) 참고.
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /* MSI 오실레이터 활성화 (4MHz) */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = 0;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;  // 4MHz
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
  RCC_OscInitStruct.PLL.PLLM = 1;  // M=1 (4MHz 입력)
  RCC_OscInitStruct.PLL.PLLN = 40; // N=40 (4MHz * 40 = 160MHz)
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // R=2 (160MHz / 2 = 80MHz)
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // 사용 안 함
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // 사용 안 함
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /* SYSCLK, HCLK, PCLK1, PCLK2 설정 */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                              | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // PLL을 시스템 클럭 소스로 사용
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;  // HCLK = 80MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;   // PCLK1 = 80MHz
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;   // PCLK2 = 80MHz

  /* 플래시 레이턴시 설정 (80MHz에서 4 WS 필요, STM32L432KC 데이터시트 Table 12) */
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI1 초기화
  * - Mode 1 (CPOL=0, CPHA=1), 8-bit, ~10MHz (80MHz/8).
  * - ADS114S08 SPI 요구사항에 맞춘 설정 (데이터시트 Section 9.5.2, 최대 10MHz).
  */
static void MX_SPI1_Init(void)
{
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;  // Mode 1
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 80MHz/8 = 10MHz
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 초기화
  * - 115200 baud, 8N1, 하드웨어 플로우 제어 없음.
  * - 디버깅용 UART 설정.
  */
static void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLING_DISABLED;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO 초기화
  * - PA4: CS 출력 (기본 high).
  * - PB0: DRDY 입력 (EXTI, falling edge, pull-up).
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  HAL_GPIO_WritePin(ADS_CS_PORT, ADS_CS_PIN, GPIO_PIN_SET);

  GPIO_InitStruct.Pin = ADS_CS_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(ADS_CS_PORT, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = ADS_DRDY_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(ADS_DRDY_PORT, &GPIO_InitStruct);

  HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

/**
  * @brief 에러 핸들러
  * - 무한 루프 진입 (디버깅용).
  */
void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief Assert 에러 보고
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* 사용자 디버깅 코드 추가 가능 */
}
#endif /* USE_FULL_ASSERT */

사용법 및 테스트

STM32CubeMX 설정

  1. SPI1:
    •    핀: PA5(SCLK), PA6(MISO), PA7(MOSI).
    •    모드: Mode 1 (CPOL=0, CPHA=1).
    •    Baudrate Prescaler: 8 (~125kHz).
  2. GPIO:
    •    PA4: CS 출력, 초기값 high.
    •    PB0: DRDY 입력, EXTI falling edge, pull-up.
  3. UART2:
    •    핀: PA2(TX), PA3(RX).
    •    설정: 115200 baud, 8N1.
  4. 클럭: 80MHz.

하드웨어 연결

  • ADS114S08 전원: AVDD/DVDD=3.3V, AVSS=0V.
  • SPI 연결: STM32의 SPI1 핀과 연결.
  • 입력 신호: AIN0/AIN1에 테스트 입력(예: 전압 분배기, 센서).
  • DRDY: PB0에 연결, 풀업 저항 권장.

빌드 및 실행

  1. STM32CubeIDE에서 프로젝트 생성 및 코드 추가.
  2. 빌드 후 Nucleo-32 보드에 업로드.
  3. UART 터미널(예: Tera Term, PuTTY)로 ADC 값, GPIO 상태, 오류 메시지 확인.

확장

  • CRC 검증: SYS 레지스터의 CRC 비트 활성화 후 polynomial 0x07로 검증.
  • 다른 설정 테스트: 데이터 레이트(2.54000 SPS), 게인(1128), 다른 입력 채널.
  • 외부 참조/클럭: REFSEL=00/01, CLKSEL=1로 변경.

디버깅 팁

  • 데이터 읽기 오류: DRDY 핀이 올바르게 설정되었는지, SPI 클럭이 10MHz 이하인지 확인.
  • 캘리브레이션 실패: 입력 신호가 올바른지, STATUS 레지스터의 RDY 비트 확인.
  • UART 출력 없음: UART2 핀 연결 및 baud rate(115200) 점검.
  • PGA 레일링 오류: 입력 전압이 ±VREF/게인 범위 내인지 확인 (데이터시트 Section 9.4.4).
  • IDAC 문제: IDAC 출력 핀과 외부 부하 저항이 적절한지 확인 (데이터시트 Section 9.4.7).

결론

이 문서는 STM32와 ADS114S08을 활용한 고정밀 ADC 드라이버 구현 방법을 다뤘습니다. 사양서 기반으로 작성한 전체 기능을 활용할 수 있는  소스 코드 및 사용법과 디버깅 팁을 통해 초보자와 전문가 모두 쉽게 시작할 수 있습니다. 
관련 리소스: Texas Instruments ADS114S08 데이터시트, STM32CubeMX

 

ADS114S08 data sheet, product information and support | TI.com

Download options Latest version Version: 01.00.00.0B Release date: 12 Mar 2023 Precision ADCs The design resource accessed as www.ti.com/lit/zip/sbar021 or www.ti.com/lit/xx/sbar021b/sbar021b.zip has been migrated to a new user experience at www.ti.com/too

www.ti.com

 

 

STM32CubeMX | Software - STMicroelectronics

STM32CubeMX is a graphical tool that allows a very easy configuration of STM32 microcontrollers and microprocessors, as well as the generation of the...

www.st.com

키워드: ADS114S08, STM32, ADC 드라이버, 고정밀 데이터 수집, STM32CubeMX, SPI 인터페이스, Delta-Sigma ADC

반응형