본문 바로가기
MCU/C2000

[TMS320F28388D] DMA 사용법: DriverLib API로 DMA 설정 및 코드

by linuxgo 2025. 8. 17.
반응형

1. TMS320F28388D DMA 개요

TMS320F28388D는 Texas Instruments의 C2000 시리즈 32비트 마이크로컨트롤러로, 최대 6개의 DMA(Direct Memory Access) 채널을 제공합니다. DMA 모듈은 CPU 개입 없이 메모리 간 데이터 전송을 처리하여 실시간 애플리케이션에서 효율적인 데이터 이동을 가능하게 합니다. 모터 제어, 데이터 로깅, ADC 결과 처리 등에 적합합니다.

DMA 모듈의 주요 특징

  • 채널: 6개의 독립적인 DMA 채널 제공.
  • 트리거 소스: ePWM, ADC, SPI, McBSP, 외부 인터럽트 등 다양한 트리거 소스 지원.
  • 전송 모드: 단일 전송, 블록 전송, 버스트 전송 지원.
  • 데이터 크기: 16비트 또는 32비트 단위 전송.
  • 메모리 액세스: RAM, Flash, 외부 메모리 등 다양한 메모리 공간 접근 가능.
  • 인터럽트: 전송 완료 또는 에러 시 인터럽트 발생 가능.

DriverLib API는 복잡한 레지스터 조작을 추상화하여 DMA 설정을 간소화합니다.

2. 주요 DMA DriverLib API 함수

아래는 TMS320F28388D의 DMA 모듈을 제어하기 위해 자주 사용되는 DriverLib API 함수와 그 사용 방법입니다.

2.1. DMA 기본 설정 관련 함수

  • DMA_initController()
    • 설명: DMA 컨트롤러를 초기화하여 모든 채널을 기본 상태로 설정합니다.
    • 매개변수: 없음.
    • 사용 예: 시스템 시작 시 DMA 모듈 초기화: DMA_initController();
  • DMA_configChannel()
    • 설명: 특정 DMA 채널의 동작 모드를 설정합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소 (예: DMA_CHANNEL1).
      • channel: 채널 번호 (1~6).
      • mode: 동작 모드 (예: DMA_MODE_BLOCK, DMA_MODE_SINGLE).
    • 사용 예: DMA 채널 1을 블록 전송 모드로 설정: DMA_configChannel(DMA_CHANNEL1, 1, DMA_MODE_BLOCK);
  • DMA_configAddresses()
    • 설명: DMA 전송의 소스 및 목적지 주소를 설정합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
      • dest: 목적지 메모리 주소 (포인터).
      • src: 소스 메모리 주소 (포인터).
    • 사용 예: ADC 결과에서 버퍼로 전송: DMA_configAddresses(DMA_CHANNEL1, adcBuffer, (uint16_t *)ADCARESULT_BASE);
  • DMA_configBurst()
    • 설명: 버스트 전송 크기와 주소 증가 단위를 설정합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
      • size: 버스트당 전송 단위 수 (1~16).
      • srcStep: 소스 주소 증가 단위 (예: 1 또는 0).
      • destStep: 목적지 주소 증가 단위.
    • 사용 예: 1단위 전송, 목적지 증가: DMA_configBurst(DMA_CHANNEL1, 1, 0, 1);
  • DMA_configTransfer()
    • 설명: 전체 전송 크기와 주소 증가 단위를 설정합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
      • size: 전체 전송 단위 수.
      • srcStep: 소스 주소 증가 단위.
      • destStep: 목적지 주소 증가 단위.
    • 사용 예: 256단위 전송: DMA_configTransfer(DMA_CHANNEL1, 256, 0, 1);
  • DMA_configTrigger()
    • 설명: DMA 전송 트리거 소스를 설정합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
      • trigger: 트리거 소스 (예: DMA_TRIGGER_EPWM1_SOCA, DMA_TRIGGER_SOFTWARE).
    • 사용 예: ePWM1 SOCA 트리거 설정: DMA_configTrigger(DMA_CHANNEL1, DMA_TRIGGER_EPWM1_SOCA);
  • DMA_enableTrigger()
    • 설명: DMA 채널의 트리거를 활성화합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
    • 사용 예: 트리거 활성화: DMA_enableTrigger(DMA_CHANNEL1);
  • DMA_startChannel()
    • 설명: DMA 채널을 시작하여 데이터 전송을 개시합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
    • 사용 예: DMA 전송 시작: DMA_startChannel(DMA_CHANNEL1);

