gsram에서 DMA를 사용해 데이터를 이동시키는 예제
//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include "board.h"
//
// DMA data sections
// 이전글에 썼지만 DMA에 접근 가능한 메모리 영역이 정해져 있고 그 중 하나가 RAMGS이다.
// 따라서 source 와 destination으로 사용할 버퍼를 RAMGS 영역에 배치하는 전처리 지시 코드가 필요하다.
#pragma DATA_SECTION(sData, "ramgs0"); // map the TX data to memory
#pragma DATA_SECTION(rData, "ramgs1"); // map the RX data to memory
//
// Globals
// sData에 있는 내용을 rData로 옮길 것이다.
uint16_t sData[128]; // Send data buffer
uint16_t rData[128]; // Receive data buffer
// 전송완료를 나타내기 위한 플래그
volatile uint16_t done;
// const void* 형으로 캐스팅해 변수에 저장
const void *destAddr = (const void *)rData;
const void *srcAddr = (const void *)sData;
//
// Function Prototypes
//
void error();
// transfer 종료시 호출될 콜백함수(ISR)
__interrupt void INT_myDMA0_ISR(void);
.cmd 파일에 정의된 내용
ramgs0 : > RAMGS0, type=NOINIT
ramgs1 : > RAMGS1, type=NOINIT
void main(void)
{
uint16_t i;
//
// Initialize device clock and peripherals
//
Device_init();
// 예제라고 실행해보니 interrupt와 PIE vector를 initialize 하는 코드가 없어 의도대로 실행되지 않았고 직접 추가했다.
Interrupt_initModule();
Interrupt_initVectorTable();
// 실제 DMA 설정 코드들은 syscfg 툴을 생성되어 이 안에 담긴다.
Board_init();
//
// User specific code, enable interrupts:
// Initialize the data buffers
// 시작지와 목적지 버퍼 초기화
for(i = 0; i < 128; i++)
{
sData[i] = i;
rData[i] = 0;
}
//
// Enable interrupts required for this example
// INT_DMA_CH6 인터럽트를 enable 한다.
Interrupt_enable(INT_myDMA0);
EINT; // Enable Global Interrupts
// Start DMA channel
// DMA_CH6_BASE 스타트
DMA_startChannel(myDMA0_BASE);
done = 0; // Test is not done yet
// DMA Transfer가 종료시 호출되는 ISR에서 done을 1로 만들때까지 반복
while(!done) // wait until the DMA transfer is complete
{
// 소프트웨어 트리거를 쏘는 부분이다.
DMA_forceTrigger(myDMA0_BASE);
// NOP는 아무것도 하지 않는 어셈블리 명령어이다 그 앞의 RPT #N 은 N+1 instruction cycle 동안 아무것도 하지않는단 뜻이다.
// 컴파일러가 코드 경로를 축소하지 않도록 해서 중단점을 찍어볼 때 쓰일 수 있다.
asm(" RPT #255 || NOP");
}
//
// When the DMA transfer is complete the program will stop here
//
ESTOP0;
}
//
// error - Error Function which will halt the debugger
//
void error(void)
{
ESTOP0; //Test failed!! Stop!
for (;;);
}
//
// local_D_INTCH6_ISR - DMA Channel6 ISR
// DMA 채널 6 ISR
__interrupt void INT_myDMA0_ISR(void)
{
uint16_t i;
// DMA_CH6_BASE 스탑
DMA_stopChannel(myDMA0_BASE);
// ACK to receive more interrupts from this PIE group
EALLOW;
// INT_DMA_CH6 이 속한 그룹에 다시 인터럽트가 걸릴 수 있게 clearACK 여기선 의미가 없다.
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
EDIS;
// 잘 이동됐는지 체크
for( i = 0; i < 128; i++ )
{
//
// check for data integrity
//
if (rData[i] != i)
{
error();
}
}
// 테스트 완료
done = 1; // Test done.
return;
}
asm(" RPT #255 || NOP"); 과 ISR 내부에 중단점을 찍고 버퍼를 확인하면 데이터가 이동하는 것을 볼 수 있다.
syscfg 툴 상 DMA 설정
board.c 의 내용
EALLOW;
DMA_initController();
DMA_setEmulationMode(DMA_EMULATION_STOP);
DMA_configAddresses(myDMA0_BASE, destAddr, srcAddr);
DMA_configBurst(myDMA0_BASE, 8U, 1, 1);
DMA_configTransfer(myDMA0_BASE, 16U, 1, 1);
DMA_configWrap(myDMA0_BASE, 65535U, 0, 65535U, 0);
DMA_configMode(myDMA0_BASE, DMA_TRIGGER_SOFTWARE, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT);
DMA_setInterruptMode(myDMA0_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(myDMA0_BASE);
DMA_disableOverrunInterrupt(myDMA0_BASE);
DMA_enableTrigger(myDMA0_BASE);
DMA_stopChannel(myDMA0_BASE);
Interrupt_register(INT_myDMA0, &INT_myDMA0_ISR);
Interrupt_disable(INT_myDMA0);
EDIS;
DMA_initController();
DMA CONTROL 레지스터를 조작해 hard reset 한다.
DMA_configAddresses(myDMA0_BASE, destAddr, srcAddr);
// 함수원형
void DMA_configAddresses(uint32_t base, const void *destAddr,
const void *srcAddr)
DEBUG 모드일때(JTAG 에뮬레이터 연결시) 동작모드를 설정하는 것이다.
DMA_configBurst(myDMA0_BASE, 8U, 1, 1);
BURST_SIZE를 8, SRC_BURST_STEP 과 DST_BURST_STEP 을 1로 설정한다. (첫번째 Burst가 끝난뒤 Active 주소는 Src_Addr+7일 것이다.)
함수 안에서 두번째 매개변수 - 1U하여 BURST_SIZE 레지스터에 대입하고 있다.
HWREGH(base + DMA_O_BURST_SIZE) = size - 1U;
DMA_configTransfer(myDMA0_BASE, 16U, 1, 1);
TRANSFER_SIZE를 16, SRC_TRANSFER_STEP 과 DST_TRANSFER_STEP 을 1로 설정한다. (직전 Burst가 끝나고 다음 Burst가 시작되기 전에 Src_Addr과 Dest_Addr을 1씩 증가한다.)
위와 같이 두번째 매개변수에서 - 1U 하여 TRANSFER_SIZE 레지스터에 대입하고 있다.
HWREGH(base + DMA_O_TRANSFER_SIZE) = (uint16_t)(transferSize - 1U);
Transfer는 16번의 Burst로 이루어져 있고 1번의 Burst에 옮겨지는 Word는 8Word이니 최종적으로는 8 * 16 = 128 Word를 이동시키는 것이다.
BURST_SIZE+1 와 TRANSFER_SIZE+1 의 곱이 128이 되도록 설정만 하면 과정은 다를 수 있지만 결과는 똑같이 이동될 것이다.
DMA_configWrap(myDMA0_BASE, 65535U, 0, 65535U, 0);
WRAP 설정을한다. SRC/DST_WRAP_STEP은 0이고, 중요한건 SRC/DST_WRAP_SIZE 가 TRANSFER_SIZE보다 크니 WRAP을 사용하지 않겠다는 뜻이다. (만약 128 Word를 계속적으로 복사한다면 Transfer Size랑 같은 값으로 설정하면 될 것이다.)
DMA_configMode(myDMA0_BASE, DMA_TRIGGER_SOFTWARE, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT);
트리거 소스는 SOFTWARE 트리거로 한다. 만약에 페리페럴과 메모리간 이동이었다면 그래도 소프트웨어 트리거를 쓸 수 있지만 페리페럴 트리거 소스를 사용할 수 도있다.
예를 들어보면 SCIRXFIFO 인터럽트 레벨을 1/16 으로 설정하고 이 인터럽트를 DMA 트리거 소스로 사용하여 FIFO에 1Word가 들어왔을 때마다 해당 DMA 채널 ISR에서 1Word를 버퍼로 이동시키고 끝내는 식이다.
OneShot Mode는 disable한다. 만약 OneShot Mode가 enable 되어있다면 한번의 DMA_forceTrigger()로 127 Word가 다 옮겨질 것이다.
Continuous Mode는 disable한다. Transfer 완료 후에는 트리거가 들어와도 무시된다.
Transfer의 최소단위인 Word(Data)의 size는 16bits로 한다.
DMA_setInterruptMode(myDMA0_BASE, DMA_INT_AT_END);
Transfer 완료시에 인터럽트가 걸리는 모드로 설정한다.
DMA_enableInterrupt(myDMA0_BASE);
DMA_CH6_BASE 의 인터럽트를 enable 한다.
DMA_disableOverrunInterrupt(myDMA0_BASE);
Overrun 인터럽트는 사용하지 않는다.
DMA_enableTrigger(myDMA0_BASE);
DMA Trigger를 enable한다. MODE.CHx.PERINTE 이 비트가 enable 되어 있지 않으면 아무리 트리거를 쏴도 DMA가 작동하지 않는다.
DMA_stopChannel(myDMA0_BASE);
예제에선 초기설정 후 DMA 채널을 stop해두고 main 함수 안에서 Start하고 있다.
Interrupt_register(INT_myDMA0, &INT_myDMA0_ISR);
// 함수원형
static inline void
Interrupt_register(uint32_t interruptNumber, void (*handler)(void))
INT_DMA_CH6 에 콜백함수(ISR)를 등록한다. 이러면 PIE Vector Table에 두번째 매개변수인 함수 포인터가 카피된다.
Interrupt_disable(INT_myDMA0);
마찬가지로 초기설정 후 DMA CH 인터럽트를 disable 해두고 main 함수에서 enable 하고 있다.
'임베디드 개발 > TMS320F2838x (C28x)' 카테고리의 다른 글
TMS320F28388D ] CM에서 USB MSC 사용 (0) | 2022.12.28 |
---|---|
TMS320F28388D ] CPU2에서 SD카드 사용 + 속도 테스트 (0) | 2022.12.28 |
TMS320F28388D ] DMA 파악하기 (0) | 2022.12.11 |
TMS320F28388D ] CPU2에서 SCI, SPI 사용하기 위한 설정방법 (2) | 2022.12.06 |
TMS320F28388D ] 타이머, Timer Interrupt 사용하기 (0) | 2022.12.05 |