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

ADS124S08 24비트 ADC STM32 HAL 기반 디바이스 드라이버 구현

by linuxgo 2025. 8. 28.

1. 개요

이 보고서는 텍사스 인스트루먼트(Texas Instruments)의 ADS124S08 24비트 델타-시그마(Δ-Σ) 아날로그-디지털 변환기(ADC)를 STM32 마이크로컨트롤러에서 제어하기 위한 디바이스 드라이버 구현 내용을 설명합니다. 드라이버는 STM32 HAL 라이브러리를 기반으로 작성되었으며, ADS124S08의 모든 주요 기능을 지원하도록 설계되었습니다. 보고서는 디바이스의 상세 사양, 구현된 드라이버의 세부 사항, STM32L432KC 기준으로 한 예제 코드, 그리고 완전한 드라이버 코드를 포함합니다. 

2. ADS124S08 주요 사양

2.1 주요 특징

ADS124S08은 고정밀, 저전력 데이터 수집 시스템에 최적화된 24비트 델타-시그마 ADC입니다. 주요 특징은 다음과 같습니다.

ADS124S08 function diagram

  • 해상도: 24비트, 높은 정밀도의 아날로그-디지털 변환 제공
  • 샘플링 속도: 2.5 SPS에서 4000 SPS까지 가변 설정 가능
  • 입력 채널:
    • 단일 종단(single-ended): 최대 12채널
    • 차동(differential): 최대 6채널
  • PGA(Programmable Gain Amplifier): 이득 1, 2, 4, 8, 16, 32, 64, 128 설정 가능
  • 참조 전압: 내부 2.5V 참조 전압 또는 외부 참조 전압 선택 가능
  • 전류 소스(IDAC): 2개의 프로그래머블 전류 출력(10µA ~ 2000µA)
  • 인터페이스: SPI(Serial Peripheral Interface) 통신 프로토콜
  • 추가 기능:
    • 내부 온도 센서
    • 전원 및 참조 전압 모니터링
    • CRC(Cyclic Redundancy Check) 및 타임아웃 기능
    • GPIO: 최대 4개 지원
  • 전력 소비: 저전력 모드에서 2.3mA (typ), 절전 모드 지원
  • 패키지: 5mm × 5mm QFN 패키지

2.2 레지스터 구조

ADS124S08은 18개의 8비트 레지스터로 구성되며, 각 레지스터는 특정 기능을 설정합니다. 주요 레지스터는 다음과 같습니다:

  • ID (0x00): 디바이스 ID 및 리비전 정보
  • STATUS (0x01): 전원 온 리셋(POR), 데이터 준비(RDY), 레일 감지 플래그
  • INPMUX (0x02): 입력 채널 선택(MUXP/MUXN)
  • PGA (0x03): 이득 설정 및 변환 지연
  • DATARATE (0x04): 샘플링 속도 및 필터 설정
  • REF (0x05): 참조 전압 소스 및 버퍼 설정
  • IDACMAG/IDACMUX (0x06, 0x07): IDAC 전류 크기 및 출력 채널
  • VBIAS (0x08): 바이어스 전압 설정
  • SYS (0x09): 시스템 모니터링, CRC, 타임아웃 설정
  • OFCAL/FSCAL (0x0A~0x0F): 오프셋 및 풀스케일 교정
  • GPIODAT/GPIOCON (0x10, 0x11): GPIO 데이터 및 설정

2.3 명령어 (Opcode)

ADS124S08은 SPI를 통해 다음과 같은 명령어를 지원합니다:

  • NOP (0x00): 아무 작업 없음
  • WAKE (0x02): 절전 모드 해제
  • SLEEP (0x04): 절전 모드 진입
  • RESET (0x06): 디바이스 리셋
  • START (0x08): 변환 시작
  • STOP (0x0A): 변환 중지
  • RDATA (0x12): 변환 데이터 읽기
  • REGRD (0x20): 레지스터 읽기
  • REGWR (0x40): 레지스터 쓰기
  • SFOCAL/SYOCAL/SYGCAL (0x19, 0x16, 0x17): 교정 명령

2.4 애플리케이션

  • 산업용 제어: 온도, 압력, 유량 센서 데이터 수집
  • 의료 기기: ECG, 생체 신호 측정
  • 계측: 고정밀 전압/전류 측정
  • 환경 모니터링: 센서 네트워크 데이터 수집

3. STM32 디바이스 드라이버 구현 내용

3.1 구현 개요

ADS124S08 디바이스 드라이버는 STM32L432KC를 포함한 STM32 시리즈를 대상으로 하며, STM32 HAL 라이브러리를 사용하여 SPI 통신과 GPIO 제어를 구현하였습니다. 드라이버는 ADS124S08의 모든 주요 기능을 지원하며, 다음과 같은 요구사항을 충족합니다:

  • SPI 통신: 레지스터 읽기/쓰기, 데이터 읽기, 명령어 전송
  • GPIO 제어: 칩 선택(nCS), 시작(START), 리셋(RESET), 클럭 활성화(CKEN), 데이터 준비(DRDY) 핀 제어
  • 인터럽트 지원: DRDY 핀의 하강 에지 인터럽트를 통해 데이터 준비 감지
  • 레지스터 상태 관리: 전역 배열을 통해 레지스터 상태 추적
  • 모듈화: 재사용 가능하고 명확한 함수 구조

3.2 구현 세부 사항

3.2.1 헤더 파일 (ADS124S08.h)

  •   레지스터 및 명령어 정의: ADS124S08의 모든 레지스터 주소, 명령어, 플래그를 상수로 정의
  •   GPIO 핀 정의: STM32L432KC 기준으로 NCS, START, RESET, CKEN, FLASH, DRDY 핀을 지정
    •    예: NCS_PORT = GPIOA, NCS_PIN = GPIO_PIN_4
  •   데이터 길이 정의: 변환 데이터(3바이트), 상태(1바이트), CRC(1바이트)
  •   함수 프로토타입: 초기화, 레지스터 읽기/쓰기, 데이터 읽기, 명령어 전송, GPIO 제어 함수 선언
  •   전역 변수: registers 배열(레지스터 상태 저장), converting 및 fStart 플래그

3.2.2 소스 파일 (ADS124S08.c)

드라이버의 주요 함수는 다음과 같습니다:

  1. InitDevice: GPIO 포트 초기화, DRDY 인터럽트 설정, 기본 레지스터 값 설정, 디바이스 리셋
  2. clearChipSelect / setChipSelect: 칩 선택(nCS) 핀 제어
  3. regRead / readRegs: 단일/다중 레지스터 읽기
  4. regWrite / writeRegs: 단일/다중 레지스터 쓰기
  5. sendCommand: 단일 명령어 전송
  6. reStart: 변환 재시작(SYNC)
  7. assertStart / deassertStart: START 핀 제어
  8. assertClock / deassertClock: CKEN 핀 제어
  9. dataRead: 변환 데이터 읽기(상태 및 CRC 포함 가능)

3.2.3 인터럽트 처리

  • DRDY 인터럽트: DRDY 핀의 하강 에지를 감지하여 데이터 준비를 알림
  • 사용자는 HAL_GPIO_EXTI_Callback를 구현하여 DRDY 신호 발생 시 dataRead를 호출
  • 예시: 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{ 
	if (GPIO_Pin == DRDY_PIN) 
	{ 
    	uint32_t status, data, crc; 
   		dataRead(&hspi1, &status, &data, &crc); 
     }
}