2.2. 인터럽트 관련 함수

  • DMA_enableInterrupt()
    • 설명: DMA 채널의 인터럽트를 활성화합니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
    • 사용 예: 인터럽트 활성화: DMA_enableInterrupt(DMA_CHANNEL1);
  • DMA_setInterruptMode()
    • 설명: 인터럽트 발생 조건을 설정합니다 (예: 전송 완료 시).
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
      • mode: 인터럽트 모드 (예: DMA_INT_MODE_TRANSFER_COMPLETE).
    • 사용 예: 전송 완료 인터럽트 설정: DMA_setInterruptMode(DMA_CHANNEL1, DMA_INT_MODE_TRANSFER_COMPLETE);
  • DMA_clearInterruptFlag()
    • 설명: DMA 인터럽트 플래그를 지웁니다.
    • 매개변수:
      • base: DMA 채널의 베이스 주소.
    • 사용 예: 인터럽트 플래그 지우기: DMA_clearInterruptFlag(DMA_CHANNEL1);
  • Interrupt_register()
    • 설명: 인터럽트 서비스 루틴(ISR)을 등록합니다.
    • 매개변수:
      • intNumber: 인터럽트 번호 (예: INT_DMA_CH1).
      • handler: ISR 함수 포인터.
    • 사용 예: DMA 채널 1 ISR 등록: Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);

3. DMA 설정 및 동작 원리

  1. 시스템 초기화: 시스템 클럭, GPIO, 인터럽트 초기화 (Device_init(), Device_initGPIO()).
  2. DMA 컨트롤러 초기화: DMA_initController() 호출.
  3. DMA 채널 설정: 소스/목적지 주소, 전송 크기, 트리거 소스 설정.
  4. 트리거 및 인터럽트 설정: 트리거 소스와 인터럽트 활성화.
  5. DMA 시작: **DMA_startChannel()**로 데이터 전송 시작.

4. DMA 예제 코드

아래는 C2000Ware의 DriverLib를 기반으로 작성된 DMA 예제 코드로, Code Composer Studio(CCS)에서 실행 가능합니다. 각 예제는 독립적이며 상세한 주석을 포함합니다.

4.1. 예제 1: ADC 결과를 DMA로 버퍼에 저장

ADC 변환 결과를 DMA를 통해 메모리 버퍼로 전송합니다.

#include "driverlib.h"
#include "device.h"

// 디버깅용 상태 변수 (0: 초기화 완료, 1: DMA 동작 중, 0xFFFF: 오류)
volatile uint32_t dmaStatus = 0;

// ADC 결과 저장 버퍼
#define BUFFER_SIZE 256
uint16_t adcBuffer[BUFFER_SIZE];

// GPIO 초기화 함수
void initGPIO(void)
{
    // GPIO31을 디버깅용 LED로 설정 (LAUNCHXL-F28379D 기준)
    GPIO_setPinConfig(GPIO_31_GPIO31);        // GPIO31을 일반 GPIO로 설정
    GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT); // 출력 모드 설정
    GPIO_setPadConfig(31, GPIO_PULL_UP);       // 풀업 저항 활성화
    GPIO_writePin(31, 0);                     // 초기 LED OFF
}

// DMA 인터럽트 서비스 루틴
__interrupt void dmaCh1ISR(void)
{
    // DMA 인터럽트 플래그 지우기
    DMA_clearInterruptFlag(DMA_CHANNEL1);
    
    // PIE 인터럽트 그룹 ACK
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
    // DMA 동작 상태 업데이트
    dmaStatus = 1;
}

