แพลตฟอร์ม mbed 101 + H

Supachai Vorapojpisut
4 min readMar 15, 2020

--

เนื้อหาตอนที่ 2 นี้จะใช้บอร์ดทดลองจากชุด NUCLEO-64 Activity Kit มาเป็นตัวอย่างเรียนรู้กลุ่ม API สำหรับจัดการฮาร์ดแวร์ เนื้อหาน่าจะสอดคล้องกับการสอนวิชาไมโครคอนโทรลเลอร์ในหลายๆหลักสูตร ผมเองมีบอร์ดในกลุ่ม NUCLEO-64 อยู่หลายบอร์ด โดยเฉพาะบอร์ด NUCLEO-F401RE ที่เตรียมไว้สำหรับวิชาไมโครคอนโทรลเลอร์ที่ ม.ธรรมศาสตร์เอง ปัญหาพื้นฐานที่พบคือ นักศึกษาที่จะฝึกการเขียนโค้ดจัดการฮาร์ดแวร์ ต้องหาอุปกรณ์มาต่อกับบอร์ดทางเบรดบอร์ด ต้องเสียเวลาอยู่พอควร รวมทั้งมักเจอปัญหาของสายสัญญาณที่หลุดหรือต่อผิด ชุดทดลองนี้จึงเป็นคำตอบที่ลงตัวสำหรับคอร์สของผม

สถาปัตยกรรมไมโครคอนโทรลเลอร์ STM32F401RE

ไมโครคอนโทรลเลอร์รุ่น STM32F401RE จัดเป็นหน่วยประมวลผลประสิทธิภาพสูงทั้งในแง่ clock frequency (84 MHz) หน่วยความจำ (512 kB Flash และ 96 kB RAM) รวมทั้งมีขา GPIO อยู่ 50 ขา ซึ่งนอกจากใช้เป็น GPIO (อินพุตเอาต์พุตแบบดิจิตอล) แล้วยังสามารถเลือกให้เชื่อมต่อวงจรสนับสนุนภายในตัวชิพได้ กลุ่มของวงจรสนับสนุนในตัวชิพแบ่งออกเป็น 3 ส่วนคือ

  1. กลุ่ม control ได้แก่ กลุ่มของ timer ที่ใช้สร้างสัญญาณพัลส์เชิงเวลา เช่น สัญญาณ pulse-width modulation สำหรับควบคุมการจ่ายกำลัง
  2. กลุ่ม analog ได้แก่ ADC (analog-to-digital converter) สำหรับใช้วัดแรงดันไฟฟ้า
  3. กลุ่ม connectivity ได้แก่ พอร์ตสื่อสารอนุกรมแบบต่างๆ เช่น UART, SPI และ I2C โดยตระกูล STM32F4 ยังรองรับการเชื่อมต่อไปยัง USB และ SD-card ด้วย

ตัวบอร์ดทดลองในชุด NUCLEO-64 Activity Kit จะเปิดให้สามารถเรียนรู้ในหลายส่วน ได้แก่ LED (GPIO, PWM) ปุ่มกด (GPIO, external interrupt) เอ็นโค้ดเดอร์แบบก้านหมุน (external interrupt) จอ LCD (GPIO) เซ็นเซอร์ ADXL-335 (ADC) ไอซีนาฬิกา DS1307 (I2C) และ 8x8 dot-matrix LED (GPIO) เนื้อหาในครั้งนี้จะเริ่มจากส่วน GPIO และ external interrupt ก่อน โดยสาธิตด้วยโค้ดตัวอย่างที่ดัดแปลงจาก mbed-os-example-blinky

ปุ่มกดและ LED

API สำหรับเชื่อมต่อปุ่มกดและ LED

ผมขอเริ่มตัวอย่างแรกด้วยโค้ดตัวอย่างที่สาธิตการใช้ปุ่มกดใน 4 รูปแบบเพื่อควบคุมการแสดงผลแบบไล่สี LED จากแดงไปน้ำเงิน การศึกษาวงจรของบอร์ดทดลองพบว่าวงจรเชื่อมต่อของปุ่มกดที่ขา PA_7 ถึง PA_10 เป็นแบบ pull-up (กด = 0/ปล่อย = 1) และของ LED ที่ขา PC_10 ถึง PC_12 เป็นแบบ source (1 = ติด/0 = ดับ) ส่วน API ของ mbed ที่จะใช้ในโค้ดตัวอย่าง ได้แก่

  1. DigitalIn เป็นคลาสที่หุ้มการทำงานของอินพุตแบบดิจิตอล โดยประกาศ object ด้วยการระบุชื่อขาที่จะใช้ เช่น DigitalIn SW1(PA_7) คือการประกาศ object ชื่อ SW1 ที่ทำให้ขา PA_7 ทำงานเป็นอินพุตดิจิตอล
  2. DigitalOut เป็นคลาสที่หุ้มการทำงานของเอาต์พุตแบบดิจิตอล โดยประกาศ object ด้วยการระบุขา เช่น DigitalOut led(PC_10) คือการประกาศ object ชื่อ redLed ที่ทำให้ขา PC_10 ทำงานเป็นเอาต์พุตแบบดิจิตอล
  3. InterruptIn เป็นคลาสที่หุ้มการทำงานของ external interrupt โดยสามารถลงทะเบียนให้ทำงานตามขอบสัญญาณได้ 3 รูปแบบคือ ขอบขาขึ้น ขอบขาลง หรือทั้ง 2 ขอบ ทั้งนี้จะต้องกำหนดฟังก์ชันสำหรับเป็น interrupt handler ในตอนลงทะเบียนด้วยการเรียกเมธอด rise() และ/หรือ fall() ของ object

