ดิว.นินจา

ดิว.นินจา

Saturday, May 4, 2019

กำหนดคาบเวลาการทำงานอัลกอริทึมบน ESP8266

ในการทำงานบางประเภทบนระบบฝังตัว เช่น การสุ่มสัญญาณ การอิมพลิเมนต์ตัวควบคุมเชิงเส้น สิ่งสำคัญที่ต้องคำนึงถึงคือคาบเวลาของการประมวลผลที่จะต้องมีความแม่นยำและสามารถกำหนดได้ในโปรแกรม วิธีการง่ายสุดคือการใช้คำสั่ง delay() เพื่อหน่วงเวลา แต่จะไม่ได้รวมเวลาการคำนวณอัลกอริทึมเข้าไปด้วย ซึ่งจะมีความคลาดเคลื่อนสูงโดยเฉพาะสำหรับระบบที่ต้องการคาบเวลาการทำงานสั้นมาก ในบทความนี้รวบรวมวิธีการกำหนดคาบเวลาสำหรับ ESP8266 โดยอาศัยไทเมอร์หรือการอ่านค่าฐานเวลาจากระบบ สำหรับตัวประมวลผล ESP32 จะมีไลบรารี FreeRTOS ช่วยให้การกำหนดทาสก์รายคาบทำได้ง่ายและเป็นระบบ

การใช้ไทเมอร์แบบซอฟต์แวร์

อธิบายโดยสังเขป ไทเมอร์แบบซอฟต์แวร์ (software timer) คือการใช้ไทเมอร์ของตัวประมวลผลในลักษณะที่มีส่วนของโปรแกรมห่อหุ้มอยู่อีกชั้นหนึ่ง อาศัยฐานเวลาจากฮาร์ดแวร์เป็นตัวกำหนดความละเอียด นิยมเรียกว่า “ติ๊ก” เปรียบได้กับเข็มวินาทีของนาฬิกา ดังนั้นการกำหนดค่าคาบเวลาจะกระทำกับซอฟต์แวร์โดยไม่ได้เปลี่ยนแปลงคาบเวลาของฮาร์ดแวร์ไทเมอร์แต่อย่างใด

วิธีที่ 1

ตัวอย่างในหนังสือ "ระบบควบคุมและอินเทอร์เน็ตเชื่อมต่อสรรพสิ่ง" ผู้เขียนได้ใช้คำสั่งสำหรับสร้าง ซอฟต์แวร์ไทเมอร์ ซึ่งเป็นวิธีหนึ่งในการกำหนดให้อัลกอริทึมทำงานทุกครั้งที่เกิดอินเทอร์รัพท์ ในที่นี้จะแสดงคำสั่งที่ใช้อีกครั้งหนึ่งสำหรับอ้างอิง

หมายเหตุ : การใช้วิธีนี้ต้องเพิ่มคำสั่งนี้ที่ต้นโปรแกรม


extern "C" {
#include "user_interface.h"
}

นิยามแฮนเดิลของไทเมอร์ที่ส่วนบนของโปรแกรม


os_timer_t myTimer;

กำหนดคาบเวลาที่ต้องการ เช่น 80 มิลลิวินาที


float T = 0.08;

ในฟังก์ชัน setup() ใช้คำสั่งดังนี้เพื่อตั้งคาบเวลาและฟังก์ชันตอบสนองอินเทอร์รัพท์


os_timer_setfn(&myTimer, timerCallback, NULL);
os_timer_arm(&myTimer, 1000*T, true);

สังเกตว่าการกำหนดคาบเวลาจะมีหน่วยเป็นมิลลิวินาที อัลกอริทึมที่เรียกทำงานทุกครั้งที่เกิดอินเทอร์รัพท์ถูกเขียนไว้ในฟังก์ชัน timerCallback()


void timerCallback(void *pArg) {
// implement periodic algorithm here
}

วิธีที่ 2

อีกวิธีหนึ่งในการใช้ไทเมอร์แบบซอฟต์แวร์คืออาศัยไลบรารี Ticker ซึ่งใช้งานค่อนข้างสะดวก เริ่มโดยเรียกไลบรารีที่ส่วนบนของโปรแกรม


#include <Ticker.h>

นิยามออปเจ็คแบบ Ticker และตัวแปรคาบเวลา


Ticker msticker;
int T_ms = 80;  // milliseconds