3.2.4 주요 구현 특징

  • STM32 HAL 기반: 안정성과 호환성 보장
  • 모듈화: 단일 책임 원칙 준수
  • 레지스터 상태 관리: registers 배열로 동기화 유지
  • 타이밍 관리: 칩 선택 및 리셋 대기 시간 포함
  • 확장성: 애플리케이션별 레지스터 설정 용이

3.3 사용 방법

  1. SPI 설정: STM32CubeMX 또는 코드로 SPI1 설정 (8비트, CPOL=0, CPHA=0, 최대 4MHz)
  2. GPIO 설정: ADS124S08.h의 핀 정의를 하드웨어에 맞게 수정
  3. 초기화: InitDevice(&hspi1) 호출
  4. 데이터 읽기: DRDY 인터럽트 또는 폴링 방식으로 dataRead 호출
  5. 레지스터 설정: regWrite/writeRegs로 설정 조정
  6. 인터럽트 처리: HAL_GPIO_EXTI_Callback에 DRDY 로직 추가

3.4 제한사항 및 주의사항

  • 하드웨어 연결: SPI 및 GPIO 핀 올바른 연결 필수
  • SPI 클럭: 최대 4MHz 준수
  • 인터럽트 구현: DRDY 인터럽트 사용자 구현 필요
  • 타이밍: 애플리케이션에 따라 대기 시간 조정 가능
  • 레지스터 설정: 데이터 속도, 이득 등 최적화 필요

4. 구현 코드

4.1 헤더 파일 (ADS124S08.h)


/*
 * 파일명: ADS124S08.h
 * 설명: ADS124S08 24비트 델타-시그마 ADC를 STM32 마이크로컨트롤러에서 제어하기 위한 헤더 파일
 *       SPI 통신 및 GPIO를 통해 디바이스 설정, 데이터 읽기, 명령어 전송 등을 지원
 */

#ifndef ADS124S08_H_
#define ADS124S08_H_

#include "stm32l4xx_hal.h" // STM32L4 HAL 라이브러리 포함
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

/* 레지스터 수 정의 */
#define NUM_REGISTERS 18 // ADS124S08의 총 레지스터 수 (0x00 ~ 0x11)

/* 레지스터 주소 마스크 정의 */
#define ID_ADDR_MASK        0x00 // ID 레지스터: 디바이스 정보 및 버전
#define STATUS_ADDR_MASK    0x01 // 상태 레지스터: 디바이스 상태 플래그
#define INPMUX_ADDR_MASK    0x02 // 입력 멀티플렉서: 입력 채널 선택
#define PGA_ADDR_MASK       0x03 // PGA 설정: 이득 및 변환 지연
#define DATARATE_ADDR_MASK  0x04 // 데이터 속도: 샘플링 속도 및 필터 설정
#define REF_ADDR_MASK       0x05 // 참조 전압: 참조 소스 및 버퍼 설정
#define IDACMAG_ADDR_MASK   0x06 // IDAC 전류 크기: 전류 출력 설정
#define IDACMUX_ADDR_MASK   0x07 // IDAC 출력 선택: IDAC 연결 채널
#define VBIAS_ADDR_MASK     0x08 // VBIAS 설정: 바이어스 전압 활성화
#define SYS_ADDR_MASK       0x09 // 시스템 설정: 모니터링, CRC, 타임아웃
#define OFCAL0_ADDR_MASK    0x0A // 오프셋 교정 0: 오프셋 보정 하위 바이트
#define OFCAL1_ADDR_MASK    0x0B // 오프셋 교정 1: 오프셋 보정 중간 바이트
#define OFCAL2_ADDR_MASK    0x0C // 오프셋 교정 2: 오프셋 보정 상위 바이트
#define FSCAL0_ADDR_MASK    0x0D // 풀스케일 교정 0: 이득 보정 하위 바이트
#define FSCAL1_ADDR_MASK    0x0E // 풀스케일 교정 1: 이득 보정 중간 바이트
#define FSCAL2_ADDR_MASK    0x0F // 풀스케일 교정 2: 이득 보정 상위 바이트
#define GPIODAT_ADDR_MASK   0x10 // GPIO 데이터: GPIO 입출력 데이터
#define GPIOCON_ADDR_MASK   0x11 // GPIO 설정: GPIO 모드 설정

/* 명령어 마스크 정의 */
#define NOP_OPCODE_MASK     0x00 // NOP: 아무 작업 수행 안 함
#define WAKE_OPCODE_MASK    0x02 // WAKE: 절전 모드 해제
#define SLEEP_OPCODE_MASK   0x04 // SLEEP: 절전 모드 진입
#define RESET_OPCODE_MASK   0x06 // RESET: 디바이스 리셋
#define START_OPCODE_MASK   0x08 // START: 변환 시작
#define STOP_OPCODE_MASK    0x0A // STOP: 변환 중지
#define SFOCAL_OPCODE_MASK  0x19 // SFOCAL: 시스템 풀 오프셋 교정
#define SYOCAL_OPCODE_MASK  0x16 // SYOCAL: 시스템 오프셋 교정
#define SYGCAL_OPCODE_MASK  0x17 // SYGCAL: 시스템 이득 교정
#define RDATA_OPCODE_MASK   0x12 // RDATA: 변환 데이터 읽기
#define REGRD_OPCODE_MASK   0x20 // REGRD: 레지스터 읽기
#define REGWR_OPCODE_MASK   0x40 // REGWR: 레지스터 쓰기

/* ID 레지스터 정의 */
#define ADS_ID_A            0x00 // 리비전 A
#define ADS_ID_B            0x80 // 리비전 B
#define ADS_124S08          0x00 // ADS124S08 디바이스
#define ADS_124S06          0x01 // ADS124S06 디바이스
#define ADS_114S08          0x04 // ADS114S08 디바이스
#define ADS_114S06          0x05 // ADS114S06 디바이스

/* 상태 레지스터 플래그 정의 */
#define ADS_FL_POR          0x80 // 전원 온 리셋 플래그
#define ADS_RDY             0x40 // 데이터 준비 완료 플래그
#define ADS_FL_P_RAILP      0x20 // 양극 레일 감지 플래그
#define ADS_FL_P_RAILN      0x10 // 음극 레일 감지 플래그
#define ADS_FL_N_RAILP      0x08 // 양극 입력 레일 감지 플래그
#define ADS_FL_N_RAILN      0x04 // 음극 입력 레일 감지 플래그
#define ADS_FL_REF_L1       0x02 // 참조 전압 레벨 1 플래그
#define ADS_FL_REF_L0       0x01 // 참조 전압 레벨 0 플래그