// ADC 및 DMA 초기화 함수
void initADCandDMA(void)
{
    // ADC-A 클럭 활성화
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_ADCA);
    
    // ADC 설정: 50MHz 클럭, 12비트 해상도, 단일 엔드 모드
    ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
    ADC_setVREF(ADCA_BASE, ADC_REFERENCE_INTERNAL, ADC_REFERENCE_3_3V);
    ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
    
    // ADC SOC0 설정: ePWM1 SOCA 트리거, ADCIN0 채널, 샘플링 윈도우 15 사이클
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);
    
    // ADC 컨버터 활성화
    ADC_enableConverter(ADCA_BASE);
    
    // ADC 안정화를 위해 1ms 대기
    DEVICE_DELAY_US(1000);
    
    // ePWM1 클럭 활성화
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM1);
    
    // ePWM1 주기 설정: 100kHz (200MHz / 2000 = 100kHz)
    EPWM_setTimeBasePeriod(EPWM1_BASE, 2000);
    
    // ePWM1 카운터 모드: Up 카운터
    EPWM_setCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
    
    // SOCA 트리거 설정: 카운터=0일 때 발생
    EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
    EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);
    EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
    
    // ePWM1 모듈 활성화
    EPWM_enableModule(EPWM1_BASE);
    
    // DMA 컨트롤러 초기화
    DMA_initController();
    
    // DMA 채널 1 설정: 블록 전송 모드
    DMA_configChannel(DMA_CHANNEL1, DMA_CH1_BASE, DMA_MODE_BLOCK);
    
    // DMA 주소 설정: ADC 결과 레지스터에서 adcBuffer로 전송
    DMA_configAddresses(DMA_CHANNEL1, adcBuffer, (uint16_t *)ADCARESULT_BASE);
    
    // 버스트 전송: 단일 16비트 데이터, 소스 고정, 목적지 증가
    DMA_configBurst(DMA_CHANNEL1, 1, 0, 1);
    
    // 전체 전송: BUFFER_SIZE(256) 단위 전송
    DMA_configTransfer(DMA_CHANNEL1, BUFFER_SIZE, 0, 1);
    
    // 트리거 소스: ePWM1 SOCA
    DMA_configTrigger(DMA_CHANNEL1, DMA_TRIGGER_EPWM1_SOCA);
    
    // DMA 인터럽트 활성화
    DMA_enableInterrupt(DMA_CHANNEL1);
    
    // DMA 트리거 활성화
    DMA_enableTrigger(DMA_CHANNEL1);
    
    // DMA 채널 시작
    DMA_startChannel(DMA_CHANNEL1);
}

void main(void)
{
    // 시스템 클럭, PLL, Watchdog, 주변 장치 초기화
    Device_init();
    
    // GPIO 기본 초기화
    Device_initGPIO();
    
    // 디버깅용 GPIO 설정
    initGPIO();
    
    // 인터럽트 모듈 및 벡터 테이블 초기화
    Interrupt_initModule();
    Interrupt_initVectorTable();
    
    // DMA 채널 1 인터럽트 ISR 등록
    Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);
    
    // 시스템 클럭 확인 (200MHz 예상)
    uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
    if (sysClockHz != 200000000)
    {
        dmaStatus = 0xFFFF; // 오류 상태
        GPIO_writePin(31, 0); // LED OFF로 오류 표시
        ESTOP0; // 디버깅용 정지
    }
    
    // ADC 및 DMA 초기화
    initADCandDMA();
    
    // 초기화 오류 확인
    if (dmaStatus != 1)
    {
        dmaStatus = 0xFFFF; // 초기화 실패
        GPIO_writePin(31, 0); // LED OFF
        ESTOP0; // 디버깅용 정지
    }
    
    // 글로벌 인터럽트 활성화
    EINT;
    
    // Watchdog 활성화 (실시간 애플리케이션용)
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
    SysCtl_serviceWatchdog();
    
    // 무한 루프: DMA가 하드웨어로 자동 전송
    for(;;)
    {
        // 디버깅용: 상태 표시 (정상 동작 시 LED ON)
        GPIO_writePin(31, (dmaStatus == 1) ? 1 : 0);
        DEVICE_DELAY_US(1000000); // 1초 대기 (CPU 부하 감소)
    }
}

설명:

  • 기능: ePWM1의 SOCA 트리거로 ADC 변환 결과를 DMA를 통해 메모리 버퍼로 전송.
  • 설정: ADC(12비트, 50MHz), ePWM1(100kHz, SOCA 트리거), DMA 채널 1(ADC 결과 → 버퍼).
  • GPIO: GPIO31(LED).
  • 출력: ADC 결과가 adcBuffer에 저장, 정상 동작 시 LED ON.
  • 용도: 실시간 데이터 수집, 모터 제어 피드백.

4.2. 예제 2: 메모리 간 데이터 전송

DMA를 사용하여 메모리 간 데이터를 전송합니다.

#include "driverlib.h"
#include "device.h"

// 디버깅용 상태 변수 (0: 초기화 완료, 1: DMA 동작 중, 0xFFFF: 오류)
volatile uint32_t dmaStatus = 0;

// 소스 및 목적지 버퍼
#define BUFFER_SIZE 256
uint16_t srcBuffer[BUFFER_SIZE];
uint16_t destBuffer[BUFFER_SIZE];

// GPIO 초기화 함수
void initGPIO(void)
{
    // GPIO31을 디버깅용 LED로 설정 (LAUNCHXL-F28379D 기준)
    GPIO_setPinConfig(GPIO_31_GPIO31);        // GPIO31을 일반 GPIO로 설정
    GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT); // 출력 모드 설정
    GPIO_setPadConfig(31, GPIO_PULL_UP);       // 풀업 저항 활성화
    GPIO_writePin(31, 0);                     // 초기 LED OFF
}

