กลับไปหน้ารวมไฟล์
frequency-generator-and-frequency-counter-for-pro-micro-089c39.md

ภาพรวม

หลายครั้งที่เราต้องการสร้างสัญญาณความถี่ที่โปรแกรมได้ หรือทำเครื่องนับความถี่ (Frequency Counter) ไว้ใช้ในงานต่างๆ โปรเจคนี้จะสอนวิธีทำฟังก์ชันทั้งสองแบบ (หรือจะทำแค่แบบเดียวก็ได้) โดยใช้อุปกรณ์ราคาถูก (≈$5-$17) อย่างบอร์ด Pro Micro (รุ่น Sparkfun DEV-12640 หรือของโคลนที่เทียบเท่าได้) ที่ใช้ชิพ ATMega32U4 และเขียนโค้ดด้วย Arduino IDE ฟังก์ชันพวกนี้เอาไปใช้แยกกัน หรือจะเอาไปรวมกันในโปรเจคใหญ่ๆ หรือทำเป็นอุปกรณ์เดี่ยวๆ ก็ได้ เพราะมันเป็นฟังก์ชันที่แยกจากกัน เลยจะอธิบายแยกกันไป ส่วนหนึ่งของโปรเจคนี้ก็จะมีโมดูลหลัก (main module) ร่วมกัน ที่เอาไว้ตั้งค่าเครื่องกำเนิดความถี่ หรืออ่านค่าจากเครื่องนับความถี่ ซึ่งทำได้ผ่านพอร์ตอนุกรม (serial port) หรืออีกทางนึง ถ้าอยากให้เป็นอุปกรณ์เดี่ยวๆ ก็ต่อปุ่มกด 4 ปุ่มกับจอ LCD เข้าไปก็ใช้ได้เลย

ต่างจากโปรเจคอื่นๆ ในเว็บนี้ที่มักจะจบเป็นอุปกรณ์สำเร็จรูป โปรเจคนี้เน้นไปที่การสร้าง "บล็อคฟังก์ชัน" ของเครื่องกำเนิดความถี่และเครื่องนับความถี่แทน สมมติว่าน้องๆ จะเอาโมดูลพวกนี้ไปเขียนโปรแกรมหลัก (main module) ต่อเอง ตามวัตถุประสงค์ที่ต้องการ อาจจะเพิ่มจอ LCD, คีย์บอร์ด หรืออินเตอร์เฟสอื่นๆ หรือจะเอาไปเป็นส่วนหนึ่งของโปรเจคใหญ่ๆ ที่น้องทำอยู่ก็ได้ สุดท้าย เพราะทั้งเครื่องกำเนิดความถี่และเครื่องนับความถี่ ต่างก็อาศัยการทำงานของออสซิลเลเตอร์ภายใน (internal crystal oscillator) ของ Pro Micro ซึ่งปกติแล้วไม่ได้ปรับเทียบความแม่นยำมา เราก็จะลงรายละเอียดวิธีโมดูลนี้เพื่อปรับปรุงความแม่นยำของออสซิลเลเตอร์ด้วย

หมายเหตุไว้นิดนึงว่า บอร์ด Arduino อื่นๆ ที่ใช้ชิพ ATMega32U4 อย่าง Arduino Leonardo หรือ Arduino Micro ก็ใช้ได้เหมือนกันนะ

เครื่องกำเนิดความถี่ (Frequency Generator)

ไลบรารีนี้ใช้สร้างเครื่องกำเนิดความถี่ปรับค่าได้ โดยใช้ Timer 4 บนบอร์ด Arduino Pro Micro (ที่ใช้ชิพ ATMega32U4) เราสามารถเอาสัญญาณคลื่นสี่เหลี่ยมความถี่ปรับได้นี้ ไปส่งออกให้กับอุปกรณ์หรือวงจรอื่นๆ ที่เราออกแบบไว้ได้ เครื่องกำเนิดความถี่จะส่งสัญญาณคลื่นสี่เหลี่ยม Duty Cycle 50% ออกมาได้ตั้งแต่ 1 Hz ไปจนถึงประมาณ 12MHz โดยใช้ฮาร์ดแวร์ที่มีอยู่แล้วในตัวบอร์ด Pro Micro เลย และยังใช้ฟังก์ชันอื่นๆ ของบอร์ดได้ตามปกติ Timer 4 บนโปรเซสเซอร์/บอร์ดตัวนี้มีพิเศษอยู่นิดนึง คือมันสามารถเชื่อมต่อกับสัญญาณนาฬิกาหลัก (main clock) ของโปรเซสเซอร์ได้ หรือจะเชื่อมต่อกับ PLL ที่อยู่ในตัวชิพ ATMega32U4 เพื่อให้ได้ความถี่ตั้งต้นที่สูงขึ้น และมีค่าสเกลเพิ่มเติม เช่น 1.5x clock ทำให้มีชุดค่าตัวนับ (count values) ให้เลือกมากขึ้น เพื่อให้ได้สัญญาณเอาท์พุทที่ความถี่ใกล้เคียงกับที่เราต้องการมากที่สุด โปรเซสเซอร์ตระกูล ATMega/Tiny อื่นๆ บางตัวก็มี Timer แบบนี้ แต่ไม่ค่อยพบเห็นบ่อยนัก

