Skip to content

Chapter 17

Hongjayeong-02 edited this page Oct 5, 2026 · 1 revision

17. Direct Memory Access (DMA)

Goal

CPU가 직접 for문으로 데이터를 복사하지 않고, DMA2 Stream0 hardware가 source array를 destination array로 복사하도록 구성한다.

sensor_data_arr
{892, 731, 1234, 90, 23}
        │
        │ DMA2 Stream0
        ▼
temp_data_arr
{892, 731, 1234, 90, 23}

전송 완료 후:

DMA Transfer Complete
        ↓
TCIF0 = 1
        ↓
DMA2_Stream0_IRQHandler()
        ↓
g_transfer_cmplt = 1
        ↓
UART 결과 출력

이번 실습:

DMA       : DMA2
Stream    : Stream0
Mode      : Memory-to-Memory
Data Size : 16-bit
Source    : sensor_data_arr[]
Dest      : temp_data_arr[]
Length    : 5
Interrupt : Transfer Complete / Transfer Error

1. Why DMA?

CPU가 직접 배열을 복사한다면:

for (int i = 0; i < BUFFER_SIZE; i++)
{
    temp_data_arr[i] = sensor_data_arr[i];
}

동작은:

CPU
 ↓
Read Source
 ↓
Write Destination
 ↓
Read Source
 ↓
Write Destination
 ↓
...

즉 데이터 이동을 CPU가 직접 수행한다.

DMA를 사용하면:

CPU
 ↓
DMA 설정
 ↓
Start
 ↓

DMA Hardware ├── Read Source ├── Write Destination ├── Read Source ├── Write Destination └── ...

CPU → 다른 작업 가능

이 된다.


2. DMA Architecture

STM32F439ZI에는 DMA controller가 존재하고 각 controller는 여러 Stream을 가진다.

이번 실습:

DMA2
└── Stream0

를 사용한다.

DMA transfer의 기본 요소:

Source Address
Destination Address
Data Count
Data Width
Transfer Direction
Address Increment
Interrupt

이 설정되면 hardware가 데이터를 직접 이동한다.


3. Important Registers

이번 코드에서 핵심:

DMA2_Stream0->CR
DMA2_Stream0->NDTR
DMA2_Stream0->PAR
DMA2_Stream0->M0AR
DMA2_Stream0->FCR

DMA2->LISR DMA2->LIFCR

역할:

Register 역할
CR Direction, size, increment, interrupt, enable
NDTR 전송할 data 개수
PAR Source address
M0AR Destination address
FCR FIFO configuration
LISR Stream0 interrupt status
LIFCR Stream0 interrupt flag clear

4. Practice Code

Project:

Inc/
├── dma.h
└── uart.h

Src/ ├── dma.c ├── uart.c └── main.c


5. dma.h

#ifndef DMA_H__
#define DMA_H__

#include <stdint.h> #include "stm32f4xx.h"

/*

  • Configure DMA2 Stream0 for:
  • Memory → Memory
    
  • transfer.
  • Data width:
  • 16-bit
    
  • Source / destination address:
  • automatically incremented.
    

*/ void dma2_mem2mem_config(void);

/*

  • Start one DMA transfer.
  • src_buff:
  • source address
    
  • dest_buff:
  • destination address
    
  • len:
  • number of 16-bit data items
    

*/ void dma_transfer_start( uint32_t src_buff, uint32_t dest_buff, uint32_t len );

#endif


6. dma.c

#include "dma.h"

/*

  • ============================================================
  • DMA Register Definitions
  • ============================================================ */

/*

  • RCC AHB1ENR:
  • Enable DMA2 peripheral clock. */ #define DMA2EN RCC_AHB1ENR_DMA2EN

/*

  • DMA Stream Control Register EN bit.
  • EN = 1
  • → Stream active
  • EN = 0
  • → Stream disabled */ #define DMA_SCR_EN DMA_SxCR_EN

/*

  • Memory Increment Mode.
  • After each transfer:
  • destination address += data size */ #define DMA_SCR_MINC DMA_SxCR_MINC

