Friday, October 9, 2015

การใช้งาน pulseIn ฟังก์ชัน ตอนที่ 1

หัวข้อนี้จะกล่าวถึงการใช้ฟังก์ชัน pulseIn และใช้บอร์ด Arduino UNO R3 เพื่อวัดความกว้างเพาส์สัญญาณ (Pulse width) หลักการทำงานจะอาศัยการตรวจวัดสัญญาณ HIGH หรือ LOW ที่ส่งเข้าขา digital และเซตขานั้นให้เป็น input port ฟังก์ชัน pulseIn มีหลักในการใช้ดังนี้

กรณีที่1

เมื่อตรวจพบสัญญาณขาเข้าเป็น HIGH ฟังก์ชันนี้จะเริ่มนับเวลาในหน่วยไมโครวินาที (microsec, $\mu$s) และรอจนกว่าจะเจอสัญญาณ LOW ถึงจะหยุดนับเวลา


กรณีที่2 กำหนดให้ตรวจสัญญาณ LOW ก่อน ฟังก์ชันนี้จะเริ่มนับเวลาในหน่วยไมโครวินาที และรอจนกว่าจะเจอสัญญาณ HIGH ถึงจะหยุดนับเวลา


รูปที่1 แสดงสัญญาณคลื่นสี่เหลี่ยม (square wave) และตรวจวัดสัญญาณ HIGH ---> LOW และ ตรวจวัดสัญญาณ LOW ---> HIGH

สรุปได้ว่าฟังก์ชัน pulseIn ทำหน้าที่เป็นนาฬิกาจับเวลาโดยใช้สัญญาณ LOW หรือ HIGH เป็นตัวกำหนด start/stop นั่นเอง

ฟังก์ชัน pulseIn มีรูปแบบการใช้งาน (https://www.arduino.cc/en/Reference/PulseIn)

Syntax

1) ===> pulseIn(pin, value)

2) ===> pulseIn(pin, value, timeout)

pin คือ กำหนดสัญญาณเข้าไหน เช่น ขา digital3, pin คือ 3

value คือ พารามิเตอร์ที่เรากำหนดให้บอร์ดตรวจวัดสัญญาณ HIGH หรือ LOW ก่อน (กำหนดให้เริ่มนับเวลาดังกล่าวข้างต้น)

timeout คือช่วงเวลาสูงสุดที่ฟังก์ชันนี้ยังทำงานอยู่ หากไม่กำหนดพารามิเตอร์นี้ เช่นกรณีแรกจะกำหนด default ไว้ที่ 1 วินาที หรือ 1,000,000 ไมโครวินาที   นั่นหมายความว่าถ้าความกว้างเพาส์สัญญาณที่จะวัดมากกว่า 1 วินาที ฟังก์ชันนี้จะไม่ทำงานและส่งค่า 0 ออกมาแทน ดังนั้นหากต้องการกำหนดให้ timeout สูงสุด 5 วินาที จะเขียนได้ว่า

pulseIn(3, HIGH, 5000000)

ตัวอย่างการใช้งาน และแสดงผลทาง serial communication

int pin = 3;
unsigned long duration;    

void setup()

  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 

  pinMode(pin, INPUT);
}

void loop()
{
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    Serial.print("DelT=");
    Serial.print(time);
    Serial.println(" ms");
}


ตัวอย่างการใช้งานและแสดงผลทางจอ LCD

#include
LiquidCrystal lcd(11, 12, 4, 5, 6, 7);

int pin = 3;
unsigned long duration;
 

void setup()

{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("UP VeloMeterV1.0");
  delay(2500);
  pinMode(pin, INPUT);
}

void loop()

{
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    lcd.setCursor(0, 1);lcd.print("DelT=         ms");
    lcd.setCursor(6, 1);lcd.print(time);
}


ก่อนจะทดสอบกับอุปกรณ์จริงขอทำการจำลองในโปรแกรมก่อน ในภาพใช้สวิทซ์เป็นเพื่อกำหนดสัญญาณ HIGH เมื่อสวิทซ์ on และเป็น LOW เมื่อสวิทซ์ off ได้ผลลัพธ์ดังภาพ โดยต่อสัญญาณเข้าที่ขา digital 3 จากรูปกำหนดแกนเวลาใน oscilloscope เป็น 0.5 s/div หากอ่านค่าเวลาเทียบกับตัวเลขที่ได้จากฟังก์ชัน pulseIn ที่แสดงในจอ LCD สอดคล้องกันเป็นอย่างดี ดังนั้นการทดสอบครั้งนี้ถือว่าบรรลุวัตถุประสงค์ขั้นต้น


   รูปที่2 ภาพการจำลองการวัดความกว้าง pulse ในโปรแกรม proteus ด้วยฟังก์ชัน pulseIn ใน Arduino โดยวัดเวลาที่กดสวิทซ์ 


ลำดับต่อไปจะทดสอบกับอุปกรณ์จริงโดยใช้ function generator (GWINSTEK รุ่น SFG-1013) เป็นเครื่องกำเนิดสัญญาณคลื่นสี่เหลี่ยมความถี่ต่างๆ เพื่อทดสอบความถูกต้อง (accuracy) และความแม่นยำ (precise) จากนั้นใช้ oscilloscope (Tektronix รุ่น TDS-1002) เพื่อวัดความกว้างเพาส์ (pulse width) เทียบกับฟังก์ชัน pulseIn ของ Arduino เพื่อให้เห็นภาพที่ชัดเจนขึ้นขออธิบายเกี่ยวสัญญาณสี่เหลี่ยมและการคำนวณเล็กน้อย

