[TH] ESP-IDF Ep.8 : DAC Output Part 2

บทความนี้กล่าวถึงการใช้งาน GPIO ของ ESP32 เพื่อทำหน้าที่นำออกสัญญาณแอนาล็อกผ่านทางโมดูล DAC ขนาด 8 บิตของไมโครคอนโทรลเลอร์ ESP32 ต่อจากคราวที่แล้ว โดยในบทความนี้เป็นการใช้การสร้างคลื่นแบบ Cosine เพื่อนำออกสัญญาณแอนาล็อกของไมโครคอนโทรลเลอร์ผ่านลำโพง และแสดงให้เห็นรูปของคลื่นที่ได้จากจอแสดงผลของออสซิโลสโคป ส่วนบอร์ดทดลองยังคงใช้ตามภาพที่ 1

ภาพที่ 1 การต่อใช้งานประกอบตัวอย่างการใช้งาน DAC

โครงสร้างของโครงงาน

โครงสร้างของโครงงานของ ESP-IDF เป็นดังภาพที่ 2 คือ ในไดเร็กทอรีหรือโฟลเดอร์ของโครงงานจะมีไฟล์ CMakeList.txt และ sdkconfig กับไดเร็กทอรีชื่อ main สำหรับเก็บรหัสต้นฉบับของโครงงาน โดยในไดเร็กทอรีดังกล่าวมีไฟล์ภาษา C และ CMakeLists.txt

ภาพที่ 2 โครงสร้างของโครงงาน

จากโครงสร้างในภาพที่ 2 ต้องสร้างโค้ดของไฟล์ CMakeLists.txt ดังนี้ ซึ่งเนื้อหาในโค้ดได้กำหนดรุ่นขั้นต่ำของโปรแกรม cmake และกำหนดค่าการใช้งานของ cmake เบื้องต้นตามจ้นฉบับที่มากับ ESP-IDF พร้อมทั้งตั้งชื่อโครงงานเป็น ep08

cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ep08)

สิ่งที่เขียน main/CMakeLists.txt เป็นดังงต่อไปนี้ เพื่อกำหนดรายการไฟล์ที่จะต้องคอมไพล์ ซึ่งกำหนดไว้เป็น main.c และกำหนดไดเร็กทอรีที่เก็บไฟล์ส่วนหัวเอาไว้เป็นค่าว่างซึ่งหมายถึงที่เดียวกับ main.c หรือในไดเร็กทอรี main

idf_component_register(SRCS "main.c"
                    INCLUDE_DIRS "")

เมื่อสร้างโครงสร้างได้เหมือนดังภาพที่ 2 ให้สั่งเลือก target ของระบบเป็น ESP32 ดังนี้

idf.py set-target esp32

ส่วน sdkconfig เกิดจากการเรียกใช้คำสั่งต่อไปนี้ idf.py menuconfig

idf.py menuconfig

จากหน้าจอกำหนดการตั้งค่าให้เข้าไปที่ Component Config –> FreeRTOS และกำหนด Tick rate (Hz) เป็น 1000 ดังภาพที่ 3 หลังจากนั้นบันทึกและออกจากการตั้งค่า

ภาพที่ 3 ตั้งค่า Tick rate (Hz)

ที่มักจะลืมกันคือตั้งค่าขนาดความจุของหน่วยความจำรอม (Flash size) ดังภาพที่ 4 ให้ตรงกับขนาดที่ติดตั้งบนบอร์ด จากเสนู Serial flasher config ซึ่งในบทความใช้เป็น 4MB หลังจากนั้นกด S และ Q เพื่อบันทึกและออกจากการตั้งค่า

ภาพที่ 4 กำหนดขนาด flash size เป็น 4MB

การใช้รูปคลื่น cosine

ในชุดไลบรารี DAC ของไมโครคอนโทรลเลอร์ ESP32 มีชุดคำสั่งเกี่ยวกับการเปิด/ปิดการสร้างคลื่นแบบโคไซน์ (cosine) ออกทางช่องสัญญาณ 1 และ 2 โดยต้องกำหนดคุณลักษณะของคลื่นในโครงสร้างข้อมูลประเภท dac_cw_config_t ดังมีรายละเอีดยต่อไปนี้

struct dac_cw_config_t {

dac_channel_t en_ch;

dac_cw_scale_t scale;

dac_cw_phase_t phase;

uint32_t freq;

uint8_t offset;

};