/*

  • Peripheral Increment Mode.
  • In memory-to-memory mode,
  • PAR is used as the source address.
  • Therefore PINC causes the source address
  • to increase after every transfer. */ #define DMA_SCR_PINC DMA_SxCR_PINC

/*

  • Transfer Complete Interrupt Enable. */ #define DMA_SCR_TCIE DMA_SxCR_TCIE

/*

  • Transfer Error Interrupt Enable. */ #define DMA_SCR_TEIE DMA_SxCR_TEIE

/*

  • Disable Direct Mode.
  • DMDIS = 1
  • → FIFO mode enabled. */ #define DMA_SFCR_DMDIS DMA_SxFCR_DMDIS

/*

  • Stream0 Transfer Complete flag. */ #define LISR_TCIF0 DMA_LISR_TCIF0

/*

  • Stream0 Transfer Error flag. */ #define LISR_TEIF0 DMA_LISR_TEIF0

/*

  • Clear Stream0 Transfer Complete flag. */ #define LIFCR_CTCIF0 DMA_LIFCR_CTCIF0

/*

  • Clear Stream0 Transfer Error flag. */ #define LIFCR_CTEIF0 DMA_LIFCR_CTEIF0

/*

  • ============================================================
  • DMA2 Stream0 Configuration
  • ============================================================ */

void dma2_mem2mem_config(void) { /* * -------------------------------------------------------- * Step 1. Enable DMA2 Clock * -------------------------------------------------------- * * DMA2 registers cannot be used until * the AHB1 peripheral clock is enabled. */ RCC->AHB1ENR |= DMA2EN;

/*
 * --------------------------------------------------------
 * Step 2. Disable Stream Before Configuration
 * --------------------------------------------------------
 *
 * DMA configuration fields must not be modified
 * while the stream is enabled.
 *
 * Writing CR = 0 requests Stream0 disable.
 */
DMA2_Stream0-&gt;CR = 0U;


/*
 * IMPORTANT:
 *
 * Hardware may need a short time to actually
 * complete the disable operation.
 *
 * Therefore wait until EN really becomes 0.
 */
while ((DMA2_Stream0-&gt;CR &amp; DMA_SCR_EN) != 0U)
{
}


/*
 * --------------------------------------------------------
 * Step 3. Memory Data Size = 16-bit
 * --------------------------------------------------------
 *
 * MSIZE<span>[1:0]</span>:
 *
 *     00 → 8-bit
 *     01 → 16-bit
 *     10 → 32-bit
 *
 * Set:
 *
 *     bit13 = 1
 *     bit14 = 0
 *
 * → 16-bit
 */
DMA2_Stream0-&gt;CR |=  (1U &lt;&lt; 13);
DMA2_Stream0-&gt;CR &amp;= ~(1U &lt;&lt; 14);


/*
 * --------------------------------------------------------
 * Step 4. Source Data Size = 16-bit
 * --------------------------------------------------------
 *
 * PSIZE<span>[1:0]</span>:
 *
 * In memory-to-memory mode PAR is used
 * for the source address.
 *
 *     01 → 16-bit
 */
DMA2_Stream0-&gt;CR |=  (1U &lt;&lt; 11);
DMA2_Stream0-&gt;CR &amp;= ~(1U &lt;&lt; 12);


/*
 * --------------------------------------------------------
 * Step 5. Destination Address Increment
 * --------------------------------------------------------
 *
 * MINC = 1
 *
 * Destination:
 *
 * temp_data_arr<span>[0]</span>
 *        ↓
 * temp_data_arr<span>[1]</span>
 *        ↓
 * temp_data_arr<span>[2]</span>
 *        ↓
 * ...
 */
DMA2_Stream0-&gt;CR |= DMA_SCR_MINC;


/*
 * --------------------------------------------------------
 * Step 6. Source Address Increment
 * --------------------------------------------------------
 *
 * PINC = 1
 *
 * Source:
 *
 * sensor_data_arr<span>[0]</span>
 *        ↓
 * sensor_data_arr<span>[1]</span>
 *        ↓
 * sensor_data_arr<span>[2]</span>
 *        ↓
 * ...
 */
DMA2_Stream0-&gt;CR |= DMA_SCR_PINC;


/*
 * --------------------------------------------------------
 * Step 7. Select Memory-to-Memory Direction
 * --------------------------------------------------------
 *
 * DIR<span>[1:0]</span>:
 *
 *     00 → Peripheral-to-Memory
 *     01 → Memory-to-Peripheral
 *     10 → Memory-to-Memory
 *
 * Therefore:
 *
 *     bit6 = 0
 *     bit7 = 1
 */
DMA2_Stream0-&gt;CR &amp;= ~(1U &lt;&lt; 6);
DMA2_Stream0-&gt;CR |=  (1U &lt;&lt; 7);


/*
 * --------------------------------------------------------
 * Step 8. Enable Transfer Complete Interrupt
 * --------------------------------------------------------
 *
 * When NDTR reaches zero after successful
 * transfer:
 *
 *     TCIF0 = 1
 *
 * and an interrupt is generated.
 */
DMA2_Stream0-&gt;CR |= DMA_SCR_TCIE;


/*
 * --------------------------------------------------------
 * Step 9. Enable Transfer Error Interrupt
 * --------------------------------------------------------
 *
 * If a transfer error occurs:
 *
 *     TEIF0 = 1
 */
DMA2_Stream0-&gt;CR |= DMA_SCR_TEIE;


/*
 * --------------------------------------------------------
 * Step 10. Enable FIFO Mode
 * --------------------------------------------------------
 *
 * DMDIS = 1
 *
 * Direct Mode Disabled
 * → FIFO Mode Enabled
 */
DMA2_Stream0-&gt;FCR |= DMA_SFCR_DMDIS;


/*
 * --------------------------------------------------------
 * Step 11. FIFO Threshold = Full
 * --------------------------------------------------------
 *
 * FTH<span>[1:0]</span> = 11
 *
 * FIFO threshold:
 *
 *     Full
 */
DMA2_Stream0-&gt;FCR |= (1U &lt;&lt; 0);
DMA2_Stream0-&gt;FCR |= (1U &lt;&lt; 1);


/*
 * --------------------------------------------------------
 * Step 12. Enable Stream0 IRQ in NVIC
 * --------------------------------------------------------
 *
 * DMA transfer completion/error can now
 * generate a Cortex-M interrupt.
 */
NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}


