แพลตฟอร์มฮาร์ดแวร์ ได้ปฏิวัติโลกของอุปกรณ์อิเล็กทรอนิกส์ด้วยความง่ายในการสร้างต้นแบบและโปรเจ็กต์ที่ใช้งานได้ อย่างไรก็ตาม สำหรับผู้ที่ต้องการพัฒนาการเขียนโปรแกรมไปอีกขั้นและเพิ่มประสิทธิภาพการใช้ทรัพยากร ให้โค้ดสะอาดและเพิ่มประสิทธิภาพ แมโคร กลายเป็นเครื่องมือสำคัญ
ในบทความนี้เราจะเจาะลึกถึงการใช้งาน มาโครใน Arduino:คืออะไร ใช้ยังไง มีข้อดีและข้อจำกัดอย่างไร และเราจะทำเช่นนี้โดยการรวบรวมข้อมูลที่ครอบคลุมและเป็นประโยชน์ที่สุดจากแหล่งข้อมูลที่ดีที่สุดที่มีบนอินเทอร์เน็ต เขียนขึ้นใหม่ด้วยวิธีที่ชัดเจนและทันสมัยเพื่อให้ใช้งานได้จริง
มาโครใน Arduino คืออะไร?
แมโครเป็นไดเรกทิฟพรีโพรเซสเซอร์ ใน C/C++ ที่อนุญาตให้คุณแทนที่ข้อความก่อนที่จะคอมไพล์โค้ด แทนที่จะดำเนินการคำสั่งเหมือนฟังก์ชันดั้งเดิม แมโครจะทำงานโดยการแทนที่บางส่วนของข้อความต้นฉบับ ซึ่งมี ผลกระทบโดยตรงต่อวิธีการสร้างรหัสไบนารีขั้นสุดท้าย.
พรีโปรเซสเซอร์ จะทำงานก่อนการคอมไพล์จริง และรับผิดชอบในการใช้การแทนที่เหล่านี้ ใน Arduino สิ่งนี้ช่วยให้ กำหนดค่าคงที่รวมไฟล์ตามเงื่อนไขหรือสร้างไฟล์ขนาดเล็ก คุณสมบัติออนไลน์ ที่ประหยัดเวลาและความจำ
ตัวอย่างพื้นฐาน: คำจำกัดความเช่น #define LED_PIN 13
ทำให้โค้ดทั้งหมดถูกแทนที่โดยอัตโนมัติ LED_PIN
โดย 13
ก่อนที่จะรวบรวม
สิ่งนี้อาจดูไม่สำคัญ แต่ให้ประโยชน์ วิธีที่มีประสิทธิภาพในการเขียนโค้ดที่ยืดหยุ่นและบำรุงรักษาได้มากขึ้น.
ข้อดีของการใช้แมโคร
การนำแมโครมาใช้ในโครงการ Arduino สามารถให้ประโยชน์เฉพาะเจาะจงได้หลายประการ ดังนี้:
- ปรับปรุงการอ่านโค้ดให้ดีขึ้น: การนำชื่อสัญลักษณ์มาใช้ซ้ำช่วยให้เข้าใจวัตถุประสงค์ของแต่ละองค์ประกอบได้ง่ายขึ้น
- เพิ่มประสิทธิภาพ: การไม่สร้างการเรียกใช้ฟังก์ชันทำให้แมโครสามารถดำเนินการได้เร็วขึ้น
- ลดการใช้ RAM: มีประโยชน์อย่างยิ่งบนบอร์ดที่มีทรัพยากรจำกัด เช่น Arduino UNO.
- อนุญาตให้ปรับเปลี่ยนตามเงื่อนไข: สามารถคอมไพล์ชิ้นส่วนโค้ดต่าง ๆ ได้ ขึ้นอยู่กับประเภทของบอร์ด Arduino ที่ใช้
มาโครพื้นฐาน: การใช้ #define
คำสั่ง #กำหนด มันถูกใช้งานมากที่สุด ใช้ได้ทั้งสำหรับ กำหนดค่าคงที่ สำหรับ สร้างฟังก์ชั่นฉีดอัตโนมัติ ในช่วงเวลาก่อนการคอมไพล์
ตัวอย่างที่ 1: กำหนดพิน
#define PINLED 13
void setup() {
pinMode(PINLED, OUTPUT);
}
void loop() {
digitalWrite(PINLED, HIGH);
delay(500);
digitalWrite(PINLED, LOW);
delay(500);
}
ตัวอย่างที่ 2: มาโครเป็นฟังก์ชันอินไลน์
int itemCounter = 0;
#define COUNT_ITEM() do { itemCounter++; } while(0)
void setup() {
Serial.begin(9600);
COUNT_ITEM();
COUNT_ITEM();
}
void loop() {
Serial.println(itemCounter);
}
อย่างที่คุณเห็น การใช้รูปแบบ ทำ { … } while(0) ช่วยให้แน่ใจว่าแมโครจะทำงานอย่างปลอดภัยแม้จะใช้ภายในโครงสร้างที่มีเงื่อนไขก็ตาม
## ตัวดำเนินการและการเรียงต่อกันของแมโคร
ตัวดำเนินการ ## เป็นเครื่องมือพรีโปรเซสเซอร์อันทรงพลัง ซึ่งช่วยให้สามารถเชื่อมโยงตัวระบุเข้าด้วยกันได้ สิ่งนี้มีประโยชน์มากเมื่อคุณต้องการสร้างชื่อตัวแปรแบบไดนามิก
ตัวอย่างการปฏิบัติ:
#define GENERAR_VARIABLE(no) \
int var##no = no;
void setup() {
GENERAR_VARIABLE(3); // crea int var3 = 3
}
คำเตือนที่สำคัญ: ตัวดำเนินการนี้ไม่สามารถใช้งานร่วมกับบอร์ด Arduino ได้ทุกรุ่น ตัวอย่างเช่น มันอาจจะทำงานได้ดีกับ Uno หรือ Esplora แต่ล้มเหลวกับ Mega นอกจากนี้ คุณไม่สามารถสร้างแมโครแบบซ้อนภายในแมโครอื่นโดยใช้ ## โดยตรงได้
มาโครและการบันทึกหน่วยความจำ
ข้อได้เปรียบหลักประการหนึ่งของการใช้แมโครใน Arduino คือ การประหยัดแรม- Arduino มีความจุจำกัด ดังนั้นการโหลดสตริงข้อความโดยตรงลงใน RAM อาจกลายเป็นปัญหาสำคัญได้
เทคนิคขั้นสูงในการหลีกเลี่ยงสิ่งนี้เกี่ยวข้องกับการใช้ บังคับแบบอินไลน์ และโหลดสตริงจากหน่วยความจำโปรแกรม (PROGMEM):
#include <HardwareSerial.h>
#define MYSERIAL Serial
#define FORCE_INLINE __attribute__((always_inline)) inline
FORCE_INLINE void printFromFlash(const char *str) {
char ch = pgm_read_byte(str);
while (ch) {
MYSERIAL.write(ch);
ch = pgm_read_byte(++str);
}
}
#define SERIAL_LOG(x) (MYSERIAL.print(x))
#define SERIAL_LOGLN(x) (MYSERIAL.println(x))
การใช้แมโครเหล่านี้สามารถสร้างความแตกต่างระหว่างโปรเจ็กต์ที่ทำงานได้หรือไม่ โดยเฉพาะในแอปพลิเคชันที่มีจอแสดงผลหรือเซ็นเซอร์หลายตัว
มาโครรวมกับฟังก์ชั่น
นอกจากนี้ มาโครยังอำนวยความสะดวกในการเรียกฟังก์ชันแบบไดนามิก โดยอิงตามประเภทที่ส่งผ่านเป็นพารามิเตอร์ ตัวอย่างที่ชัดเจนและชัดเจนคือ:
#define FUNC_LENTA(tipo) \
{ funcion_##tipo##_lenta(); }
#define FUNC_RAPIDA(tipo) \
{ funcion_##tipo##_rapida(); }
void funcion_caminar_lenta() {
Serial.println("Andando despacio");
}
void funcion_caminar_rapida() {
Serial.println("Andando rápido");
}
void setup() {
Serial.begin(9600);
FUNC_LENTA(caminar);
}
void loop() {
FUNC_RAPIDA(caminar);
}
ต้องขอบคุณตัวดำเนินการ ## และแมโคร เราจึงหลีกเลี่ยงการทำซ้ำโครงสร้างและรวมตรรกะแบบไดนามิกให้เป็นศูนย์กลางได้.
มาโครพร้อมพารามิเตอร์เอาท์พุต
นอกจากนี้ยังสามารถใช้แมโครเพื่อรวมวัตถุขนาดเล็กหรือการแปลงได้:
#define BOOL_OUT() (bool){false}
#define NUM_OUT(a,b) (float){a+b}
#define STR_OUT(msg) (String){msg}
void loop() {
Serial.println(BOOL_OUT());
Serial.println(NUM_OUT(1.2, 3.4));
Serial.println(STR_OUT("Mensaje"));
}
แนวทางปฏิบัติที่ดีและข้อควรระวังในการใช้แมโคร
การใช้แมโครมากเกินไปหรือประมาทอาจทำให้เกิด ข้อผิดพลาดที่แก้ไขได้ยาก- ตัวอย่างเช่น การแทนที่ที่ไม่ถูกต้องหรือการกำหนดชื่อที่ขัดแย้งกับชื่อในไลบรารีภายนอก
กฎพื้นฐานบางประการเพื่อหลีกเลี่ยงปัญหา:
- หลีกเลี่ยงการเว้นวรรคหรือแบ่งบรรทัดที่ไม่จำเป็น ภายในมหภาค
- ไม่รวมความคิดเห็น ภายในแมโครที่ซับซ้อนซึ่งใช้หลายบรรทัด
- ใช้ชื่อที่ไม่ซ้ำใคร หรือมีคำนำหน้า (เช่น ชื่อโครงการ) เพื่อหลีกเลี่ยงความขัดแย้ง
- แทนที่แมโครด้วยค่าคงที่หรือฟังก์ชันจริง เมื่อใดก็ตามที่เป็นไปได้ C++ สมัยใหม่มีทางเลือกที่สะอาดและปลอดภัยยิ่งขึ้น
ในทางกลับกัน การใช้แมโครมากเกินไปอาจทำให้ความชัดเจนของโค้ดลดลง เป้าหมายควรเป็นการปรับปรุงประสิทธิภาพและการสร้างโมดูลโดยไม่กระทบต่อความสามารถในการบำรุงรักษา
คำสั่งเงื่อนไขและการคอมไพล์แบบปรับตัว
ฟังก์ชันการใช้งานที่ใช้งานได้จริงที่สุดอย่างหนึ่งในโครงการที่ปรับขนาดได้คือการใช้แมโครเพื่อ สร้างโค้ดตามเงื่อนไขสิ่งที่มีประโยชน์มากเมื่อคุณต้องการให้ภาพร่างแบบเดียวกันใช้งานบนบอร์ดที่แตกต่างกัน
ตัวอย่างทั่วไป:
#ifdef ARDUINO_MEGA
#define LEDPIN 53
#else
#define LEDPIN 13
#endif
ยังมีประโยชน์ในการควบคุมการดีบักหรือการแสดงข้อความคอมไพเลอร์ด้วย ข้อความ #pragma หรือแม้กระทั่งสร้างข้อผิดพลาดภายใต้เงื่อนไขบางประการด้วย #ข้อผิดพลาด.
มาโครคอมไพเลอร์ภายใน
พรีโปรเซสเซอร์ GCC สำหรับ AVR (ใช้ใน Arduino) ประกอบด้วยหลาย ๆ แมโครพิเศษที่ให้ข้อมูลระบบมีประโยชน์มากในระหว่างการพัฒนา:
- __ไลน์__: หมายเลขบรรทัดปัจจุบัน.
- __ไฟล์__: ชื่อของไฟล์ปัจจุบัน
- __เวลา__และ__วันที่__: เวลาและวันที่รวบรวม
- __ฟังก์ชัน__: ชื่อของฟังก์ชั่นปัจจุบัน
ช่วยให้สามารถควบคุมเวอร์ชัน สร้างโครงสร้างบันทึก และอำนวยความสะดวกในการบำรุงรักษาและการติดตามข้อผิดพลาดโดยไม่รบกวนโค้ดหลัก
มาโครเป็นวิธีที่ทรงพลังและยืดหยุ่นในการจัดโครงสร้างโครงการ Arduino มันอนุญาตให้คุณกำหนดค่าคงที่ บันทึกความทรงจำ, ปรับเปลี่ยนโค้ด ขึ้นอยู่กับสภาพแวดล้อมการดำเนินการ และสร้างบล็อกที่นำกลับมาใช้ใหม่ได้โดยไม่ต้องทำซ้ำบรรทัด แน่นอนว่าต้องมีวินัย ความชัดเจน และความรู้เพื่อหลีกเลี่ยงข้อผิดพลาดเล็กๆ น้อยๆ หรือการสูญเสียความสามารถในการอ่าน เมื่อใช้ได้อย่างถูกต้องแล้ว ถือเป็นทรัพย์สินอันล้ำค่าสำหรับนักพัฒนาระดับกลางและขั้นสูง