/* 입력 멀티플렉서(MUXP/MUXN) 정의 */
#define ADS_P_AIN0          0x00 // 양극 입력 AIN0
#define ADS_P_AIN1          0x10 // 양극 입력 AIN1
#define ADS_P_AIN2          0x20 // 양극 입력 AIN2
#define ADS_P_AIN3          0x30 // 양극 입력 AIN3
#define ADS_P_AIN4          0x40 // 양극 입력 AIN4
#define ADS_P_AIN5          0x50 // 양극 입력 AIN5
#define ADS_P_AIN6          0x60 // 양극 입력 AIN6
#define ADS_P_AIN7          0x70 // 양극 입력 AIN7
#define ADS_P_AIN8          0x80 // 양극 입력 AIN8
#define ADS_P_AIN9          0x90 // 양극 입력 AIN9
#define ADS_P_AIN10         0xA0 // 양극 입력 AIN10
#define ADS_P_AIN11         0xB0 // 양극 입력 AIN11
#define ADS_P_AINCOM        0xC0 // 양극 입력 AINCOM
#define ADS_N_AIN0          0x00 // 음극 입력 AIN0
#define ADS_N_AIN1          0x01 // 음극 입력 AIN1
#define ADS_N_AIN2          0x02 // 음극 입력 AIN2
#define ADS_N_AIN3          0x03 // 음극 입력 AIN3
#define ADS_N_AIN4          0x04 // 음극 입력 AIN4
#define ADS_N_AIN5          0x05 // 음극 입력 AIN5
#define ADS_N_AIN6          0x06 // 음극 입력 AIN6
#define ADS_N_AIN7          0x07 // 음극 입력 AIN7
#define ADS_N_AIN8          0x08 // 음극 입력 AIN8
#define ADS_N_AIN9          0x09 // 음극 입력 AIN9
#define ADS_N_AIN10         0x0A // 음극 입력 AIN10
#define ADS_N_AIN11         0x0B // 음극 입력 AIN11
#define ADS_N_AINCOM        0x0C // 음극 입력 AINCOM

/* PGA 레지스터 정의 */
#define ADS_DELAY_14        0x00 // 변환 지연 14 tmod
#define ADS_DELAY_25        0x20 // 변환 지연 25 tmod
#define ADS_DELAY_64        0x40 // 변환 지연 64 tmod
#define ADS_DELAY_256       0x60 // 변환 지연 256 tmod
#define ADS_DELAY_1024      0x80 // 변환 지연 1024 tmod
#define ADS_DELAY_2048      0xA0 // 변환 지연 2048 tmod
#define ADS_DELAY_4096      0xC0 // 변환 지연 4096 tmod
#define ADS_DELAY_1         0xE0 // 변환 지연 1 tmod
#define ADS_PGA_BYPASS      0x00 // PGA 비활성화
#define ADS_PGA_ENABLED     0x08 // PGA 활성화
#define ADS_GAIN_1          0x00 // 이득 1
#define ADS_GAIN_2          0x01 // 이득 2
#define ADS_GAIN_4          0x02 // 이득 4
#define ADS_GAIN_8          0x03 // 이득 8
#define ADS_GAIN_16         0x04 // 이득 16
#define ADS_GAIN_32         0x05 // 이득 32
#define ADS_GAIN_64         0x06 // 이득 64
#define ADS_GAIN_128        0x07 // 이득 128

/* 데이터 속도 레지스터 정의 */
#define ADS_GLOBALCHOP      0x80 // 글로벌 초핑 활성화
#define ADS_CLKSEL_EXT      0x40 // 외부 클럭 선택
#define ADS_CONVMODE_SS     0x20 // 단일 샷 변환 모드
#define ADS_FILTERTYPE_LL   0x10 // 저지대역 필터
#define ADS_DR_2_5          0x00 // 데이터 속도 2.5 SPS
#define ADS_DR_5            0x01 // 데이터 속도 5 SPS
#define ADS_DR_10           0x02 // 데이터 속도 10 SPS
#define ADS_DR_16           0x03 // 데이터 속도 16.6 SPS
#define ADS_DR_20           0x04 // 데이터 속도 20 SPS
#define ADS_DR_50           0x05 // 데이터 속도 50 SPS
#define ADS_DR_60           0x06 // 데이터 속도 60 SPS
#define ADS_DR_100          0x07 // 데이터 속도 100 SPS
#define ADS_DR_200          0x08 // 데이터 속도 200 SPS
#define ADS_DR_400          0x09 // 데이터 속도 400 SPS
#define ADS_DR_800          0x0A // 데이터 속도 800 SPS
#define ADS_DR_1000         0x0B // 데이터 속도 1000 SPS
#define ADS_DR_2000         0x0C // 데이터 속도 2000 SPS
#define ADS_DR_4000         0x0D // 데이터 속도 4000 SPS

/* 참조 전압 레지스터 정의 */
#define ADS_FLAG_REF_DISABLE 0x00 // 참조 전압 비활성화
#define ADS_FLAG_REF_EN_L0  0x40 // 레벨 0 참조 전압 활성화
#define ADS_FLAG_REF_EN_BOTH 0x80 // 두 참조 전압 활성화
#define ADS_FLAG_REF_EN_10M 0xC0 // 10MOhm 참조 전압 활성화
#define ADS_REFP_BYP_DISABLE 0x20 // 양극 참조 버퍼 비활성화
#define ADS_REFP_BYP_ENABLE 0x00 // 양극 참조 버퍼 활성화
#define ADS_REFN_BYP_DISABLE 0x10 // 음극 참조 버퍼 비활성화
#define ADS_REFN_BYP_ENABLE 0x00 // 음극 참조 버퍼 활성화
#define ADS_REFSEL_P0       0x00 // 참조 입력 REFP0/REFN0
#define ADS_REFSEL_P1       0x04 // 참조 입력 REFP1/REFN1
#define ADS_REFSEL_INT      0x08 // 내부 참조 전압
#define ADS_REFINT_OFF      0x00 // 내부 참조 전압 끄기
#define ADS_REFINT_ON_PDWN  0x01 // 내부 참조 전압 (절전 시 유지)
#define ADS_REFINT_ON_ALWAYS 0x02 // 내부 참조 전압 항상 켜기

/* IDAC 전류 크기 레지스터 정의 */
#define ADS_FLAG_RAIL_ENABLE 0x80 // 레일 감지 활성화
#define ADS_FLAG_RAIL_DISABLE 0x00 // 레일 감지 비활성화
#define ADS_PSW_OPEN        0x00 // 전원 스위치 열림
#define ADS_PSW_CLOSED      0x40 // 전원 스위치 닫힘
#define ADS_IDACMAG_OFF     0x00 // IDAC 비활성화
#define ADS_IDACMAG_10      0x01 // IDAC 10µA
#define ADS_IDACMAG_50      0x02 // IDAC 50µA
#define ADS_IDACMAG_100     0x03 // IDAC 100µA
#define ADS_IDACMAG_250     0x04 // IDAC 250µA
#define ADS_IDACMAG_500     0x05 // IDAC 500µA
#define ADS_IDACMAG_750     0x06 // IDAC 750µA
#define ADS_IDACMAG_1000    0x07 // IDAC 1000µA
#define ADS_IDACMAG_1500    0x08 // IDAC 1500µA
#define ADS_IDACMAG_2000    0x09 // IDAC 2000µA

