Loading web-font TeX/Math/Italic

Friday, October 9, 2015

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

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

กรณีที่1

เมื่อตรวจพบสัญญาณขาเข้าเป็น HIGH ฟังก์ชันนี้จะเริ่มนับเวลาในหน่วยไมโครวินาที (microsec, \mus) และรอจนกว่าจะเจอสัญญาณ 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);
}


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