이 글에서는 Analog Devices의 AD9833 DDS(직접 디지털 합성, Direct Digital Synthesis) 파형 발생기 IC와 이를 STM32 마이크로컨트롤러에서 제어하기 위한 드라이버 구현을 상세히 다룹니다.
AD9833은 사인파, 삼각파, 구형파를 고정밀으로 생성할 수 있는 저전력 프로그래머블 IC로, SPI 인터페이스를 통해 제어됩니다. 28비트 주파수 해상도와 12비트 위상 제어를 지원하며, 외부 컴포넌트 없이 최대 12.5 MHz의 파형을 출력할 수 있습니다. 이러한 특성 덕분에 AD9833은 테스트 장비, 센서 응용(근접/모션/결함 감지), 주파수 스위프, 클럭 생성 등 다양한 분야에서 활용됩니다.
본 글에서는 다음을 다룹니다:
- AD9833의 주요 특징과 동작 원리 – 데이터시트(Rev. G)를 바탕으로 정리
- STM32Cube HAL 기반 AD9833 드라이버 구현 – 초기화, 주파수/위상 설정, 파형 선택, 슬립 모드 등 모든 기능 커버
- 실제 코드 예제와 상세 한글 주석 – STM32 프로젝트에 쉽게 적용 가능하도록 제공
AD9833 IC 사양
AD9833은 저전력, 프로그래머블 DDS IC로, 사인파, 삼각파, 구형파를 생성합니다. SPI 인터페이스를 통해 제어됴며, 외부 컴포넌트 없이 고정밀 파형을 출력합니다.
일반 특징 및 응용
- 특징:
- 디지털 프로그래머블 주파수 및 위상 (28비트 주파수, 12비트 위상).
- 출력 파형: 사인파, 삼각파, 구형파 (MSB 또는 MSB/2).
- 3-와이어 SPI 인터페이스 (최대 40 MHz 클럭).
- 저전력 슬립 모드: MCLK 비활성화, DAC 파워다운.
- 외부 컴포넌트 불필요.
- 전원 공급: 2.3 V ~ 5.5 V.
- 동작 온도: -40°C ~ +105°C.
- 패키지: 10-lead MSOP, 자동차 인증 (AEC-Q100).
- 응용:
- 주파수 자극/파형 생성, 유량 측정.
- 센서 응용: 근접, 모션, 결함 감지.
- 테스트/의료 장비, 주파수 스위프, 클럭 생성기, TDR.
기능 블록 다이어그램
- 주요 컴포넌트: DDS 코어, 28비트 위상 어큐뮬레이터, SIN ROM, 10비트 DAC, 레귤레이터, MUX, SPI 인터페이스.
- 신호 흐름:
- MCLK 입력 → 위상 어큐뮬레이터.
- SIN ROM (사인파) 또는 MUX (삼각파/구형파)로 전달.
- 10비트 DAC로 아날로그 변환 → IOUT 출력.
사양 상세
- 전원:
- VDD: 2.3 V ~ 5.5 V.
- 소비 전력: 12.65 mW (3 V, 5.5 mA).
- 출력 주파수:
- 범위: 0 Hz ~ 12.5 MHz (MCLK=25 MHz).
- 해상도: 0.1 Hz (25 MHz), 0.004 Hz (1 MHz).
- DAC:
- 해상도: 10비트, 25 MSPS.
- 출력 전압: 최대 0.65 V, 최소 38 mV (VDD=3.3V).
- INL: ±1 LSB, DNL: ±0.5 LSB.
- 동적 사양:
- SNR: 55~60 dB.
- THD: -66~-56 dBc.
- SFDR: -60 dBc (wide), -78 dBc (narrow).
- 로직 입력:
- VINH: 1.7 V (2.3 V VDD) ~ 2.8 V (5.5 V VDD).
- VINL: 0.5 V ~ 0.8 V.
- 클럭:
- MCLK: 최대 25 MHz, 일반적으로 25 MHz 외부 클럭 사용.
- 온도 범위: -40°C ~ +105°C.
핀 설명
- COMP: DAC 바이어스 커패시터.
- VDD: 전원 (2.3 V ~ 5.5 V).
- CAP/2.5V: 내부 레귤레이터 커패시터 (2.5 V).
- DGND: 디지털 그라운드.
- MCLK: 외부 클럭 입력 (최대 25 MHz).
- FSYNC/SCK/SDATA: SPI 인터페이스.
- IOUT: 파형 출력.
레지스터 맵 및 프로그래밍 시퀀스
- 명령/레지스터:
- 제어 (0x0000): 16비트, B28, HLB, FSELECT, PSELECT, RESET, SLEEP1/12, OPBITEN, DIV2, MODE.
- FREQ0/1: 28비트, LSB (0x4000/0x6000), MSB (0x5000/0x7000).
- PHASE0/1: 12비트 (0xC000/0xE000).
- 제어 비트:
- B28: 28비트 주파수 쓰기 (1), 14비트 쓰기 (0).
- HLB: 14비트 쓰기 시 상/하위 선택.
- FSELECT/PSELECT: FREQ0/1, PHASE0/1 선택.
- RESET: 리셋 활성화.
- SLEEP1: MCLK 비활성화.
- SLEEP12: DAC 파워다운.
- OPBITEN: 구형파 출력 (MSB/MSB2).
- DIV2: 구형파 주파수 MSB (1) 또는 MSB/2 (0).
- MODE: 삼각파 (1), 사인파 (0).
- 프로그래밍 시퀀스:
- 리셋 (RESET=1 → 0).
- 제어 비트 설정 (파형, 슬립 모드 등).
- 주파수/위상 레지스터 작성.
- FSELECT/PSELECT로 출력 선택.
애플리케이션 노트
- 주파수 코드: (freq_hz * 2^28) / MCLK.
- 위상 코드: (phase_deg * 4096) / 360.
- 구형파: OPBITEN=1 시 MSB (DIV2=1) 또는 MSB/2 (DIV2=0).
- 저전력: SLEEP1/12로 소비 전력 최소화.
AD9833 드라이버 구현 상세
드라이버는 SPI 인터페이스를 통해 AD9833의 모든 기능을 제어합니다. STM32Cube HAL 라이브러리를 사용하여 STM32F4 시리즈에 최적화되었습니다. 주요 기능은 초기화, 주파수/위상 설정, 파형 선택, 슬립 모드입니다.
구현 내용
- 헤더 파일: 레지스터 주소, 제어 비트, 함수 프로토타입 정의.
- 소스 파일:
- 헬퍼 함수: 16비트 SPI 명령 쓰기.
- 초기화: 리셋 후 사인파 기본 설정.
- 제어: 리셋, 전체 제어 비트 설정.
- 주파수/위상: FREQ0/1, PHASE0/1 설정 (28비트 주파수, 12비트 위상).
- 선택: 활성 주파수/위상 레지스터 선택.
- 파형: 사인, 삼각, 구형파 (MSB/MSB/2) 설정.
- 슬립 모드: MCLK/DAC 비활성화.
- 주파수 계산: (freq_hz * 2^28) / MCLK.
- 사용 예시:
- 초기화: 리셋 후 사인파.
- 주파수 설정: FREQ0에 1 kHz.
- 파형 선택: 사인파, 구형파 등.
하드웨어 연결
- SPI1:
- MOSI: PA7.
- SCK: PA5.
- CS: PA4 (GPIO 출력).
- 속도: 최대 40 MHz, 여기서는 1 MHz (prescaler 16).
- MCLK: 25 MHz 외부 클럭.
- VDD: 3.3 V.
- DGND: 그라운드.
STM32CubeIDE 설정
- SPI1: 활성화, 모드 0 (CPOL=0, CPHA=0), 8비트, MSB First.
- GPIO: PA4를 출력으로 설정 (CS).
- 클럭: 예: 84 MHz (STM32F401).
- 프로젝트: HAL 기반, hspi1 핸들 사용.
드라이버 코드
ad9833_driver.h
#ifndef AD9833_DRIVER_H
#define AD9833_DRIVER_H
#include "stm32f4xx_hal.h"
// 레지스터 주소 및 명령 - 데이터시트 참조
#define AD9833_CMD_CONTROL 0x0000 // 제어 레지스터: 파형, 선택, 슬립 모드 등
#define AD9833_CMD_FREQ0_LSB 0x4000 // FREQ0 하위 14비트
#define AD9833_CMD_FREQ0_MSB 0x5000 // FREQ0 상위 14비트
#define AD9833_CMD_FREQ1_LSB 0x6000 // FREQ1 하위 14비트
#define AD9833_CMD_FREQ1_MSB 0x7000 // FREQ1 상위 14비트
#define AD9833_CMD_PHASE0 0xC000 // PHASE0 12비트
#define AD9833_CMD_PHASE1 0xE000 // PHASE1 12비트
// 제어 레지스터 비트 - 제어 플래그 정의
#define AD9833_CTRL_B28 (1 << 13) // 28비트 주파수 쓰기 모드 (LSB/MSB 분리)
#define AD9833_CTRL_HLB (1 << 12) // B28=0일 때 상/하위 바이트 선택
#define AD9833_CTRL_FSELECT (1 << 11) // FREQ1 (1) 또는 FREQ0 (0) 선택
#define AD9833_CTRL_PSELECT (1 << 10) // PHASE1 (1) 또는 PHASE0 (0) 선택
#define AD9833_CTRL_RESET (1 << 8) // 리셋 활성화
#define AD9833_CTRL_SLEEP1 (1 << 7) // MCLK 비활성화 (저전력 모드)
#define AD9833_CTRL_SLEEP12 (1 << 6) // DAC 파워다운
#define AD9833_CTRL_OPBITEN (1 << 5) // 구형파 출력 활성화 (MSB/MSB2)
#define AD9833_CTRL_DIV2 (1 << 3) // 구형파: MSB (1) 또는 MSB/2 (0)
#define AD9833_CTRL_MODE (1 << 1) // 삼각파 (1) 또는 사인파 (0)
// 함수 프로토타입 - AD9833의 모든 기능 커버
HAL_StatusTypeDef AD9833_WriteCommand(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint16_t cmd); // 16비트 SPI 명령 쓰기
HAL_StatusTypeDef AD9833_Init(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin); // 초기화: 리셋 후 사인파 설정
HAL_StatusTypeDef AD9833_Reset(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t enable); // 리셋 활성/비활성
HAL_StatusTypeDef AD9833_SetControl(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint16_t control_bits); // 전체 제어 비트 설정
HAL_StatusTypeDef AD9833_SetFrequency(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t freq_reg, uint32_t freq_hz, uint32_t mclk_hz); // 주파수 설정 (FREQ0/1)
HAL_StatusTypeDef AD9833_SetPhase(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t phase_reg, uint16_t phase_deg); // 위상 설정 (PHASE0/1)
HAL_StatusTypeDef AD9833_SelectFreqPhase(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t fselect, uint8_t pselect); // 주파수/위상 레지스터 선택
HAL_StatusTypeDef AD9833_SetWaveform(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t mode, uint8_t opbiten, uint8_t div2); // 파형 설정 (사인/삼각/구형)
HAL_StatusTypeDef AD9833_SleepMode(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t sleep1, uint8_t sleep12); // 슬립 모드 설정
#endif // AD9833_DRIVER_H
ad9833_driver.c
#include "ad9833_driver.h"
#include <math.h> // 주파수 코드 계산을 위한 pow() 함수 포함
// 16비트 SPI 명령 쓰기
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택 GPIO, cmd - 16비트 명령 (레지스터 + 데이터)
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_WriteCommand(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint16_t cmd) {
uint8_t data[2] = {(cmd >> 8) & 0xFF, cmd & 0xFF}; // 16비트 명령을 두 바이트로 분할 (MSB 우선)
HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); // 칩 선택 신호 저전위로 (전송 시작)
HAL_StatusTypeDef ret = HAL_SPI_Transmit(hspi, data, 2, HAL_MAX_DELAY); // 2바이트 전송
HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); // 칩 선택 신호 고전위로 (전송 종료)
return ret;
}
// AD9833 초기화: 리셋 후 기본 제어 설정 (사인파, 슬립 비활성화)
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택 GPIO
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_Init(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin) {
HAL_StatusTypeDef ret = AD9833_Reset(hspi, cs_port, cs_pin, 1); // 리셋 활성화
if (ret != HAL_OK) return ret;
HAL_Delay(10); // 리셋 안정화를 위한 대기 (데이터시트 권장)
ret = AD9833_Reset(hspi, cs_port, cs_pin, 0); // 리셋 비활성화
if (ret != HAL_OK) return ret;
return AD9833_SetControl(hspi, cs_port, cs_pin, 0x0000); // 기본 설정: 사인파, FREQ0, PHASE0, 슬립 비활성화
}
// 리셋 활성화/비활성화
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, enable - 1(리셋 활성), 0(비활성)
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_Reset(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t enable) {
uint16_t ctrl = enable ? AD9833_CTRL_RESET : 0; // 리셋 비트 설정 또는 해제
return AD9833_SetControl(hspi, cs_port, cs_pin, ctrl);
}
// 제어 레지스터에 전체 비트 설정
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, control_bits - 제어 플래그 비트마스크
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SetControl(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint16_t control_bits) {
return AD9833_WriteCommand(hspi, cs_port, cs_pin, control_bits); // 16비트 제어 명령 쓰기
}
// FREQ0 또는 FREQ1에 주파수 설정 (28비트 튜닝 워드, LSB → MSB 순서로 쓰기)
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, freq_reg - 0(FREQ0) 또는 1(FREQ1), freq_hz - 주파수(Hz), mclk_hz - MCLK (예: 25000000)
// 공식: freq_code = (freq_hz * 2^28) / mclk_hz
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SetFrequency(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t freq_reg, uint32_t freq_hz, uint32_t mclk_hz) {
uint32_t freq_code = (uint32_t)((freq_hz * pow(2, 28)) / mclk_hz); // 28비트 튜닝 워드 계산
uint16_t lsb_cmd = (freq_reg ? AD9833_CMD_FREQ1_LSB : AD9833_CMD_FREQ0_LSB) | (freq_code & 0x3FFF); // 하위 14비트 + 명령
uint16_t msb_cmd = (freq_reg ? AD9833_CMD_FREQ1_MSB : AD9833_CMD_FREQ0_MSB) | ((freq_code >> 14) & 0x3FFF); // 상위 14비트 + 명령
HAL_StatusTypeDef ret = AD9833_WriteCommand(hspi, cs_port, cs_pin, lsb_cmd); // LSB 먼저 쓰기 (데이터시트 순서)
if (ret != HAL_OK) return ret;
return AD9833_WriteCommand(hspi, cs_port, cs_pin, msb_cmd); // MSB 쓰기
}
// PHASE0 또는 PHASE1에 위상 설정 (12비트 코드)
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, phase_reg - 0(PHASE0) 또는 1(PHASE1), phase_deg - 위상(0-360도)
// 공식: phase_code = (phase_deg * 4096) / 360
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SetPhase(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t phase_reg, uint16_t phase_deg) {
uint16_t phase_code = (uint16_t)((phase_deg * 4096.0) / 360.0); // 각도를 12비트 위상 오프셋으로 변환
uint16_t cmd = (phase_reg ? AD9833_CMD_PHASE1 : AD9833_CMD_PHASE0) | (phase_code & 0x0FFF); // 명령 + 12비트 위상
return AD9833_WriteCommand(hspi, cs_port, cs_pin, cmd);
}
// 활성 주파수 및 위상 레지스터 선택
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, fselect - 1(FREQ1), 0(FREQ0), pselect - 1(PHASE1), 0(PHASE0)
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SelectFreqPhase(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t fselect, uint8_t pselect) {
uint16_t ctrl = (fselect ? AD9833_CTRL_FSELECT : 0) | (pselect ? AD9833_CTRL_PSELECT : 0); // FSELECT 및 PSELECT 비트 설정
return AD9833_SetControl(hspi, cs_port, cs_pin, ctrl);
}
// 파형 유형 설정: 사인파(mode=0), 삼각파(mode=1), 구형파(opbiten=1, div2=0/1로 MSB/MSB2)
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, mode - 1(삼각파), 0(사인파), opbiten - 1(구형파), div2 - 1(MSB), 0(MSB/2)
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SetWaveform(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t mode, uint8_t opbiten, uint8_t div2) {
uint16_t ctrl = (mode ? AD9833_CTRL_MODE : 0) | (opbiten ? AD9833_CTRL_OPBITEN : 0) | (div2 ? AD9833_CTRL_DIV2 : 0); // 파형 제어 비트 조합
return AD9833_SetControl(hspi, cs_port, cs_pin, ctrl);
}
// 슬립 모드 설정: MCLK 비활성화(sleep1), DAC 파워다운(sleep12)
// 매개변수: hspi - SPI 핸들, cs_port/pin - 칩 선택, sleep1 - 1(MCLK 비활성), sleep12 - 1(DAC 파워다운)
// 반환: 성공 시 HAL_OK
HAL_StatusTypeDef AD9833_SleepMode(SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, uint8_t sleep1, uint8_t sleep12) {
uint16_t ctrl = (sleep1 ? AD9833_CTRL_SLEEP1 : 0) | (sleep12 ? AD9833_CTRL_SLEEP12 : 0); // 슬립 모드 비트 조합
return AD9833_SetControl(hspi, cs_port, cs_pin, ctrl);
}
사용 예시
#include "ad9833_driver.h"
SPI_HandleTypeDef hspi1;
int main(void) {
HAL_Init();
// STM32CubeIDE에서 SystemClock_Config, MX_SPI1_Init, MX_GPIO_Init 설정 가정
// AD9833 초기화
AD9833_Init(&hspi1, GPIOA, GPIO_PIN_4);
// FREQ0에 1 kHz 설정 (25 MHz MCLK)
AD9833_SetFrequency(&hspi1, GPIOA, GPIO_PIN_4, 0, 1000, 25000000);
// PHASE0에 0도 설정
AD9833_SetPhase(&hspi1, GPIOA, GPIO_PIN_4, 0, 0);
// FREQ0 및 PHASE0 선택, 사인파 설정
AD9833_SelectFreqPhase(&hspi1, GPIOA, GPIO_PIN_4, 0, 0);
AD9833_SetWaveform(&hspi1, GPIOA, GPIO_PIN_4, 0, 0, 0); // 사인파
// 필요 시 구형파로 전환
// AD9833_SetWaveform(&hspi1, GPIOA, GPIO_PIN_4, 0, 1, 0); // 구형파 (MSB/2)
while (1) {
HAL_Delay(1000); // 계속 실행하거나 주파수/위상 변경
}
}
추가 참고
- 클럭: 25 MHz 외부 클럭 권장, 주파수 해상도 0.1 Hz.
- 디버깅: SPI 통신 오류 시 오실로스코프/로직 분석기 사용.
- 확장: 주파수 스위프 구현 시 FREQ0/1 전환 활용.
결론
이번 글에서는 AD9833 DDS 파형 발생기의 주요 사양과 동작 원리를 살펴보고, 이를 STM32Cube HAL 기반 드라이버 코드로 구현하는 방법을 상세히 다뤘습니다. 소개한 드라이버는 초기화, 주파수/위상 설정, 파형 선택, 슬립 모드까지 포함하여 데이터시트(Rev. G)의 모든 핵심 기능을 STM32 환경에서 바로 활용할 수 있도록 설계되었습니다.
실제 프로젝트에 적용할 때는 다음 사항에 유의해야 합니다:
- 하드웨어 연결: SPI 라인의 안정성, MCLK(25MHz) 외부 클럭 공급, 전원(3.3V/5V) 호환성 확인
- 클럭 및 타이밍 설정: STM32 SPI 클럭과 AD9833 타이밍 일치 여부 검증
- 보정(Calibration): 출력 파형 정확도 확인 → 오실로스코프 사용
- 에러 처리: 통신 오류, 레지스터 설정 오류에 대한 예외 처리 루틴 구현
앞으로는 이 드라이버를 기반으로 테스트 장비, 센서 응용, 주파수 스위프 시스템 등 다양한 응용으로 확장할 수 있습니다.
참고 문헌
- Analog Devices, "AD9833 Data Sheet," Rev. G, www.analog.com.