เฟิร์มแวร์สำหรับตั้งค่าความถี่ มีอัลกอริทึมเฉพาะตัว ที่จะตั้งค่าความถี่เอาท์พุทให้ใกล้เคียงกับความถี่ที่เราขอมากที่สุด โดยการเลือกจากหนึ่งในสามความถี่นาฬิกาอินพุทที่ต่างกัน (ผ่าน PLL ในชิพ), ค่าตัวหารสัญญาณแบบไบนารี (binary prescaler value) และค่าตัวนับ (count value) ด้วยตัวแปรทั้งหมดเนี่ย ถ้าต้องมานั่งคำนวณหาค่าที่เหมาะสมสำหรับแต่ละตัวเพื่อให้ได้ความถี่ที่ต้องการจากภายนอก มันคงจะปวดหัวน่าดู แต่ด้วยอัลกอริทึมในเฟิร์มแวร์ เฟิร์มแวร์จะคำนวณหาค่าที่ดีที่สุดสำหรับตัวแปรทั้งสามนี้ให้เอง เพื่อให้ความถี่เอาท์พุทใกล้เคียงที่สุด พอคำนวณและตั้งค่าเสร็จ บอร์ด Pro Micro ก็จะส่งสัญญาณความถี่ที่เราต้องการออกมาได้เลย โดยไม่ต้องให้เฟิร์มแวร์/ซอฟต์แวร์เข้ามายุ่งอีก เพราะเราใช้โหมด Timer/Counter แบบโหลดค่าอัตโนมัติ (auto reload) โดยไม่ใช้การขัดจังหวะ (interrupt)

ฮาร์ดแวร์ของเครื่องกำเนิดความถี่

บอร์ด Pro Micro จะส่งสัญญาณนาฬิกาความถี่ที่เราเลือก ออกมาที Arduino Digital pin 5 (ขาโปรเซสเซอร์ PC6) เป็นสัญญาณคลื่นสี่เหลี่ยม 0-5V Duty Cycle 50% โดยไม่ต้องปรับแต่งคอนโทรลเลอร์เพิ่มเติม อีกทางเลือกนึง เราสามารถตั้งค่าให้ส่งสัญญาณออกทาง Arduino Digital pin 10 (ขาโปรเซสเซอร์ PB6) แทนได้ ถ้าต้องการ ภาพด้านล่างแสดงตำแหน่งขา (pin) สำหรับส่งสัญญาณเอาท์พุทของเครื่องกำเนิดความถี่บนบอร์ด Pro Micro

สำหรับงานของน้องนะ วงจรเพิ่มเติมอาจจะไม่จำเป็นก็ได้ แต่ถ้าต้องการระดับสัญญาณที่ต่างออกไป หรือต้องการกำลังไฟมากกว่าที่ขาเอาต์พุตของโมดูลจะจ่ายไหว น้องอาจต้องเพิ่มวงจรภายนอก เช่น วงจรขยายสัญญาณ (amplifier), วงจรปรับระดับแรงดัน (level shifter) หรือตัวแบ่งแรงดัน (resistitive divider) เข้าไป ภาพยังแสดงแหล่งจ่ายไฟ 5V ด้วย ซึ่งน้องสามารถใช้จ่ายไฟให้วงจรภายนอกพวกนี้ได้ ถ้ามันกินไฟไม่เกิน 100mA นะจ๊ะ

