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

STM32 에서 AD9833 DDS 파형발생기 제어: 드라이버 설계와 코드 구현

by linuxgo 2025. 8. 23.
반응형

이 글에서는 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 인터페이스.
  • 신호 흐름:
    1. MCLK 입력 → 위상 어큐뮬레이터.
    2. SIN ROM (사인파) 또는 MUX (삼각파/구형파)로 전달.
    3. 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).
  • 프로그래밍 시퀀스:
    1. 리셋 (RESET=1 → 0).
    2. 제어 비트 설정 (파형, 슬립 모드 등).
    3. 주파수/위상 레지스터 작성.
    4. 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.
  • 사용 예시:
    1. 초기화: 리셋 후 사인파.
    2. 주파수 설정: FREQ0에 1 kHz.
    3. 파형 선택: 사인파, 구형파 등.

하드웨어 연결

  • 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): 출력 파형 정확도 확인 → 오실로스코프 사용
  • 에러 처리: 통신 오류, 레지스터 설정 오류에 대한 예외 처리 루틴 구현

앞으로는 이 드라이버를 기반으로 테스트 장비, 센서 응용, 주파수 스위프 시스템 등 다양한 응용으로 확장할 수 있습니다.

참고 문헌

  1. Analog Devices, "AD9833 Data Sheet," Rev. G, www.analog.com.
반응형