โดยแต่ละคุณสมบัติเป็นดังนี้

  1. en_ch เป็นหมายเลขช่องสัญญาณที่เปิดการทำงานการสร้างคลื่นแบบโคไซน์ โดยช่องสัญญาณได้แก่
    1. DAC_CHANNEL_1 ซึ่งเป็นขา 25 ของ ESP32 และขา 17 ของ ESP32-S2
    2. DAC_CHANNEL_2 ซึ่งเป็นขา 26 ของ ESP32 และขา 18 ของ ESP32-S2
  2. scale เป็นค่าความสูงของคลื่น (amplitude) ซึ่งกำหนดได้ 4 ระดับ คือ
    1. DAC_CW_SCALE_1 ให้ลูกคลื่นมีความสูงปกติ
    2. DAC_CW_SCALE_2 ให้ลูกคลื่นมีความสูง 1/2 ของขนาดปกติ
    3. DAC_CW_SCALE_4 ให้ลูกคลื่นมีความสูง 1/4 ของขนาดปกติ
    4. DAC_CW_SCALE_8 ให้ลูกคลื่นมีความสูง 1/8 ของขนาดปกติ
  3. phase เป็นค่าการเลื่อนเฟสของคลื่น ซึ่งกำหนดได้ 2 ลักษณะ คือ
    1. DAC_CW_PHASE_0 ให้เริ่มจากเฟส 0 องศา
    2. DAC_CW_PHASE_180 ให้เริ่มจากเฟส 180 องศา
  4. freq เป็นค่าความถี่ของคลื่อนซึ่งมีค่าอยู่ในช่วง 130Hz ~ 55,000Hz ซึ่งค่าสูงสุดที่กำหนดได้คือ 100KHz แต่คลื่นความถี่สูงเป็นช่วงที่หูของมนุษย์ไม่ได้ยิน
  5. offset สำหรับกำหนดระดับค่าแรงดันไฟฟ้ากระแสตรงที่นำออกมาในรูปแบบของคลื่นโคไซน์ ซึ่งต้องกำหนดค่าอยู่ในช่วง -128 ~127 แต่ที่ต้องพึงระวังคือ การใส่ค่าที่ไม่สมเหตุสมผลจะส่งผลต่อช่วงค่าความสูงของคลื่น

การใช้งาน DAC จะต้องนำเข้าไฟล์ส่วนหัวดังนี้ เพื่อใช้สำหรับเปิดหรือปิดการทำงานของ I2S

#include <driver/dac.h>

และเรียกใช้งานไฟล์ส่วนหัว dac_common.h สำหรับใช้งานคำสั่งเกี่ยวกับ DAC ของไมโครคอนโทรลเลอร์ ESP32

#include <driver/dac_common.h>

สำหรับค่าของช่องสัญญาณถูกกำหนดไว้ในไฟล์ส่วนหัวที่ชื่อ dac_channel.h

คำสั่ง

การสั่งเปิดหรือปิดการทำงานของการสร้างคลื่นโคไซน์ทำได้ด้วยการเรียกใช้คำสั่ง dac_cw_generator_enable() และ dac_cw_generactor_disable() ดังรูปแบบการใช้ต่อไปนี้

esp_err_t dac_cw_generator_enable()
esp_err_t dac_cw_generator_disable()

โดยค่าคืนกลับจากการทำงานของคำสั่ง dac_cw_generator_enable และ dac_cw_generator_disable เป็นดังนี้

  • ESP_OK

และคำสั่งสำหรับตั้งค่าการทำงานของการสร้างคลื่นโคไซน์มีรูปแบบการใช้งานดังนี้

esp_err_t dac_cw_generator_config( &ตัวแปรที่เก็บการตั้งค่า )

โดยค่าคืนกลับจากการทำงานของคำสั่ง dac_cw_generator_enable และ dac_cw_generator_disable เป็นดังนี้

  • ESP_OK สำหรับการตั้งค่าสำเร็จลุล่วง
  • ESP_ERR_INVALID_ARG สำหรับการตั้งค่าผิดพลาด

กรณีที่ต้องการใช้งาน GPIO แบบอื่น ๆ สามารถเข้าไปอ่านบทความต่าง ๆ ดังต่อไปนี้เพิ่มเติม

  1. นำออกข้อมูลสัญญาณดิจิทัล
  2. นำเข้าสัญญาณดิจิทัล
  3. นำเข้าสัญญาณแอนาล็อก
  4. นำออกสัญญาณแอนาล็อก (ตอน 2)

ตัวอย่างโปรแกรม

วงจรแอลอีดีที่ใช้ในการทดลองครั้งนี้เป็นดังภาพที่ 5 อุปกรณ์ที่ใช้ในการทดลองได้แก่

  1. บอร์ด esp32
  2. บอร์ดทดลอง
  3. โมดูลลำโพง
    1. ทรานซิสเตอร์
    2. ตัวต้านทาน 2 ตัว
    3. บัซเซอร์
