การใช้งานของ ADC ใน CH32V003 นั้นมีการทำงานหลายแบบมาก บทความนี้จะมาสรุปเกี่ยวกับ วิธีการใช้งาน ADC และ ประเภทของ ADC สำหรับการใช้งาน เรามาดูภาพรวม และ แนวทางการใช้งานกันเลย


1. โหมดการทำงานของ ADC ใน CH32V003

CH32V003 มี ADC แบบ 10-bit ความละเอียด รองรับการทำงานได้ 3 แบบหลัก ๆ:

โหมดการทำงานการใช้งาน
Single Conversion Modeแปลงสัญญาณแค่ครั้งเดียว เมื่อถูกสั่ง (Software Trigger) หรือ Hardware Triggerอ่านค่า ADC ทีละตัว
Continuous Conversion Modeทำการแปลง ADC ต่อเนื่องไม่หยุดจนกว่าจะสั่งหยุดใช้วัดค่า real-time ต่อเนื่อง เช่น เซ็นเซอร์อุณหภูมิ
Scan Mode (Multiple Channel)อ่านหลายช่องสัญญาณตามลำดับอัตโนมัติวัดหลาย input เช่น sensor หลายตัว

หมายเหตุ:

  • ช่อง ADC Input (Analog Inputs) มีทั้งหมด 8 ช่อง คือ AIN0-AIN7
  • รองรับทั้ง Trigger ภายนอก (จาก Timer เป็นต้น) และ Trigger ภายใน (Software)

2. ขั้นตอนการทำงานของ ADC ใน MCU

ลำดับการทำงานของ ADC โดยทั่วไป เป็นดังนี้:

  1. เปิด Clock ของ ADC
    เปิดใช้งาน Clock ผ่าน RCC ก่อน (RCC_APB2PeriphClockCmd)
  2. Configure ADC Parameters
    ตั้งค่า ADC ด้วยโครงสร้าง ADC_InitTypeDef
    • โหมด (Single, Continuous, Scan)
    • Alignment (Right/Left)
    • External Trigger
    • จำนวน Channels ที่ต้องแปลง
  3. Configure ADC Channels
    เลือก Channel ที่ต้องการอ่าน และกำหนดลำดับ
  4. Enable ADC
    เปิดการทำงาน ADC (ADC_Cmd(ADCx, ENABLE))
  5. Calibration (เฉพาะบางกรณี)
    รีเซ็ต และเริ่มการปรับเทียบค่าภายใน ADC
  6. Start Conversion
    สั่งเริ่มต้นการแปลง ADC (ADC_SoftwareStartConvCmd)
  7. Wait Until Conversion Complete
    เช็คสถานะ EOC (End of Conversion) Flag
  8. Read Data
    อ่านค่าผลลัพธ์จาก Register (ADC_GetConversionValue)

3. โค้ดตัวอย่างการเขียนโปรแกรมใช้งาน ADC

#include "ch32v00x_adc.h"
#include "ch32v00x_rcc.h"

void ADC1_Init(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // เปิด clock ADC1

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;          // ทำงานแบบอิสระ
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;               // ไม่ scan หลาย channel
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;          // เปิดการทำงานต่อเนื่อง
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // ไม่มี trigger ภายนอก
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;      // ชิดขวา
    ADC_InitStructure.ADC_NbrOfChannel = 1;                     // ใช้แค่ 1 channel
    ADC_Init(ADC1, &ADC_InitStructure);                         // ตั้งค่า ADC1

    ADC_Cmd(ADC1, ENABLE);  // เปิดใช้งาน ADC1

    ADC_ResetCalibration(ADC1);  // รีเซ็ตการ calibrate
    while(ADC_GetResetCalibrationStatus(ADC1)); // รอ reset calibration เสร็จ

    ADC_StartCalibration(ADC1); // เริ่ม calibration
    while(ADC_GetCalibrationStatus(ADC1)); // รอ calibration เสร็จ

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); // ตั้ง channel 0 เป็นตัววัด

    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // เริ่มสั่งแปลง
}

uint16_t ADC1_Read(void)
{
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // รอแปลงเสร็จ
    return ADC_GetConversionValue(ADC1); // อ่านค่า
}

4. คำอธิบายฟังก์ชัน (Line-by-Line Explanation)

void ADC1_Init(void)
{
    ADC_InitTypeDef ADC_InitStructure; // สร้างโครงสร้างเก็บค่าตั้งค่า ADC

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // เปิด clock ของ ADC1

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // เลือกโหมดอิสระ
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; // ไม่ต้อง scan หลาย channel
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // เปิด continuous mode
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // ไม่มี external trigger
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // ชิดขวา (ค่าที่อ่านได้เหมือนเป็น unsigned)
    ADC_InitStructure.ADC_NbrOfChannel = 1; // ใช้แค่ 1 channel
    ADC_Init(ADC1, &ADC_InitStructure); // ส่งข้อมูลเข้าไปยัง ADC1

    ADC_Cmd(ADC1, ENABLE); // เปิดใช้งาน ADC1

    ADC_ResetCalibration(ADC1); // รีเซ็ตการตั้งค่า calibration
    while(ADC_GetResetCalibrationStatus(ADC1)); // รอจนกว่าการรีเซ็ต calibration จะเสร็จสิ้น

    ADC_StartCalibration(ADC1); // เริ่มต้นกระบวนการ calibration ใหม่
    while(ADC_GetCalibrationStatus(ADC1)); // รอ calibration เสร็จ

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); // ตั้งให้ใช้ช่อง 0

    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // เริ่มแปลงข้อมูล ADC แบบ software trigger
}
uint16_t ADC1_Read(void)
{
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // รอ flag End of Conversion
    return ADC_GetConversionValue(ADC1); // อ่านค่า ADC
}

5. ประเภทการอ่านค่า ADC

  • Single Read: สั่งแปลงทุกครั้งที่ต้องการค่าใหม่
  • Auto Continuous: เริ่มแค่ครั้งเดียว จากนั้นอ่านค่าใหม่ได้ตลอดเวลา
  • Trigger-based: เริ่มแปลงเมื่อตรงกับสัญญาณ Trigger ภายนอก เช่น Timer Event

6. การใช้ DMA ร่วมกับ ADC

  • CH32V003 รองรับ การใช้ DMA อ่านค่าจาก ADC อัตโนมัติ
  • ไม่ต้องรอค่าสำเร็จจาก EOC flag
  • ใช้ ADC_DMACmd(ADCx, ENABLE); เพื่อเปิดโหมด DMA

สรุปสั้น ๆ แบบภาพรวม

หมวดรายละเอียด
จำนวน Channel8 ช่อง (AIN0-AIN7)
ความละเอียด10-bit
แหล่ง TriggerSoftware / Timer
รองรับ DMAมี
ความเร็ว Samplingขึ้นอยู่กับ Clock (สูงสุด 24MHz สำหรับ ADC Clock)
ฟังก์ชันสำคัญADC_Init, ADC_Cmd, ADC_SoftwareStartConvCmd, ADC_GetConversionValue, ADC_DMACmd