ความแม่นยำของความถี่ที่เจ้าเครื่องกำเนิดความถี่ตัวนี้สร้างได้เนี่ย ขึ้นอยู่กับคริสตัลออสซิลเลเตอร์บนบอร์ด Pro Micro โดยตรงเลย มักจะคลาดเคลื่อนประมาณ 0.1-0.2% แต่โดยทั่วไปก็จะใกล้กับความถี่ที่ตั้งไว้มากกว่านั้นแหละ ถ้าน้องต้องการความแม่นยำสูงกว่านี้ ไปดูหัวข้อด้านล่างที่พูดถึงการปรับแต่งคริสตัลออสซิลเลเตอร์ของ Pro Micro ได้เลย ความแม่นยำของความถี่ยังขึ้นกับค่าต่างๆ ที่ตั้งไว้ในรีจิสเตอร์ของชิปโปรเซสเซอร์ด้วย แม้อัลกอริทึมจะพยายามหาค่าที่ดีที่สุดสำหรับ PLL, ตัวแบ่งความถี่ (prescaler) และตัวนับ (counter) แต่ก็มีบางความถี่ที่ฮาร์ดแวร์สร้างไม่ได้โดยตรง ถ้าไม่มีซอฟต์แวร์เข้ามาช่วย ซึ่งมันเกินขอบเขตของโปรเจคนี้ไปแล้ว ความถี่ต่ำๆ ส่วนใหญ่ (ต่ำกว่าประมาณ 5KHz) จะออกมาเป๊ะเวอร์ (หรือเกือบเป๊ะ) หมายความว่าโมดูลสามารถสร้างความถี่ที่ต้องการได้ตรงเป๊ะ แต่เมื่อความถี่ที่ขอสูงขึ้นเรื่อยๆ ก็จะเริ่มมี "ช่องว่าง" ที่ความถี่เป๊ะๆ ทำไม่ได้ ในกรณีนี้ โมดูลจะตั้งค่าให้ใกล้เคียงที่สุด ซึ่งสำหรับงานส่วนใหญ่ก็ใช้ได้อยู่แล้วจ้า ดีวตี้ไซเคิล (Duty Cycle) ของสัญญาณเอาต์พุตจะถูกตรึงไว้ที่ 50%

ฟังก์ชันอินเตอร์เฟซซอฟต์แวร์ของเครื่องกำเนิดความถี่

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

** ฟังก์ชัน Set **

ตั้งค่าความถี่ด้วยคำสั่ง set ซึ่งจะตั้งค่าให้ใกล้เคียงกับค่าที่ขอได้มากที่สุด ฟังก์ชัน `FrequencyGenerator::set` รับพารามิเตอร์เป็นเลขจำนวนเต็มแบบ long ซึ่งคือความถี่ที่ต้องการในหน่วย Hz ค่านี้มีได้ตั้งแต่ต่ำกว่า 0 (จะคืนค่าความถี่ปัจจุบัน), 0 (ปิดเครื่องกำเนิด) ไปจนถึงค่าที่สูงกว่าครึ่งหนึ่งของความถี่สัญญาณนาฬิกาของไมโครคอนโทรลเลอร์ (8MHz) เท่าที่ทดลองกับบอร์ด Pro Micro ทั่วไป มันทำงานได้ถึงประมาณ 12MHz เลยทีเดียว ฟังก์ชัน `FrequencyGenerator::set` จะคืนค่าความถี่ที่ตั้งได้จริงถ้าสามารถตั้งตามที่ขอได้ หรือคืนค่า -1 ถ้าตั้งค่าตามที่ต้องการไม่ได้ เนื่องจากตัวจับเวลา (timer) ถูกตั้งค่าให้รีโหลดอัตโนมัติ มันจึงไม่ต้องการการแทรกสัญญาณ (interrupt) หรือโอเวอร์เฮดจากซอฟต์แวร์อื่นๆ เลย — แค่เรียกใช้ฟังก์ชันนี้ แล้วฮาร์ดแวร์ก็จะสร้างความถี่เอาต์พุตให้เองโดยอัตโนมัติ ไม่ต้องไปยุ่งกับมันอีก

ถ้าอยากปิดเครื่องกำเนิดความถี่ ก็แค่เรียกฟังก์ชัน `FrequencyGenerator::set` โดยส่งความถี่เป็น 0 ไป มันจะปิดการทำงานของเครื่องกำเนิดและทำให้ขาเอาต์พุตอยู่ในสถานะอิมพีแดนซ์สูง (high impedance) ส่วนการขอค่าความถี่ปัจจุบัน ก็สามารถทำได้โดยเรียกฟังก์ชัน `FrequencyGenerator::set` ด้วยค่า -1 (หรือเลขลบใดๆ ก็ได้)

** ฟังก์ชัน Read **

ยังมีฟังก์ชัน `FrequencyGenerator::read` ให้ใช้สำหรับขอค่าความถี่ปัจจุบันของโมดูลเครื่องกำเนิดความถี่อีกด้วย ค่าที่ได้กลับมาจะเป็นความถี่จริงที่คอนโทรลเลอร์กำลังส่งออกอยู่ (และเป็นค่าที่ใกล้เคียงที่สุดที่ฮาร์ดแวร์ทำได้) ซึ่งอาจจะต่างจากความถี่ล่าสุดที่ตั้งไว้เล็กน้อย ฟังก์ชันนี้คืนค่าเป็นเลขจำนวนเต็มแบบ long