7. dma_transfer_start()

void dma_transfer_start(
uint32_t src_buff,
uint32_t dest_buff,
uint32_t len
)
{
/*
* --------------------------------------------------------
* Step 1. Set Source Address
* --------------------------------------------------------
*
* In this Mem-to-Mem configuration:
*
* PAR
*
* is used as the SOURCE address.
*
* Example:
*
*     &sensor_data_arr[0]
*/
DMA2_Stream0->PAR = src_buff;
/*
 * --------------------------------------------------------
 * Step 2. Set Destination Address
 * --------------------------------------------------------
 *
 * M0AR:
 *
 *     Memory 0 Address Register
 *
 * points to the destination buffer.
 *
 * Example:
 *
 *     &amp;temp_data_arr<span>[0]</span>
 */
DMA2_Stream0-&gt;M0AR = dest_buff;


/*
 * --------------------------------------------------------
 * Step 3. Set Number of Data Items
 * --------------------------------------------------------
 *
 * NDTR:
 *
 *     Number of Data To Transfer
 *
 * BUFFER_SIZE = 5
 *
 * Because data width is 16-bit:
 *
 * this means:
 *
 *     5 × 16-bit values
 */
DMA2_Stream0-&gt;NDTR = len;


/*
 * --------------------------------------------------------
 * Step 4. Enable DMA Stream
 * --------------------------------------------------------
 *
 * EN = 1
 *
 * As soon as the stream is enabled,
 * DMA hardware begins the transfer.
 */
DMA2_Stream0-&gt;CR |= DMA_SCR_EN;

}


8. Source / Destination Arrays

#define BUFFER_SIZE  5U