// DMA 인터럽트 서비스 루틴
__interrupt void dmaCh1ISR(void)
{
    // DMA 인터럽트 플래그 지우기
    DMA_clearInterruptFlag(DMA_CHANNEL1);
    
    // PIE 인터럽트 그룹 ACK
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
    // DMA 동작 상태 업데이트
    dmaStatus = 1;
}

// DMA 초기화 함수
void initDMA(void)
{
    // 소스 버퍼 초기화 (0부터 255까지 값으로 채움)
    for (uint16_t i = 0; i < BUFFER_SIZE; i++)
    {
        srcBuffer[i] = i;
    }
    
    // DMA 컨트롤러 초기화
    DMA_initController();
    
    // DMA 채널 1 설정: 블록 전송 모드
    DMA_configChannel(DMA_CHANNEL1, DMA_CH1_BASE, DMA_MODE_BLOCK);
    
    // DMA 주소 설정: srcBuffer에서 destBuffer로 전송
    DMA_configAddresses(DMA_CHANNEL1, destBuffer, srcBuffer);
    
    // 버스트 전송: 단일 16비트 데이터, 소스 및 목적지 주소 증가
    DMA_configBurst(DMA_CHANNEL1, 1, 1, 1);
    
    // 전체 전송: BUFFER_SIZE(256) 단위 전송
    DMA_configTransfer(DMA_CHANNEL1, BUFFER_SIZE, 1, 1);
    
    // 트리거 소스: 소프트웨어 트리거
    DMA_configTrigger(DMA_CHANNEL1, DMA_TRIGGER_SOFTWARE);
    
    // DMA 인터럽트 활성화
    DMA_enableInterrupt(DMA_CHANNEL1);
    
    // DMA 트리거 활성화
    DMA_enableTrigger(DMA_CHANNEL1);
    
    // DMA 채널 시작
    DMA_startChannel(DMA_CHANNEL1);
}

void main(void)
{
    // 시스템 클럭, PLL, Watchdog, 주변 장치 초기화
    Device_init();
    
    // GPIO 기본 초기화
    Device_initGPIO();
    
    // 디버깅용 GPIO 설정
    initGPIO();
    
    // 인터럽트 모듈 및 벡터 테이블 초기화
    Interrupt_initModule();
    Interrupt_initVectorTable();
    
    // DMA 채널 1 인터럽트 ISR 등록
    Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);
    
    // 시스템 클럭 확인 (200MHz 예상)
    uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
    if (sysClockHz != 200000000)
    {
        dmaStatus = 0xFFFF; // 오류 상태
        GPIO_writePin(31, 0); // LED OFF로 오류 표시
        ESTOP0; // 디버깅용 정지
    }
    
    // DMA 초기화
    initDMA();
    
    // 초기화 오류 확인
    if (dmaStatus != 1)
    {
        dmaStatus = 0xFFFF; // 초기화 실패
        GPIO_writePin(31, 0); // LED OFF
        ESTOP0; // 디버깅용 정지
    }
    
    // 글로벌 인터럽트 활성화
    EINT;
    
    // Watchdog 활성화 (실시간 애플리케이션용)
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
    SysCtl_serviceWatchdog();
    
    // 무한 루프: DMA가 하드웨어로 자동 전송
    for(;;)
    {
        // 디버깅용: 상태 표시 (정상 동작 시 LED ON)
        GPIO_writePin(31, (dmaStatus == 1) ? 1 : 0);
        DEVICE_DELAY_US(1000000); // 1초 대기 (CPU 부하 감소)
    }
}

설명:

  • 기능: 소스 버퍼에서 목적지 버퍼로 데이터를 DMA를 통해 전송.
  • 설정: DMA 채널 1, 소프트웨어 트리거, 16비트 데이터 전송.
  • GPIO: GPIO31(LED).
  • 출력: srcBuffer 데이터가 destBuffer로 복사, 정상 동작 시 LED ON.
  • 용도: 대량 데이터 처리, 메모리 초기화.

5. 추가 고려 사항

  • 트리거 선택: 애플리케이션 요구사항에 따라 적절한 트리거 소스(ePWM, ADC, 소프트웨어 등) 선택.
  • 메모리 정렬: DMA 전송 시 소스/목적지 주소는 16비트 또는 32비트 정렬 권장.
  • 인터럽트 관리: 전송 완료 인터럽트를 활용하여 동적 제어 가능.
  • C2000Ware: 예제 코드는 C2000Ware의 DriverLib 기반. C:\ti\c2000에 설치.
  • 디버깅: CCS의 .syscfg 툴로 DMA 설정 시각적 구성 가능.

키워드: TMS320F28388D, DMA, DriverLib, C2000, ADC, 메모리 전송, Code Composer Studio, 인터럽트, 실시간 제어

반응형