รายละเอียดและตัวเลือกของโค้ดเครื่องกำเนิดความถี่

ฟังก์ชันการทำงานของเครื่องกำเนิดความถี่ทั้งหมดอยู่ที่ไฟล์ "FrequencyGenerator.cpp" ส่วนเฮดเดอร์ไฟล์ที่มีโปรโตไทป์ของคลาสอยู่ที่ไฟล์ "FrequencyGenerator.h" โมดูลนี้ถูกเขียนขึ้นมาเฉพาะสำหรับ Timer 4 ของชิป ATMega32U4 บนบอร์ด Pro Micro (หรือไมโครคอนโทรลเลอร์อื่นๆ ที่มีบล็อกการทำงานคล้ายกัน) โดยสมมติว่าคอนโทรลเลอร์ทำงานเป็นอุปกรณ์ USB โดยมี PLL ตั้งไว้ที่ 96MHz และใช้คริสตัล 16MHz (นี่คือค่าตั้งต้นของบอร์ด Pro Micro) มันอาจจะถูกปรับใช้กับ Timer ตัวอื่นได้ แต่ความละเอียด (resolution) อาจจะน้อยลงนะจ๊ะ

โค้ดในโมดูล "FrequencyGenerator.cpp" นี่แหละที่เก็บอัลกอริทึมสำหรับตั้งค่าตัวเรจิสเตอร์ต่างๆ ในโปรเซสเซอร์ให้ได้ค่าที่ดีที่สุดสำหรับความถี่อินพุทที่เราต้องการ มันทำงานโดยการคำนวณหาค่าที่ดีที่สุดสำหรับแต่ละการตั้งค่า PLL แล้วก็หาว่าการตั้งค่า PLL แบบไหนที่ให้ความถี่เอาท์พุทใกล้เคียงกับค่าที่เราต้องการมากที่สุด

ส่วนค่า prescaler กับ count นั้นได้มาจากการคำนวณ log2 ของความถี่ที่ต้องการ แล้วแปลงค่าเหล่านั้นให้เป็นค่า prescaler กับ count สำหรับตัวเรจิสเตอร์ พอเจอคอมโบที่ดีที่สุดแล้ว ก็จะเซ็ตตัวเรจิสเตอร์จริงๆ ให้เป็นค่าเหล่านั้นแหละ ถ้าอยากรู้รายละเอียดลึกๆ ลองอ่านคอมเมนต์ในโค้ดดูได้ มีอธิบายอัลกอริทึมไว้ละเอียดเลย

ในโมดูล FrequencyGenerator.cpp เนี่ย มีตัวกำหนดการคอมไพล์แบบมีเงื่อนไข (conditional compilation defines) อยู่ 2 ตัว ที่เราจะเลือกกำหนดหรือไม่กำหนดก็ได้

  • ตัวแรกคือ 'FRQGENUSEPB6' ถ้ากำหนดให้มีค่าไม่ใช่ศูนย์ มันจะใช้ Arduino Digital pin 10 (ซึ่งคือพินโปรเซสเซอร์ PB6) เป็นเอาท์พุทของเจเนอเรเตอร์ แทนที่จะใช้ค่าเริ่มต้นคือ Arduino Digital pin 5 (พินโปรเซสเซอร์ PC6)
  • อีกตัวคือ 'FRQGENDEBUG' ถ้ากำหนดให้มีค่าไม่ใช่ศูนย์ มันจะส่งค่าของตัวแปรบางตัวออกไปที่พอร์ตอนุกรม (serial port) ตอนที่กำลังตั้งค่าความถี่อยู่ เพื่อให้เราใช้ดีบั๊กได้

Frequency Counter

ไลบรารีตัวนี้ทำหน้าที่เป็นเครื่องนับความถี่ (frequency counter) โดยใช้ทรัพยากรฮาร์ดแวร์ timer/counter ที่มีอยู่แล้วในโมดูล Pro Micro มันใช้ Timer 0 และใช้ไทเมอร์อีกตัวเป็นไทเมอร์ "เกต" (gate timer) บนโมดูล Pro Micro

มันทำงานได้สองโหมดหลักๆ

  1. โหมดนับความถี่แบบดั้งเดิม (Traditional Frequency Counter): นับจำนวนพัลส์ที่เกิดขึ้นบนพินอินพุทภายในช่วงเวลาที่กำหนด (gate time) โหมดนี้เหมาะสำหรับวัดสัญญาณความถี่สูง
  2. โหมดวัดคาบเวลา (Period Counter): วัดเวลาของสัญญาณอินพุทหนึ่งรอบหรือหลายรอบ แล้วคำนวณกลับมาเป็นความถี่ โหมดนี้ให้ความละเอียดระดับซับเฮิรตซ์ (sub Hertz) ได้ดีกว่า เวลาวัดสัญญาณความถี่ต่ำ