ภาพที่ 5 วงจรเชื่อมต่อโมดูลบัซเซอร์

ตัวอย่างโปรแกรมสร้างคลื่นความถี่ 262Hz ซึ่งเป็นค่าความถี่ของเสียงตัว C ของเปียโนเป็นเวลา 2 วินาที เขียนโค้ดได้ดังนี้

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <sdkconfig.h>
#include <driver/gpio.h>
#include <driver/dac.h>
#include <driver/dac_common.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#define pinDAC DAC_CHANNEL_2

void app_main(void)
{  
  dac_cw_config_t cwCfg;

  cwCfg.en_ch = pinDAC;
  cwCfg.scale = DAC_CW_SCALE_1;
  cwCfg.phase = DAC_CW_PHASE_180;
  cwCfg.freq = 262; // 262Hz
  cwCfg.offset = 127;

  printf("Ep.08 DAC\n"); 
  dac_i2s_enable();
  dac_i2s_disable();
  if (dac_cw_generator_config(&cwCfg) == ESP_OK) {
    printf("Start cosine-wave generator ...");
    dac_cw_generator_enable();
    dac_output_enable( pinDAC );
    vTaskDelay( 2000/portTICK_PERIOD_MS ); // 2 seconds
    dac_cw_generator_disable();
    dac_output_disable( pinDAC );
    printf("done.\n");
  } else {
    printf("Error : ESP_ERR_INVALID_ARG!\n");
  }
  while (1) {
    vTaskDelay( 10/portTICK_PERIOD_MS ); 
  }
}

กรณีที่ต้องสร้างความถี่เสียงอื่น ๆ สามารถเปลี่ยนค่าความถี่ได้ดังนี้ (ที่มา ammmusic)

  • โด ใช้ค่าความถี่ 262Hz
  • เร ใช้ค่าความถี่ 294Hz
  • มี ใช้ค่าความถี่ 330Hz
  • ฟา ใช้ค่าความถี่ 349Hz
  • ซอล ใช้ค่าความถี่ 370Hz
  • ลา ใช้ค่าความถี่ 440Hz
  • ที ใช้ค่าความถี่ 495Hz

คอมไพล์และอัพโหลด

ทำการคอมไพล์ หลังจากนั้น flash ลงชิพ และเข้าโปรแกรม Serial Monitor สั่งงานดังนี้

idf.py -p /dev/ttyUSB0 build flash monitor

ตัวอย่างผลลัพธ์ของโปรแกรมเป็นดังภาพที่ 6 และกราฟที่แสดงผลด้วยออสซิโลสโคปเป็นดังภาพที่ 7

ภาพที่ 6 ผลลัพธ์จากโปรแกรม ep8
ภาพที่ 7 กราฟผลลัพธ์ของโปรแกรมตัวอย่าง

จากภาพที่ 7 จะพบว่ายอดของคลื่นนั้นไม่สมบูรณ์ จึงลองเปลี่ยนการตั้งค่าใหม่เป็นดังนี้ และได้ผลลัพธ์ของลูกคลื่นเป็นดังภาพที่ 8

  cwCfg.en_ch = pinDAC;
  cwCfg.scale = DAC_CW_SCALE_2;
  cwCfg.phase = DAC_CW_PHASE_0;
  cwCfg.freq = 262; // 262Hz
  cwCfg.offset = 48;
ภาพที่ 8 ภาพลูกคลื่นเมื่อมีการปรับการตั้งค่าใหม่

สรุป

จากบทความนี้จะพบว่า การนำออกสัญญาณแอนาล็อกนั้น มีขั้นตอน 3 ขั้นตอน คือ

  1. ตั้งค่าด้วย dac_cw_generator_config()
  2. เปิดการทำงานด้วยคำสั่ง dac_cw_generator_enable()
  3. ปิดการทำงานด้วยคำสั่ง dac_cw_generator_disable()

แต่อย่างไรก็ดี ลักษณะของลูกคลื่นนั้นมีผลต่อคุณภาพของเสียง ดังนั้น ควรตรวจสอบการตั้งค่าและลักษณะของคลื่นที่ได้ประกอบด้วยเสมอเพื่อไม่ให้เกิดการได้ค่าที่ไม่ถูกต้องดังภาพที่ 7 และสุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ

ท่านใดต้องการพูดคุยสามารถคอมเมนท์ได้เลยครับ

แหล่งอ้างอิง

  1. ESP-IDF : DAC
  2. ammmusic

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-10-12, 2021-12-26