การอ้างขาสัญญาณในแพลตฟอร์ม mbed ทำได้หลายวิธีขึ้นอยู่กับบอร์ดและหัวต่อที่ใช้ เช่น ขาสัญญาณแอนะล็อกของบอร์ด NUCLEO-F401RE ที่ต่อกับแกน X ของเซ็นเซอร์ ADXL-335 สามารถอ้างได้ทั้งแบบ Arduino (ขา A0) และแบบชื่อขา STM32 (ขา PA_0) การปรับแก้โค้ดไปใช้กับชิพหน่วยประมวลผลที่ไม่มีบอร์ดที่รองรับด้วย mbed จึงทำได้ไม่ยากนัก

ชื่อและหน้าที่ของขาสัญญาณบนหัวต่อ CN6/CN8 (Arduino) และหัวต่อ CN7 (Morpho)

โค้ดตัวอย่างจะประกาศ object แบบ DigitalIn สำหรับปุ่มกด SW1/SW2 แบบ InterruptIn สำหรับปุ่มกด SW3/SW4 และแบบ DigitalOut สำหรับ LED ทั้ง 3 สี การทำงานจะวนไล่แสดงสีบน LED โดยขึ้นอยู่กับเงื่อนไขดังนี้ (1) ปุ่ม SW1 กดค้างเพื่อหยุด (2) ปุ่ม SW2 กดยาวกว่า 0.5 วินาทีเพื่อสลับสถานะหยุด/วนต่อ (3) สลับสถานะหยุด/วนต่อในขณะกดปุ่ม SW3 (4) สลับสถานะหยุด/วนต่อในขณะปล่อยปุ่ม SW4

#include "mbed.h"
#define WAIT_TIME 500 // หน่วยเป็นมิลลิวินาที
DigitalOut led1(LED1);
DigitalIn SW1(PA_7), SW2(PA_8);
InterruptIn SW3(PA_9), SW4(PA_10);
DigitalOut RED(PC_10), GREEN(PC_11), BLUE(PC_12);
bool cond1 = true, cond2 = true, cond3 = true, cond4 = true;void sw3Handler() {
cond3 = !cond3; // กดเพื่อสลับสถานะหยุด/วนต่อ
}
void sw4Handler() {
cond4 = !cond4; // ปล่อยเพื่อสลับสถานะหยุด/วนต่อ
}
int main() {
int count = 0;
SW3.fall(&sw3Handler);
SW4.rise(&sw4Handler);
while (true) {
cond1 = SW1; // กดค้างเพื่อค้าง
int sum = 0;
while (!SW2) {
sum++;
if (sum > 5) {
break;
}
thread_sleep_for(100);
}
if (sum >= 3) {
cond2 = !cond2; // กดยาว 0.5 วินาทีเพื่อสลับสถานะหยุด/วนต่อ
}
if (cond1 && cond2 && cond3 && cond4) {
count++; // อัพเดทรูปแบบ LED
}
led1 = !led1;
RED = count & (1 << 0);
GREEN = count & (1 << 1);
BLUE = count & (1 << 2);
thread_sleep_for(WAIT_TIME);
printf(“%d,%d,%d,%d\n”, cond1, cond2, cond3, cond4);
}
}

API ของ mbed จะเน้นการใช้ OOP มากกว่า Arduino ซึ่งการทำงานของฮาร์ดแวร์จะถูกหุ้มด้วยคลาส การจัดการฮาร์ดแวร์จึงเน้นที่การเรียกใช้ method ของ object เช่น SW1.fall() หรือใช้ operator overloading เช่น cond1 = SW1 โดยตัวกระทำ = จะไปเรียกใช้ read() อ่านค่า นอกจากนี้แพลตฟอร์ม mbed ยังรองรับ stdio สำหรับพิมพ์และรับข้อความด้วยฟังก์ชัน printf() และ scanf() บอร์ดในกลุ่ม NUCLEO-64 จะเป็นการใช้ UART (9600,8,n,1) ติดต่อผ่านส่วนเชื่อมต่อ ST-Link/V2 มายังคอมพิวเตอร์ โค้ดตัวอย่างได้มีการใช้คำสั่ง printf() ในช่วงท้าย loop จึงมีการพิมพ์ข้อความออกมาให้ดูได้ โดยเลือกจาก View > Serial Monitor ของ Mbed Studio