/* IDAC 출력 멀티플렉서 정의 */
#define ADS_IDAC2_A0        0x00 // IDAC2 출력 AIN0
#define ADS_IDAC2_A1        0x10 // IDAC2 출력 AIN1
#define ADS_IDAC2_A2        0x20 // IDAC2 출력 AIN2
#define ADS_IDAC2_A3        0x30 // IDAC2 출력 AIN3
#define ADS_IDAC2_A4        0x40 // IDAC2 출력 AIN4
#define ADS_IDAC2_A5        0x50 // IDAC2 출력 AIN5
#define ADS_IDAC2_A6        0x60 // IDAC2 출력 AIN6
#define ADS_IDAC2_A7        0x70 // IDAC2 출력 AIN7
#define ADS_IDAC2_A8        0x80 // IDAC2 출력 AIN8
#define ADS_IDAC2_A9        0x90 // IDAC2 출력 AIN9
#define ADS_IDAC2_A10       0xA0 // IDAC2 출력 AIN10
#define ADS_IDAC2_A11       0xB0 // IDAC2 출력 AIN11
#define ADS_IDAC2_AINCOM    0xC0 // IDAC2 출력 AINCOM
#define ADS_IDAC2_OFF       0xF0 // IDAC2 비활성화
#define ADS_IDAC1_A0        0x00 // IDAC1 출력 AIN0
#define ADS_IDAC1_A1        0x01 // IDAC1 출력 AIN1
#define ADS_IDAC1_A2        0x02 // IDAC1 출력 AIN2
#define ADS_IDAC1_A3        0x03 // IDAC1 출력 AIN3
#define ADS_IDAC1_A4        0x04 // IDAC1 출력 AIN4
#define ADS_IDAC1_A5        0x05 // IDAC1 출력 AIN5
#define ADS_IDAC1_A6        0x06 // IDAC1 출력 AIN6
#define ADS_IDAC1_A7        0x07 // IDAC1 출력 AIN7
#define ADS_IDAC1_A8        0x08 // IDAC1 출력 AIN8
#define ADS_IDAC1_A9        0x09 // IDAC1 출력 AIN9
#define ADS_IDAC1_A10       0x0A // IDAC1 출력 AIN10
#define ADS_IDAC1_A11       0x0B // IDAC1 출력 AIN11
#define ADS_IDAC1_AINCOM    0x0C // IDAC1 출력 AINCOM
#define ADS_IDAC1_OFF       0x0F // IDAC1 비활성화

/* VBIAS 레지스터 정의 */
#define ADS_VBIAS_LVL_DIV2  0x00 // VBIAS 레벨: VREF/2
#define ADS_VBIAS_LVL_DIV12 0x80 // VBIAS 레벨: VREF/12
#define ADS_VB_AINC         0x40 // VBIAS AINCOM 활성화
#define ADS_VB_AIN5         0x20 // VBIAS AIN5 활성화
#define ADS_VB_AIN4         0x10 // VBIAS AIN4 활성화
#define ADS_VB_AIN3         0x08 // VBIAS AIN3 활성화
#define ADS_VB_AIN2         0x04 // VBIAS AIN2 활성화
#define ADS_VB_AIN1         0x02 // VBIAS AIN1 활성화
#define ADS_VB_AIN0         0x01 // VBIAS AIN0 활성화

/* 시스템 레지스터 정의 */
#define ADS_SYS_MON_OFF     0x00 // 시스템 모니터링 비활성화
#define ADS_SYS_MON_SHORT   0x20 // 단락 회로 모니터링
#define ADS_SYS_MON_TEMP    0x40 // 온도 모니터링
#define ADS_SYS_MON_ADIV4   0x60 // 아날로그 전원 ÷4 모니터링
#define ADS_SYS_MON_DDIV4   0x80 // 디지털 전원 ÷4 모니터링
#define ADS_SYS_MON_BCS_2   0xA0 // BCS ÷2 모니터링
#define ADS_SYS_MON_BCS_1   0xC0 // BCS ÷1 모니터링
#define ADS_SYS_MON_BCS_10  0xE0 // BCS ÷10 모니터링
#define ADS_CALSAMPLE_1     0x00 // 캘리브레이션 샘플 1
#define ADS_CALSAMPLE_4     0x08 // 캴리브레이션 샘플 4
#define ADS_CALSAMPLE_8     0x10 // 캴리브레이션 샴플 8
#define ADS_CALSAMPLE_16    0x18 // 캴리브레이션 샴플 16
#define ADS_TIMEOUT_DISABLE 0x00 // 타임아웃 비활성화
#define ADS_TIMEOUT_ENABLE  0x04 // 타임아웃 활성화
#define ADS_CRC_DISABLE     0x00 // CRC 비활성화
#define ADS_CRC_ENABLE      0x02 // CRC 활성화
#define ADS_SENDSTATUS_DISABLE 0x00 // 상태 전송 비활성화
#define ADS_SENDSTATUS_ENABLE 0x01 // 상태 전송 활성화

/* GPIO 방향 정의 (0: 출력, 1: 입력) */
#define ADS_GPIO0_DIR_INPUT 0x10 // GPIO0 입력
#define ADS_GPIO1_DIR_INPUT 0x20 // GPIO1 입력
#define ADS_GPIO2_DIR_INPUT 0x40 // GPIO2 입력
#define ADS_GPIO3_DIR_INPUT 0x80 // GPIO3 입력

/* GPIO 설정 정의 (0: 아날로그 입력, 1: GPIO) */
#define ADS_GPIO0_CON_GPIO  0x01 // GPIO0을 GPIO로 설정
#define ADS_GPIO1_CON_GPIO  0x02 // GPIO1을 GPIO로 설정
#define ADS_GPIO2_CON_GPIO  0x04 // GPIO2을 GPIO로 설정
#define ADS_GPIO3_CON_GPIO  0x08 // GPIO3을 GPIO로 설정

/* 외부 핀 정의 (STM32L432KC 기준) */
#define NCS_PORT            GPIOA // 칩 선택 포트
#define NCS_PIN             GPIO_PIN_4 // 칩 선택 핀 (SPI CS)
#define START_PORT          GPIOA // 시작 포트
#define START_PIN           GPIO_PIN_1 // 시작 핀
#define RESET_PORT          GPIOB // 리셋 포트
#define RESET_PIN           GPIO_PIN_0 // 리셋 핀
#define CKEN_PORT           GPIOB // 클럭 활성화 포트
#define CKEN_PIN            GPIO_PIN_1 // 클럭 활성화 핀
#define FLASH_PORT          GPIOB // 플래시 포트
#define FLASH_PIN           GPIO_PIN_2 // 플래시 핀
#define DRDY_PORT           GPIOA // 데이터 준비 포트
#define DRDY_PIN            GPIO_PIN_8 // 데이터 준비 핀
#define SPI_BASE            SPI1 // SPI 모듈

/* 데이터 길이 정의 */
#define DATA_LENGTH         3 // 변환 데이터 길이 (24비트 = 3바이트)
#define STATUS_LENGTH       1 // 상태 데이터 길이
#define CRC_LENGTH          1 // CRC 데이터 길이

/* 칩 선택 대기 시간 */
#define CHIP_SELECT_WAIT_TIME 0 // 칩 선택 활성화 후 대기 시간 (µs)

/* 데이터 모드 정의 */
#define DATA_MODE_NORMAL    0x00 // 일반 데이터 모드
#define DATA_MODE_STATUS    0x01 // 상태 데이터 포함 모드
#define DATA_MODE_CRC       0x02 // CRC 데이터 포함 모드