/*

  • Source buffer.
  • Stored in initialized data memory. */ uint16_t sensor_data_arr[BUFFER_SIZE] = { 892, 731, 1234, 90, 23 };

/*

  • Destination buffer.
  • DMA will fill this array.
  • CPU does NOT execute:
  • temp_data_arr<span>[i]</span> = sensor_data_arr<span>[i]</span>;
    

*/ uint16_t temp_data_arr[BUFFER_SIZE];

/*

  • Set by DMA ISR.
  • main() waits for this flag.
  • volatile is mandatory because:
  • main() reads it
    
  • ISR writes it
    

*/ volatile uint8_t g_transfer_cmplt = 0U;


9. main.c

#include <stdint.h>

#include "dma.h" #include "uart.h"

#define BUFFER_SIZE 5U

/*

  • ============================================================
  • Source Buffer
  • ============================================================ */ uint16_t sensor_data_arr[BUFFER_SIZE] = { 892, 731, 1234, 90, 23 };

/*

  • ============================================================
  • Destination Buffer
  • ============================================================
  • Before DMA:
  • not yet copied
    
  • After DMA:
  • {892, 731, 1234, 90, 23}
    

*/ uint16_t temp_data_arr[BUFFER_SIZE];

/*

  • ============================================================
  • DMA Completion Flag
  • ============================================================
  • ISR:
  • writes this variable.
    
  • main:
  • reads this variable.
    

*/ volatile uint8_t g_transfer_cmplt = 0U;

/*

  • Existing practice used uart_print_uint16()
  • for decimal output.
  • If this helper is already present in uart.c,
  • use the project version directly. */ void uart_print_uint16(uint16_t value);

int main(void) { /* * -------------------------------------------------------- * Step 1. Initialize Completion Flag * -------------------------------------------------------- */ g_transfer_cmplt = 0U;

/*
 * --------------------------------------------------------
 * Step 2. Initialize USART3
 * --------------------------------------------------------
 *
 * DMA copy result will be printed through UART.
 */
uart_init();


/*
 * --------------------------------------------------------
 * Step 3. Configure DMA2 Stream0
 * --------------------------------------------------------
 *
 * Configuration:
 *
 *     Memory-to-Memory
 *     Source increment
 *     Destination increment
 *     16-bit → 16-bit
 *     TC interrupt
 *     TE interrupt
 */
dma2_mem2mem_config();


/*
 * --------------------------------------------------------
 * Step 4. Start DMA Transfer
 * --------------------------------------------------------
 *
 * Source:
 *
 *     sensor_data_arr
 *
 * Destination:
 *
 *     temp_data_arr
 *
 * Length:
 *
 *     5
 */
dma_transfer_start(
    (uint32_t)sensor_data_arr,
    (uint32_t)temp_data_arr,
    BUFFER_SIZE
);


/*
 * --------------------------------------------------------
 * Step 5. Wait for DMA Completion
 * --------------------------------------------------------
 *
 * DMA hardware is transferring the data.
 *
 * When transfer completes:
 *
 *     DMA2_Stream0_IRQHandler()
 *
 * sets:
 *
 *     g_transfer_cmplt = 1
 *
 * NOTE:
 *
 * This busy-wait is used only to make this
 * educational test simple.
 *
 * In a real application the CPU could perform
 * other work while DMA is running.
 */
while (g_transfer_cmplt == 0U)
{
}


/*
 * --------------------------------------------------------
 * Step 6. Print Destination Buffer
 * --------------------------------------------------------
 *
 * If DMA worked correctly:
 *
 * temp_data_arr[]
 *
 * must exactly match:
 *
 * sensor_data_arr[]
 */
for (uint32_t i = 0U;
     i &lt; BUFFER_SIZE;
     i++)
{
    uart_write_string("Temp buffer<span>[");

    uart_print_uint16((uint16_t)i);

    uart_write_string("]</span>: ");

    uart_print_uint16(temp_data_arr<span>[i]</span>);

    uart_write_string("\r\n");
}


/*
 * Clear software completion flag after verification.
 */
g_transfer_cmplt = 0U;


/*
 * Test complete.
 */
while (1)
{
}

}


