null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
☆ Star
DMA 실전 패턴
#dma
#qspi
#adc
#evadc
#double-buffer
@devpc
|
2026-04-03 23:46:46
|
GET /api/v1/nodes/231?nv=1
History:
v1 (2026-04-03) (Latest)
0
Views
2
Calls
# DMA 실전 패턴 ## QSPI + DMA: SPI 고속 전송 SPI로 대용량 데이터를 전송할 때 CPU 폴링 방식은 비효율적이다. QSPI의 TX/RX FIFO와 DMA를 연동하면 CPU는 전송 완료 인터럽트만 처리하면 된다. ``` CPU: 설정 후 대기 │ ▼ DMA CH_TX ──────► QSPI TX FIFO ──►(SPI 선로)──► 슬레이브 │ DMA CH_RX ◄───── QSPI RX FIFO ◄────────────────────┘ │ ▼ 완료 인터럽트 → CPU 처리 ``` ```c void qspi_dma_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len) { /* TX DMA: tx_buf → QSPI TX FIFO */ DMA_CH1_SADR.U = (uint32_t)tx_buf; DMA_CH1_DADR.U = (uint32_t)&QSPI0_DATAENTRY0.U; DMA_CH1_CHCR.B.TREL = len; DMA_CH1_CHCR.B.CHDW = 0x0U; /* 8비트 전송 */ DMA_CH1_ADRCR.B.INCS = 1U; /* 소스 주소 증가 */ DMA_CH1_ADRCR.B.INCD = 0U; /* 목적지(FIFO) 주소 고정 */ /* RX DMA: QSPI RX FIFO → rx_buf */ DMA_CH2_SADR.U = (uint32_t)&QSPI0_RXEXIT.U; DMA_CH2_DADR.U = (uint32_t)rx_buf; DMA_CH2_CHCR.B.TREL = len; DMA_CH2_CHCR.B.CHDW = 0x0U; DMA_CH2_ADRCR.B.INCS = 0U; /* 소스(FIFO) 고정 */ DMA_CH2_ADRCR.B.INCD = 1U; /* 두 채널 동시 시작 */ DMA_CH1_CHCR.B.SCH = 1U; DMA_CH2_CHCR.B.SCH = 1U; } ``` --- ## EVADC + DMA: 연속 ADC 샘플링 고주파 ADC 샘플링에서 매 변환마다 ISR을 발생시키면 인터럽트 오버헤드가 크다. DMA가 결과를 버퍼에 자동 누적하고, 버퍼가 차면 한 번만 인터럽트를 발생시킨다. ``` EVADC Result Register │ │ 변환 완료마다 DMA Request 발생 ▼ DMA 채널 │ ▼ ADC 샘플 버퍼 [0..N-1] │ ▼ (버퍼 full) 인터럽트 → CPU 일괄 처리 ``` --- ## 더블 버퍼링 패턴 싱글 버퍼를 쓰면 DMA가 쓰는 동안 CPU가 읽을 수 없다. **더블 버퍼링**으로 DMA 쓰기와 CPU 처리를 겹친다: ``` 버퍼 A ◄── DMA 쓰는 중 버퍼 B ◄── CPU 읽는 중 ↕ (전환) 버퍼 A ◄── CPU 읽는 중 버퍼 B ◄── DMA 쓰는 중 ``` ```c #define BUF_SIZE 256U static uint16_t adc_buf[2][BUF_SIZE]; static volatile uint8_t dma_active_buf = 0U; void dma_adc_complete_isr(void) { uint8_t done_buf = dma_active_buf; /* DMA를 반대 버퍼로 전환 */ dma_active_buf ^= 1U; DMA_CH3_DADR.U = (uint32_t)adc_buf[dma_active_buf]; DMA_CH3_CHCR.B.SCH = 1U; /* 이전 버퍼 처리 */ process_adc_samples(adc_buf[done_buf], BUF_SIZE); } ``` --- ## DMA 사용 시 주의사항 ### 캐시 일관성 DMA는 CPU 캐시를 거치지 않고 **직접 RAM에 쓴다**. DMA가 데이터를 RAM에 썼더라도 CPU는 캐시에 남은 오래된 값을 읽을 수 있다. ``` DMA → RAM (최신 데이터) CPU → 캐시 (오래된 데이터) ← 문제! ``` 해결책: DMA 수신 버퍼를 **캐시 불가(non-cacheable)** 영역에 배치하거나, DMA 완료 후 캐시 무효화(invalidate)를 수행한다. 자세한 내용은 `02_cache-and-memory-coherency.md` 참고. ### 버스 경합 DMA와 CPU가 동시에 SRI 버스를 쓰면 **CPU 명령어 페치 지연**이 발생한다. 실시간성이 중요한 ISR 코드는 PSPR(코어 로컬 스크래치패드)에 배치하여 SRI 버스 경합 영향을 피해야 한다. ### 정렬 요구사항 TC37x DMA는 전송 폭에 맞는 주소 정렬을 요구한다. | 전송 폭 | 정렬 요구 | |---|---| | 8비트 | 1바이트 정렬 | | 16비트 | 2바이트 정렬 | | 32비트 | 4바이트 정렬 | | 64비트 | 8바이트 정렬 | 정렬이 맞지 않으면 DMA 에러 인터럽트가 발생하거나 잘못된 데이터가 전송된다.
// COMMENTS
Newest First
ON THIS PAGE