หลังจากนั้นใน setup() กำหนดฟังก์ชันรายคาบและคาบเวลาโดยคำสั่งดังนี้


msticker.attach_ms(T_ms, timerCallback);

โดยในที่นี้ฟังก์ชันรายคาบคือ timerCallback() ที่นิยามเป็นแบบ void


void timerCallback()   {
// implement your periodic algorithm here
}

หมายเหตุ : หากต้องการกำหนดคาบเวลาหน่วยเป็นวินาที สามารถใช้ msticker.attach() แทน

การใช้ไทเมอร์แบบฮาร์ดแวร์

วิธีการนี้จะโปรแกรมไทเมอร์ที่เป็นฮาร์ดแวร์บน ESP8266 โดยตรง ซึ่งจะมีไทเมอร์ 0 และ 1 แต่ ESP8266 จะใช้ไทเมอร์ 0 สำหรับการเชื่อมต่อ WiFi ดังนั้นไม่ควรใช้ไทเมอร์นี้ ตัวเลือกที่เหลือคือ timer 1

ข้อดีของการใช้ไทเมอร์แบบฮาร์ดแวร์ คือสามารถกำหนดความละเอียดได้เป็นหน่วยไมโครวินาที ตัวอย่างการตั้งค่าในฟังก์ชัน setup()


timer1_attachInterrupt(timerCallback);
timer1_isr_init();
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
timer1_write(ticks);

ความสัมพันธ์ของ ticks กับตัวหารคือ


//  TIM_DIV1 = 0,   //80MHz (80 ticks/us - 104857.588 us max)
//  TIM_DIV16 = 1,  //5MHz (5 ticks/us - 1677721.4 us max)
//  TIM_DIV256 = 3 //312.5Khz (1 tick = 3.2us - 26843542.4 us max)

อัลกอริทึมที่เรียกใช้เขียนไว้ใน timerCallback() ที่นิยามดังนี้


void ICACHE_RAM_ATTR timerCallback(void) {
 // implement your periodic algorithm here 
}

การตรวจสอบเวลาปัจจุบันจาก ESP8266

นับตั้งแต่จ่ายไฟเลี้ยงให้อุปกรณ์ ESP8266 จะมีฐานเวลาของตัวเองที่เพิ่มขึ้นเรื่อยๆ โดยอ่านค่าได้ตลอดเวลาทั้งแบบหน่วยเป็นไมโครวินาทีและมิลลิวินาที ดังนั้นสามารถใช้วิธีอ่านค่าเวลาและสร้างเงื่อนไขในการประมวลผลอัลกอริทึมต่อเมื่อค่าเวลานับจากการรันครั้งสุดท้ายเท่ากับหรือมากกว่าคาบเวลาที่ตั้งไว้ ในกรณีนี้อัลกอริทึมสามารถใส่ไว้ในฟังก์ชัน loop()

ตัวอย่างการตั้งค่าคาบเวลาเท่ากับ 80 มิลลิวินาที นิยามตัวแปร


unsigned long newmillis = 0, lastmillis=0;
unsigned long T_ms = 80;

ใน loop() ใช้เงื่อนไขดังนี้


    newmillis = millis();
    if (newmillis-lastmillis > T_ms)  {
      lastmillis = newmillis;
      periodic_function();
    }

โดยอัลอกริทึมรายคาบเขียนไว้ใน periodic_function() หากต้องการคาบเวลาหน่วยเป็นไมโครวินาที สามารถใช้ micros() โดยโครงสร้างของโปรแกรมจะไม่ต่างกัน

หมายเหตุ : ในตัวอย่างนี้ไม่ได้เขียนป้องกันกรณีเกิด overflow

periodic_blinks.zip : ตัวอย่างโปรแกรมสำหรับการตั้งค่ารายคาบวิธีต่างๆ ผลที่ได้คือ LED บนบอร์ดของ NodeMCU V2 จะกระพริบด้วยคาบเวลา 80 มิลลิวินาที

No comments:

Post a Comment

แนะนำหนังสือ “ตัวควบคุมป้อนกลับบนอินเทอร์เน็ตโดย ESP8266”

ปัจจุบันเมื่อกล่าวถึงอุปกรณ์ IoT (Internet of Things) คงมีน้อยคนที่จะไม่รู้จัก ในยุคที่การเข้าถึงอินเทอร์เน็ตเป็นกิจวัตรประจำวันของมนุษย์เ...