1. 개요
이 문서는 아날로그 디바이스(Analog Devices)의 AD7124 24비트 델타-시그마(Δ-Σ) ADC를 STM32 마이크로컨트롤러에서 제어하기 위한 디바이스 드라이버 구현 내용을 다룹니다. 드라이버는 STM32 HAL 라이브러리를 기반으로 작성되었으며, AD7124의 주요 기능을 안정적이고 직관적으로 활용할 수 있도록 설계되었습니다.
본 문서는 다음과 같은 내용을 포함합니다:
- AD7124 주요 사양 및 레지스터 구조 개요
- STM32 기반 드라이버 구현 세부 사항
- STM32L432KC 예제 코드를 통한 실제 활용 방법
- 완전한 드라이버 코드(ad7124.h, ad7124.c, main.c)
2. AD7124 주요 사양
2.1 주요 특징
AD7124는 고정밀, 저전력 데이터 수집 시스템에 최적화된 24비트 델타-시그마 ADC입니다. 주요 특징은 다음과 같습니다:
- 해상도: 24비트, 고정밀 아날로그-디지털 변환 제공
- 샘플링 속도: 1.17 SPS에서 19,200 SPS까지 가변 설정 가능
- 입력 채널:
- AD7124-4: 단일 종단 8채널 또는 차동 4채널
- AD7124-8: 단일 종단 16채널 또는 차동 8채널
- PGA(Programmable Gain Amplifier): 이득 1, 2, 4, 8, 16, 32, 64, 128 설정 가능
- 참조 전압: 내부 2.5V 참조 전압 또는 외부 참조 전압 선택 가능
- 인터페이스: SPI(Serial Peripheral Interface) 통신 프로토콜
- 추가 기능:
- 내부 온도 센서
- 전원 및 참조 전압 모니터링
- CRC(Cyclic Redundancy Check) 지원
- GPIO: 최대 4개 지원 (AD7124-8에서)
- 전력 소비: 저전력 모드에서 1.5mA (typ), 절전 모드 지원
- 패키지: 5mm × 5mm LFCSP 또는 TSSOP 패키지
2.2 레지스터 구조
AD7124는 56개의 8비트 레지스터로 구성되며, 각 레지스터는 특정 기능을 설정합니다. 주요 레지스터는 다음과 같습니다:
- STATUS (0x00): 디바이스 상태, 데이터 준비(RDY), 오류 플래그
- ADC_CONTROL (0x01): ADC 모드, 전원 모드, 클럭 선택
- DATA (0x02): 변환 데이터 저장
- IOCON1/IOCON2 (0x03, 0x04): GPIO 및 전류 출력 설정
- ID (0x05): 디바이스 ID 및 리비전 정보
- ERROR/ERROR_EN (0x06, 0x07): 오류 상태 및 활성화 설정
- CH0_MAP ~ CH15_MAP (0x09~0x18): 채널 설정 (입력 선택, 활성화)
- CFG0 ~ CFG7 (0x19~0x20): PGA, 참조 전압, 버퍼 설정
- FILT0 ~ FILT7 (0x21~0x28): 필터 타입 및 샘플링 속도 설정
- OFFS0 ~ OFFS7 (0x29~0x30): 오프셋 교정
- GAIN0 ~ GAIN7 (0x31~0x38): 이득 교정
2.3 명령어
AD7124는 SPI를 통해 다음과 같은 명령어를 지원합니다. 이는 AD7124 데이터시트(Rev. D)와 드라이버 코드에서 확인된 내용입니다:
- NOP (0x00): 아무 작업을 수행하지 않으며, SPI 통신 동기화 또는 더미 데이터 전송에 사용됩니다.
- RESET (0xFF 반복): 64비트(8바이트) 연속 0xFF 전송으로 디바이스를 리셋하며, 모든 레지스터를 기본값으로 복원합니다.
ad7124_reset
함수에서 구현됩니다. - READ_DATA (자동): 명시적 명령어는 없으나,
DATA
레지스터(0x02)를 읽기 위해0x42
(0x40 | 0x02
) 명령을 전송하거나, 연속 읽기 모드(CONT_READ
)를 활성화하여 자동으로 변환 데이터를 읽습니다.ad7124_read_data
함수에서 지원됩니다. - REGRD (0x40 | addr): 지정된 레지스터를 읽기 위해 통신 레지스터에
0x40 | 레지스터 주소
를 전송합니다.ad7124_no_check_read_register
함수에서 구현됩니다. - REGWR (0x00 | addr): 지정된 레지스터에 쓰기 위해 통신 레지스터에
0x00 | 레지스터 주소
를 전송합니다.ad7124_no_check_write_register
함수에서 구현됩니다.
2.4 애플리케이션
- 산업용 제어: 온도, 압력, 스트레인 게이지 데이터 수집
- 의료 기기: 생체 신호 측정
- 계측: 고정밀 전압/전류 측정
- 환경 모니터링: 센서 네트워크 데이터 수집
3. STM32 디바이스 드라이버 구현 내용
3.1 구현 개요
AD7124 디바이스 드라이버는 STM32L432KC를 포함한 STM32 시리즈를 대상으로 하며, STM32 HAL 라이브러리를 사용하여 SPI 통신과 GPIO 제어를 구현하였습니다. 드라이버는 AD7124의 모든 주요 기능을 지원하며, 다음과 같은 요구사항을 충족합니다:
- SPI 통신: 레지스터 읽기/쓰기, 데이터 읽기, 명령어 전송
- GPIO 제어: 칩 선택(CS), 데이터 준비(RDY) 핀 제어
- 인터럽트 지원: RDY 핀의 하강 에지 인터럽트를 통해 데이터 준비 감지
- 레지스터 상태 관리: 전역 배열을 통해 레지스터 상태 추적
- 모듈화: 재사용 가능하고 명확한 함수 구조
3.2 구현 세부 사항
3.2.1 헤더 파일 (ad7124.h)
- 레지스터 및 명령어 정의: AD7124의 모든 레지스터 주소, 비트 필드, 명령어를 상수로 정의
- GPIO 핀 정의: STM32L432KC 기준으로 CS_PORT, CS_PIN 지정 (예: GPIOA, GPIO_PIN_4)
- 데이터 길이 정의: 변환 데이터(3바이트), 상태(1바이트), CRC(1바이트)
- 구조체 정의:
ad7124_dev
,ad7124_init_param
구조체로 디바이스와 초기화 파라미터 관리 - 함수 프로토타입: 초기화, 레지스터 읽기/쓰기, 데이터 읽기, 리셋, CRC 계산 함수 선언
- 전역 변수:
ad7124_regs
배열(레지스터 상태 저장),use_crc
,check_ready
플래그
3.2.2 소스 파일 (ad7124.c)
드라이버의 주요 함수는 다음과 같습니다:
- ad7124_setup: SPI, GPIO, 레지스터 초기화 및 디바이스 리셋
- ad7124_read_register / ad7124_write_register: 단일 레지스터 읽기/쓰기
- ad7124_no_check_read_register / ad7124_no_check_write_register: 상태 확인 없이 레지스터 읽기/쓰기
- ad7124_reset: 디바이스 리셋
- ad7124_wait_for_spi_ready: SPI 준비 대기
- ad7124_wait_for_conv_ready: 변환 준비 대기
- ad7124_read_data: 변환 데이터 읽기
- ad7124_compute_crc8: CRC 계산
- ad7124_update_crcsetting / ad7124_update_dev_spi_settings: CRC 및 SPI 설정 업데이트
3.2.3 인터럽트 처리
- RDY 인터럽트: RDY 핀의 하강 에지를 감지하여 데이터 준비를 알림
- 사용자는
HAL_GPIO_EXTI_Callback
를 구현하여 RDY 신호 발생 시ad7124_read_data
를 호출 - 예시:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{ if (GPIO_Pin == RDY_PIN)
{
int32_t data;
ad7124_read_data(&dev, &data);
}
}
3.2.4 주요 구현 특징
STM32 HAL 기반: 안정성과 호환성 보장
모듈화: 단일 책임 원칙 준수
레지스터 상태 관리: ad7124_regs 배열로 동기화 유지
타이밍 관리: 리셋 및 SPI 준비 대기 시간 포함
확장성: 애플리케이션별 레지스터 설정 용이
3.3 사용 방법
SPI 설정: STM32CubeMX 또는 코드로 SPI1 설정 (8비트, CPOL=0, CPHA=1, 최대 5MHz)
GPIO 설정: ad7124.h의 CS 핀 정의를 하드웨어에 맞게 수정
초기화: ad7124_setup(&dev, init_param) 호출
데이터 읽기: RDY 인터럽트 또는 폴링 방식으로 ad7124_read_data 호출
레지스터 설정: ad7124_write_register로 설정 조정
인터럽트 처리: HAL_GPIO_EXTI_Callback에 RDY 로직 추가
3 .4 제한사항 및 주의사항
하드웨어 연결: SPI 및 GPIO 핀 올바른 연결 필수
SPI 클럭: 최대 5MHz 준수
인터럽트 구현: RDY 인터럽트 사용자 구현 필요
타이밍: 애플리케이션에 따라 대기 시간 조정 가능
레지스터 설정: 데이터 속도, 이득 등 최적화 필요
구현 코드
구현 코드: ad7124.h, ad7124.c, main.c
#ifndef __AD7124_H__
#define __AD7124_H__
/* STM32 HAL 라이브러리 포함 */
#include <stdint.h>
#include "stm32l4xx_hal.h"
/* 레지스터 읽기/쓰기 권한 정의 */
#define AD7124_RW 1 // 읽기 및 쓰기 가능
#define AD7124_R 2 // 읽기 전용
#define AD7124_W 3 // 쓰기 전용
/* AD7124 레지스터 주소 정의 */
#define AD7124_COMM_REG 0x00 // 통신 레지스터: 명령어 및 주소 설정
#define AD7124_STATUS_REG 0x00 // 상태 레지스터: 디바이스 상태 및 데이터 준비 플래그
#define AD7124_ADC_CTRL_REG 0x01 // ADC 제어 레지스터: 동작 모드, 전원 모드 설정
#define AD7124_DATA_REG 0x02 // 데이터 레지스터: 변환 데이터 저장
#define AD7124_IO_CTRL1_REG 0x03 // IO 제어 레지스터 1: GPIO 및 전류 출력 설정
#define AD7124_IO_CTRL2_REG 0x04 // IO 제어 레지스터 2: 바이어스 전압 설정
#define AD7124_ID_REG 0x05 // ID 레지스터: 디바이스 ID 및 리비전 정보
#define AD7124_ERR_REG 0x06 // 오류 레지스터: 오류 상태 플래그
#define AD7124_ERREN_REG 0x07 // 오류 활성화 레지스터: 오류 감지 활성화 설정
#define AD7124_CH0_MAP_REG 0x09 // 채널 0 매핑: 입력 채널 및 설정
#define AD7124_CH1_MAP_REG 0x0A // 채널 1 매핑
#define AD7124_CH2_MAP_REG 0x0B // 채널 2 매핑
#define AD7124_CH3_MAP_REG 0x0C // 채널 3 매핑
#define AD7124_CH4_MAP_REG 0x0D // 채널 4 매핑
#define AD7124_CH5_MAP_REG 0x0E // 채널 5 매핑
#define AD7124_CH6_MAP_REG 0x0F // 채널 6 매핑
#define AD7124_CH7_MAP_REG 0x10 // 채널 7 매핑
#define AD7124_CH8_MAP_REG 0x11 // 채널 8 매핑 (AD7124-8 전용)
#define AD7124_CH9_MAP_REG 0x12 // 채널 9 매핑
#define AD7124_CH10_MAP_REG 0x13 // 채널 10 매핑
#define AD7124_CH11_MAP_REG 0x14 // 채널 11 매핑
#define AD7124_CH12_MAP_REG 0x15 // 채널 12 매핑
#define AD7124_CH13_MAP_REG 0x16 // 채널 13 매핑
#define AD7124_CH14_MAP_REG 0x17 // 채널 14 매핑
#define AD7124_CH15_MAP_REG 0x18 // 채널 15 매핑
#define AD7124_CFG0_REG 0x19 // 설정 0 레지스터: PGA, 참조 전압 설정
#define AD7124_CFG1_REG 0x1A // 설정 1 레지스터
#define AD7124_CFG2_REG 0x1B // 설정 2 레지스터
#define AD7124_CFG3_REG 0x1C // 설정 3 레지스터
#define AD7124_CFG4_REG 0x1D // 설정 4 레지스터
#define AD7124_CFG5_REG 0x1E // 설정 5 레지스터
#define AD7124_CFG6_REG 0x1F // 설정 6 레지스터
#define AD7124_CFG7_REG 0x20 // 설정 7 레지스터
#define AD7124_FILT0_REG 0x21 // 필터 0 레지스터: 필터 타입 및 샘플링 속도
#define AD7124_FILT1_REG 0x22 // 필터 1 레지스터
#define AD7124_FILT2_REG 0x23 // 필터 2 레지스터
#define AD7124_FILT3_REG 0x24 // 필터 3 레지스터
#define AD7124_FILT4_REG 0x25 // 필터 4 레지스터
#define AD7124_FILT5_REG 0x26 // 필터 5 레지스터
#define AD7124_FILT6_REG 0x27 // 필터 6 레지스터
#define AD7124_FILT7_REG 0x28 // 필터 7 레지스터
#define AD7124_OFFS0_REG 0x29 // 오프셋 0 레지스터: 오프셋 교정
#define AD7124_OFFS1_REG 0x2A // 오프셋 1 레지스터
#define AD7124_OFFS2_REG 0x2B // 오프셋 2 레지스터
#define AD7124_OFFS3_REG 0x2C // 오프셋 3 레지스터
#define AD7124_OFFS4_REG 0x2D // 오프셋 4 레지스터
#define AD7124_OFFS5_REG 0x2E // 오프셋 5 레지스터
#define AD7124_OFFS6_REG 0x2F // 오프셋 6 레지스터
#define AD7124_OFFS7_REG 0x30 // 오프셋 7 레지스터
#define AD7124_GAIN0_REG 0x31 // 이득 0 레지스터: 이득 교정
#define AD7124_GAIN1_REG 0x32 // 이득 1 레지스터
#define AD7124_GAIN2_REG 0x33 // 이득 2 레지스터
#define AD7124_GAIN3_REG 0x34 // 이득 3 레지스터
#define AD7124_GAIN4_REG 0x35 // 이득 4 레지스터
#define AD7124_GAIN5_REG 0x36 // 이득 5 레지스터
#define AD7124_GAIN6_REG 0x37 // 이득 6 레지스터
#define AD7124_GAIN7_REG 0x38 // 이득 7 레지스터
/* 통신 레지스터 비트 정의 */
#define AD7124_COMM_REG_WEN (0 << 7) // 쓰기 활성화 비트 (0: 쓰기 가능)
#define AD7124_COMM_REG_WR (0 << 6) // 쓰기 명령
#define AD7124_COMM_REG_RD (1 << 6) // 읽기 명령
#define AD7124_COMM_REG_RA(x) ((x) & 0x3F) // 레지스터 주소 (6비트)
/* 상태 레지스터 비트 정의 */
#define AD7124_STATUS_REG_RDY (1 << 7) // 데이터 준비 플래그
#define AD7124_STATUS_REG_ERROR_FLAG (1 << 6) // 오류 발생 플래그
#define AD7124_STATUS_REG_POR_FLAG (1 << 4) // 전원 온 리셋 플래그
#define AD7124_STATUS_REG_CH_ACTIVE(x) ((x) & 0xF) // 활성 채널 번호
/* ADC 제어 레지스터 비트 정의 */
#define AD7124_ADC_CTRL_REG_DOUT_RDY_DEL (1 << 12) // 데이터 출력 지연 활성화
#define AD7124_ADC_CTRL_REG_CONT_READ (1 << 11) // 연속 읽기 모드
#define AD7124_ADC_CTRL_REG_DATA_STATUS (1 << 10) // 데이터와 상태 동시 전송
#define AD7124_ADC_CTRL_REG_CS_EN (1 << 9) // CS 핀 활성화
#define AD7124_ADC_CTRL_REG_REF_EN (1 << 8) // 내부 참조 전압 활성화
#define AD7124_ADC_CTRL_REG_POWER_MODE(x) (((x) & 0x3) << 6) // 전원 모드 (0: 저전력, 1: 중간, 2: 고속)
#define AD7124_ADC_CTRL_REG_MODE(x) (((x) & 0xF) << 2) // 동작 모드 (예: 0: 연속 변환)
#define AD7124_ADC_CTRL_REG_CLK_SEL(x) (((x) & 0x3) << 0) // 클럭 선택 (0: 내부 클럭)
/* IO 제어 레지스터 1 비트 정의 */
#define AD7124_IO_CTRL1_REG_GPIO_DAT2 (1 << 23) // GPIO2 데이터
#define AD7124_IO_CTRL1_REG_GPIO_DAT1 (1 << 22) // GPIO1 데이터
#define AD7124_IO_CTRL1_REG_GPIO_CTRL2 (1 << 19) // GPIO2 제어
#define AD7124_IO_CTRL1_REG_GPIO_CTRL1 (1 << 18) // GPIO1 제어
#define AD7124_IO_CTRL1_REG_PDSW (1 << 15) // 전원 스위치
#define AD7124_IO_CTRL1_REG_IOUT1(x) (((x) & 0x7) << 11) // IOUT1 전류 설정
#define AD7124_IO_CTRL1_REG_IOUT0(x) (((x) & 0x7) << 8) // IOUT0 전류 설정
#define AD7124_IO_CTRL1_REG_IOUT_CH1(x) (((x) & 0xF) << 4) // IOUT1 채널 선택
#define AD7124_IO_CTRL1_REG_IOUT_CH0(x) (((x) & 0xF) << 0) // IOUT0 채널 선택
/* IO 제어 레지스터 1 (AD7124-8 전용) 비트 정의 */
#define AD7124_8_IO_CTRL1_REG_GPIO_DAT4 (1 << 23) // GPIO4 데이터
#define AD7124_8_IO_CTRL1_REG_GPIO_DAT3 (1 << 22) // GPIO3 데이터
#define AD7124_8_IO_CTRL1_REG_GPIO_DAT2 (1 << 21) // GPIO2 데이터
#define AD7124_8_IO_CTRL1_REG_GPIO_DAT1 (1 << 20) // GPIO1 데이터
#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL4 (1 << 19) // GPIO4 제어
#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL3 (1 << 18) // GPIO3 제어
#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL2 (1 << 17) // GPIO2 제어
#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL1 (1 << 16) // GPIO1 제어
/* IO 제어 레지스터 2 비트 정의 */
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS7 (1 << 15) // VBIAS7 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS6 (1 << 14) // VBIAS6 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS5 (1 << 11) // VBIAS5 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS4 (1 << 10) // VBIAS4 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS3 (1 << 5) // VBIAS3 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS2 (1 << 4) // VBIAS2 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS1 (1 << 1) // VBIAS1 활성화
#define AD7124_IO_CTRL2_REG_GPIO_VBIAS0 (1 << 0) // VBIAS0 활성화
/* IO 제어 레지스터 2 (AD7124-8 전용) 비트 정의 */
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS15 (1 << 15) // VBIAS15 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS14 (1 << 14) // VBIAS14 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS13 (1 << 13) // VBIAS13 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS12 (1 << 12) // VBIAS12 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS11 (1 << 11) // VBIAS11 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS10 (1 << 10) // VBIAS10 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS9 (1 << 9) // VBIAS9 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS8 (1 << 8) // VBIAS8 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS7 (1 << 7) // VBIAS7 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS6 (1 << 6) // VBIAS6 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS5 (1 << 5) // VBIAS5 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS4 (1 << 4) // VBIAS4 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS3 (1 << 3) // VBIAS3 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS2 (1 << 2) // VBIAS2 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS1 (1 << 1) // VBIAS1 활성화
#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS0 (1 << 0) // VBIAS0 활성화
/* ID 레지스터 비트 정의 */
#define AD7124_ID_REG_DEVICE_ID(x) (((x) & 0xF) << 4) // 디바이스 ID
#define AD7124_ID_REG_SILICON_REV(x) (((x) & 0xF) << 0) // 실리콘 리비전
/* 오류 레지스터 비트 정의 */
#define AD7124_ERR_REG_LDO_CAP_ERR (1 << 19) // LDO 커패시터 오류
#define AD7124_ERR_REG_ADC_CAL_ERR (1 << 18) // ADC 교정 오류
#define AD7124_ERR_REG_ADC_CONV_ERR (1 << 17) // ADC 변환 오류
#define AD7124_ERR_REG_ADC_SAT_ERR (1 << 16) // ADC 포화 오류
#define AD7124_ERR_REG_AINP_OV_ERR (1 << 15) // AINP 오버볼티지 오류
#define AD7124_ERR_REG_AINP_UV_ERR (1 << 14) // AINP 언더볼티지 오류
#define AD7124_ERR_REG_AINM_OV_ERR (1 << 13) // AINM 오버볼티지 오류
#define AD7124_ERR_REG_AINM_UV_ERR (1 << 12) // AINM 언더볼티지 오류
#define AD7124_ERR_REG_REF_DET_ERR (1 << 11) // 참조 전압 감지 오류
#define AD7124_ERR_REG_DLDO_PSM_ERR (1 << 9) // 디지털 LDO 오류
#define AD7124_ERR_REG_ALDO_PSM_ERR (1 << 7) // 아날로그 LDO 오류
#define AD7124_ERR_REG_SPI_IGNORE_ERR (1 << 6) // SPI 무시 오류
#define AD7124_ERR_REG_SPI_SLCK_CNT_ERR (1 << 5) // SPI 클럭 카운트 오류
#define AD7124_ERR_REG_SPI_READ_ERR (1 << 4) // SPI 읽기 오류
#define AD7124_ERR_REG_SPI_WRITE_ERR (1 << 3) // SPI 쓰기 오류
#define AD7124_ERR_REG_SPI_CRC_ERR (1 << 2) // SPI CRC 오류
#define AD7124_ERR_REG_MM_CRC_ERR (1 << 1) // 메모리 맵 CRC 오류
#define AD7124_ERR_REG_ROM_CRC_ERR (1 << 0) // ROM CRC 오류
/* 오류 활성화 레지스터 비트 정의 */
#define AD7124_ERREN_REG_MCLK_CNT_EN (1 << 22) // 메인 클럭 카운터 활성화
#define AD7124_ERREN_REG_LDO_CAP_CHK_TEST_EN (1 << 21) // LDO 커패시터 체크 활성화
#define AD7124_ERREN_REG_LDO_CAP_CHK(x) (((x) & 0x3) << 19) // LDO 커패시터 체크 설정
#define AD7124_ERREN_REG_ADC_CAL_ERR_EN (1 << 18) // ADC 교정 오류 활성화
#define AD7124_ERREN_REG_ADC_CONV_ERR_EN (1 << 17) // ADC 변환 오류 활성화
#define AD7124_ERREN_REG_ADC_SAT_ERR_EN (1 << 16) // ADC 포화 오류 활성화
#define AD7124_ERREN_REG_AINP_OV_ERR_EN (1 << 15) // AINP 오버볼티지 오류 활성화
#define AD7124_ERREN_REG_AINP_UV_ERR_EN (1 << 14) // AINP 언더볼티지 오류 활성화
#define AD7124_ERREN_REG_AINM_OV_ERR_EN (1 << 13) // AINM 오버볼티지 오류 활성화
#define AD7124_ERREN_REG_AINM_UV_ERR_EN (1 << 12) // AINM 언더볼티지 오류 활성화
#define AD7124_ERREN_REG_REF_DET_ERR_EN (1 << 11) // 참조 전압 오류 활성화
#define AD7124_ERREN_REG_DLDO_PSM_TRIP_TEST_EN (1 << 10) // 디지털 LDO 테스트 활성화
#define AD7124_ERREN_REG_DLDO_PSM_ERR_ERR (1 << 9) // 디지털 LDO 오류 활성화
#define AD7124_ERREN_REG_ALDO_PSM_TRIP_TEST_EN (1 << 8) // 아날로그 LDO 테스트 활성화
#define AD7124_ERREN_REG_ALDO_PSM_ERR_EN (1 << 7) // 아날로그 LDO 오류 활성화
#define AD7124_ERREN_REG_SPI_IGNORE_ERR_EN (1 << 6) // SPI 무시 오류 활성화
#define AD7124_ERREN_REG_SPI_SCLK_CNT_ERR_EN (1 << 5) // SPI 클럭 카운트 오류 활성화
#define AD7124_ERREN_REG_SPI_READ_ERR_EN (1 << 4) // SPI 읽기 오류 활성화
#define AD7124_ERREN_REG_SPI_WRITE_ERR_EN (1 << 3) // SPI 쓰기 오류 활성화
#define AD7124_ERREN_REG_SPI_CRC_ERR_EN (1 << 2) // SPI CRC 오류 활성화
#define AD7124_ERREN_REG_MM_CRC_ERR_EN (1 << 1) // 메모리 맵 CRC 오류 활성화
#define AD7124_ERREN_REG_ROM_CRC_ERR_EN (1 << 0) // ROM CRC 오류 활성화
/* 채널 매핑 레지스터 비트 정의 */
#define AD7124_CH_MAP_REG_CH_ENABLE (1 << 15) // 채널 활성화
#define AD7124_CH_MAP_REG_SETUP(x) (((x) & 0x7) << 12) // 설정 선택 (0~7)
#define AD7124_CH_MAP_REG_AINP(x) (((x) & 0x1F) << 5) // 양극 입력 선택
#define AD7124_CH_MAP_REG_AINM(x) (((x) & 0x1F) << 0) // 음극 입력 선택
/* 설정 레지스터 비트 정의 */
#define AD7124_CFG_REG_BIPOLAR (1 << 11) // 양극성 입력 모드
#define AD7124_CFG_REG_BURNOUT(x) (((x) & 0x3) << 9) // 번아웃 전류 설정
#define AD7124_CFG_REG_REF_BUFP (1 << 8) // 참조 전압 양극 버퍼 활성화
#define AD7124_CFG_REG_REF_BUFM (1 << 7) // 참조 전압 음극 버퍼 활성화
#define AD7124_CFG_REG_AIN_BUFP (1 << 6) // 입력 양극 버퍼 활성화
#define AD7124_CFG_REG_AINN_BUFM (1 << 5) // 입력 음극 버퍼 활성화
#define AD7124_CFG_REG_REF_SEL(x) ((x) & 0x3) << 3 // 참조 전압 소스 선택
#define AD7124_CFG_REG_PGA(x) (((x) & 0x7) << 0) // PGA 이득 선택 (1~128)
/* 필터 레지스터 비트 정의 */
#define AD7124_FILT_REG_FILTER(x) (((x) & 0x7) << 21) // 필터 타입 (0: Sinc4, 1: Sinc3 등)
#define AD7124_FILT_REG_REJ60 (1 << 20) // 60Hz 제거 활성화
#define AD7124_FILT_REG_POST_FILTER(x) (((x) & 0x7) << 17) // 포스트 필터 설정
#define AD7124_FILT_REG_SINGLE_CYCLE (1 << 16) // 단일 주기 변환
#define AD7124_FILT_REG_FS(x) (((x) & 0x7FF) << 0) // 필터 샘플링 속도 설정
/* 레지스터 정보 구조체 */
struct ad7124_st_reg {
int32_t addr; // 레지스터 주소
int32_t value; // 레지스터 값
int32_t size; // 레지스터 크기 (바이트)
int32_t rw; // 읽기/쓰기 권한 (AD7124_RW, AD7124_R, AD7124_W)
};
/* AD7124 레지스터 열거형 */
enum ad7124_registers {
AD7124_Status = 0x00,
AD7124_ADC_Control,
AD7124_Data,
AD7124_IOCon1,
AD7124_IOCon2,
AD7124_ID,
AD7124_Error,
AD7124_Error_En,
AD7124_Mclk_Count,
AD7124_Channel_0,
AD7124_Channel_1,
AD7124_Channel_2,
AD7124_Channel_3,
AD7124_Channel_4,
AD7124_Channel_5,
AD7124_Channel_6,
AD7124_Channel_7,
AD7124_Channel_8,
AD7124_Channel_9,
AD7124_Channel_10,
AD7124_Channel_11,
AD7124_Channel_12,
AD7124_Channel_13,
AD7124_Channel_14,
AD7124_Channel_15,
AD7124_Config_0,
AD7124_Config_1,
AD7124_Config_2,
AD7124_Config_3,
AD7124_Config_4,
AD7124_Config_5,
AD7124_Config_6,
AD7124_Config_7,
AD7124_Filter_0,
AD7124_Filter_1,
AD7124_Filter_2,
AD7124_Filter_3,
AD7124_Filter_4,
AD7124_Filter_5,
AD7124_Filter_6,
AD7124_Filter_7,
AD7124_Offset_0,
AD7124_Offset_1,
AD7124_Offset_2,
AD7124_Offset_3,
AD7124_Offset_4,
AD7124_Offset_5,
AD7124_Offset_6,
AD7124_Offset_7,
AD7124_Gain_0,
AD7124_Gain_1,
AD7124_Gain_2,
AD7124_Gain_3,
AD7124_Gain_4,
AD7124_Gain_5,
AD7124_Gain_6,
AD7124_Gain_7,
AD7124_REG_NO // 레지스터 총 개수
};
/* AD7124 레지스터 배열 선언 (ad7124.c에서 정의) */
extern struct ad7124_st_reg ad7124_regs[AD7124_REG_NO];
/* AD7124 디바이스 구조체 */
struct ad7124_dev {
SPI_HandleTypeDef *hspi; // SPI 핸들러
struct ad7124_st_reg *regs; // 레지스터 배열 포인터
int16_t use_crc; // CRC 사용 여부 (0: 비활성, 1: 활성)
int16_t check_ready; // 디바이스 준비 상태 확인 여부
int16_t spi_rdy_poll_cnt; // SPI 준비 대기 폴링 횟수
GPIO_TypeDef *cs_port; // CS 핀 포트
uint16_t cs_pin; // CS 핀 번호
};
/* AD7124 초기화 파라미터 구조체 */
struct ad7124_init_param {
SPI_HandleTypeDef *hspi; // SPI 핸들러
struct ad7124_st_reg *regs; // 레지스터 배열 포인터
int16_t spi_rdy_poll_cnt; // SPI 준비 대기 폴링 횟수
GPIO_TypeDef *cs_port; // CS 핀 포트
uint16_t cs_pin; // CS 핀 번호
};
/* AD7124 상수 정의 */
#define AD7124_CRC8_POLYNOMIAL_REPRESENTATION 0x07 // CRC8 다항식 (x8 + x2 + x + 1)
#define AD7124_DISABLE_CRC 0 // CRC 비활성화
#define AD7124_USE_CRC 1 // CRC 활성화
/* 함수 프로토타입 선언 */
int32_t ad7124_read_register(struct ad7124_dev *dev, struct ad7124_st_reg* p_reg); // 레지스터 읽기
int32_t ad7124_write_register(struct ad7124_dev *dev, struct ad7124_st_reg reg); // 레지스터 쓰기
int32_t ad7124_no_check_read_register(struct ad7124_dev *dev, struct ad7124_st_reg* p_reg); // 준비 확인 없이 레지스터 읽기
int32_t ad7124_no_check_write_register(struct ad7124_dev *dev, struct ad7124_st_reg reg); // 준비 확인 없이 레지스터 쓰기
int32_t ad7124_reset(struct ad7124_dev *dev); // 디바이스 리셋
int32_t ad7124_wait_for_spi_ready(struct ad7124_dev *dev, uint32_t timeout); // SPI 준비 대기
int32_t ad7124_wait_to_power_on(struct ad7124_dev *dev, uint32_t timeout); // 전원 온 대기
int32_t ad7124_wait_for_conv_ready(struct ad7124_dev *dev, uint32_t timeout); // 변환 준비 대기
int32_t ad7124_read_data(struct ad7124_dev *dev, int32_t* p_data); // 변환 데이터 읽기
uint8_t ad7124_compute_crc8(uint8_t* p_buf, uint8_t buf_size); // CRC8 계산
void ad7124_update_crcsetting(struct ad7124_dev *dev); // CRC 설정 업데이트
void ad7124_update_dev_spi_settings(struct ad7124_dev *dev); // SPI 설정 업데이트
int32_t ad7124_setup(struct ad7124_dev **device, struct ad7124_init_param init_param); // 디바이스 초기화
int32_t ad7124_remove(struct ad7124_dev *dev); // 디바이스 메모리 해제
#endif /* __AD7124_H__ */
#include <stdlib.h>
#include <stdbool.h>
#include "ad7124.h"
/* 에러 코드 정의 */
#define INVALID_VAL -1 // 잘못된 인자
#define COMM_ERR -2 // 통신 에러
#define TIMEOUT -3 // 타임아웃 발생
/* 리셋 후 대기 시간 (ms) - 데이터시트 기준 2ms 필요, 여유를 위해 4ms 설정 */
#define AD7124_POST_RESET_DELAY 4
/* CS 핀 제어 매크로 */
#define AD7124_CS_LOW(dev) HAL_GPIO_WritePin(dev->cs_port, dev->cs_pin, GPIO_PIN_RESET) // CS 핀 LOW로 설정
#define AD7124_CS_HIGH(dev) HAL_GPIO_WritePin(dev->cs_port, dev->cs_pin, GPIO_PIN_SET) // CS 핀 HIGH로 설정
/* AD7124 레지스터 초기값 배열 정의 */
struct ad7124_st_reg ad7124_regs[AD7124_REG_NO] = {
{0x00, 0x00, 1, 2}, // 상태 레지스터: 읽기 전용, 1바이트
{0x01, 0x0000, 2, 1}, // ADC 제어 레지스터: 읽기/쓰기, 2바이트
{0x02, 0x0000, 3, 2}, // 데이터 레지스터: 읽기 전용, 3바이트
{0x03, 0x0000, 3, 1}, // IO 제어 레지스터 1: 읽기/쓰기, 3바이트
{0x04, 0x0000, 2, 1}, // IO 제어 레지스터 2: 읽기/쓰기, 2바이트
{0x05, 0x02, 1, 2}, // ID 레지스터: 읽기 전용, 1바이트
{0x06, 0x0000, 3, 2}, // 오류 레지스터: 읽기 전용, 3바이트
{0x07, 0x0040, 3, 1}, // 오류 활성화 레지스터: 읽기/쓰기, 3바이트
{0x08, 0x00, 1, 2}, // MCLK 카운트 레지스터: 읽기 전용, 1바이트
{0x09, 0x8001, 2, 1}, // 채널 0 매핑: 읽기/쓰기, 2바이트
{0x0A, 0x0001, 2, 1}, // 채널 1 매핑
{0x0B, 0x0001, 2, 1}, // 채널 2 매핑
{0x0C, 0x0001, 2, 1}, // 채널 3 매핑
{0x0D, 0x0001, 2, 1}, // 채널 4 매핑
{0x0E, 0x0001, 2, 1}, // 채널 5 매핑
{0x0F, 0x0001, 2, 1}, // 채널 6 매핑
{0x10, 0x0001, 2, 1}, // 채널 7 매핑
{0x11, 0x0001, 2, 1}, // 채널 8 매핑 (AD7124-8 전용)
{0x12, 0x0001, 2, 1}, // 채널 9 매핑
{0x13, 0x0001, 2, 1}, // 채널 10 매핑
{0x14, 0x0001, 2, 1}, // 채널 11 매핑
{0x15, 0x0001, 2, 1}, // 채널 12 매핑
{0x16, 0x0001, 2, 1}, // 채널 13 매핑
{0x17, 0x0001, 2, 1}, // 채널 14 매핑
{0x18, 0x0001, 2, 1}, // 채널 15 매핑
{0x19, 0x0860, 2, 1}, // 설정 0 레지스터: 읽기/쓰기, 2바이트
{0x1A, 0x0860, 2, 1}, // 설정 1 레지스터
{0x1B, 0x0860, 2, 1}, // 설정 2 레지스터
{0x1C, 0x0860, 2, 1}, // 설정 3 레지스터
{0x1D, 0x0860, 2, 1}, // 설정 4 레지스터
{0x1E, 0x0860, 2, 1}, // 설정 5 레지스터
{0x1F, 0x0860, 2, 1}, // 설정 6 레지스터
{0x20, 0x0860, 2, 1}, // 설정 7 레지스터
{0x21, 0x060180, 3, 1}, // 필터 0 레지스터: 읽기/쓰기, 3바이트
{0x22, 0x060180, 3, 1}, // 필터 1 레지스터
{0x23, 0x060180, 3, 1}, // 필터 2 레지스터
{0x24, 0x060180, 3, 1}, // 필터 3 레지스터
{0x25, 0x060180, 3, 1}, // 필터 4 레지스터
{0x26, 0x060180, 3, 1}, // 필터 5 레지스터
{0x27, 0x060180, 3, 1}, // 필터 6 레지스터
{0x28, 0x060180, 3, 1}, // 필터 7 레지스터
{0x29, 0x800000, 3, 1}, // 오프셋 0 레지스터: 읽기/쓰기, 3바이트
{0x2A, 0x800000, 3, 1}, // 오프셋 1 레지스터
{0x2B, 0x800000, 3, 1}, // 오프셋 2 레지스터
{0x2C, 0x800000, 3, 1}, // 오프셋 3 레지스터
{0x2D, 0x800000, 3, 1}, // 오프셋 4 레지스터
{0x2E, 0x800000, 3, 1}, // 오프셋 5 레지스터
{0x2F, 0x800000, 3, 1}, // 오프셋 6 레지스터
{0x30, 0x800000, 3, 1}, // 오프셋 7 레지스터
{0x31, 0x500000, 3, 1}, // 이득 0 레지스터: 읽기/쓰기, 3바이트
{0x32, 0x500000, 3, 1}, // 이득 1 레지스터
{0x33, 0x500000, 3, 1}, // 이득 2 레지스터
{0x34, 0x500000, 3, 1}, // 이득 3 레지스터
{0x35, 0x500000, 3, 1}, // 이득 4 레지스터
{0x36, 0x500000, 3, 1}, // 이득 5 레지스터
{0x37, 0x500000, 3, 1}, // 이득 6 레지스터
{0x38, 0x500000, 3, 1}, // 이득 7 레지스터
};
/**
* @brief 디바이스 준비 상태 확인 없이 지정된 레지스터를 읽음
* @param dev 디바이스 구조체 포인터
* @param p_reg 읽을 레지스터 정보 구조체 포인터
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_no_check_read_register(struct ad7124_dev *dev, struct ad7124_st_reg* p_reg)
{
int32_t ret = 0;
uint8_t buffer[8] = {0}; // SPI 송수신 버퍼
uint8_t i = 0;
uint8_t check8 = 0, add_status_length = 0; // CRC 및 상태 바이트 길이 변수
uint8_t msg_buf[8] = {0}; // CRC 계산용 메시지 버퍼
// 입력 유효성 검사
if (!dev || !p_reg)
return INVALID_VAL;
// 명령어 구성: 읽기 명령 + 레지스터 주소
buffer[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD | AD7124_COMM_REG_RA(p_reg->addr);
// 데이터 레지스터 읽기 시 상태 전송 활성화 확인
if ((p_reg->addr == AD7124_DATA_REG) && (dev->regs[AD7124_ADC_Control].value & AD7124_ADC_CTRL_REG_DATA_STATUS)) {
add_status_length = 1; // 상태 바이트 추가
}
// SPI 통신: 데이터 송수신
AD7124_CS_LOW(dev); // CS 핀 LOW
ret = HAL_SPI_TransmitReceive(dev->hspi, buffer, buffer,
((dev->use_crc != AD7124_DISABLE_CRC) ? p_reg->size + 1 : p_reg->size) + 1 + add_status_length, 1000);
AD7124_CS_HIGH(dev); // CS 핀 HIGH
if (ret != HAL_OK)
return COMM_ERR; // 통신 에러 반환
// CRC 활성화 시 CRC 검사
if (dev->use_crc == AD7124_USE_CRC) {
msg_buf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD | AD7124_COMM_REG_RA(p_reg->addr);
for (i = 1; i < p_reg->size + 2 + add_status_length; ++i) {
msg_buf[i] = buffer[i];
}
check8 = ad7124_compute_crc8(msg_buf, p_reg->size + 2 + add_status_length);
}
// CRC 오류 확인
if (check8 != 0) {
return COMM_ERR;
}
// 상태 바이트가 포함된 경우 상태 레지스터 업데이트
if (add_status_length) {
dev->regs[AD7124_Status].value = buffer[p_reg->size + 1];
}
// 레지스터 값 구성
p_reg->value = 0;
for (i = 1; i < p_reg->size + 1; i++) {
p_reg->value <<= 8;
p_reg->value += buffer[i];
}
return ret;
}
/**
* @brief 디바이스 준비 상태 확인 없이 지정된 레지스터에 값을 씀
* @param dev 디바이스 구조체 포인터
* @param reg 쓸 레지스터 정보 구조체
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_no_check_write_register(struct ad7124_dev *dev, struct ad7124_st_reg reg)
{
int32_t ret = 0;
int32_t reg_value = 0;
uint8_t wr_buf[8] = {0}; // SPI 쓰기 버퍼
uint8_t i = 0;
uint8_t crc8 = 0; // CRC 값
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
// 명령어 구성: 쓰기 명령 + 레지스터 주소
wr_buf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_WR | AD7124_COMM_REG_RA(reg.addr);
// 쓰기 버퍼에 데이터 채우기
reg_value = reg.value;
for (i = 0; i < reg.size; i++) {
wr_buf[reg.size - i] = reg_value & 0xFF;
reg_value >>= 8;
}
// CRC 활성화 시 CRC 계산
if (dev->use_crc != AD7124_DISABLE_CRC) {
crc8 = ad7124_compute_crc8(wr_buf, reg.size + 1);
wr_buf[reg.size + 1] = crc8;
}
// SPI 통신: 데이터 쓰기
AD7124_CS_LOW(dev); // CS 핀 LOW
ret = HAL_SPI_Transmit(dev->hspi, wr_buf, (dev->use_crc != AD7124_DISABLE_CRC) ? reg.size + 2 : reg.size + 1, 1000);
AD7124_CS_HIGH(dev); // CS 핀 HIGH
if (ret != HAL_OK)
return COMM_ERR;
return ret;
}
/**
* @brief 디바이스 준비 상태를 확인한 후 지정된 레지스터를 읽음
* @param dev 디바이스 구조체 포인터
* @param p_reg 읽을 레지스터 정보 구조체 포인터
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_read_register(struct ad7124_dev *dev, struct ad7124_st_reg* p_reg)
{
int32_t ret;
// 오류 레지스터가 아니고 준비 확인 활성화 시 SPI 준비 대기
if (p_reg->addr != AD7124_ERR_REG && dev->check_ready) {
ret = ad7124_wait_for_spi_ready(dev, dev->spi_rdy_poll_cnt);
if (ret < 0)
return ret;
}
ret = ad7124_no_check_read_register(dev, p_reg);
return ret;
}
/**
* @brief 디바이스 준비 상태를 확인한 후 지정된 레지스터에 값을 씀
* @param dev 디바이스 구조체 포인터
* @param p_reg 쓸 레지스터 정보 구조체
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_write_register(struct ad7124_dev *dev, struct ad7124_st_reg p_reg)
{
int32_t ret;
// 준비 확인 활성화 시 SPI 준비 대기
if (dev->check_ready) {
ret = ad7124_wait_for_spi_ready(dev, dev->spi_rdy_poll_cnt);
if (ret < 0)
return ret;
}
ret = ad7124_no_check_write_register(dev, p_reg);
return ret;
}
/**
* @brief 디바이스를 리셋
* @param dev 디바이스 구조체 포인터
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_reset(struct ad7124_dev *dev)
{
int32_t ret = 0;
uint8_t wr_buf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 리셋 명령 (8바이트 0xFF)
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
// SPI 통신: 리셋 명령 전송
AD7124_CS_LOW(dev); // CS 핀 LOW
ret = HAL_SPI_Transmit(dev->hspi, wr_buf, 8, 1000);
AD7124_CS_HIGH(dev); // CS 핀 HIGH
if (ret != HAL_OK)
return COMM_ERR;
// 리셋 후 CRC 비활성화
dev->use_crc = AD7124_DISABLE_CRC;
// 전원 온 대기
ret = ad7124_wait_to_power_on(dev, dev->spi_rdy_poll_cnt);
// 리셋 후 안정화 대기
HAL_Delay(AD7124_POST_RESET_DELAY);
return ret;
}
/**
* @brief 디바이스가 사용자 요청을 수락할 준비가 될 때까지 대기
* @param dev 디바이스 구조체 포인터
* @param timeout 대기 폴링 횟수
* @return 성공 시 0, 타임아웃 시 TIMEOUT
*/
int32_t ad7124_wait_for_spi_ready(struct ad7124_dev *dev, uint32_t timeout)
{
struct ad7124_st_reg *regs;
int32_t ret;
int8_t ready = 0;
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
regs = dev->regs;
// SPI 준비 상태 확인
while (!ready && --timeout) {
ret = ad7124_read_register(dev, ®s[AD7124_Error]);
if (ret < 0)
return ret;
ready = (regs[AD7124_Error].value & AD7124_ERR_REG_SPI_IGNORE_ERR) == 0;
}
return timeout ? 0 : TIMEOUT;
}
/**
* @brief 디바이스가 전원 온 리셋을 완료할 때까지 대기
* @param dev 디바이스 구조체 포인터
* @param timeout 대기 폴링 횟수
* @return 성공 시 0, 타임아웃 시 TIMEOUT
*/
int32_t ad7124_wait_to_power_on(struct ad7124_dev *dev, uint32_t timeout)
{
struct ad7124_st_reg *regs;
int32_t ret;
int8_t powered_on = 0;
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
regs = dev->regs;
// 전원 온 상태 확인
while (!powered_on && timeout--) {
ret = ad7124_read_register(dev, ®s[AD7124_Status]);
if (ret < 0)
return ret;
powered_on = (regs[AD7124_Status].value & AD7124_STATUS_REG_POR_FLAG) == 0;
}
return (timeout || powered_on) ? 0 : TIMEOUT;
}
/**
* @brief 새로운 변환 결과가 준비될 때까지 대기
* @param dev 디바이스 구조체 포인터
* @param timeout 대기 폴링 횟수
* @return 성공 시 0, 타임아웃 시 TIMEOUT
*/
int32_t ad7124_wait_for_conv_ready(struct ad7124_dev *dev, uint32_t timeout)
{
struct ad7124_st_reg *regs;
int32_t ret;
int8_t ready = 0;
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
regs = dev->regs;
// 변환 준비 상태 확인
while (!ready && --timeout) {
ret = ad7124_read_register(dev, ®s[AD7124_Status]);
if (ret < 0)
return ret;
ready = (regs[AD7124_Status].value & AD7124_STATUS_REG_RDY) == 0;
}
return timeout ? 0 : TIMEOUT;
}
/**
* @brief 디바이스에서 변환 데이터를 읽음
* @param dev 디바이스 구조체 포인터
* @param p_data 읽은 데이터를 저장할 포인터
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_read_data(struct ad7124_dev *dev, int32_t* p_data)
{
struct ad7124_st_reg *regs;
int32_t ret;
// 입력 유효성 검사
if (!dev)
return INVALID_VAL;
regs = dev->regs;
// 데이터 레지스터 읽기
ret = ad7124_read_register(dev, ®s[AD7124_Data]);
// 읽은 데이터 저장
*p_data = regs[AD7124_Data].value;
return ret;
}
/**
* @brief 데이터 버퍼에 대한 CRC8 체크섬 계산
* @param p_buf 데이터 버퍼 포인터
* @param buf_size 버퍼 크기 (바이트)
* @return 계산된 CRC8 값
*/
uint8_t ad7124_compute_crc8(uint8_t * p_buf, uint8_t buf_size)
{
uint8_t i = 0;
uint8_t crc = 0;
while (buf_size) {
for (i = 0x80; i != 0; i >>= 1) {
bool cmp1 = (crc & 0x80) != 0;
bool cmp2 = (*p_buf & i) != 0;
if (cmp1 != cmp2) {
crc <<= 1;
crc ^= AD7124_CRC8_POLYNOMIAL_REPRESENTATION;
} else {
crc <<= 1;
}
}
p_buf++;
buf_size--;
}
return crc;
}
/**
* @brief CRC 설정 업데이트
* @param dev 디바이스 구조체 포인터
*/
void ad7124_update_crcsetting(struct ad7124_dev *dev)
{
struct ad7124_st_reg *regs;
if (!dev)
return;
regs = dev->regs;
// 오류 활성화 레지스터의 CRC 설정 확인
if (regs[AD7124_Error_En].value & AD7124_ERREN_REG_SPI_CRC_ERR_EN) {
dev->use_crc = AD7124_USE_CRC;
} else {
dev->use_crc = AD7124_DISABLE_CRC;
}
}
/**
* @brief SPI 인터페이스 설정 업데이트
* @param dev 디바이스 구조체 포인터
*/
void ad7124_update_dev_spi_settings(struct ad7124_dev *dev)
{
struct ad7124_st_reg *regs;
if (!dev)
return;
regs = dev->regs;
// SPI 준비 확인 설정 업데이트
if (regs[AD7124_Error_En].value & AD7124_ERREN_REG_SPI_IGNORE_ERR_EN) {
dev->check_ready = 1;
} else {
dev->check_ready = 0;
}
}
/**
* @brief AD7124 디바이스 초기화
* @param device 초기화된 디바이스 구조체 포인터를 저장할 포인터
* @param init_param 초기화 파라미터 구조체
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_setup(struct ad7124_dev **device, struct ad7124_init_param init_param)
{
int32_t ret;
enum ad7124_registers reg_nr;
struct ad7124_dev *dev;
// 디바이스 구조체 메모리 할당
dev = (struct ad7124_dev *)malloc(sizeof(*dev));
if (!dev)
return INVALID_VAL;
// 초기화 파라미터 설정
dev->regs = init_param.regs;
dev->spi_rdy_poll_cnt = init_param.spi_rdy_poll_cnt;
dev->hspi = init_param.hspi;
dev->cs_port = init_param.cs_port;
dev->cs_pin = init_param.cs_pin;
// 디바이스 리셋
ret = ad7124_reset(dev);
if (ret < 0)
return ret;
// 기본적으로 준비 확인 활성화
dev->check_ready = 1;
// ADC 제어 레지스터부터 오프셋 레지스터 전까지 초기화
for (reg_nr = AD7124_Status; (reg_nr < AD7124_Offset_0) && !(ret < 0); reg_nr++) {
if (dev->regs[reg_nr].rw == AD7124_RW) {
ret = ad7124_write_register(dev, dev->regs[reg_nr]);
if (ret < 0)
break;
}
// 오류 활성화 레지스터 처리 시 CRC 및 SPI 설정 업데이트
if (reg_nr == AD7124_Error_En) {
ad7124_update_crcsetting(dev);
ad7124_update_dev_spi_settings(dev);
}
}
*device = dev;
return ret;
}
/**
* @brief AD7124 디바이스 메모리 해제
* @param dev 디바이스 구조체 포인터
* @return 성공 시 0, 실패 시 음수 에러 코드
*/
int32_t ad7124_remove(struct ad7124_dev *dev)
{
if (!dev)
return INVALID_VAL;
// 디바이스 구조체 메모리 해제
free(dev);
return 0;
}
#include "stm32l4xx_hal.h"
#include "ad7124.h"
#include <stdio.h>
#include <string.h>
/* SPI 및 UART 핸들러 전역 변수 선언 */
SPI_HandleTypeDef hspi1; // SPI1 핸들러: AD7124와 SPI 통신을 위한 설정
UART_HandleTypeDef huart2; // UART2 핸들러: 디버깅 메시지 출력용
/* AD7124 디바이스 구조체 포인터 */
struct ad7124_dev *dev; // AD7124 디바이스 구조체: 드라이버 상태와 설정 저장
/* UART 출력용 버퍼 */
char uart_buf[100]; // UART 출력 메시지 저장 버퍼: 디버깅 메시지 포맷팅용
uint32_t uart_buf_len; // UART 출력 메시지 길이: 전송할 데이터 길이 저장
/* GPIO 핀 정의 */
#define CS_PORT GPIOA // AD7124 CS(칩 선택) 핀 포트: GPIOA 사용
#define CS_PIN GPIO_PIN_4 // AD7124 CS 핀 번호: PA4 핀
#define RDY_PORT GPIOB // AD7124 RDY(데이터 준비) 핀 포트: GPIOB 사용
#define RDY_PIN GPIO_PIN_0 // AD7124 RDY 핀 번호: PB0 핀
/**
* @brief 시스템 클럭 설정 (80 MHz)
* @note STM32L432KC 기준으로 HSI 및 PLL을 사용하여 80 MHz 클럭 설정
* - HSI(16 MHz)를 PLL 소스로 사용
* - PLL 설정: PLLM=1, PLLN=10, PLLR=2로 SYSCLK=80 MHz
* - 전압 스케일링 VOS1으로 고성능 설정
* - AHB, APB1, APB2 클럭은 분주 없이 사용
*/
static void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // 오실레이터 설정 구조체 초기화
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 클럭 설정 구조체 초기화
// 전원 제어 클럭 활성화: 전압 스케일링 제어를 위해 필요
__HAL_RCC_PWR_CLK_ENABLE();
// 전압 스케일링 설정: VOS1(고성능 모드) 선택, 80 MHz 동작 보장
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
// HSI 오실레이터 설정
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; // HSI(16 MHz) 선택
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; // PLL P 출력 분주기 (사용 안 함)
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // PLL Q 출력 분주기 (사용 안 함)
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // PLL R 출력 분주기: 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) {
while (1); // 초기화 실패 시 무한 루프
}
}
/**
* @brief SPI1 초기화 (AD7124와 통신용)
* @note AD7124 데이터시트(Rev. D) 기준 SPI 설정:
* - 데이터 크기: 8비트
* - 클럭 폴리티: CPOL=0 (LOW)
* - 클럭 페이즈: CPHA=1 (2nd 에지)
* - 최대 클럭 속도: 5 MHz
* - 소프트웨어 CS 제어
* - MSB 우선 전송
*/
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1; // SPI1 모듈 선택
hspi1.Init.Mode = SPI_MODE_MASTER; // 마스터 모드: STM32가 AD7124 제어
hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 양방향 통신 (MOSI, MISO)
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8비트 데이터 전송 (AD7124 레지스터 단위)
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 클럭 폴리티: LOW (CPOL=0)
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // 클럭 페이즈: 2nd 에지 (CPHA=1)
hspi1.Init.NSS = SPI_NSS_SOFT; // 소프트웨어 CS 제어: GPIO로 수동 제어
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 클럭 분주기: 80 MHz / 16 = 5 MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // MSB 우선 전송: AD7124 요구사항
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // TI 모드 비활성화
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // SPI CRC 비활성화 (AD7124는 자체 CRC 사용)
hspi1.Init.CRCPolynomial = 7; // CRC 다항식 (사용되지 않음)
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
while (1); // 초기화 실패 시 무한 루프
}
}
/**
* @brief USART2 초기화 (디버깅 출력용)
* @note STM32L432KC의 USART2를 사용하여 디버깅 메시지 출력
* - 보드레이트: 115200
* - 데이터 비트: 8비트
* - 패리티: 없음
* - 정지 비트: 1
*/
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2; // USART2 모듈 선택
huart2.Init.BaudRate = 115200; // 보드레이트: 115200 bps
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비트 샘플링 비활성화
if (HAL_UART_Init(&huart2) != HAL_OK) {
while (1); // 초기화 실패 시 무한 루프
}
}
/**
* @brief GPIO 초기화
* @note AD7124의 CS 및 RDY 핀 설정:
* - CS (PA4): 출력, 풀업, 고속
* - RDY (PB0): 입력, 하강 에지 인터럽트 활성화
* - SPI1 관련 핀 (PA5/SCK, PA6/MISO, PA7/MOSI)도 설정
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO 포트 클럭 활성화
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// CS 핀 (PA4) 설정: 출력, 풀업, 고속
GPIO_InitStruct.Pin = CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 푸시-풀 출력
GPIO_InitStruct.Pull = GPIO_PULLUP; // 풀업 저항
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 고속 동작
HAL_GPIO_Init(CS_PORT, &GPIO_InitStruct);
// RDY 핀 (PB0) 설정: 입력, 하강 에지 인터럽트
GPIO_InitStruct.Pin = RDY_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 하강 에지 인터럽트
GPIO_InitStruct.Pull = GPIO_PULLUP; // 풀업 저항
HAL_GPIO_Init(RDY_PORT, &GPIO_InitStruct);
// SPI1 핀 설정 (PA5: SCK, PA6: MISO, PA7: MOSI)
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 대체 기능 (SPI)
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);
// RDY 핀 인터럽트 활성화 (EXTI0)
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/**
* @brief 외부 인터럽트 콜백 함수
* @note RDY 핀 (PB0)의 하강 에지 감지 시 호출
* - AD7124의 데이터 준비 신호를 감지하여 변환 데이터 읽기
* - 읽은 데이터를 UART로 출력
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == RDY_PIN) { // RDY 핀 인터럽트 확인
int32_t data;
int32_t ret;
// AD7124에서 변환 데이터 읽기 (READ_DATA 명령: 0x42 전송)
ret = ad7124_read_data(dev, &data);
if (ret == 0) {
// 데이터 읽기 성공 시 UART로 출력
uart_buf_len = sprintf(uart_buf, "ADC Data: %ld\r\n", data);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
} else {
// 데이터 읽기 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "Read Error: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
}
}
}
/**
* @brief 메인 함수
* @note AD7124 초기화 및 연속 변환 모드 설정 후 무한 루프
* - 시스템 클럭, SPI, UART, GPIO 초기화
* - AD7124 초기화 및 기본 설정
* - RDY 인터럽트를 통해 데이터 읽기
*/
int main(void)
{
// HAL 라이브러리 초기화
HAL_Init();
// 시스템 클럭 설정 (80 MHz)
SystemClock_Config();
// SPI, UART, GPIO 초기화
MX_SPI1_Init();
MX_USART2_UART_Init();
MX_GPIO_Init();
// AD7124 초기화 파라미터 설정
struct ad7124_init_param init_param = {
.hspi = &hspi1, // SPI 핸들러
.regs = ad7124_regs, // 레지스터 배열
.spi_rdy_poll_cnt = 10000, // SPI 준비 대기 폴링 횟수
.cs_port = CS_PORT, // CS 핀 포트
.cs_pin = CS_PIN // CS 핀 번호
};
// AD7124 디바이스 초기화
int32_t ret = ad7124_setup(&dev, init_param);
if (ret != 0) {
// 초기화 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "AD7124 Setup Failed: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
while (1); // 무한 루프
}
// 초기화 성공 메시지 출력
uart_buf_len = sprintf(uart_buf, "AD7124 Setup Successful\r\n");
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
// ADC 제어 레지스터 설정: 연속 변환 모드, 내부 참조 전압 활성화
dev->regs[AD7124_ADC_Control].value = AD7124_ADC_CTRL_REG_REF_EN | // 내부 2.5V 참조 전압
AD7124_ADC_CTRL_REG_MODE(0) | // 연속 변환 모드
AD7124_ADC_CTRL_REG_POWER_MODE(2); // 고속 모드
ret = ad7124_write_register(dev, dev->regs[AD7124_ADC_Control]);
if (ret != 0) {
// 레지스터 쓰기 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "ADC Control Write Failed: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
while (1);
}
// 채널 0 설정: AIN0 (+) 및 AIN1 (-), 설정 0 사용, 채널 활성화
dev->regs[AD7124_Channel_0].value = AD7124_CH_MAP_REG_CH_ENABLE | // 채널 활성화
AD7124_CH_MAP_REG_SETUP(0) | // 설정 0 선택
AD7124_CH_MAP_REG_AINP(0) | // 양극 입력: AIN0
AD7124_CH_MAP_REG_AINM(1); // 음극 입력: AIN1
ret = ad7124_write_register(dev, dev->regs[AD7124_Channel_0]);
if (ret != 0) {
// 채널 설정 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "Channel 0 Write Failed: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
while (1);
}
// 설정 0 레지스터: 양극성 입력, 내부 참조 전압, 이득 1
dev->regs[AD7124_Config_0].value = AD7124_CFG_REG_BIPOLAR | // 양극성 입력
AD7124_CFG_REG_REF_SEL(0) | // 내부 참조 전압
AD7124_CFG_REG_PGA(0); // 이득 1
ret = ad7124_write_register(dev, dev->regs[AD7124_Config_0]);
if (ret != 0) {
// 설정 쓰기 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "Config 0 Write Failed: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
while (1);
}
// 필터 0 레지스터: Sinc3 필터, 50 SPS (데이터시트 FS=384 설정 기준)
dev->regs[AD7124_Filter_0].value = AD7124_FILT_REG_FILTER(1) | // Sinc3 필터
AD7124_FILT_REG_FS(384); // 50 SPS
ret = ad7124_write_register(dev, dev->regs[AD7124_Filter_0]);
if (ret != 0) {
// 필터 설정 실패 시 에러 메시지 출력
uart_buf_len = sprintf(uart_buf, "Filter 0 Write Failed: %ld\r\n", ret);
HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 1000);
while (1);
}
// 무한 루프: RDY 인터럽트를 통해 데이터 읽기 처리
while (1) {
// 인터럽트 기반으로 동작하므로 메인 루프는 비워둠
}
}
결론
본 드라이버는 STM32 HAL 라이브러리를 기반으로 AD7124의 핵심 기능(레지스터 읽기/쓰기, 변환 데이터 수집, 리셋, CRC 검증 등)을 안정적으로 구현하였으며, 모듈화된 구조를 통해 다양한 STM32 시리즈와 손쉽게 연동할 수 있습니다. 사용자는 애플리케이션 요구사항에 맞게 레지스터 설정을 변경하여 고정밀 데이터 수집 시스템을 구축할 수 있습니다. 특히 RDY 핀 인터럽트 기반 데이터 처리와 CRC 지원 기능은 신뢰성을 강화하며, 확장성과 재사용성을 고려한 코드 구조는 장기적인 유지보수에도 유리합니다. 따라서 본 구현은 산업용 계측, 의료 기기, 환경 모니터링 등 정밀 아날로그 신호 측정이 필요한 응용 분야에서 활용할 수 있습니다.
'아날로그회로(Analog Circuit) > ADC관련' 카테고리의 다른 글
ZSSC3123 Capacitance to Digital Conditoner IC와 Arduino 인터페이스 구현 (2) | 2025.09.01 |
---|---|
ADS124S08 24비트 ADC STM32 HAL 기반 디바이스 드라이버 구현 (1) | 2025.08.28 |
ZSC31015 Sensor Signal Conditioner IC 드라이버 구현: STM32를 활용한 Zacwire 인터페이싱 (4) | 2025.08.25 |
ADS114S08 16비트 ADC STM32 HAL 기반 디바이스 드라이버 구현 (0) | 2025.08.24 |
PT100/PT1000 RTD 센서용 MAX31865 온도 변환 IC STM32 디바이스 드라이버 구현 (3) | 2025.08.24 |
PGA302 Sensor Signal Conditioner IC STM32용 I2C 드라이버 구현 (0) | 2025.08.20 |
ZSC31050 Sensor Signal Conditioner IC STM32용 I2C 드라이버 구현 (0) | 2025.08.16 |
ZSSC3230 Sensor Signal Conditioner IC 보정 절차 및 Calibration Math 분석 (1) | 2025.08.15 |