การใช้งานโมดูลนี้ทำผ่านฟังก์ชันหลัก 2-3 ฟังก์ชัน

  • ฟังก์ชันแรกใช้สำหรับอ่านค่าความถี่ที่วัดได้
  • ฟังก์ชันที่สองใช้สำหรับตั้งค่าให้เครื่องนับความถี่ทำงานในโหมดนับความถี่แบบดั้งเดิม หรือโหมดวัดคาบเวลา พร้อมทั้งตั้งค่า gate time หรือจำนวนพัลส์ที่ต้องการนำมาหาค่าเฉลี่ย
  • อีกฟังก์ชันหนึ่ง (ถ้าต้องการ) ใช้ตรวจสอบว่ามีค่าที่อ่านได้ใหม่ๆ แล้วหรือยัง

เพราะโมดูลนี้ใช้ฮาร์ดแวร์ไทเมอร์และอินเตอร์รัปท์ในการทำงาน มันจึงสามารถทำงานไปพร้อมๆ กับฟังก์ชันอื่นๆ ทั้งหมดของโมดูล Pro Micro และ Arduino environment ได้ โดยไม่กระทบโค้ดอื่นๆ ที่เราเขียนขึ้นมาเอง

เมื่อตั้งค่าให้อยู่ในโหมดนับความถี่แบบดั้งเดิม:

  • Timer 0 จะถูกตั้งค่าให้นับพัลส์อินพุท
  • Timer 1 จะถูกใช้เป็นแหล่งกำเนิดเกต (gating source)
  • ทุกครั้งที่ Timer 0 นับจนล้น (overflow) อินเตอร์รัปท์รูทีนจะเพิ่มค่าให้กับตัวแปรเก็บนับ (saved count variable)
  • ไทเมอร์เกต (Timer 1) จะถูกใช้เพื่อย้ายค่าที่นับสะสมมานี้ไปยังตัวแปรอีกตัวหนึ่งเป็นระยะๆ ซึ่งค่าจากตัวแปรนี้แหละที่จะถูกส่งกลับไปให้ผู้ใช้เมื่อมีการร้องขอค่าความถี่ หลังจากนั้นมันก็จะรีเซ็ตค่าการนับใน Timer 0 (และตัวแปรเก็บนับ) เพื่อเริ่มรอบใหม่
  • ไทเมอร์เกตสามารถตั้งค่าได้หลายช่วงเวลา เช่น 10 mS, 100 mS, 1 วินาที, 10 วินาที หรือ 100 วินาที
  • ไทเมอร์เกตก็ขับเคลื่อนด้วยอินเตอร์รัปท์เช่นกัน ทำให้โมดูลนับความถี่สามารถจับเวลา "หน้าต่างเกต" ได้อย่างแม่นยำ
  • นอกจากนี้ยังมีโหมดเกตจากภายนอก (external gating mode) ที่ให้เราใช้ระดับสัญญาณบนพินภายนอกมาควบคุมการ "เกต" เครื่องนับความถี่ได้

เมื่อตั้งค่าให้อยู่ในโหมดวัดคาบเวลา:

  • Timer 0 จะถูกใช้เพื่อนับการเปลี่ยนแปลงสัญญาณ (transition) บนพินอินพุท แบบนับครั้งเดียวหรือนับหลายครั้ง
  • เมื่อได้รับสัญญาณ transition ครั้งแรก ระบบจะบันทึกค่าไมโครวินาทีในขณะนั้น (ซึ่งเป็นส่วนหนึ่งของ Arduino environment)
  • เมื่อเกิด transition ครั้งที่สอง (หรือครั้งที่กำหนด) ระบบจะอ่านค่าไมโครวินาทีอีกครั้ง แล้วนำมาลบกับค่าที่บันทึกไว้ครั้งแรก เพื่อหาคาบเวลาของสัญญาณอินพุท
  • เมื่อผู้ใช้ร้องขอค่าความถี่ ค่าคาบเวลานี้จะถูกแปลงเป็นความถี่แล้วส่งกลับไป
  • ในโหมดวัดคาบเวลานี้ มีโหมดการหาค่าเฉลี่ยให้เลือก 3 แบบ คือ ไม่หาค่าเฉลี่ย, หาค่าเฉลี่ยจาก 10 พัลส์, หรือหาค่าเฉลี่ยจาก 100 พัลส์
  • การหาค่าเฉลี่ยทำได้โดยการตั้งค่า Timer 0 ให้สร้างอินเตอร์รัปท์เมื่อนับ transition ได้ 1, 10 หรือ 100 ครั้ง ซึ่งเท่ากับเป็นการหาค่าเฉลี่ยของสัญญาณอินพุทนั่นเอง

