Chapter 17 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

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์—์„œ๋Š” ๊ฐœ๋…๋งŒ ๋‹ค๋ฃจ๊ณ  ์‹ค์ œ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