บทความนี้แนะนำบอร์ดไมโครคอนโทรลเลอร์ ESP32 และ SAM-D21 มาใช้งานเพื่อเรียนรู้การใช้คำสั่งเกี่ยวกับ ADC (Analog to Digital Converter) และ DAC (Digital to Analog Converter) ด้วยการเชื่อมต่อขา DAC เข้ากับ ADC ดังตัวอย่างภาพที่ 1 (เชื่อม A0 เข้า A1 ของบอร์ด SAM-D21) และ 2 (เชื่อมต่อขา GPIO26 เข้ากับ GPIO36 ของ ESP32) เพื่อส่งข้อมูลที่ไป DAC และให้ ADC อ่านค่ากลับเข้ามา และส่งผลลัพธ์ออกไปที่พอร์ตอนุกรมสำหรับแสดงผลด้วย Serial Plotter ซึ่งตัวอย่างโปรแกรมสั่งส่งข้อมูล 3 แบบ คือ กราฟแบบฟันปลา กราฟแบบสามเหลี่ยม และกราฟรูปคลื่นจากฟังก์ชันไซน์
คำสั่ง
คำสั่งสำหรับการใช้งาน ADC และ DAC มีดังต่อไปนี้
การปรับความละเอียดของ ADC
คำสั่งสำหรับปรับความละเอียดของ ADC ของทั้ง ESP32 และ SAM-D21 มีรูปแบบการใช้งานดังนี้ โดย x คือตัวเลขจำนวนบิตในการทำงาน
analogReadResolution( x )
คำสั่งสำหรับปรับความละเอียดของ DAC
คำสั่งปรับความละเอียดของ DAC สำหรับ SAM-D21 ใช้คำสั่งตามรูปแบบต่อไปนี้ โดยกำหนด x เป็นค่าจำนวนเต็มของค่าบิตที่ต้องการใช้งาน ปกติมีค่าเป็น 10
analogWriteResolution(x)
คำสั่งสำหรับอ่านค่า ADC
รูปแบบคำสั่งสำหรับอ่านค่า ADC ของ SAM-D21 และ ESP32 ใช้คำสั่ง analogRead( ) พร้อมระบุหมายเลขขาสำหรับอ่านค่า โดย ESP32 จะสามารถใช้ขา 32,33,34,35,36 และ 39 ส่วน SAM-D21 ใช้ขา A1
ค่าที่อ่านได้ = analogRead( หมายเลขขา )
คำสั่งส่งข้อมูลออก DAC
สำหรับคำสั่งนำออกข้อมูลขนาด 10 บิตของ SAM-D21 นั้นสามารถใช้คำสั่งตามรูปแบบต่อไปนี้โดยกำหนดขาเป็น A0
analogWrite( ขา, ค่านำออก )
ส่วน esp32 ให้เรียกใช้คำสั่ง dacWrite() โดยระบุขาเป็น 25 หรือ 26 ดังนี้
dacWrite( ขา, ค่านำออก )
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรมวาดกราฟ 3 แบบ คือ กราฟฟันปลา (ตามตัวอย่างภาพที่ 3 และ 4) กราฟสามเหลี่ยม (ภาพที่ 5 และ 6) และกราฟคลื่นไซน์ (ดังภาพที่ 7 และ 8) มีโค้ดการทำงานดังนี้
กราฟฟันปลา
การสร้างกราฟฟันปลาเป็นการส่งค่า 0 ถึงค่าสูงสุดที่เป็นไปได้ของภาค DAC หลังจากนั้นวนรอบส่งใหม่อีกครั้ง โดยโค้ดสำหรับ SAM-D21 เป็นดังนี้ และได้ผลลัพธ์ดังตัวอย่างภาพที่ 3
#include <Arduino.h>
#define pinSpk A0
#define pinMic A1
int adcValue;
void setup() {
SerialUSB.begin(115200);
analogReadResolution(12);
analogWriteResolution(10);
}
void loop() {
for (int i = 0; i < 1024; i += 4) {
analogWrite( pinSpk, i);
adcValue = analogRead(pinMic);
SerialUSB.println(adcValue);
}
}
ส่วนโค้ดโปรแกรมสำหรับ esp32 เป็นดังนี้ โดยต้องพึงระวังเรื่องของค่าสูงสุดที่ส่งให้ DAC เนื่องจาก ESP32 มี DAC ที่ละเอียดระดับ 8 บิต สามารถส่งค่าได้ในช่วง 0 ถึง 255
#include <Arduino.h>
#define pinSpk 26
#define pinMic 36
int adcValue;
void setup() {
Serial.begin(115200);
analogReadResolution(12);
}
void loop() {
for (int i = 0; i < 255; i++) {
dacWrite( pinSpk, i );
adcValue = analogRead(pinMic);
Serial.println(adcValue);
}
}
จากภาพที่ 4 จะพบว่า กราฟจาก ADC ของ ESP32 นั้นออกมาไม่เป็นเส้นตรงเมื่อเทียบกับภาพที่ 3 และจากประสบการณ์ในการใช้งานของพวกเราพบว่าภาค ADC ของ ESP32 มีความไวต่อสัญญาณรบกวนค่อนข้างมาก ดังนั้น ต้องหาวิธีการชดเชยค่าที่ผิดพลาด และการป้องกันสัญญาณรบกวน
กราฟสามเหลี่ยม
กราฟสามเหลี่ยมมีหลักการคล้ายกับฟันปลาด้วยการเพิ่มการวนรอบเพื่อส่งค่ามากสุดกลับมาเป็น 0 ดังโค้ดของ SAM-D21 ดังต่อไปนี้ และตัวอย่างผลลัพธ์เป็นดังภาพที่ 5
#include <Arduino.h>
#define pinSpk A0
#define pinMic A1
int adcValue;
void setup() {
SerialUSB.begin(115200);
analogReadResolution(12);
analogWriteResolution(10);
}
void loop() {
for (int i = 0; i < 1024; i += 8) {
analogWrite( pinSpk, i);
adcValue = analogRead(pinMic);
SerialUSB.println(adcValue);
}
for (int i = 1023; i >= 0; i -= 8) {
analogWrite( pinSpk, i);
adcValue = analogRead(pinMic);
SerialUSB.println(adcValue);
}
}
การเขียนโค้ดสำหรับ esp32 เป็นดังนี้ และตัวอย่างที่ได้เป็นดังภาพที่ 6
#include <Arduino.h>
#define pinSpk 26
#define pinMic 36
int adcValue;
void setup() {
Serial.begin(115200);
analogReadResolution(12);
}
void loop() {
for (int i = 0; i < 255; i++) {
dacWrite( pinSpk, i );
adcValue = analogRead(pinMic);
Serial.println(adcValue);
}
for (int i = 254; i >= 0; i--) {
dacWrite( pinSpk, i );
adcValue = analogRead(pinMic);
Serial.println(adcValue);
}
}
กราฟคลื่นไซน์
การสร้างกราฟคลื่นรูปไซน์ใช้การกำหนดให้มีการวนรอบเพื่อเพิ่มค่ามุมองศาจาก 0 ไปถึง 359 โดยในแต่ละรอบทำสิ่งต่อไปนี้
- แปลงค่าองศาเป็นเรเดียน
- คำนวณหาไซน์จากมุมเรเดียน
- แปลงค่าทศนิยมให้เป็นจำนวนเต็มในช่วงค่าที่ DAC รองรับ
- อ่านค่าจาก ADC
- ส่งข้อมูลออกทางพอร์ตอนุกรมให้โปรแกรม Serial Plotter
โค้ดสำหรับ SAM-D21 เป็นดังนี้ และตัวอย่างผลลัพธ์ดังภาพที่ 7
#include <Arduino.h>
#define pinSpk A0
#define pinMic A1
int adcValue;
void setup() {
SerialUSB.begin(115200);
analogReadResolution(12);
analogWriteResolution(10);
}
void loop() {
int degree = 0;
float radian = 0.0;
float sineValue = 0.0;
int dValue = 0;
for (degree = 0; degree < 360; degree++) {
radian = (float)degree * (2.0 * 3.1415926) / 360.0;
sineValue = sin(radian);
dValue = (int)((1.0+sineValue)*510.0);
analogWrite( pinSpk, dValue);
adcValue = analogRead(pinMic);
SerialUSB.println(adcValue);
}
}
สำหรับ ESP32 มีโค้ดโปรแกรมเป็นดังนี้ และตัวอย่างผลลัพธ์เป็นดังภาพที่ 8
#include <Arduino.h>
#define pinSpk 26
#define pinMic 36
int adcValue;
void setup() {
Serial.begin(115200);
analogReadResolution(12);
}
void loop() {
int degree = 0;
float radian = 0.0;
float sineValue = 0.0;
int dValue = 0;
for (degree = 0; degree < 360; degree++) {
radian = (float)degree * (2.0 * 3.1415926) / 360.0;
sineValue = sin(radian);
dValue = (int)((1.0 + sineValue) * 125.0);
dacWrite( pinSpk, dValue );
adcValue = analogRead(pinMic);
Serial.println(adcValue);
}
}
สรุป
จากบทความนี้จะพบว่า ADC ของ esp32 มีความผดิพลาดบ้างซึ่งต้องอาศัยการชดเชยค่าเพื่อให้ค่าออกมามีความแม่นยำขึ้น และคำสั่ง DAC ของ esp32 ต้องใช้ dacWrite() แทนคำสั่ง analogWrite() ดังนั้น การเลือกใช้ขึ้นอยู่กับปัจจัยหลายด้าน เช่น ราคา ความจำเป็นของการใช้ ADC ที่มีความแม่นยำสูง ปริมาณหน่วยความจำแรม/รอม เป็นต้น สุดท้าย ขอให้สนุกกับการเขียนโปรแกรมครับ
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-11-19