/* 전역 변수 선언 */
extern uint8_t registers[NUM_REGISTERS]; // 레지스터 값을 저장하는 배열
extern bool converting; // 변환 진행 상태 플래그
extern bool fStart; // 시작 핀 상태 플래그

/* 함수 프로토타입 */
int8_t InitDevice(SPI_HandleTypeDef *hspi); // 디바이스 초기화
char regRead(SPI_HandleTypeDef *hspi, unsigned int regnum); // 단일 레지스터 읽기
void readRegs(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned int count, uint8_t *data); // 다중 레지스터 읽기
void regWrite(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned char data); // 단일 레지스터 쓰기
void writeRegs(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned int howmuch, unsigned char *data); // 다중 레지스터 쓰기
void reStart(SPI_HandleTypeDef *hspi); // 변환 재시작 (SYNC)
void sendCommand(SPI_HandleTypeDef *hspi, uint8_t op_code); // 명령어 전송
int dataRead(SPI_HandleTypeDef *hspi, uint32_t *dStatus, uint32_t *dData, uint32_t *dCRC); // 변환 데이터 읽기
void clearChipSelect(void); // 칩 선택 핀 비활성화 (Low)
void setChipSelect(void); // 칩 선택 핀 활성화 (High)
void assertStart(void); // 시작 핀 활성화 (High)
void deassertStart(void); // 시작 핀 비활성화 (Low)
void assertClock(void); // 클럭 활성화 핀 설정 (High)
void deassertClock(void); // 클럭 활성화 핀 비설정 (Low)

#endif /* ADS124S08_H_ */

4.2 소스 파일 (ADS124S08.c)


/*
 * 파일명: ADS124S08.c
 * 설명: ADS124S08 24비트 델타-시그마 ADC를 STM32 마이크로컨트롤러에서 제어하기 위한 드라이버 구현
 *       SPI 통신 및 GPIO를 통해 레지스터 설정, 데이터 읽기, 명령어 전송 등을 수행
 */

#include "stm32l4xx_hal.h"
#include "ADS124S08.h"

/* 전역 변수 정의 */
uint8_t registers[NUM_REGISTERS]; // 레지스터 값을 저장하는 배열
bool converting = false; // 변환 진행 상태 플래그
bool fStart = false; // 시작 핀 상태 플래그

/*
 * 함수: clearChipSelect
 * 설명: 칩 선택(nCS) 핀을 저전위(Low)로 설정하여 SPI 통신 시작
 */
void clearChipSelect(void) {
    HAL_GPIO_WritePin(NCS_PORT, NCS_PIN, GPIO_PIN_RESET);
    HAL_Delay(CHIP_SELECT_WAIT_TIME / 1000); // 칩 선택 대기 시간 (ms 단위)
}

/*
 * 함수: setChipSelect
 * 설명: 칩 선택(nCS) 핀을 고전위(High)로 설정하여 SPI 통신 종료
 */
void setChipSelect(void) {
    HAL_GPIO_WritePin(NCS_PORT, NCS_PIN, GPIO_PIN_SET);
}

/*
 * 함수: InitDevice
 * 설명: ADS124S08 디바이스를 초기화하고 기본 레지스터 값을 설정
 * 매개변수: hspi - SPI 핸들러 포인터
 * 반환값: 초기화 성공 시 1, 실패 시 -1
 */