10. DMA Interrupt Handler

void DMA2_Stream0_IRQHandler(void)
{
/*
* ========================================================
* Transfer Complete
* ========================================================
/
if ((DMA2->LISR & LISR_TCIF0) != 0U)
{
/
* Tell main():
*
*     DMA transfer completed successfully.
*/
g_transfer_cmplt = 1U;
    /*
     * Clear Transfer Complete Flag.
     *
     * DMA interrupt clear registers use:
     *
     *     Write 1 to Clear
     *
     * Direct assignment is preferable for W1C
     * style registers when clearing a specific flag.
     */
    DMA2-&gt;LIFCR = LIFCR_CTCIF0;
}


/*
 * ========================================================
 * Transfer Error
 * ========================================================
 */
if ((DMA2-&gt;LISR &amp; LISR_TEIF0) != 0U)
{
    /*
     * Transfer Error occurred.
     *
     * This basic exercise only clears the flag.
     *
     * A real system could additionally:
     *
     *     set error flag
     *     retry transfer
     *     log the failure
     */
    DMA2-&gt;LIFCR = LIFCR_CTEIF0;
}

}


11. Why Direct Assignment for LIFCR?

원본 실습 기록은:

DMA2->LIFCR |= LIFCR_CTCIF0;
DMA2->LIFCR |= LIFCR_CTEIF0;

형태였다.

하지만 interrupt clear register는 특정 bit에 1을 써서 flag를 clear하는 구조다.

그래서 Wiki 최종본에서는:

DMA2->LIFCR = LIFCR_CTCIF0;

처럼 clear하려는 bit만 직접 write하는 형태로 정리했다.

Ch14의:

EXTI->PR = LINE13;

과 같은 W1C register handling 원칙이다.


12. DMA Transfer Flow

초기 상태:

sensor_data_arr

[0] 892 [1] 731 [2] 1234 [3] 90 [4] 23

DMA registers:

PAR
↓
sensor_data_arr[0]

M0AR ↓ temp_data_arr[0]

NDTR ↓ 5

Transfer 시작:

DMA Stream EN = 1
↓

첫 번째 data:

Read 892
from Source
  ↓

Write 892 to Destination

  ↓

PINC Source Address + 2 bytes

MINC Destination Address + 2 bytes

NDTR 5 → 4

다음:

731
↓
1234
↓
90
↓
23

마지막:

NDTR
1 → 0
↓
Transfer Complete
↓
TCIF0 = 1

13. Why PINC and MINC?

만약 increment를 사용하지 않는다면:

Source Address
always same

Destination Address always same

가 된다.

그러면 여러 array element를 순서대로 복사할 수 없다.

이번 설정:

DMA2_Stream0->CR |= DMA_SCR_PINC;
DMA2_Stream0->CR |= DMA_SCR_MINC;

때문에:

16-bit transfer

2 bytes

씩 address가 이동한다.

Source:

&arr[0] ↓ +2 &arr[1] ↓ +2 &arr[2]

Destination도 동일하다.


14. Why 16-bit Size?

배열 type:

uint16_t

이다.

즉 element 하나:

16 bits

2 bytes

따라서 DMA:

PSIZE = 16-bit
MSIZE = 16-bit

로 맞춘다.

Source Element
16-bit

→ DMA

Destination Element 16-bit

data width가 source variable type과 맞아야 한다.


15. What Does NDTR = 5 Mean?

중요:

DMA2_Stream0->NDTR = 5;

는:

5 bytes

라는 뜻이 아니다.

현재 transfer width가:

16-bit

이므로:

5 data items
×
2 bytes

10 bytes

를 전송한다.

즉:

NDTR

Number of Transfer Items

이다.


16. Why Disable the Stream First?

DMA stream이 active 상태일 때는 여러 configuration field를 수정할 수 없다.

그래서:

DMA2_Stream0->CR = 0U;

while (DMA2_Stream0->CR & DMA_SCR_EN) { }

을 수행한다.

흐름:

Request Disable
↓
Hardware finishing current operation
↓
EN still 1
↓
Wait
↓
EN = 0
↓
Now configuration can change

Ch15 RTC의 ready flag, Ch16 IWDG의 PVU/RVU와 같은 pattern이다.

Write Hardware Control
↓
Wait Until Hardware Confirms
↓
Continue

17. Why volatile g_transfer_cmplt?

volatile uint8_t g_transfer_cmplt;

main:

while (!g_transfer_cmplt)
{
}

ISR:

g_transfer_cmplt = 1U;

즉:

main context
↕
shared variable
↕
interrupt context

구조다.

volatile이 없다면 compiler는 main만 보고:

이 loop 안에서
g_transfer_cmplt를 바꾸는 코드가 없는데?

라고 판단해 memory를 반복해서 읽지 않는 최적화를 할 가능성이 있다.

volatile은:

이 변수는 CPU 실행 흐름 밖의 원인에 의해
언제든 변경될 수 있다

고 compiler에게 알려준다.


18. Isn't Waiting for DMA Wasteful?

현재:

while (!g_transfer_cmplt)
{
}

로 기다린다.

그러면:

DMA를 써놓고 CPU가 기다리는데 무슨 의미가 있지?

라는 의문이 생길 수 있다.

맞다.

이 코드는 DMA 동작 검증용 단순 실습이다.

실제 활용:

dma_transfer_start(...);

while (1) { /* * 다른 sensor 처리 * communication * control algorithm * state machine */

if (g_transfer_cmplt)
{
    /*
     * DMA result processing
     */
}

}

형태가 더 적절하다.

핵심은 DMA가:

CPU instruction 없이
data transfer 자체를 수행

할 수 있다는 것이다.


19. DMA Interrupt Flow

DMA Transfer
↓
NDTR reaches 0
↓
TCIF0 = 1
↓
DMA2_Stream0_IRQn
↓
NVIC
↓
DMA2_Stream0_IRQHandler()
↓
g_transfer_cmplt = 1
↓
TCIF0 Clear
↓
Return

20. TC vs TE

ISR에는 if가 두 개 있다.

if (TCIF0)
{
}

if (TEIF0) { }

TCIF0

Transfer Complete

정상 종료.

TEIF0

Transfer Error

전송 중 error 발생.

둘은 의미가 다르기 때문에 독립적으로 검사한다.


21. Actual Data Verification

Source:

sensor_data_arr

[0] = 892 [1] = 731 [2] = 1234 [3] = 90 [4] = 23

실제 UART 출력:

Temp buffer[0]: 892
Temp buffer[1]: 731
Temp buffer[2]: 1234
Temp buffer[3]: 90
Temp buffer[4]: 23

즉:

Source

Destination

를 실제 board에서 확인했다.

따라서:

DMA Memory-to-Memory Transfer
PASS

이다.


22. Memory View Verification

Debugger를 사용한다면 DMA 전:

sensor_data_arr

892 731 1234 90 23

temp_data_arr

0 0 0 0 0

를 확인한다.

DMA 후:

temp_data_arr

892 731 1234 90 23

으로 바뀌는 것을 확인할 수 있다.

이 경우 CPU code에는:

temp_data_arr[i] = sensor_data_arr[i];

가 한 줄도 없다는 것이 핵심이다.


23. ADC DMA Extension

이번 chapter에서는 구현하지 않았지만 ADC와 DMA를 연결하면:

Analog Input
↓
ADC
↓
ADC->DR
↓
DMA
↓
Buffer[]

구조가 된다.

CPU가 매 conversion마다:

adc_read();

하지 않아도 된다.

보통 continuous ADC acquisition에서는:

Peripheral-to-Memory
+
Circular Mode

를 많이 사용한다.

개념:

ADC Sample
↓
Buffer[0]

ADC Sample ↓ Buffer[1]

...

Buffer End ↓ Back to Buffer[0]


24. UART DMA Extension

UART TX:

Memory Buffer
↓
DMA
↓
USART->DR
↓
TX

방향:

Memory-to-Peripheral

UART RX:

