หลักการทำงานของ DMA

โดยปกติแล้ว เวลาที่เราต้องย้ายข้อมูล เช่น:

  • อ่านข้อมูลจาก USART แล้วบันทึกใน RAM
  • หรือ อ่านข้อมูลจาก ADC แล้วเก็บใน Buffer

ถ้าไม่มี DMA: CPU จะต้องอ่านข้อมูลทีละ Byte/Word แล้วเขียนไปยังเป้าหมายเอง → กินเวลาของ CPU มาก

แต่ถ้ามี DMA: CPU แค่ตั้งค่าครั้งเดียว → แล้วปล่อยให้ DMA ทำงานแทน → CPU ว่างไปทำงานอย่างอื่นได้ทันที


ประเภทการโอนข้อมูลของ DMA

  1. Peripheral-to-Memory
    ตัวอย่างเช่น: ADC อ่านค่ามา → เขียนลง RAM อัตโนมัติ
  2. Memory-to-Peripheral
    ตัวอย่างเช่น: ส่งข้อมูลใน RAM ออก USART โดยอัตโนมัติ
  3. Memory-to-Memory
    ตัวอย่างเช่น: คัดลอกข้อมูลจากที่อยู่หนึ่งใน RAM ไปอีกที่หนึ่งใน RAM โดยตรง

จุดเด่นของการใช้ DMA

  • ลดภาระ CPU: เพราะ CPU ไม่ต้องย้ายข้อมูลเอง
  • โอนข้อมูลเร็วขึ้น: เพราะ DMA ทำการย้ายแบบต่อเนื่องในฮาร์ดแวร์
  • เหมาะกับงานที่ต้องโอนข้อมูลปริมาณมากหรือต่อเนื่อง เช่น:
    • Streaming ข้อมูล
    • การอ่านเซ็นเซอร์อย่างต่อเนื่อง
    • การสื่อสารข้อมูลจำนวนมากผ่าน USART/SPI/I2C

สรุปเปรียบเทียบ

หัวข้อไม่มี DMA (CPU ทำเอง)มี DMA
ภาระ CPUสูงต่ำ
ความเร็วโอนข้อมูลปานกลางสูงมาก
การตั้งค่าง่าย แต่ทำงานหนักต้องตั้งค่าระบบล่วงหน้า
การใช้ในงานจริงงานง่าย ๆงานต้องการความเร็ว

สรุปการใช้งาน DMA (Direct Memory Access) และขั้นตอนการเขียนโปรแกรมใช้งานบน CH32V003


1. ข้อมูลพื้นฐานของ DMA บน CH32V003

  • มี DMA Controller จำนวน 1 ชุด
  • รองรับ 7 Channels
  • รองรับรูปแบบการโอนข้อมูล:
    • Memory-to-Memory
    • Peripheral-to-Memory
    • Memory-to-Peripheral
  • รองรับ Ring Buffer (โหมดวนซ้ำ)
  • Peripheral ที่รองรับ DMA ได้แก่:
    • USART
    • I2C
    • SPI
    • ADC
    • TIMx (Timer)

เป้าหมาย คือให้ CPU ไม่ต้องทำการย้ายข้อมูลเอง → ลดภาระงานของ CPU และเพิ่มประสิทธิภาพของระบบ


2. ขั้นตอนการเขียนโปรแกรมใช้งาน DMA

(1) เปิดใช้งาน Clock ของ DMA

ต้องเปิด Clock ให้โมดูล DMA ก่อนใช้งาน โดยใช้ฟังก์ชัน SDK เช่น RCC_AHBPeriphClockCmd()

(2) ตั้งค่าโครงสร้างข้อมูล DMA_InitTypeDef

ใช้งานโครงสร้าง DMA_InitTypeDef เพื่อกำหนดพารามิเตอร์ต่าง ๆ เช่น:

  • ที่อยู่ต้นทาง (Source Address)
  • ที่อยู่ปลายทาง (Destination Address)
  • ทิศทางการโอน (Direction)
  • ขนาดข้อมูลแต่ละครั้ง (Data Size)
  • จำนวนข้อมูลทั้งหมด (Transfer Size)
  • โหมด (Normal/ Circular)
  • การเพิ่ม Address (Increment Mode)

ตัวอย่างโครงสร้าง

DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DATAR); // Peripheral source/destination 
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer; // RAM source/destination 
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // ทิศทาง 
DMA_InitStructure.DMA_BufferSize = 64; // ขนาดข้อมูล DMA_InitStructure.DMA_PeripheralInc = 
DMA_PeripheralInc_Disable; // Peripheral ไม่ต้องเพิ่ม Address 
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // RAM ต้องเพิ่ม Address 
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; 
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; 
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 
DMA_InitStructure.DMA_Priority = DMA_Priority_High; 
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

(3) เปิดใช้งาน DMA Channel

เมื่อตั้งค่าพร้อมแล้ว เปิด DMA Channel ด้วยฟังก์ชัน DMA_Cmd() เช่น:

DMA_Cmd(DMA1_Channel1, ENABLE);

(4) จัดการ Interrupt (ถ้าต้องการ)

หากต้องการใช้งาน DMA Interrupt เช่น เมื่อโอนข้อมูลเสร็จสิ้น ให้:

  • เปิดใช้งาน Interrupt
  • เขียนฟังก์ชัน ISR (Interrupt Service Routine)

ตัวอย่างเปิด Interrupt:

DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

ใน ISR:

void DMA1_Channel1_IRQHandler(void) {
 if(DMA_GetITStatus(DMA1_IT_TC1)) { 
     DMA_ClearITPendingBit(DMA1_IT_TC1); 
     // ใส่โค้ดหลังโอนข้อมูลเสร็จ 
 } 
}

3. สรุปขั้นตอนแบบลำดับ

  1. เปิด Clock ให้ DMA
  2. เตรียมข้อมูลที่ต้องโอน (Buffer / Peripheral)
  3. กำหนดค่า DMA_InitTypeDef
  4. เรียก DMA_Init() เพื่อลงทะเบียนการตั้งค่า
  5. เปิดใช้งาน DMA Channel (DMA_Cmd(ENABLE))
  6. (ถ้าจำเป็น) เปิด DMA Interrupt และเขียนฟังก์ชัน IRQ Handler
  7. เริ่มการโอนข้อมูล (Peripheral จะ trigger DMA ตามเงื่อนไข)

หมายเหตุพิเศษ

  • ต้องแน่ใจว่า Peripheral ที่เกี่ยวข้อง (USART/SPI/ADC ฯลฯ) เปิด DMA Request ก่อน เช่น USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
  • ต้องระวังเรื่อง Size Alignment เช่น Peripheral/Memory ขนาดไม่ตรงกัน อาจทำให้ DMA Error ได้