int8_t InitDevice(SPI_HandleTypeDef *hspi) {
    // GPIO 포트 클럭 활성화
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    // GPIO 초기화 구조체
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // START 핀 설정 (출력)
    GPIO_InitStruct.Pin = START_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(START_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(START_PORT, START_PIN, GPIO_PIN_RESET); // 초기 상태: Low

    // RESET 핀 설정 (출력)
    GPIO_InitStruct.Pin = RESET_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(RESET_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(RESET_PORT, RESET_PIN, GPIO_PIN_SET); // 초기 상태: High (리셋 비활성)

    // CKEN 핀 설정 (출력)
    GPIO_InitStruct.Pin = CKEN_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(CKEN_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(CKEN_PORT, CKEN_PIN, GPIO_PIN_RESET); // 초기 상태: Low

    // FLASH 핀 설정 (출력)
    GPIO_InitStruct.Pin = FLASH_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(FLASH_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(FLASH_PORT, FLASH_PIN, GPIO_PIN_RESET); // 초기 상태: Low

    // DRDY 핀 설정 (인터럽트 입력)
    GPIO_InitStruct.Pin = DRDY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 하강 에지 인터럽트
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(DRDY_PORT, &GPIO_InitStruct);

    // DRDY 인터럽트 활성화
    HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

    // 기본 레지스터 값 설정
    registers[ID_ADDR_MASK]      = 0x08; // ID 레지스터 기본값
    registers[STATUS_ADDR_MASK]  = 0x80; // 상태 레지스터 (POR 플래그)
    registers[INPMUX_ADDR_MASK]  = 0x01; // 입력: AIN0(양극), AIN1(음극)
    registers[PGA_ADDR_MASK]     = 0x00; // PGA 비활성화, 이득 1
    registers[DATARATE_ADDR_MASK]= 0x14; // 데이터 속도 20 SPS, 저지대역 필터
    registers[REF_ADDR_MASK]     = 0x10; // 내부 참조 전압, REFN 버퍼 비활성화
    registers[IDACMAG_ADDR_MASK] = 0x00; // IDAC 비활성화
    registers[IDACMUX_ADDR_MASK] = 0xFF; // IDAC 출력 비활성화
    registers[VBIAS_ADDR_MASK]   = 0x00; // VBIAS 비활성화
    registers[SYS_ADDR_MASK]     = 0x10; // 시스템 모니터링 비활성화, 샘플 8
    registers[OFCAL0_ADDR_MASK]  = 0x00; // 오프셋 교정 0
    registers[OFCAL1_ADDR_MASK]  = 0x00; // 오프셋 교정 1
    registers[OFCAL2_ADDR_MASK]  = 0x00; // 오프셋 교정 2
    registers[FSCAL0_ADDR_MASK]  = 0x00; // 풀스케일 교정 0
    registers[FSCAL1_ADDR_MASK]  = 0x00; // 풀스케일 교정 1
    registers[FSCAL2_ADDR_MASK]  = 0x40; // 풀스케일 교정 2
    registers[GPIODAT_ADDR_MASK] = 0x00; // GPIO 데이터 초기화
    registers[GPIOCON_ADDR_MASK] = 0x00; // GPIO 설정 초기화

    // 시작 플래그 초기화 및 칩 선택 비활성화
    fStart = false;
    setChipSelect();
    deassertStart();

    // 디바이스 리셋 명령 전송
    sendCommand(hspi, RESET_OPCODE_MASK);
    HAL_Delay(1); // 리셋 후 안정화 대기

    return 1; // 초기화 성공
}

/*
 * 함수: regRead
 * 설명: 지정된 레지스터에서 단일 바이트 데이터를 읽음
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - regnum: 읽을 레지스터 주소 (0x00 ~ 0x11)
 * 반환값: 읽은 레지스터 값
 */
char regRead(SPI_HandleTypeDef *hspi, unsigned int regnum) {
    uint8_t txData[3] = {0};
    uint8_t rxData[3] = {0};

    // 명령어 구성: REGRD + 레지스터 주소
    txData[0] = REGRD_OPCODE_MASK | (regnum & 0x1F); // 읽기 명령 + 주소
    txData[1] = 0x00; // 읽을 바이트 수 (1바이트)
    txData[2] = 0x00; // 더미 데이터

    clearChipSelect(); // 칩 선택 활성화
    HAL_SPI_TransmitReceive(hspi, txData, rxData, 3, HAL_MAX_DELAY); // SPI 송수신
    setChipSelect(); // 칩 선택 비활성화

    // 유효한 레지스터일 경우 배열에 저장
    if (regnum < NUM_REGISTERS) {
        registers[regnum] = rxData[2];
    }

    return (char)rxData[2]; // 읽은 데이터 반환
}

/*
 * 함수: readRegs
 * 설명: 지정된 레지스터부터 연속된 여러 레지스터 데이터를 읽음
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - regnum: 시작 레지스터 주소
 *   - count: 읽을 레지스터 수
 *   - data: 읽은 데이터를 저장할 배열 포인터
 */
void readRegs(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned int count, uint8_t *data) {
    uint8_t txData[2] = {0};
    uint8_t rxData[2] = {0};
    uint8_t dummy = 0x00;

    // 명령어 구성
    txData[0] = REGRD_OPCODE_MASK | (regnum & 0x1F); // 읽기 명령 + 주소
    txData[1] = count - 1; // 읽을 바이트 수 (0부터 시작)

    clearChipSelect(); // 칩 선택 활성화
    HAL_SPI_Transmit(hspi, txData, 2, HAL_MAX_DELAY); // 명령 전송

    // 연속 데이터 읽기
    for (unsigned int i = 0; i < count; i++) {
        HAL_SPI_TransmitReceive(hspi, &dummy, &data[i], 1, HAL_MAX_DELAY);
        if (regnum + i < NUM_REGISTERS) {
            registers[regnum + i] = data[i]; // 레지스터 배열 업데이트
        }
    }
    setChipSelect(); // 칩 선택 비활성화
}

/*
 * 함수: regWrite
 * 설명: 지정된 레지스터에 단일 바이트 데이터를 씀
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - regnum: 쓸 레지스터 주소
 *   - data: 쓸 데이터
 */
void regWrite(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned char data) {
    uint8_t txData[3] = {0};

    // 명령어 구성
    txData[0] = REGWR_OPCODE_MASK | (regnum & 0x1F); // 쓰기 명령 + 주소
    txData[1] = 0x00; // 쓸 바이트 수 (1바이트)
    txData[2] = data; // 쓸 데이터

    clearChipSelect(); // 칩 선택 활성화
    HAL_SPI_Transmit(hspi, txData, 3, HAL_MAX_DELAY); // 데이터 전송
    setChipSelect(); // 칩 선택 비활성화

    // 유효한 레지스터일 경우 배열 업데이트
    if (regnum < NUM_REGISTERS) {
        registers[regnum] = data;
    }
}

/*
 * 함수: writeRegs
 * 설명: 지정된 레지스터부터 연속된 여러 레지스터에 데이터를 씀
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - regnum: 시작 레지스터 주소
 *   - howmuch: 쓸 레지스터 수
 *   - data: 쓸 데이터 배열 포인터
 */
void writeRegs(SPI_HandleTypeDef *hspi, unsigned int regnum, unsigned int howmuch, unsigned char *data) {
    uint8_t txData[2] = {0};

    // 명령어 구성
    txData[0] = REGWR_OPCODE_MASK | (regnum & 0x1F); // 쓰기 명령 + 주소
    txData[1] = howmuch - 1; // 쓸 바이트 수

    clearChipSelect(); // 칩 선택 활성화
    HAL_SPI_Transmit(hspi, txData, 2, HAL_MAX_DELAY); // 명령 전송

    // 연속 데이터 쓰기
    for (unsigned int i = 0; i < howmuch; i++) {
        HAL_SPI_Transmit(hspi, &data[i], 1, HAL_MAX_DELAY);
        if (regnum + i < NUM_REGISTERS) {
            registers[regnum + i] = data[i]; // 레지스터 배열 업데이트
        }
    }
    setChipSelect(); // 칩 선택 비활성화
}

/*
 * 함수: sendCommand
 * 설명: ADS124S08에 단일 명령어를 전송
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - op_code: 전송할 명령어 코드
 */
void sendCommand(SPI_HandleTypeDef *hspi, uint8_t op_code) {
    clearChipSelect(); // 칩 선택 활성화
    HAL_SPI_Transmit(hspi, &op_code, 1, HAL_MAX_DELAY); // 명령 전송
    setChipSelect(); // 칩 선택 비활성화
}

/*
 * 함수: reStart
 * 설명: 변환을 재시작하기 위해 STOP 및 START 명령을 순차적으로 전송 (SYNC)
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 */
void reStart(SPI_HandleTypeDef *hspi) {
    sendCommand(hspi, STOP_OPCODE_MASK); // 변환 중지
    HAL_Delay(1); // 안정화 대기
    sendCommand(hspi, START_OPCODE_MASK); // 변환 시작
}

/*
 * 함수: assertStart
 * 설명: START 핀을 고전위(High)로 설정하여 변환 시작 (하드웨어 제어)
 */
void assertStart(void) {
    fStart = true;
    HAL_GPIO_WritePin(START_PORT, START_PIN, GPIO_PIN_SET);
}

/*
 * 함수: deassertStart
 * 설명: START 핀을 저전위(Low)로 설정하여 변환 중지 (하드웨어 제어)
 */
void deassertStart(void) {
    fStart = false;
    HAL_GPIO_WritePin(START_PORT, START_PIN, GPIO_PIN_RESET);
}

/*
 * 함수: assertClock
 * 설명: 외부 클럭 활성화 핀(CKEN)을 고전위(High)로 설정
 */
void assertClock(void) {
    HAL_GPIO_WritePin(CKEN_PORT, CKEN_PIN, GPIO_PIN_SET);
}

/*
 * 함수: deassertClock
 * 설명: 외부 클럭 활성화 핀(CKEN)을 저전위(Low)로 설정
 */
void deassertClock(void) {
    HAL_GPIO_WritePin(CKEN_PORT, CKEN_PIN, GPIO_PIN_RESET);
}

/*
 * 함수: dataRead
 * 설명: ADS124S08에서 최신 변환 데이터를 읽음
 * 매개변수:
 *   - hspi: SPI 핸들러 포인터
 *   - dStatus: 상태 데이터를 저장할 포인터
 *   - dData: 변환 데이터를 저장할 포인터
 *   - dCRC: CRC 데이터를 저장할 포인터
 * 반환값: 24비트 변환 데이터
 */
int dataRead(SPI_HandleTypeDef *hspi, uint32_t *dStatus, uint32_t *dData, uint32_t *dCRC) {
    uint8_t txData = 0x00; // 더미 데이터
    uint8_t rxData = 0x00;
    int iData = 0;

    clearChipSelect(); // 칩 선택 활성화

    // 상태 데이터 읽기 (SYS 레지스터의 SENDSTAT 설정 확인)
    if ((registers[SYS_ADDR_MASK] & ADS_SENDSTATUS_ENABLE) == DATA_MODE_STATUS) {
        HAL_SPI_TransmitReceive(hspi, &txData, &rxData, 1, HAL_MAX_DELAY);
        *dStatus = rxData;
    }

    // 변환 데이터 읽기 (24비트 = 3바이트)
    HAL_SPI_TransmitReceive(hspi, &txData, &rxData, 1, HAL_MAX_DELAY);
    iData = rxData;
    iData = (iData << 8); // 상위 바이트 이동
    HAL_SPI_TransmitReceive(hspi, &txData, &rxData, 1, HAL_MAX_DELAY);
    iData |= rxData;
    iData = (iData << 8); // 중간 바이트 이동
    HAL_SPI_TransmitReceive(hspi, &txData, &rxData, 1, HAL_MAX_DELAY);
    iData |= rxData; // 하위 바이트

    // CRC 데이터 읽기 (SYS 레지스터의 CRC 설정 확인)
    if ((registers[SYS_ADDR_MASK] & ADS_CRC_ENABLE) == DATA_MODE_CRC) {
        HAL_SPI_TransmitReceive(hspi, &txData, &rxData, 1, HAL_MAX_DELAY);
        *dCRC = rxData;
    }

    setChipSelect(); // 칩 선택 비활성화
    *dData = iData; // 변환 데이터 저장
    return iData; // 변환 데이터 반환
}

4.3 STM32L432KC 예제 코드 (main.c)

아래는 STM32L432KC에서 ADS124S08 드라이버를 사용해 AIN0과 AIN1 채널의 차동 입력을 읽는 예제 코드입니다. 시스템 클럭은 80 MHz로 설정되며, 모든 주요 라인에 상세한 한글 주석이 추가되었습니다.


/*
 * 파일명: main.c
 * 설명: STM32L432KC에서 ADS124S08 드라이버를 사용해 AIN0-AIN1 차동 입력을 읽는 예제
 *       시스템 클럭 80 MHz, SPI 통신 및 DRDY 인터럽트를 통해 데이터를 읽고,
 *       UART(USART2)를 통해 결과를 출력
 */

#include "stm32l4xx_hal.h" // STM32L4 HAL 라이브러리 포함
#include "ADS124S08.h"     // ADS124S08 드라이버 헤더 파일 포함
#include <stdio.h>         // 표준 입출력 함수 사용 (snprintf 등)
#include <string.h>        // 문자열 처리 함수 사용 (strlen 등)

/* 전역 변수 선언 */
SPI_HandleTypeDef hspi1;         // SPI1 핸들러: ADS124S08과의 통신을 위해 사용
UART_HandleTypeDef huart2;       // USART2 핸들러: 디버깅 출력을 위해 사용
char uart_buf[50];               // UART 전송 버퍼: 출력 메시지 저장
int uart_buf_len;                // UART 전송 버퍼 길이: 전송할 데이터 크기

/* 함수 프로토타입 선언 */
void SystemClock_Config(void);    // 시스템 클럭 설정 함수
static void MX_GPIO_Init(void);   // GPIO 초기화 함수
static void MX_SPI1_Init(void);   // SPI1 초기화 함수
static void MX_USART2_UART_Init(void); // USART2 초기화 함수

/**
  * @brief  시스템 클럭 설정
  * @details STM32L432KC의 시스템 클럭을 80 MHz로 설정
  *          HSI 오실레이터와 PLL을 사용하여 클럭 구성
  */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // 오실레이터 설정 구조체 초기화
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 클럭 설정 구조체 초기화

    // HSI 오실레이터 활성화 (16 MHz)
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; // HSI 오실레이터 선택
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;                   // HSI 활성화
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; // HSI 기본 캘리브레이션 값
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;               // PLL 활성화
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;       // PLL 소스로 HSI 선택
    RCC_OscInitStruct.PLL.PLLM = 1;                            // PLL 입력 분주: 16 MHz / 1 = 16 MHz
    RCC_OscInitStruct.PLL.PLLN = 10;                           // PLL 배수: 16 MHz * 10 = 160 MHz
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;                // PLLP 분주 (사용 안 함)
    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;                // PLLQ 분주 (사용 안 함)
    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;                // PLLR 분주: 160 MHz / 2 = 80 MHz
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {     // 오실레이터 설정 적용
        while (1); // 설정 실패 시 무한 루프
    }

    // 시스템 클럭 설정
    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;        // AHB 클럭: 80 MHz
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;         // APB1 클럭: 80 MHz
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;         // APB2 클럭: 80 MHz
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { // 클럭 설정 적용, 플래시 레이턴시 4
        while (1); // 설정 실패 시 무한 루프
    }

    // 시스템 타이머 클럭 활성화
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); // 1ms 단위로 시스템 틱 설정
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); // 시스템 틱 클럭 소스 설정
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); // 시스템 틱 인터럽트 우선순위 설정
}