การดูข้อความทาง Serial Monitor

เอ็นโค้ดเดอร์และ LCD ตัวอักษร

API สำหรับเชื่อมต่อเอ็นโค้ดเดอร์และ LCD ตัวอักษร

ตัวอย่างที่สองจะสาธิตการเขียนโค้ดรับค่าปุ่มหมุนที่เป็นเอ็นโค้ดเดอร์แล้วไปแสดงบนจอ LCD แบบตัวอักษร 16x2 เพื่อแสดงถึงการเพิ่มไลบรารีที่นอกเหนือจาก mbed-os โค้ดตัวอย่างจะใช้คลาส InterruptIn เพื่อรับสัญญาณ PULSE_A และ PULSE_B ที่ต่อกับขา PC_0 และ PC_1 แล้วนับตัวเลขใน 2 ทิศทาง (+/-) แล้วนำค่าตัวเลขไปแสดงบนจอ LCD การเขียนโค้ดในส่วน LCD จะต้องเพิ่มไลบรารี TextLCD ซึ่งหุ้มขั้นตอนการส่งค่าข้อมูลแบบ 4 บิตให้กับไอซี HD44780 การเพิ่มไลบรารีใน Mbed Studio รวมไปถึงเครื่องมือพัฒนาอื่นๆจะใช้กลไก version control แบบ git ในการ clone repo เพื่อดึงส่วนโค้ดมารวมกับโปรแกรมของเรา

การเพิ่มไลบรารีใน Mbed Studio

ขั้นตอนการเพิ่มไลบรารีของ Mbed Studio เริ่มจากการเลือกเมนู View > Libraries เพื่อแสดงหน้าต่างย่อย Libraries ในส่วนผลลัพธ์ จากนั้นจึงกดปุ่ม + เพื่อเพิ่มไลบรารีด้วยการกรอกข้อมูล URL ของไลบรารี ในกรณีไลบรารี TextLCD จะมี URL อยู่ที่ https://os.mbed.com/users/simon/code/TextLCD/ จากนั้นจึงกดปุ่มยืนยันเพื่อเริ่มการดาวน์โหลดส่วนโค้ดมายังโปรแกรม

บอร์ดทดลองในชุด NUCLEO-64 Activity Kit ได้ต่อขา RW ของจอ LCD กับขา PB_1 ซึ่งแตกต่างจากเงื่อนไขของไลบรารี TextLCD ที่ขา RW ต้องต่อกับ GND เพื่อให้สามารถทำงานได้ทันที โค้ดตัวอย่างจึงไม่สามารถประกาศ object ของคลาส TextLCD ได้โดยตรงเหมือนคลาสอื่นๆ ผมจึงใช้การเรียกตัวกระทำ new เพื่อสร้าง object แบบ runtime หลังจากกำหนดค่าของขา PB_1 ให้เป็น LOW ก่อน ส่วนการอัพเดทการแสดงผล LCD ใช้การตรวจจับตำแหน่งและทิศทางการหมุนของเอ็นโค้ดเดอร์ด้วย external interrupt (หมุนตามเข็ม +1, หมุนทวนเข็ม -1) แล้วเปรียบเทียบว่ามีการเปลี่ยนแปลงหรือไม่

#include "mbed.h"
#include "TextLCD.h"
#define WAIT_TIME 500 //msecDigitalOut led1(LED1);
InterruptIn PULSE_A(PC_0), PULSE_B(PC_1);
DigitalOut rwPin(PB_1), ctrsPin(PB_8);
TextLCD* lcd;
int value = 0;void AchHandler() {
if (PULSE_B) {
value++;
}
}
void BchHandler() {
if (PULSE_A) {
value--;
}
}
int main() {
int past_value = value;
PULSE_A.fall(&AchHandler);
PULSE_B.fall(&BchHandler);
rwPin = 0;
ctrsPin = 1;
lcd = new TextLCD(PB_0, PB_2, PB_4, PB_5, PB_6, PB_7, TextLCD::LCD16x2);
lcd->printf(“Wait rotation”);
while (true) {
led1 = !led1;
if (value != past_value) {
lcd->cls();
lcd->printf(“Value: %d”, value);
past_value = value;
}
thread_sleep_for(WAIT_TIME);
}
}
ผลของโค้ดตัวอย่าง

อ้างอิง

  1. เอกสารข้อมูลทางเทคนิคและวงจรของชุดทดลอง ดาวน์โหลดจาก https://inex.co.th/shop/education-board/arm/nucleo-64-activity-kit.html
  2. “แพลตฟอร์ม mbed 101” ดูออนไลน์ https://medium.com/@vsupacha_90388/แพลตฟอร์ม-mbed-101-f62beed36adb

--

--

Supachai Vorapojpisut
Supachai Vorapojpisut

Written by Supachai Vorapojpisut

Assistant Professor at Thammasat University

No responses yet