เวลาที่คลื่นเคลื่อนที่ครบ 1 รอบ เรียกว่า คาบการเคลื่อนที่ หรือ period มักใช้สัญลักษณ์เป็น "T" มีหน่วยเป็น วินาที ปริมาณที่เป็นส่วนกลับของคาบเรียกว่า ความถี่ หรือ frequency มักใช้สัญลักษณ์เป็น "f" มีหน่วยเป็น รอบต่อวนาที หรือ Hertz (Hz) หรือจะกล่าวว่าความถี่กำลังบอกว่า ใน 1 วินาที คลื่นเคลื่อนที่ผ่านจุดใดจุดหนึ่งไปกี่ลูกนั่นเองเขียนความสัมพันธ์ทางคณิตศาสตร์ ระหว่าง T กับ f ได้เป็น

$T=\frac{1}{f}$

สำหรับคลื่นสี่เหลี่ยมมีค่า ดิวตี้ไซเคิล (duty cycle) 50 % ดังภาพด้านล่างนั้น คลื่น 1 ลูกจะประกอบด้วยสัญญาณค่าสูงครึ่งหนึ่ง และอีกครึ่งหนึ่งเป็นสัญญาณค่าต่ำ หรือกล่าวได้ว่าความกว้างของเพาส์สัญญาณสูงเท่ากับความกว้างเพาส์สัญญาณต่ำ สมมติสัญญาณสี่เหลี่ยมมีความถี่ 50 Hz คาบการเคลื่อนที่ T คำนวณได้ดังนี้

$T=\frac{1}{50\, Hz}=0.02\, s=20\, \, ms$

ดังนั้น pulse width จะเท่ากับ 10 ms


รูปที่ 3 แสดงคลื่นสี่เหลี่ยมและความกว้างสัญญาณ (ภาพจาก www.electronics-tutorials.ws)

ในการทดสอบครั้งนี้จะอ่านค่า ความกว้างเพาส์ในหน่วย มิลลิวินาที และขอยกตัวอย่างมาให้เห็นเพียงสองความถี่ คือ 5 และ 25 Hz รูปที่ 4 แสดงความถี่ของสัญญาณสี่เหลี่ยมที่ตั้ง 25 Hz (รูปที่4 ก) รูปสัญญาณและความกว้างเพาส์จากออสซิลโลสโคป (รูปที่4 ข) และค่าความกว้างเพาส์ที่อ่านได้จาก Arduino (รูปที่4 ค) จากการทดลองพบว่า Arduino อ่านค่าความกว้างเพาส์ได้สอดคล้องกับการคำนวณและค่าที่อ่านได้จากออสซิลโลสโคป เป็นอย่างดี

                      รูปที่ 4 แสดงความถี่ รูปสัญญาณ และความกว้างเพาส์ที่ความถี่ 25 Hz

สำหรับที่ความถี่ต่ำ พบว่าค่าที่อ่านได้จาก Arduino ไม่ตรงกับผลการคำนวณและค่าที่อ่านได้จากออสซิลโลสโคป ซึ่งความผิดเพี้ยนส่วนนี้จะได้ทำการทดลองและทดสอบต่อไปเพื่อหาความคลาดเคลื่อนที่เกิดขึ้น


                   รูปที่ 5 แสดงความถี่ รูปสัญญาณ และความกว้างเพาส์ที่ความถี่ 5 Hz

เมื่อทดสอบวัดความกว้างเพาส์เพื่อนำมาเทียบกับผลการคำนวณเชิงทฤษฎี ได้ผลลัพธ์ดังนี้พบว่าที่ความถี่สูงขึ้น (กรณีนี้ f สูงกว่า 10 Hz) ฟังก์ชัน pulseIn จะวัดได้ถูกต้อง แสดงดังกราฟด้านล่าง

 รูปที่ 6 ภาพซ้ายมือแสดง กราฟระหว่างความกว้างเพาส์ (positive pulse width) กับความถี่ โดยเปรียบเทียบระหว่าง ค่าการคำนวณกับการทดลอง ส่วนภาพด้านขวามือแสดง เปรียบเทียบผลต่างของ pulse width กับความถี่

ภาพที่ 6 บ่งบอกความคลาดเคลื่อนของฟังก์ชัน pulse in พบว่าความถี่ต่ำกว่า 10 Hz จะทำให้ความคลาดเคลื่อนมีค่ามากขึ้น  ดังนั้นจากผลการทดลองนี้การอ่านค่าความถี่ต่ำไม่ควรใช้ฟังก์ชัน pulseIn

นอกจากนี้ข้าพเจ้ายังนำ pusleIn ฟังก์ชัน ไปเขียนรวมกับวิธีการส่งข้อมูลไปให้ MS excel มีรายละเอียดของ source code ดังนี้ (การส่งข้อมูลถึง MS excel ลองติดตามในนี้เลย Arduino2Excel)

#include
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

int x = 0;
int row = 0;
int pin = 3;
unsigned long duration;

void setup()

{
  Serial.begin(128000); // opens serial port, sets data rate to 9600 bps
  Serial.println("CLEARDATA");
  Serial.println("LABEL,Time,Index,SensorValueA0");
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("UP VeloMeterV1.0");
  delay(2500);
  pinMode(pin, INPUT);
}

void loop()

{
    row++;
    x++;

    Serial.print("DATA,TIME");
    Serial.print(","); Serial.print(x);
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    Serial.print(","); Serial.println(time);
    lcd.setCursor(0, 1);lcd.print("DelT=         ms");
    lcd.setCursor(6, 1);lcd.print(time);
}


และได้ผลดังภาพด้านล่างนี้ (ส่วนรายละเอียดจะมาเล่าให้ฟังในโอกาสต่อไป)