/**
  * @brief  SPI1 초기화
  * @details SPI1을 마스터 모드로 설정, ADS124S08과 통신을 위해 250 kHz 클럭 사용
  */
static void MX_SPI1_Init(void) {
    hspi1.Instance = SPI1;                             // SPI1 모듈 선택
    hspi1.Init.Mode = SPI_MODE_MASTER;                 // SPI 마스터 모드
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;       // 양방향 통신 (MOSI, MISO)
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;           // 8비트 데이터 전송
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;         // 클럭 극성: Low (CPOL=0)
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;             // 클럭 페이즈: 1 Edge (CPHA=0)
    hspi1.Init.NSS = SPI_NSS_SOFT;                     // 소프트웨어로 NSS(칩 선택) 제어
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; // 80 MHz / 256 = 약 312.5 kHz
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;            // MSB 먼저 전송
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;            // TI 모드 비활성화
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // CRC 비활성화
    hspi1.Init.CRCPolynomial = 7;                      // CRC 폴리노미얼 (사용 안 함)
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {              // SPI 초기화
        while (1); // 초기화 실패 시 무한 루프
    }
}

/**
  * @brief  USART2 초기화
  * @details 디버깅 출력을 위해 USART2를 115200 baud로 설정
  */
static void MX_USART2_UART_Init(void) {
    huart2.Instance = USART2;                          // USART2 모듈 선택
    huart2.Init.BaudRate = 115200;                    // 전송 속도 115200 baud
    huart2.Init.WordLength = UART_WORDLENGTH_8B;       // 8비트 데이터 길이
    huart2.Init.StopBits = UART_STOPBITS_1;           // 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;  // 16배 오버샘플링
    huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; // 1비트 샘플링 비활성화
    huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1; // UART 클럭 분주 1
    if (HAL_UART_Init(&huart2) != HAL_OK) {           // UART 초기화
        while (1); // 초기화 실패 시 무한 루프
    }
}

/**
  * @brief  GPIO 초기화
  * @details SPI1 및 USART2 핀을 설정, ADS124S08 관련 GPIO 핀은 ADS124S08.c에서 설정
  */