USART->DR
↓
DMA
↓
Memory Buffer

방향:

Peripheral-to-Memory

UART peripheral 자체에서도 DMA request를 enable해야 한다.

예:

USART CR3
DMAT / DMAR

이번 chapter에서는 개념만 다루고 실제 구현은 하지 않았다.


25. Build Result

실제 실습:

Build    : PASS
Download : PASS
Run      : PASS
DMA Copy : PASS

Program size:

text = 1640 bytes
data = 12 bytes
bss  = 2572 bytes

26. Why .data = 12?

이번에는 초기값이 존재하는 global array:

uint16_t sensor_data_arr[5] =
{
892,
731,
1234,
90,
23
};

가 있다.

크기:

5
×
2 bytes

10 bytes

추가 초기화 data 및 alignment의 영향까지 포함되어 실습 build에서는:

.data = 12 bytes

가 기록됐다.

반면:

uint16_t temp_data_arr[5];

처럼 초기값이 없는 global variable은 .bss 영역에 배치된다.


27. Verification

Build

make clean
make

Expected:

text = 1640
data = 12
bss  = 2572

DMA Configuration

Debugger:

DMA2_Stream0->CR
DMA2_Stream0->FCR

확인:

DIR   = Memory-to-Memory
PINC  = 1
MINC  = 1

PSIZE = 16-bit MSIZE = 16-bit

TCIE = 1 TEIE = 1


Addresses

확인:

DMA2_Stream0->PAR

→ sensor_data_arr 주소와 일치.

DMA2_Stream0->M0AR

→ temp_data_arr 주소와 일치.


NDTR

전송 전:

NDTR = 5

전송 중:

5
↓
4
↓
3
↓
2
↓
1
↓
0

전송 완료:

NDTR = 0

Interrupt

Breakpoint:

void DMA2_Stream0_IRQHandler(void)

전송 완료 시 ISR에 진입하는지 확인한다.

Watch:

DMA2->LISR
g_transfer_cmplt

Expected:

TCIF0
0 → 1

g_transfer_cmplt 0 → 1


Destination Buffer

Expected:

temp_data_arr[0] = 892
temp_data_arr[1] = 731
temp_data_arr[2] = 1234
temp_data_arr[3] = 90
temp_data_arr[4] = 23

28. Screenshots to Add

images/ch17_build.png
images/ch17_dma_registers.png
images/ch17_addresses.png
images/ch17_ndtr.png
images/ch17_dma_isr.png
images/ch17_memory_before.png
images/ch17_memory_after.png
images/ch17_uart_result.png

가장 좋은 evidence:

Source Array
+
PAR / M0AR
+
NDTR = 5 → 0
+
TC Interrupt
+
Destination Array
+
UART Result

이다.


29. Key Points

CPU
↓
DMA Configuration
↓
Source Address
Destination Address
Length
Direction
Size
↓
DMA Enable
↓
Hardware Data Transfer
↓
TC Interrupt
↓
CPU Notification
  • DMA는 CPU 대신 data movement를 수행하는 hardware다.

  • 이번 실습은 DMA2 Stream0의 Memory-to-Memory transfer를 사용한다.

  • source와 destination은 모두 uint16_t이므로 PSIZE/MSIZE를 16-bit로 설정한다.

  • PINC와 MINC를 enable하여 양쪽 address를 자동 증가시킨다.

  • PAR는 이번 Mem-to-Mem 설정에서 source address로 사용한다.

  • M0AR은 destination address다.

  • NDTR=5는 5byte가 아니라 5개의 16-bit transfer item을 의미한다.

  • Stream 설정 전 EN=0을 반드시 확인한다.

  • Transfer Complete와 Transfer Error interrupt를 각각 enable한다.

  • g_transfer_cmplt는 ISR과 main이 공유하므로 volatile이다.

  • 실제 board에서 {892, 731, 1234, 90, 23}이 정확히 복사되는 것을 확인했다.

  • ADC/UART DMA는 이번 chapter에서는 개념만 다루고 실제 구현하지 않았다.

Clone this wiki locally