💾 hal::dma
Direct Memory Access - High Performance Data Transfer
391 satır
~15 fonksiyon
Zero-Copy
📖 Genel Bakış
DMA (Direct Memory Access), CPU müdahalesi olmadan bellek ve çevre birimleri arasında veri transferi sağlar. ADC, USART, SPI gibi modüllerle yüksek hızlı veri aktarımı için kullanılır.
🔑 Temel Özellikler
- Memory-to-memory transfers
- Peripheral-to-memory transfers
- Memory-to-peripheral transfers
- Circular/Normal mode
- Priority levels (4)
- 8/16/32-bit data width
- Transfer complete interrupts
- Multiple channels/streams
🚀 Hızlı Başlangıç
içe_aktar hal::dma
// Memory-to-memory transfer
değişken src = [1, 2, 3, 4, 5]
değişken dst = [0; 5]
değişken dma_ch = dma.init(dma.DMA1, dma.CH1, yap
direction: dma.M2M, // Memory to Memory
src_increment: doğru,
dst_increment: doğru,
data_size: dma.SIZE_8BIT
son)
dma.transfer(dma_ch, src.as_ptr(), dst.as_ptr(), 5)
dma.wait(dma_ch) // Wait for completion
💡 Örnek: ADC with DMA (Continuous Sampling)
içe_aktar hal::dma, hal::adc
sabit SAMPLE_COUNT = 1000
değişken adc_buffer = [0u16; SAMPLE_COUNT]
fonksiyon adc_dma_init() yap
// Initialize ADC
adc.clock_enable()
değişken adc1 = adc.init(adc.ADC1, yap
resolution: adc.RESOLUTION_12BIT,
continuous: doğru,
dma_enable: doğru
son)
adc.channel_config(adc1, adc.CHANNEL_0, yap
sampling_time: adc.SAMPLETIME_480CYCLES
son)
// Setup DMA
dma.clock_enable(dma.DMA2)
değişken dma_ch = dma.init(dma.DMA2, dma.STREAM0, yap
direction: dma.P2M, // Peripheral to Memory
src_address: adc.get_data_register(adc1),
dst_address: adc_buffer.as_ptr(),
data_count: SAMPLE_COUNT,
src_increment: yanlış, // Same ADC register
dst_increment: doğru, // Increment buffer
circular: doğru, // Continuous sampling
data_size: dma.SIZE_16BIT,
priority: dma.PRIORITY_HIGH
son)
dma.start(dma_ch)
adc.start(adc1)
son
fonksiyon calculate_average() -> kesir yap
değişken toplam = 0
her sample içinde adc_buffer için yap
toplam += sample
son
dön toplam.kesir() / SAMPLE_COUNT.kesir()
son
fonksiyon ana() yap
adc_dma_init()
döngü yap
değişken avg = calculate_average()
değişken voltage = avg * 3.3 / 4095.0
io.println("Average voltage: {:.3f}V".formatla(voltage))
core.delay_ms(1000)
son
son
💡 Örnek: USART TX with DMA
içe_aktar hal::dma, hal::usart
değişken dma_complete = yanlış
fonksiyon dma_complete_callback() yap
dma_complete = doğru
son
fonksiyon usart_dma_send(uart: usart.Handle, data: yazı) -> Sonuç[Hiçbir, Hata] yap
// Setup DMA for USART TX
değişken dma_ch = dma.init(dma.DMA1, dma.CH4, yap
direction: dma.M2P, // Memory to Peripheral
src_address: data.as_ptr(),
dst_address: usart.get_tx_register(uart),
data_count: data.uzunluk(),
src_increment: doğru,
dst_increment: yanlış, // Same USART register
data_size: dma.SIZE_8BIT,
priority: dma.PRIORITY_MEDIUM,
callback: dma_complete_callback
son)
dma_complete = yanlış
dma.start(dma_ch)
// Enable USART DMA mode
usart.enable_dma_tx(uart)
// Wait for completion
döngü dma_complete değilse yap
core.delay_ms(1)
son
dön Tamam(Hiçbir)
son
fonksiyon ana() yap
değişken uart = usart.init(usart.USART1, yap
baud_rate: 115200
son)
usart_dma_send(uart, "Hello from DMA!\n")?
io.println("DMA transfer complete!")
son
💡 Örnek: Memory-to-Memory Fast Copy
içe_aktar hal::dma
fonksiyon fast_memcpy(dest: *değişken u8, src: *u8, size: sayı) yap
// DMA memory-to-memory is much faster than CPU copy
değişken dma_ch = dma.init(dma.DMA2, dma.STREAM0, yap
direction: dma.M2M, // Memory to Memory
src_address: src,
dst_address: dest,
data_count: size,
src_increment: doğru,
dst_increment: doğru,
data_size: dma.SIZE_32BIT, // 32-bit for efficiency
priority: dma.PRIORITY_VERY_HIGH
son)
dma.start(dma_ch)
dma.wait(dma_ch)
son
fonksiyon ana() yap
// Large buffer copy test
sabit SIZE = 10000
değişken src_buffer = [0u8; SIZE]
değişken dst_buffer = [0u8; SIZE]
// Fill source with test data
her i içinde 0..SIZE için yap
src_buffer[i] = (i % 256).u8()
son
// Benchmark
değişken start = core.uptime_us()
fast_memcpy(dst_buffer.as_mut_ptr(), src_buffer.as_ptr(), SIZE)
değişken elapsed = core.uptime_us() - start
io.println("DMA copy {}KB in {}us", SIZE/1024, elapsed)
io.println("Speed: {:.2f}MB/s", SIZE.kesir() / elapsed.kesir())
son
💡 Örnek: Double Buffer (Ping-Pong)
içe_aktar hal::dma, hal::adc
sabit BUFFER_SIZE = 512
değişken buffer_a = [0u16; BUFFER_SIZE]
değişken buffer_b = [0u16; BUFFER_SIZE]
değişken current_buffer = 0
fonksiyon buffer_half_complete() yap
// Buffer A is full, start processing while DMA fills B
process_samples(buffer_a)
current_buffer = 1
son
fonksiyon buffer_complete() yap
// Buffer B is full, start processing while DMA fills A
process_samples(buffer_b)
current_buffer = 0
son
fonksiyon process_samples(buffer: []u16) yap
değişken max_val = 0u16
her sample içinde buffer için yap
eğer sample > max_val ise max_val = sample
son
io.println("Max value: {}", max_val)
son
fonksiyon adc_double_buffer_init() yap
// Init ADC
değişken adc1 = adc.init(adc.ADC1, yap
resolution: adc.RESOLUTION_12BIT,
continuous: doğru,
dma_enable: doğru
son)
// DMA double buffer mode
değişken dma_ch = dma.init(dma.DMA2, dma.STREAM0, yap
direction: dma.P2M,
src_address: adc.get_data_register(adc1),
dst_address_0: buffer_a.as_ptr(), // Ping buffer
dst_address_1: buffer_b.as_ptr(), // Pong buffer
data_count: BUFFER_SIZE,
circular: doğru,
double_buffer: doğru,
src_increment: yanlış,
dst_increment: doğru,
data_size: dma.SIZE_16BIT,
half_complete_callback: buffer_half_complete,
complete_callback: buffer_complete
son)
dma.start(dma_ch)
adc.start(adc1)
son
fonksiyon ana() yap
adc_double_buffer_init()
// Process in background via interrupts
döngü yap
core.sleep() // CPU sleeps while DMA works
son
son
💡 Örnek: SPI Display with DMA Burst
içe_aktar hal::dma, hal::spi, hal::gpio
// Display framebuffer (128x64 OLED = 1024 bytes)
sabit WIDTH = 128
sabit HEIGHT = 64
değişken framebuffer = [0u8; (WIDTH * HEIGHT) / 8]
fonksiyon display_update() yap
// CS low
gpio.pin_write(gpio.PORT_A, 4, gpio.LOW)
// Command: set column/page address
spi.write_byte(spi1, 0x00) // Command mode
// DMA burst transfer entire framebuffer
değişken dma_ch = dma.init(dma.DMA1, dma.STREAM3, yap
direction: dma.M2P,
src_address: framebuffer.as_ptr(),
dst_address: spi.get_data_register(spi1),
data_count: framebuffer.uzunluk(),
src_increment: doğru,
dst_increment: yanlış,
data_size: dma.SIZE_8BIT,
priority: dma.PRIORITY_HIGH
son)
dma.start(dma_ch)
dma.wait(dma_ch)
// CS high
gpio.pin_write(gpio.PORT_A, 4, gpio.HIGH)
son
fonksiyon draw_pixel(x: sayı, y: sayı, color: bool) yap
değişken byte_index = x + (y / 8) * WIDTH
değişken bit_index = y % 8
eğer color ise yap
framebuffer[byte_index] |= (1 << bit_index)
son yoksa yap
framebuffer[byte_index] &= ~(1 << bit_index)
son
son
fonksiyon ana() yap
// Draw test pattern
her x içinde 0..WIDTH için yap
her y içinde 0..HEIGHT için yap
draw_pixel(x, y, (x + y) % 2 == 0)
son
son
// Fast DMA update (instead of slow SPI byte-by-byte)
display_update() // ~8ms with DMA vs 100ms+ without
son
💡 Örnek: USART RX with DMA Circular Buffer
içe_aktar hal::dma, hal::usart
sabit RX_BUFFER_SIZE = 256
değişken rx_buffer = [0u8; RX_BUFFER_SIZE]
değişken read_pos = 0
fonksiyon uart_dma_rx_init(uart: usart.Handle) yap
// DMA in circular mode for continuous RX
değişken dma_ch = dma.init(dma.DMA1, dma.STREAM5, yap
direction: dma.P2M,
src_address: usart.get_rx_register(uart),
dst_address: rx_buffer.as_ptr(),
data_count: RX_BUFFER_SIZE,
src_increment: yanlış,
dst_increment: doğru,
circular: doğru, // Never stops, wraps around
data_size: dma.SIZE_8BIT,
priority: dma.PRIORITY_MEDIUM
son)
usart.enable_dma_rx(uart)
dma.start(dma_ch)
son
fonksiyon available() -> sayı yap
// Get DMA write position
değişken write_pos = RX_BUFFER_SIZE - dma.get_counter(dma_ch)
eğer write_pos >= read_pos ise yap
dön write_pos - read_pos
son yoksa yap
dön RX_BUFFER_SIZE - read_pos + write_pos
son
son
fonksiyon read_byte() -> Seçenek[u8] yap
eğer available() > 0 ise yap
değişken data = rx_buffer[read_pos]
read_pos = (read_pos + 1) % RX_BUFFER_SIZE
dön Bazı(data)
son
dön Hiçbir
son
fonksiyon ana() yap
değişken uart = usart.init(usart.USART1, yap
baud_rate: 115200
son)
uart_dma_rx_init(uart)
döngü yap
eğer available() > 0 ise yap
eşle read_byte() yap
Bazı(byte) => io.print("{:c}", byte),
Hiçbir => {}
son
son
core.delay_ms(10)
son
son
⚙️ DMA Yapılandırma Tipleri
// DMA Direction
enum Direction {
P2M, // Peripheral to Memory (ADC, USART RX)
M2P, // Memory to Peripheral (USART TX, DAC)
M2M // Memory to Memory (fast copy)
}
// Data Size
enum DataSize {
SIZE_8BIT, // Byte
SIZE_16BIT, // Half-word
SIZE_32BIT // Word
}
// Priority
enum Priority {
LOW,
MEDIUM,
HIGH,
VERY_HIGH
}
// DMA Configuration
yapı DMAConfig yap
direction: Direction,
src_address: *u8,
dst_address: *u8,
data_count: sayı,
src_increment: bool,
dst_increment: bool,
circular: bool,
data_size: DataSize,
priority: Priority,
double_buffer: bool, // Optional
dst_address_1: *u8, // For double buffer
half_complete_callback: fn(), // Optional
complete_callback: fn() // Optional
son
📚 DMA Fonksiyonları
// DMA channel initialization
fonksiyon init(dma: DMAInstance, channel: Channel, config: DMAConfig) -> Handle
// Transfer control
fonksiyon start(handle: Handle)
fonksiyon stop(handle: Handle)
fonksiyon wait(handle: Handle) // Blocking wait
fonksiyon is_complete(handle: Handle) -> bool
// Status queries
fonksiyon get_counter(handle: Handle) -> sayı // Remaining transfers
fonksiyon get_current_buffer(handle: Handle) -> sayı // 0 or 1 (double buffer)
// Advanced
fonksiyon transfer(handle: Handle, src: *u8, dst: *u8, count: sayı)
fonksiyon circular_transfer(handle: Handle, src: *u8, dst: *u8, count: sayı)
// Clock control
fonksiyon clock_enable(dma: DMAInstance)
fonksiyon clock_disable(dma: DMAInstance)
⚡ Performans İpuçları
- 32-bit transfers: 4x faster than 8-bit for aligned data
- Circular mode: Zero overhead for continuous streaming (ADC, USART)
- Double buffer: Process one buffer while filling the other (zero data loss)
- Priority: Set VERY_HIGH for time-critical transfers
- Burst mode: Use for large block transfers (framebuffers, files)
📊 DMA Kullanım Senaryoları
| Senaryo | Direction | Mode | Avantaj |
|---|---|---|---|
| ADC Continuous Sampling | P2M | Circular | CPU-free data acquisition |
| USART High-Speed TX | M2P | Normal | Non-blocking transmission |
| SPI Display Update | M2P | Normal | 10-20x faster refresh |
| Audio Streaming | M2P (DAC) | Double Buffer | Glitch-free playback |
| Large Buffer Copy | M2M | Normal | Hardware acceleration |
🖥️ Platform Desteği
- STM32F1: DMA1 (7 channels), DMA2 (5 channels)
- STM32F4: DMA1/DMA2 (8 streams each, FIFO support)
- STM32L4: DMA1/DMA2 (7 channels each, low-power)
- GD32: Compatible with STM32 DMA architecture
- ESP32: GDMA (general purpose DMA)
⚠️ Önemli Notlar
- Memory Alignment: DMA buffer'ları RAM'de olmalıdır (not flash/peripheral memory)
- Circular Mode: Infinite loop için kullanılır, ADC/USART streaming ideal
- NVIC Enable: DMA interrupt kullanırken NVIC enable edilmelidir
- Double Buffer: Zero data loss için ping-pong buffering kullanın
- Buffer Safety: DMA transfer sırasında buffer modify edilmemelidir
- Cache Coherency: STM32F7/H7'de cache invalidation gerekir
- Data Width: 32-bit aligned data için SIZE_32BIT kullanın (4x hızlı)
- Priority: Aynı öncelikte multiple DMA kullanımında arbitration olur
🔗 İlgili Modüller
hal::adc- ADC with DMA (continuous sampling)hal::dac- DAC with DMA (waveform generation)hal::usart- UART with DMA (high-speed serial)hal::spi- SPI with DMA (display, SD card)hal::i2c- I2C with DMA (sensor reading)hal::int- DMA interrupts (NVIC configuration)hal::timer- Timer-triggered DMA
📖 Referanslar
- AN4031: Using STM32 DMA Controller
- AN4943: DMA in STM32F7
- Reference Manual: DMA Controller (DMA) chapter