static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0}; // GPIO 설정 구조체 초기화

    // GPIO 포트 클럭 활성화
    __HAL_RCC_GPIOA_CLK_ENABLE(); // GPIOA 클럭 활성화 (SPI1, UART2, ADS124S08 핀)
    __HAL_RCC_GPIOB_CLK_ENABLE(); // GPIOB 클럭 활성화 (ADS124S08 핀)

    // SPI1 핀 설정 (PA5: SCK, PA6: MISO, PA7: MOSI)
    GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // SPI1 핀 선택
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                    // 대체 기능 푸시-풀 모드
    GPIO_InitStruct.Pull = GPIO_NOPULL;                        // 풀업/풀다운 비활성화
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;         // 고속 동작
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;                 // SPI1 대체 기능 매핑
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                   // GPIOA 핀 초기화

    // UART2 핀 설정 (PA2: TX, PA3: RX)
    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;            // UART2 핀 선택
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                   // 대체 기능 푸시-풀 모드
    GPIO_InitStruct.Pull = GPIO_NOPULL;                       // 풀업/풀다운 비활성화
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;        // 고속 동작
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;              // USART2 대체 기능 매핑
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                   // GPIOA 핀 초기화
}

/**
  * @brief  DRDY 인터럽트 콜백
  * @details ADS124S08의 DRDY 핀 하강 에지 감지 시 호출
  *          변환 데이터를 읽고 전압으로 변환하여 UART로 출력
  * @param GPIO_Pin 인터럽트가 발생한 GPIO 핀
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == DRDY_PIN) { // DRDY 핀에서 인터럽트 발생 확인
        uint32_t status, data, crc; // 상태, 데이터, CRC 저장 변수
        int adc_value = dataRead(&hspi1, &status, &data, &crc); // ADS124S08에서 데이터 읽기

        // ADC 값을 전압으로 변환 (참조 전압 2.5V, 이득 1)
        // 24비트 ADC 값(부호 포함)을 풀스케일(2^23)로 나누고 참조 전압 곱
        float voltage = ((float)adc_value / 0x7FFFFF) * 2.5;

        // UART로 ADC 값과 전압 출력
        uart_buf_len = snprintf(uart_buf, sizeof(uart_buf), "ADC Value: %d, Voltage: %.6f V\r\n", adc_value, voltage);
        HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, HAL_MAX_DELAY); // UART 전송
    }
}

/**
  * @brief  메인 함수
  * @details 시스템 초기화, ADS124S08 설정, 변환 시작 후 DRDY 인터럽트를 통해 데이터 처리
  */
int main(void) {
    // STM32 HAL 라이브러리 초기화
    HAL_Init(); // HAL 초기화, 기본 시스템 설정 수행

    // 시스템 클럭 설정 (80 MHz)
    SystemClock_Config(); // 시스템 클럭을 80 MHz로 설정

    // GPIO 및 주변 장치 초기화
    MX_GPIO_Init();       // SPI1 및 UART2 GPIO 핀 초기화
    MX_SPI1_Init();       // SPI1 초기화 (ADS124S08 통신용)
    MX_USART2_UART_Init(); // USART2 초기화 (디버깅 출력용)

    // ADS124S08 초기화
    if (InitDevice(&hspi1) != 1) { // ADS124S08 드라이버 초기화
        // 초기화 실패 시 오류 메시지 출력
        uart_buf_len = snprintf(uart_buf, sizeof(uart_buf), "ADS124S08 Init Failed\r\n");
        HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, HAL_MAX_DELAY);
        while (1); // 무한 루프
    }

    // ADS124S08 설정: AIN0-AIN1 차동 입력, 20 SPS, 이득 1, 내부 참조 전압
    regWrite(&hspi1, INPMUX_ADDR_MASK, ADS_P_AIN0 | ADS_N_AIN1); // 입력 채널 설정: AIN0(양극), AIN1(음극)
    regWrite(&hspi1, PGA_ADDR_MASK, ADS_PGA_BYPASS | ADS_GAIN_1); // PGA 비활성화, 이득 1 설정
    regWrite(&hspi1, DATARATE_ADDR_MASK, ADS_FILTERTYPE_LL | ADS_DR_20); // 20 SPS, 저지대역 필터 설정
    regWrite(&hspi1, REF_ADDR_MASK, ADS_REFSEL_INT | ADS_REFINT_ON_ALWAYS); // 내부 2.5V 참조 전압, 항상 켜기
    regWrite(&hspi1, SYS_ADDR_MASK, ADS_CALSAMPLE_8 | ADS_SENDSTATUS_ENABLE); // 캘리브레이션 샘플 8, 상태 전송 활성화

    // 변환 시작
    assertStart(); // START 핀을 High로 설정하여 ADC 변환 시작

    // 무한 루프: DRDY 인터럽트에 의해 데이터 처리
    while (1) {
        HAL_Delay(100); // CPU 부하 감소를 위해 100ms 대기
    }
}

/**
  * @brief  시스템 클럭 및 NVIC 설정 후 호출되는 사용자 정의 함수
  * @details 시스템 전역 설정 초기화
  */
void HAL_MspInit(void) {
    __HAL_RCC_SYSCFG_CLK_ENABLE(); // SYSCFG 클럭 활성화 (인터럽트 설정용)
    __HAL_RCC_PWR_CLK_ENABLE();    // PWR 클럭 활성화 (전원 관리용)
}

/**
  * @brief  SPI MSP 초기화
  * @details SPI1 모듈의 클럭 활성화
  * @param hspi SPI 핸들러
  */
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) {
    if (hspi->Instance == SPI1) { // SPI1 모듈 확인
        __HAL_RCC_SPI1_CLK_ENABLE(); // SPI1 클럭 활성화
    }
}

/**
  * @brief  UART MSP 초기화
  * @details USART2 모듈의 클럭 활성화
  * @param huart UART 핸들러
  */
void HAL_UART_MspInit(UART_HandleTypeDef* huart) {
    if (huart->Instance == USART2) { // USART2 모듈 확인
        __HAL_RCC_USART2_CLK_ENABLE(); // USART2 클럭 활성화
    }
}

/**
  * @brief  SPI MSP 해제
  * @details SPI1 모듈의 클럭 비활성화
  * @param hspi SPI 핸들러
  */
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) {
    if (hspi->Instance == SPI1) {
        __HAL_RCC_SPI1_CLK_DISABLE(); // SPI1 클럭 비활성화
    }
}

/**
  * @brief  UART MSP 해제
  * @details USART2 모듈의 클럭 비활성화
  * @param huart UART 핸들러
  */
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart) {
    if (huart->Instance == USART2) {
        __HAL_RCC_USART2_CLK_DISABLE(); // USART2 클럭 비활성화
    }
}

5. 결론

이 문서는 ADS124S08 24비트 ADC를 STM32L432KC에서 제어하기 위한 디바이스 드라이버와 예제 구현을 상세히 설명하였습니다. 드라이버는 STM32 HAL 라이브러리를 기반으로 하며, SPI 통신, GPIO 제어, DRDY 인터럽트를 통해 ADS124S08의 모든 기능을 지원을 하며, 예제 코드는 시스템 클럭을 80 MHz로 설정하고, AIN0-AIN1 차동 입력을 20 SPS로 읽어 UART로 출력하며, 상세한 주석을 통해 코드의 각 부분을 명확히 설명되었습니다. 이 드라이버와 예제는 산업용 제어, 의료 기기, 계측 등 다양한 애플리케이션에 활용될 수 있으며, 필요에 따라 레지스터 설정이나 인터럽트 로직을 확장할 수 있습니다.