ฮาร์ดแวร์ของตัวนับความถี่ (Counter Hardware)

สัญญาณอินพุตสำหรับตัวนับความถี่จะต่อเข้ากับ Arduino Digital pin 6 (ซึ่งคือขา PD7 ของตัวประมวลผล) บนบอร์ด Pro Micro (ATMega32U4) นะน้อง โดยมันจะนับพัลส์ที่ตกลงจาก High ไป Low (low going pulses) รูปด้านล่างแสดงขาอินพุตของตัวนับ และขา External Gate Input ด้วย (เดี๋ยวอธิบายละเอียดทีหลัง) ส่วนขา External Gate Input นี่ ถ้าจะใช้ ก็อยู่ที่ Arduino Digital pin 9 (ขา PB5 ของตัวประมวลผล) แต่น้องสามารถย้ายไปขาอื่นได้ถ้าอยาก

แน่นอนว่าสัญญาณอินพุตต้องเป็นระดับ TTL ที่เหมาะสมถึงจะนับได้นะ วงจรขยายสัญญาณ (Input amplifiers) สำหรับขยายสัญญาณระดับต่ำกว่าให้มาเป็น TTL (ถ้าจำเป็น) นั้นอยู่นอกเหนือขอบเขตของงานนี้แล้วจ้า มีแหล่งจ่ายไฟ 5V แสดงไว้ด้วย ซึ่งสามารถใช้จ่ายไฟให้วงจรภายนอกได้ ถ้าต้องการและกินไฟไม่เกิน 100mA

ความแม่นยำของตัวนับความถี่นี้ขึ้นอยู่กับคริสตัลออสซิลเลเตอร์บนโมดูล Pro Micro โดยตรงเลย มักจะอยู่ภายใน 0.1-0.2% แต่ปกติแล้วมันจะใกล้ค่าจริงมากกว่านี้ซะอีก ดูหัวข้อด้านล่างที่อธิบายการปรับแต่งคริสตัลออสซิลเลเตอร์ของ Pro Micro ถ้าต้องการความแม่นยำที่สูงกว่านี้นะ

ความแม่นยำยังขึ้นกับความแม่นยำของตัวจับเวลา Gate (gate timer) สำหรับโหมดตัวนับความถี่แบบดั้งเดิม (traditional frequency counter mode) และตัวจับเวลาในหน่วยมิลลิวินาที (milliseconds timer) สำหรับโหมดการวัดคาบ (period measuring mode) อีกด้วย ทั้งสองตัวจับเวลาในโปรเจคนี้ขับเคลื่อนด้วยอินเตอร์รัพท์ (interrupt driven) หมายความว่ามันทำงานอัตโนมัติโดยไม่ต้องมีการแทรกแซงจากซอฟต์แวร์ และเกิดขึ้นเร็วที่สุดเท่าที่จะเป็นไปได้ในสภาพแวดล้อม Arduino แม้ว่าจะมี overhead บางส่วนสำหรับ routine ของอินเตอร์รัพท์ แต่โดยปกติแล้วมันน้อยมากจนมองข้ามได้ และความถี่ที่อ่านได้มักจะใกล้เคียงหรือตรงกับความถี่จริงเป๊ะๆ เลย

จากการทดสอบโมดูลต่างๆ ในช่วงพัฒนางานนี้พบว่า ในโหมดตัวนับความถี่แบบดั้งเดิม โมดูล Pro Micro สามารถนับความถี่ได้อย่างน่าเชื่อถือสูงสุดถึงประมาณ 8MHz (ซึ่งคือ 1/2 ของความถี่สัญญาณนาฬิกาของตัวประมวลผล) ส่วนในโหมดวัดคาบ ซึ่งออกแบบมาวัดความถี่ต่ำด้วยความละเอียดที่มากขึ้น สามารถวัดความถี่ได้สูงสุด 20 KHz อย่างน่าเชื่อถือเมื่อตั้งค่าให้เฉลี่ย (averaging) ที่ 10 และ 100 และ 10 KHz เมื่อปิดการเฉลี่ย (ค่าเฉลี่ยเป็น 1) ในโหมดวัดคาบนี้ ถ้าความถี่สูงกว่าค่าเหล่านี้ โมดูลอาจจะรายงานค่าเป็น '999999' หรือในบางกรณีที่หายากมากอาจได้ค่าผิดเพี้ยนไป

