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 모듈 초기화 DMA_initController();
DMA_configMode()
- 설명: 특정 DMA 채널의 동작 모드와 트리거 소스를 설정합니다. 전송 모드(단일/연속), 데이터 크기(16비트/32비트), 트리거 소스(소프트웨어, ePWM 등)를 지정합니다.
- 매개변수:
- base: DMA 채널의 베이스 주소 (예: DMA_CH1_BASE).
- trigger: 트리거 소스 (예: DMA_TRIGGER_SOFTWARE, DMA_TRIGGER_EPWM1_SOCA).
- config: 동작 모드 플래그 (예: DMA_CFG_ONESHOT_DISABLE, DMA_CFG_CONTINUOUS_DISABLE, DMA_CFG_SIZE_16BIT).
- 사용 예:
// DMA 채널 1을 소프트웨어 트리거, 단일 전송, 16비트 모드로 설정 DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_SOFTWARE, (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT));
DMA_configAddresses()
- 설명: DMA 전송의 소스 및 목적지 메모리 주소를 설정합니다.
- 매개변수:
- base: DMA 채널의 베이스 주소 (예: DMA_CH1_BASE).
- dest: 목적지 메모리 주소 (포인터).
- src: 소스 메모리 주소 (포인터).
- 사용 예:
// ADC 결과 레지스터에서 버퍼로 데이터 전송 uint16_t adcBuffer[256]; DMA_configAddresses(DMA_CH1_BASE, adcBuffer, (uint16_t *)ADCARESULT_BASE);
DMA_configBurst()
- 설명: 버스트 전송의 크기와 소스/목적지 주소 증가 단위를 설정합니다. 버스트는 한 번의 트리거로 전송되는 데이터 단위입니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- size: 버스트당 전송 단위 수 (1~16).
- srcStep: 소스 주소 증가 단위 (바이트 단위, 예: 2는 16비트 증가).
- destStep: 목적지 주소 증가 단위 (바이트 단위).
- 사용 예:
// 1워드 버스트, 소스 고정, 목적지 2바이트 증가 DMA_configBurst(DMA_CH1_BASE, 1, 0, 2);
DMA_configTransfer()
- 설명: 전체 전송 크기와 주소 증가 단위를 설정합니다. 전체 전송은 여러 버스트로 구성됩니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- size: 전체 전송 단위 수 (버스트 횟수).
- srcStep: 소스 주소 증가 단위 (버스트 간, 바이트 단위).
- destStep: 목적지 주소 증가 단위 (버스트 간, 바이트 단위).
- 사용 예:
// 256번 전송, 소스 고정, 목적지 2바이트 증가 DMA_configTransfer(DMA_CH1_BASE, 256, 0, 2);
DMA_enableTrigger()
- 설명: DMA 채널의 트리거를 활성화하여 트리거 신호를 수신할 준비를 합니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- 사용 예:
// DMA 채널 1 트리거 활성화 DMA_enableTrigger(DMA_CH1_BASE);
DMA_startChannel()
- 설명: DMA 채널을 시작하여 데이터 전송을 개시합니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- 사용 예:
// DMA 채널 1 전송 시작 DMA_startChannel(DMA_CH1_BASE);
2.2. 인터럽트 관련 함수
DMA_enableInterrupt()
- 설명: DMA 채널의 인터럽트를 활성화하여 전송 완료 또는 오류 시 인터럽트를 발생시킵니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- 사용 예:
// DMA 채널 1 인터럽트 활성화 DMA_enableInterrupt(DMA_CH1_BASE);
DMA_setInterruptMode()
- 설명: 인터럽트 발생 조건을 설정합니다 (예: 전송 완료 시).
- 매개변수:
- base: DMA 채널의 베이스 주소.
- mode: 인터럽트 모드 (예: DMA_INT_AT_END).
- 사용 예:
// 전송 완료 시 인터럽트 설정 DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
DMA_clearInterruptFlag()
- 설명: DMA 인터럽트 플래그를 지워 추가 인터럽트를 수신할 수 있도록 합니다.
- 매개변수:
- base: DMA 채널의 베이스 주소.
- 사용 예:
// DMA 채널 1 인터럽트 플래그 지우기 DMA_clearInterruptFlag(DMA_CH1_BASE);
Interrupt_register()
- 설명: 인터럽트 서비스 루틴(ISR)을 특정 인터럽트 번호에 등록합니다.
- 매개변수:
- intNumber: 인터럽트 번호 (예: INT_DMA_CH1).
- handler: ISR 함수 포인터.
- 사용 예:
// DMA 채널 1 ISR 등록 Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);
3. DMA 설정 및 동작 원리
- 시스템 초기화: 시스템 클럭, GPIO, 인터럽트 벡터 테이블을 초기화합니다.
Device_init(); // 시스템 클럭 및 주변 장치 초기화
Device_initGPIO(); // GPIO 초기화
Interrupt_initModule(); // 인터럽트 모듈 초기화
Interrupt_initVectorTable(); // 인터럽트 벡터 테이블 초기화
- DMA 컨트롤러 초기화: DMA 모듈을 기본 상태로 설정합니다.
DMA_initController(); // DMA 컨트롤러 초기화
- DMA 채널 설정: 소스/목적지 주소, 버스트/전송 크기, 트리거 소스를 설정합니다.
DMA_configAddresses(DMA_CH1_BASE, destBuffer, srcBuffer);
DMA_configBurst(DMA_CH1_BASE, 8, 2, 2);
DMA_configTransfer(DMA_CH1_BASE, 16, 16, 16);
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_SOFTWARE, (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT));
- 트리거 및 인터럽트 설정: 트리거 활성화 및 인터럽트를 설정하여 전송 완료를 감지합니다.
DMA_enableTrigger(DMA_CH1_BASE);
DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH1_BASE);
Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);
Interrupt_enable(INT_DMA_CH1);
- DMA 시작: DMA 채널을 시작하여 데이터 전송을 개시합니다.
DMA_startChannel(DMA_CH1_BASE);
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;
// DMA 전송 완료 플래그: 0은 미완료, 1은 완료
volatile uint16_t done = 0;
// ADC 결과 저장 버퍼 크기 정의 (1024 샘플, 16의 배수로 DMA 전송 최적화)
#define RESULTS_BUFFER_SIZE 1024
// ADC 결과 버퍼를 GS0 RAM에 배치 (고속 액세스 메모리)
#pragma DATA_SECTION(adcABuffer, "ramgs0");
#pragma DATA_SECTION(adcCBuffer, "ramgs0");
uint16_t adcABuffer[RESULTS_BUFFER_SIZE]; // ADCA (A3 채널) 결과 저장 버퍼
uint16_t adcCBuffer[RESULTS_BUFFER_SIZE]; // ADCC (C3 채널) 결과 저장 버퍼
// 함수 프로토타입 선언
__interrupt void adcA1ISR(void); // ADC 인터럽트 서비스 루틴
__interrupt void dmach1ISR(void); // DMA 인터럽트 서비스 루틴
void configureEPWM(uint32_t epwmBase); // ePWM 모듈 설정 함수
void initializeDMA(void); // DMA 초기화 함수
void configureDMAChannels(void); // DMA 채널 설정 함수
// GPIO 초기화 함수
// GPIO31을 디버깅용 LED로 설정 (LAUNCHXL-F2838x 기준)
void initGPIO(void)
{
// GPIO31을 일반 GPIO로 설정
GPIO_setPinConfig(GPIO_31_GPIO31);
// GPIO31을 출력 모드로 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// GPIO31에 내부 풀업 저항 활성화 (외부 회로 안정성 확보)
GPIO_setPadConfig(31, GPIO_PIN_TYPE_PULLUP);
// 초기 상태: LED OFF (GPIO31 = 0)
GPIO_writePin(31, 0);
}
// ADC 및 DMA 초기화 함수
// ADCA와 ADCC를 설정하고, DMA를 통해 결과를 버퍼에 저장하도록 준비
void initADCandDMA(void)
{
// ADC-A 및 ADC-C의 클럭 활성화 (SYSCTL을 통해 주변 장치 활성화)
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_ADCA);
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_ADCC);
// ADC 설정: 200MHz SYSCLK에서 50MHz ADCCLK 생성 (DIV_4_0)
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
ADC_setPrescaler(ADCC_BASE, ADC_CLK_DIV_4_0);
// ADC 모드 설정: 12비트 해상도, 단일 엔드 모드 (외부 3.3V VREF 가정)
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
ADC_setMode(ADCC_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
// ADC SOC0 설정: ePWM2 SOCA로 트리거, A3/C3 채널, 샘플링 윈도우 15 사이클
// - A3 (ADCA)와 C3 (ADCC) 채널 선택
// - 샘플링 윈도우 15 사이클은 일반적인 입력 신호에 적합 (고임피던스 신호는 조정 필요)
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM2_SOCA, ADC_CH_ADCIN3, 15);
ADC_setupSOC(ADCC_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM2_SOCA, ADC_CH_ADCIN3, 15);
// ADC 인터럽트 설정: ADCINT2로 SOC0 재트리거 (연속 변환 활성화)
// - ADCINT2가 SOC0을 재트리거하여 연속 변환 구현
ADC_setInterruptSOCTrigger(ADCA_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_ADCINT2);
ADC_setInterruptSOCTrigger(ADCC_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_ADCINT2);
// ADC 컨버터 활성화 (아날로그 회로 전원 ON)
ADC_enableConverter(ADCA_BASE);
ADC_enableConverter(ADCC_BASE);
// ADC 안정화를 위해 1ms 대기 (데이터시트: 최소 500us 필요)
DEVICE_DELAY_US(1000);
// 결과 버퍼 초기화: adcABuffer와 adcCBuffer를 0으로 초기화
uint16_t resultsIndex;
for(resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++)
{
adcABuffer[resultsIndex] = 0;
adcCBuffer[resultsIndex] = 0;
}
// 인터럽트 플래그 초기화
// - DMA 채널 1, 2의 트리거 플래그 초기화
// - ADC 인터럽트 플래그 초기화 (ADCINT1, ADCINT2)
// - ePWM2의 ADC 트리거 플래그 초기화
DMA_clearTriggerFlag(DMA_CH1_BASE);
DMA_clearTriggerFlag(DMA_CH2_BASE);
HWREGH(ADCA_BASE + ADC_O_INTFLGCLR) = 0x3U; // ADCA의 ADCINT1, ADCINT2 플래그 초기화
HWREGH(ADCC_BASE + ADC_O_INTFLGCLR) = 0x3U; // ADCC의 ADCINT1, ADCINT2 플래그 초기화
EPWM_forceADCTriggerEventCountInit(EPWM2_BASE, EPWM_SOC_A);
EPWM_clearADCTriggerFlag(EPWM2_BASE, EPWM_SOC_A);
// ePWM2 클럭 활성화 및 설정
// - TBCLKSYNC 비활성화 후 설정, 설정 완료 후 활성화
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
configureEPWM(EPWM2_BASE);
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
// DMA 초기화 및 채널 설정
initializeDMA();
configureDMAChannels();
}
// ADC 인터럽트 서비스 루틴
// - ADCA의 ADCINT1 발생 시 호출
// - 첫 변환 후 ePWM 트리거 비활성화 및 인터럽트 비활성화
#pragma CODE_SECTION(adcA1ISR, ".TI.ramfunc");
__interrupt void adcA1ISR(void)
{
// ePWM2의 ADC SOCA 트리거 비활성화 (추가 변환 방지)
EPWM_disableADCTrigger(EPWM2_BASE, EPWM_SOC_A);
// ADCA의 ADCINT1 인터럽트 비활성화 (단일 인터럽트 처리)
Interrupt_disable(INT_ADCA1);
// 인터럽트 그룹 1에 대한 ACK (ADCA 인터럽트 처리 완료)
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}
// DMA 인터럽트 서비스 루틴
// - DMA 채널 1의 전송 완료 시 호출
// - ADC 트리거 제거 및 전송 완료 플래그 설정
#pragma CODE_SECTION(dmach1ISR, ".TI.ramfunc");
__interrupt void dmach1ISR(void)
{
// ADC SOC0의 인터럽트 트리거 제거 (ADCA, ADCC 변환 중지)
ADC_setInterruptSOCTrigger(ADCA_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
ADC_setInterruptSOCTrigger(ADCC_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
// DMA 전송 완료 플래그 설정
done = 1;
// DMA 동작 상태 업데이트 (1: 완료)
dmaStatus = 1;
// DMA 인터럽트 그룹 7에 대한 ACK (DMA 전송 완료 처리)
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}
// ePWM 설정 함수
// - ePWM2를 40us 주기(50kHz)로 설정, ADC SOCA 트리거 생성
void configureEPWM(uint32_t epwmBase)
{
// 타이머 베이스 초기화 (TBCTL 레지스터 초기화)
HWREGH(epwmBase + EPWM_O_TBCTL) = 0x0000U;
// 주기 설정: 200MHz / 4000 = 50kHz (40us 주기)
EPWM_setTimeBasePeriod(epwmBase, 4000U);
// AQ 설정: TBCTR=0에서 출력 HIGH
EPWM_setActionQualifierAction(epwmBase, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
// AQ 설정: TBCTR=CMPA에서 출력 LOW (50% 듀티 사이클)
EPWM_setActionQualifierAction(epwmBase, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
// CMPA 설정: 2000 (50% 듀티 사이클)
EPWM_setCounterCompareValue(epwmBase, EPWM_COUNTER_COMPARE_A, 2000U);
// ADC 트리거 소스: TBCTR=0에서 SOCA 발생
EPWM_setADCTriggerSource(epwmBase, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);
// ADC 트리거 프리스케일: 1 (매 주기마다 트리거)
EPWM_setADCTriggerEventPrescale(epwmBase, EPWM_SOC_A, 1U);
// ADC 트리거 카운트 초기화 활성화
EPWM_enableADCTriggerEventCountInit(epwmBase, EPWM_SOC_A);
}
// DMA 초기화 함수
// - DMA 컨트롤러 초기화 및 에뮬레이션 모드 설정
void initializeDMA(void)
{
// DMA 컨트롤러 초기화
DMA_initController();
// 에뮬레이션 모드: 디버깅 중에도 DMA 동작 (자유 실행)
DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
}
// DMA 채널 설정 함수
// - DMA 채널 1, 2를 설정하여 ADCA, ADCC 결과를 버퍼로 전송
void configureDMAChannels(void)
{
// DMA 채널 1: ADCA (A3 채널) 결과 전송
// - 소스: ADCARESULT_BASE, 목적지: adcABuffer
DMA_configAddresses(DMA_CH1_BASE, (uint16_t *)&adcABuffer, (uint16_t *)ADCARESULT_BASE);
// 버스트 설정: 16워드 단위, 소스/목적지 주소 2씩 증가
DMA_configBurst(DMA_CH1_BASE, 16, 2, 2);
// 전송 설정: (1024/16)번 전송, 소스 -14, 목적지 +2
DMA_configTransfer(DMA_CH1_BASE, (RESULTS_BUFFER_SIZE >> 4), -14, 2);
// 모드 설정: ADCINT2 트리거, 원샷/연속 비활성화, 32비트 전송
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA2,
(DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_32BIT));
// DMA 채널 1 트리거 활성화
DMA_enableTrigger(DMA_CH1_BASE);
// 오버런 인터럽트 비활성화
DMA_disableOverrunInterrupt(DMA_CH1_BASE);
// 인터럽트 모드: 전송 완료 시 발생
DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
// DMA 채널 1 인터럽트 활성화
DMA_enableInterrupt(DMA_CH1_BASE);
// DMA 채널 2: ADCC (C3 채널) 결과 전송
// - 소스: ADCCRESULT_BASE, 목적지: adcCBuffer
DMA_configAddresses(DMA_CH2_BASE, (uint16_t *)&adcCBuffer, (uint16_t *)ADCCRESULT_BASE);
// 버스트 설정: 16워드 단위, 소스/목적지 주소 2씩 증가
DMA_configBurst(DMA_CH2_BASE, 16, 2, 2);
// 전송 설정: (1024/16)번 전송, 소스 -14, 목적지 +2
DMA_configTransfer(DMA_CH2_BASE, (RESULTS_BUFFER_SIZE >> 4), -14, 2);
// 모드 설정: ADCINT2 트리거, 원샷/연속 비활성화, 32비트 전송
DMA_configMode(DMA_CH2_BASE, DMA_TRIGGER_ADCA2,
(DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_32BIT));
// DMA 채널 2 트리거 활성화
DMA_enableTrigger(DMA_CH2_BASE);
// 오버런 인터럽트 비활성화
DMA_disableOverrunInterrupt(DMA_CH2_BASE);
// 인터럽트 모드: 전송 완료 시 발생
DMA_setInterruptMode(DMA_CH2_BASE, DMA_INT_AT_END);
// DMA 채널 2 인터럽트 활성화
DMA_enableInterrupt(DMA_CH2_BASE);
}
// 메인 함수
// - 시스템 초기화, ADC/DMA 설정, 데이터 수집 및 디버깅
void main(void)
{
// 시스템 클럭, PLL, 주변 장치 초기화 (F2838x 디바이스 설정)
Device_init();
// GPIO 기본 초기화 (F2838x의 기본 GPIO 설정)
Device_initGPIO();
// 디버깅용 GPIO31 (LED) 설정
initGPIO();
// 인터럽트 모듈 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// 인터럽트 서비스 루틴 등록
// - DMA 채널 1 인터럽트: dmach1ISR
// - ADCA 인터럽트 1: adcA1ISR
Interrupt_register(INT_DMA_CH1, &dmach1ISR);
Interrupt_register(INT_ADCA1, &adcA1ISR);
// 시스템 클럭 확인: 200MHz 예상
// - 클럭이 200MHz가 아니면 오류 처리
uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
if (sysClockHz != 200000000)
{
dmaStatus = 0xFFFF; // 오류 상태 설정
GPIO_writePin(31, 0); // LED OFF로 오류 표시
ESTOP0; // 디버깅용 정지
}
// ADC 및 DMA 초기화
initADCandDMA();
// 인터럽트 활성화
// - DMA 채널 1 인터럽트
// - ADCA 인터럽트 1
Interrupt_enable(INT_DMA_CH1);
Interrupt_enable(INT_ADCA1);
// 글로벌 인터럽트 활성화
EINT;
// 실시간 디버깅 인터럽트 활성화
ERTM;
// DMA 채널 1, 2 시작
DMA_startChannel(DMA_CH1_BASE);
DMA_startChannel(DMA_CH2_BASE);
// ePWM2의 ADC SOCA 트리거 활성화
EPWM_enableADCTrigger(EPWM2_BASE, EPWM_SOC_A);
// DMA 전송 완료 대기
// - 100ms 주기로 GPIO31 상태 업데이트 (디버깅용 LED)
while(done == 0)
{
GPIO_writePin(31, (dmaStatus == 1) ? 1 : 0); // 상태 표시: 완료 시 LED ON
DEVICE_DELAY_US(100000); // 100ms 대기
}
// DMA 전송 완료 후 처리
GPIO_writePin(31, 1); // LED ON으로 완료 표시
ESTOP0; // 디버깅용 정지
}
설명:
- 기능: 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;
// DMA 전송 완료 플래그: 0은 미완료, 1은 완료
volatile uint16_t done = 0;
// 소스 및 목적지 버퍼 정의: 128개의 16비트 데이터
#define BUFFER_SIZE 128
#pragma DATA_SECTION(srcBuffer, "ramgs0"); // 소스 버퍼: RAMGS0 (고속 메모리)
#pragma DATA_SECTION(destBuffer, "ramgs1"); // 목적지 버퍼: RAMGS1 (고속 메모리)
uint16_t srcBuffer[BUFFER_SIZE]; // 소스 버퍼: 테스트 데이터
uint16_t destBuffer[BUFFER_SIZE]; // 목적지 버퍼: DMA 전송 결과
// 함수 프로토타입 선언
__interrupt void dmaCh1ISR(void); // DMA 채널 1 인터럽트 서비스 루틴
void initGPIO(void); // GPIO 초기화 함수
void initDMA(void); // DMA 초기화 및 설정 함수
void error(void); // 오류 처리 함수
// GPIO 초기화 함수
// - GPIO31을 디버깅용 LED로 설정 (LAUNCHXL-F28379D 기준)
void initGPIO(void)
{
// GPIO31을 일반 GPIO로 설정
GPIO_setPinConfig(GPIO_31_GPIO31);
// GPIO31을 출력 모드로 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// GPIO31에 내부 풀업 저항 활성화 (외부 회로 안정성 확보)
GPIO_setPadConfig(31, GPIO_PIN_TYPE_PULLUP);
// 초기 상태: LED OFF (GPIO31 = 0)
GPIO_writePin(31, 0);
}
// 오류 처리 함수
// - 데이터 무결성 실패 또는 기타 오류 시 호출
// - 디버거 정지 및 무한 루프
void error(void)
{
dmaStatus = 0xFFFF; // 오류 상태 설정
GPIO_writePin(31, 0); // LED OFF로 오류 표시
ESTOP0; // 디버깅용 정지
for(;;); // 무한 루프
}
// DMA 인터럽트 서비스 루틴
// - DMA 채널 1 전송 완료 시 호출, 데이터 무결성 검사
#pragma CODE_SECTION(dmaCh1ISR, ".TI.ramfunc");
__interrupt void dmaCh1ISR(void)
{
uint16_t i;
// DMA 채널 1 정지
DMA_stopChannel(DMA_CH1_BASE);
// DMA 트리거 플래그 지우기
DMA_clearTriggerFlag(DMA_CH1_BASE);
// 인터럽트 그룹 7에 대한 ACK (DMA 인터럽트 처리 완료)
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
// 데이터 무결성 검사: destBuffer[i] == i 확인
for (i = 0; i < BUFFER_SIZE; i++)
{
if (destBuffer[i] != i)
{
error(); // 데이터 불일치 시 오류 처리
}
}
// DMA 동작 상태 업데이트: 전송 완료
dmaStatus = 1;
// 전송 완료 플래그 설정
done = 1;
// 디버깅용: LED ON
GPIO_writePin(31, 1);
}
// DMA 초기화 및 설정 함수
// - DMA 컨트롤러와 채널 1을 설정하여 RAMGS0에서 RAMGS1으로 데이터 전송
void initDMA(void)
{
uint16_t i;
// 소스 버퍼 초기화: 0부터 127까지 값으로 채움 (테스트 데이터)
for (i = 0; i < BUFFER_SIZE; i++)
{
srcBuffer[i] = i;
}
// 목적지 버퍼 초기화: 0으로 채움
for (i = 0; i < BUFFER_SIZE; i++)
{
destBuffer[i] = 0;
}
// DMA 컨트롤러 초기화
DMA_initController();
// 에뮬레이션 모드: 디버깅 중에도 DMA 동작 (자유 실행)
DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
// DMA 채널 1 설정
// - 소스: srcBuffer (RAMGS0), 목적지: destBuffer (RAMGS1)
DMA_configAddresses(DMA_CH1_BASE, (uint16_t *)destBuffer, (uint16_t *)srcBuffer);
// 버스트 설정: 8워드 단위, 소스/목적지 주소 2바이트 증가
DMA_configBurst(DMA_CH1_BASE, 8, 2, 2);
// 전송 설정: 16번 전송 (128/8), 소스/목적지 주소 16바이트 증가 (8*2)
DMA_configTransfer(DMA_CH1_BASE, (BUFFER_SIZE / 8), 16, 16);
// 모드 설정: 소프트웨어 트리거, 단일 전송, 16비트 데이터
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_SOFTWARE,
(DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT));
// DMA 인터럽트 설정: 전송 완료 시 발생
DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH1_BASE);
// DMA 트리거 활성화
DMA_enableTrigger(DMA_CH1_BASE);
// 초기화 완료 상태 설정
dmaStatus = 0;
}
// 메인 함수
// - 시스템 초기화, DMA 설정, 데이터 전송 및 디버깅
void main(void)
{
// 시스템 클럭, PLL, 주변 장치 초기화 (F28379D 디바이스 설정)
Device_init();
// GPIO 기본 초기화
Device_initGPIO();
// 디버깅용 GPIO31 (LED) 설정
initGPIO();
// 인터럽트 모듈 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// DMA 채널 1 인터럽트 ISR 등록
Interrupt_register(INT_DMA_CH1, &dmaCh1ISR);
// 시스템 클럭 확인: 200MHz 예상
// - 클럭이 200MHz가 아니면 오류 처리
uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
if (sysClockHz != 200000000)
{
dmaStatus = 0xFFFF; // 오류 상태 설정
GPIO_writePin(31, 0); // LED OFF로 오류 표시
error(); // 오류 처리
}
// DMA 초기화 및 설정
initDMA();
// DMA 인터럽트 활성화
Interrupt_enable(INT_DMA_CH1);
// 글로벌 인터럽트 활성화
EINT;
// 실시간 디버깅 인터럽트 활성화
ERTM;
// DMA 전송 시작 및 완료 대기
// - 8워드 × 16 전송 완료까지 소프트웨어 트리거 반복
done = 0;
while (!done)
{
DMA_forceTrigger(DMA_CH1_BASE); // 소프트웨어 트리거 강제 실행
asm(" RPT #255 || NOP"); // 대기 (참조 코드와 동일)
GPIO_writePin(31, (dmaStatus == 1) ? 1 : 0); // 상태 표시
}
// 전송 완료 후 처리
GPIO_writePin(31, 1); // LED ON으로 완료 표시
ESTOP0; // 디버깅용 정지
}
설명:
- 기능: 소스 버퍼에서 목적지 버퍼로 데이터를 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, 인터럽트, 실시간 제어
'MCU > C2000' 카테고리의 다른 글
TMS320F28377D DSP EMIF 사용법 : Bitfield 구조 활용 예제 코드(수정) (1) | 2025.08.17 |
---|---|
TMS320F28377D DSP eCAP 사용법 : Bitfield 구조 활용 예제 코드(수정) (1) | 2025.08.17 |
TMS320F28377D DSP eQEP 사용법 : Bitfield 구조 활용 예제 코드(수정) (0) | 2025.08.17 |
TMS320F28377D DSP ePWM 사용법 : Bitfield 구조 활용 예제 코드(수정) (1) | 2025.08.17 |
TMS320F28388D DSP SPI 사용법: DriverLib API로 SPI 설정 및 코드(수정) (3) | 2025.08.17 |
TMS320F28388D DSP CAN 사용법: DriverLib API로 CAN 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP SDFM 사용법: DriverLib API로 SDFM 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP CLB 사용법: DriverLib API로 CLB 설정 및 코드(수정) (0) | 2025.08.17 |