728x90
반응형
DMA(Direct Memory Access) 처리: 개념과 사용법, 그리고 RK3399 예제
DMA란 무엇인가?
DMA(Direct Memory Access)는 CPU의 개입을 최소화하여 메모리와 주변 장치 간에 데이터를 직접 전송할 수 있는 기능입니다. 이를 통해 데이터 전송 속도를 대폭 향상시키고 CPU의 부하를 줄일 수 있습니다. DMA는 고속 데이터 전송이 필요한 임베디드 시스템에서 필수적인 기능으로 사용됩니다.
DMA의 작동 원리
- CPU는 DMA 컨트롤러에 데이터 전송 작업을 요청합니다.
- DMA 컨트롤러는 데이터 소스와 목적지 주소를 설정합니다.
- 데이터 전송이 진행되며, CPU는 다른 작업을 수행할 수 있습니다.
- 전송 완료 시 DMA 컨트롤러는 인터럽트를 통해 CPU에 완료를 알립니다.
이와 같은 방식으로 DMA는 CPU가 직접 데이터 전송을 처리하지 않도록 하여 시스템 성능을 최적화합니다.
DMA의 주요 용도
- 대용량 데이터 전송
- 고속 네트워크 패킷 처리
- 오디오/비디오 데이터 스트리밍
- 저장 장치와 메모리 간 데이터 복사
DMA 설정 및 사용 방법
DMA 설정 단계
- DMA 채널 및 스트림 선택
- 데이터 전송 방향 설정 (메모리 -> 메모리, 메모리 -> 주변 장치 등)
- 데이터 버퍼 크기 및 주소 설정
- DMA 인터럽트 설정
- DMA 활성화
RK3399에서 DMA 설정하기
RK3399은 ARM Cortex-A72와 Cortex-A53 코어를 기반으로 하는 고성능 SoC(System on Chip)로, DMA를 활용해 다양한 고속 데이터 전송 작업을 처리할 수 있습니다.
아래는 RK3399에서 DMA를 설정하고 사용하는 방법에 대한 코드 예제입니다.
DMA 예제: RK3399에서 메모리 간 데이터 전송
다음은 RK3399의 DMA 컨트롤러를 활용하여 메모리 간 데이터를 전송하는 예제입니다. 이 코드는 Linux 커널 모듈로 작성되었습니다.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/slab.h>
#define BUF_SIZE 1024
static struct completion dma_complete;
static void dma_callback(void *completion)
{
complete(completion);
}
static int __init rk3399_dma_example_init(void)
{
struct dma_chan *dma_chan;
dma_addr_t src_addr, dst_addr;
char *src_buf, *dst_buf;
struct dma_async_tx_descriptor *tx;
struct dma_slave_config dma_cfg;
enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int ret;
/* DMA 채널 요청 */
dma_chan = dma_request_chan(NULL, "dma0chan0");
if (IS_ERR(dma_chan)) {
pr_err("DMA 채널 요청 실패\n");
return PTR_ERR(dma_chan);
}
/* 소스 및 목적지 메모리 할당 */
src_buf = kmalloc(BUF_SIZE, GFP_KERNEL);
dst_buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!src_buf || !dst_buf) {
pr_err("메모리 할당 실패\n");
ret = -ENOMEM;
goto free_dma;
}
/* DMA 주소 매핑 */
src_addr = dma_map_single(dma_chan->device->dev, src_buf, BUF_SIZE, DMA_TO_DEVICE);
dst_addr = dma_map_single(dma_chan->device->dev, dst_buf, BUF_SIZE, DMA_FROM_DEVICE);
/* DMA 전송 설정 */
memset(&dma_cfg, 0, sizeof(dma_cfg));
dma_cfg.direction = DMA_MEM_TO_MEM;
dma_cfg.src_addr = src_addr;
dma_cfg.dst_addr = dst_addr;
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.src_maxburst = 16;
dma_cfg.dst_maxburst = 16;
ret = dmaengine_slave_config(dma_chan, &dma_cfg);
if (ret) {
pr_err("DMA 설정 실패\n");
goto unmap_memory;
}
/* DMA 작업 준비 */
init_completion(&dma_complete);
tx = dmaengine_prep_dma_memcpy(dma_chan, dst_addr, src_addr, BUF_SIZE, flags);
if (!tx) {
pr_err("DMA 준비 실패\n");
ret = -EIO;
goto unmap_memory;
}
tx->callback = dma_callback;
tx->callback_param = &dma_complete;
/* DMA 실행 */
dmaengine_submit(tx);
dma_async_issue_pending(dma_chan);
/* 전송 완료 대기 */
wait_for_completion(&dma_complete);
pr_info("DMA 전송 완료!\n");
unmap_memory:
dma_unmap_single(dma_chan->device->dev, src_addr, BUF_SIZE, DMA_TO_DEVICE);
dma_unmap_single(dma_chan->device->dev, dst_addr, BUF_SIZE, DMA_FROM_DEVICE);
kfree(src_buf);
kfree(dst_buf);
free_dma:
dma_release_channel(dma_chan);
return ret;
}
static void __exit rk3399_dma_example_exit(void)
{
pr_info("DMA 예제 모듈 종료\n");
}
module_init(rk3399_dma_example_init);
module_exit(rk3399_dma_example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("소프트웨어 공장");
MODULE_DESCRIPTION("RK3399에서 DMA를 이용한 메모리 간 데이터 전송 예제");
결론
DMA는 임베디드 시스템에서 데이터 전송의 효율성을 극대화하는 중요한 기술입니다. RK3399과 같은 플랫폼에서 DMA를 활용하면 고속 데이터 전송이 가능하며 CPU 리소스를 절약할 수 있습니다. 본 포스팅의 예제 코드를 기반으로 다양한 DMA 활용 사례를 확장해보세요.
반응형
'Linux > Kernel Driver' 카테고리의 다른 글
Embedded Linux에서 전력 관리 기술과 Device Driver의 전력 관리 구현 (0) | 2025.03.27 |
---|---|
Embedded Linux Kernel 디버깅 가이드 (0) | 2025.03.26 |
Interrupt Handling in Embedded Linux (0) | 2025.03.24 |
USB Device Driver 작성 및 Linux Kernel에서 USB 인터페이스 사용 방법 (0) | 2025.03.23 |
USART Driver 작성 및 Linux Kernel에서의 USART 인터페이스 사용 방법 (0) | 2025.03.22 |