ถ้าแอปพลิเคชันของน้องต้องวัดความถี่ที่สูงกว่า 8 MHz ล่ะก็ สามารถเพิ่มวงจรแบ่งความถี่ (prescaler หรือ divider chain) ภายนอกเข้ากับสัญญาณอินพุตก่อนส่งเข้ามายังโมดูลนับนี้ แล้วค่อยปรับค่าที่โมดูลคืนกลับมาให้ถูกต้องตามสัดส่วนที่แบ่งไปได้ ใช้ IC อย่าง 74196 หรือใช้ 74F74 หลายๆ ตัวต่อกันก็วัดความถี่ได้สูงถึงประมาณ 60-100MHz และยังมี IC prescaler อื่นๆ สำหรับความถี่ที่สูงกว่านั้นอีก ถ้าใช้ prescaler น้องสามารถปรับค่า define ที่ชื่อ 'FCPRESCALER' ในโมดูล "FrequencyCounter.cpp" ให้เป็นค่าตัวหาร (divisor value) ที่ใช้ได้ เพื่อให้ความถี่ที่คืนค่าออกมานั้นถูกต้องเมื่อใช้ prescaler แล้ว

ฟังก์ชันอินเตอร์เฟซซอฟต์แวร์ของตัวนับ (Counter Software Interface Functions)

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

** ฟังก์ชันกำหนดโหมด (Mode Function) **

ฟังก์ชันแรก 'FrequencyCounter::mode' ใช้สำหรับตั้งค่าหรืออ่านโหมดการนับ ซึ่งรวมถึงโหมดและค่า Gate time หรือจำนวนครั้งที่ใช้เฉลี่ย (number of averages) ฟังก์ชันนี้ถูกเรียกด้วยพารามิเตอร์เดียวซึ่งระบุโหมดที่จะใช้ หรือขอค่าปัจจุบัน ถ้าพารามิเตอร์อินพุตเป็น -1 (หรือค่าที่น้อยกว่า 0 ใดๆ) ฟังก์ชันจะคืนค่าโหมดปัจจุบันออกมา ถ้าพารามิเตอร์อินพุตอยู่ในช่วง 0 ถึง 9 จะตั้งโหมดดังนี้:

ฟังก์ชันนี้จะคืนค่าโหมดปัจจุบันที่เลือกอยู่เสมอ หรือคืนค่า -1 ถ้าพารามิเตอร์ที่ใส่เข้ามาอยู่นอกเหนือจากค่าที่กล่าวมาข้างบน โหมด 6 ถึง 9 อาจจะมีหรือไม่มีก็ได้ ขึ้นอยู่กับว่ามอดูลถูกคอมไพล์มาอย่างไร ถ้าเรียกฟังก์ชัน `FrequencyCounter::mode` โดยไม่ใส่พารามิเตอร์ มันจะคืนค่าโหมดปัจจุบันมาให้

** ฟังก์ชันอ่านค่า (Read Function) **

ฟังก์ชันที่สอง `FrequencyCounter::read` ใช้สำหรับอ่านค่าจากตัวนับความถี่และคืนค่าออกมาเป็นสตริงที่เป็นตัวเลขทศนิยมของความถี่ที่อ่านได้ ฟังก์ชันนี้จะถูกเรียกโดยส่งพอยน์เตอร์ไปยังบัฟเฟอร์สตริง (ที่ฟังก์ชันนี้จะเติมข้อมูลให้) และพารามิเตอร์ชื่อ `Wait` ซึ่งถ้าไม่เป็นศูนย์ จะทำให้ฟังก์ชันรอจนกว่าจะได้ค่าการนับความถี่ "สดใหม่" มา ฟังก์ชันจะคืนค่าพอยน์เตอร์ไปยังสตริงที่เก็บค่าความถี่ ซึ่งจะมีรูปแบบประมาณ "12345678" หรือ "12345.67890" (หรือคล้ายๆ กัน) ตำแหน่งบัฟเฟอร์สตริงที่ส่งเข้ามาต้องมีขนาดใหญ่พอที่จะเก็บสตริงที่ฟังก์ชันจะสร้างขึ้นมา — แนะนำให้มีอย่างน้อย 15 ตัวอักษรขึ้นไป พารามิเตอร์ `Wait` ควรเป็นศูนย์เพื่อเอาค่าความถี่ล่าสุดที่อ่านไว้แล้ว หรือไม่เป็นศูนย์เพื่อรอให้ได้ค่าการนับความถี่ "สดใหม่" มา ต้องระวังไว้นิดนึงว่า ถ้าเรียกฟังก์ชันโดยตั้งพารามิเตอร์ `Wait` เป็นค่าที่ไม่ใช่ศูนย์ ฟังก์ชันอาจจะใช้เวลาถึง 100 วินาทีกว่าจะคืนค่ากลับมา ขึ้นอยู่กับว่าโมดูลตัวนับความถี่กำลังอยู่ในโหมดไหน ถ้าไม่อยากรอนานขนาดนั้น ก็มีฟังก์ชันแบบโพลลิ่งให้ใช้เช็คว่ามีค่าความถี่ใหม่อ่านได้แล้วหรือยัง ฟังก์ชันนี้คืนค่าเป็นสตริงแทนที่จะเป็นประเภทข้อมูลไบนารีทศนิยมจริงๆ เพื่อที่เราจะได้ไม่ต้องใช้ฟังก์ชันเกี่ยวกับทศนิยมของคอมไพเลอร์ (ซึ่งกินพื้นที่โค้ดไปเยอะมาก) นั่นเอง

** ฟังก์ชันเช็คความพร้อม (Available Function) **

อย่างที่บอกไปในย่อหน้าก่อนหน้า ยังมีฟังก์ชันสำหรับเช็คว่ามีค่าความถี่ "สดใหม่" พร้อมให้อ่านแล้วหรือไม่ด้วย ฟังก์ชัน `FrequencyCounter::available` นี้ไม่ต้องการพารามิเตอร์ใดๆ และจะคืนค่า 1 ถ้ามีค่าพร้อมแล้ว หรือคืนค่า 0 ถ้ายังไม่มี

ผู้ใช้ส่วนใหญ่จะต้องการแค่สองหรือสามฟังก์ชันที่กล่าวมาข้างต้นเท่านั้น ในสถานการณ์พิเศษ ก็ยังมีฟังก์ชันอื่นๆ อีกสองสามตัวให้ใช้

** ฟังก์ชันอ่านค่าแบบไบนารี (Read Binary Function) **

เมื่อฟังก์ชัน `FrequencyCounter::read` ถูกเรียกโดยมีแค่พารามิเตอร์ "Wait" (และไม่มีพอยน์เตอร์ไปยังพารามิเตอร์สตริง) ฟังก์ชันอ่านจะคืนค่าเป็นจำนวนเต็มแบบ long ซึ่งคือค่าการนับหรือคาบเวลาที่ยังไม่ได้ปรับแก้ของความถี่ที่อ่านได้ ผู้ใช้จะต้องทำการปรับสเกล/แปลงค่านี้ให้เป็นค่าที่เหมาะสมกับความต้องการของแอปพลิเคชันเอง เหมือนกับเวอร์ชันสตริงของฟังก์ชัน `FrequencyCounter::read` ฟังก์ชันนี้ก็รับพารามิเตอร์ `Wait` ด้วยเหมือนกัน

ข้อมูล Frontmatter ดั้งเดิม

apps:
  - "1x Arduino IDE"
author: "Rick-G1"
category: "Tools & Equipment"
components:
  - "1x LCD AlphaNumeric Display 16 x 2 (w/ backlight)"
  - "1x Pro Micro - 5V/16MHz"
  - "1x Arduino Leonardo"
description: "มาดูไลบรารีเทพๆ ที่จะทำให้ Pro Micro กับ Leonardo ของเราเป็นได้ทั้ง Frequency Generator ปรับค่าได้ และ Frequency Counter งานง่ายแต่หล่อ เอาไปใช้กับแล็บหรืองานฮาร์ดแวร์วัยรุ่นๆ จัดไป!"
difficulty: "Advanced"
documentationLinks: []
downloadableFiles:
  - "https://github.com/Rick-G1/FrequencyGenerator"
  - "https://github.com/Rick-G1/FrequencyCounter"
encryptedPayload: "U2FsdGVkX1/P47EZaP0daBjz1VsrkgloeCL6EP+nG5kN0RizWPlNeQDr3v9A39hA1AlkvNFCfIHIj3HFW2rM6jm3fykJVQeBVUehB4ygRqA="
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/frequency-generator-and-frequency-counter-for-pro-micro-089c39_cover.jpg"
lang: "en"
likes: 0
passwordHash: "a035c8fc00b3a34aea61be388b4b2811241e978ebb7196a5849b405391556f92"
price: 2450
seoDescription: "Frequency Generator and Frequency Counter libraries for Pro Micro and Leonardo boards. Variable frequency implementation made easy."
tags:
  - "test equipment"
  - "lab stuff"
title: "Frequency Generator and Frequency Counter for Pro Micro"
tools: []
videoLinks: []
views: 7518