เขียนโปรแกรม LED กระพริบด้วย MPLAB Harmony Framework

MPLAB Harmony Framework เป็น Framework หรือ Library สำหรับเขียนโปรแกรมบน Microcontroller 32-bit ของ Microchip รองรับ PIC32MZ, PIC32MX ตัว Framework มี Library ให้ใช้งานหลากหลาย อาทิเช่น USB, TCP/IP stack, Graphics, Wifi, Audio decoder เป็นต้น ข้อดีของ Harmony คือการรวม Library หลายๆตัวไว้ในโปรเจคเดียวกัน สามารถทำได้ง่ายดายและสะดวก หากใครเคยใช้ Microchip Library for Application หรือ MLA การรวม Library ต่างๆไว้ในโปรเจคเดียวกันทำได้ลำบากมาก ผู้ใช้ต้องเพิ่มไฟล์ที่ต้องใช้เข้าไปในโปรเจคเอง ต้องเขียนไฟล์ config สำหรับ Library แต่ละตัวเอง และการทำงานของแต่ละ Library ก็ต้องมีการรอการทำงานกัน ไม่ได้เขียนเป็น state machine แบบ Harmony ข้อดีอีกอย่างของ Harmony คือมี Help ที่ค่อนข้างละเอียดหากสงสัยหรือไม่เข้าใจอะไรสามารถค้นหาในไฟล์ Help ของ Harmony ได้ ผมเคยทดลองใช้งาน STM32CubeF4 ซึ่งการใช้งานก็จะคล้ายๆ Harmony แต่ STM32CubeF4 มี Help ที่ไม่ค่อยละเอียดเท่าไหร่ หากสงสัยในฟังก์ชั่นอะไรในไฟล์ Help ก็ยังมีให้อ่านไม่ค่อยละเอียดครับ

กล่าวถึงข้อดีของ MPLAB Harmony Framework ไปแล้ว ต่อไปขอกล่าวถึงข้อเสียของ Harmony ที่ผมเคยเจอ และถามหลายๆคนแล้วก็ถือว่าเป็นข้อเสียหลักของ Harmony เลยก็คือ การเขียนโปรแกรมแบบ State machine ซึ่งหากใครเคยเขียนโปรแกรมบน Microcontroller ของ Microchip มาแล้วทั้ง PIC16, PIC18, PIC24, PIC32 โดยใช้ภาษาซี หรือ Library แบบเดิม แล้วมาเขียนบน Harmony จะงงเอามากๆ ครั้งแรกที่ผมลองไล่ดูโปรแกรมตัวอย่างของ Harmony ผมไล่ไม่ถูกเลยจริงๆว่าจะเริ่มยังไง จะเขียนตรงไหน จะแก้ไขโปรแกรมตรงไหน ยิ่งถ้าใครไม่ถนัดเรื่อง Structure, Union, Pointer, Function pointer ยิ่งจะงงไปกันใหญ่ เพราะ Harmony ใช้การเขียนโปรแกรมแบบนี้เยอะมาก

วันนี้ผมเลยจะมาเขียนบทความเริ่มต้นการเขียนโปรแกรม LED กระพริบด้วย MPLAB Harmony Framework เอามาให้คนที่สนใจจะเขียนโปรแกรมด้วย Harmony ได้เรียนรู้และศึกษาครับ

มาเริ่มกันที่โปรแกรมที่ต้องดาวน์โหลดมาใช้ในการเขียนโปรแแกรมด้วย Harmony ก็จะมีโปรแกรม MPLAB X IDE, MPLAB XC32 เป็น C Compiler และ MPLAB Harmony Framework ในบทความนี้จะใช้ Harmony เวอร์ชั่น 2.02.00b ซึ่งเป็นเวอร์ชั่นเบต้า โปรแกรมทั้งหมดสามารถดาวน์โหลดได้ฟรีบนเว็บไซต์ของ Microchip ครับ

การติดตั้ง Plugin MPLAB Harmony Configurator
ในกรณีที่ใช้ Harmony เวอร์ชั่นเบต้า เราต้องติดตั้ง Plugin เองโดยเข้าไปที่เมนู Tools -> Plugins

แล้วเลือกที่ Tab Downloaded คลิกที่ปุ่ม Add Plugins… แล้ว Browse ไปหาไฟล์ D:\microchip\harmony\v2_02_00b\utilities\mhc\com-microchip-mplab-modules-mhc.nbm เสร็จแล้วก็คลิกที่ปุ่ม Open

หลังจากนั้นก็คลิกที่ปุ่ม Install

ต่อไปก็ให้คลิกที่ยอมรับ License agreement แล้วคลิกที่ปุ่ม Install แล้วก็รอจนกว่าการติดตั้งจะเสร็จ หลังจากนั้นก็ให้ Restart MPLAB X IDE เพื่อให้การติดตั้ง Plugin สมบูรณ์ก็เป็นอันว่าติดตั้งสำเร็จแล้วครับ

หลังจากที่ติดตั้ง Plugin MPLAB Harmony Configurator เสร็จแล้ว ก็มาเริ่มสร้างโปรเจคสำหรับเขียนโปรแกรมด้วย Harmony กัน โดยเริ่มจากเข้าไปเมนู File -> New Project…

แล้วเลือกที่ Microchip Embedded และ 32-bit MPLAB Harmony Project เสร็จแล้วให้คลิกที่ปุ่ม Next

ขั้นตอนต่อไปจะเป็นการตั้งค่าตำแหน่ง Directory ของ MPLAB Harmony Framework โดยคลิก Browse ไปหา Path ของ Harmony ที่ทำการติดตั้งไว้ แล้วก็ตั้งชื่อโปรเจค เลือก MCU ที่จะใช้แล้วคลิกที่ปุ่ม Finish

หลังจากที่คลิกปุ่ม Finish แล้ว โปรแกรม Harmony Configurator ก็จะแสดงขึ้นมา ในตัวอย่างนี้ยังไม่ใช้ BSP หรือ Board Support Package โดยเราจะตั้งค่าต่างๆเอง โดยเริ่มจากการตั้งค่า Clock ให้กับ MCU ให้ตั้งค่า POSC ให้ตรงกับ Hardware ที่ใช้ เลือก POSCMOD เป็น EC ตั้งค่า UPLLFSEL ให้ตรงกับความถี่ที่ใช้ เลือก PLLICLK เป็น POSC แล้วคลิกที่ปุ่ม Auto-Calculate หลังจากนั้นจะมีหน้าต่างให้ใส่ความถี่ของ MCU ก็ป้อนความถี่ที่ MCU สามารถทำงานได้คือ 200MHz แล้วคลิกที่ปุ่ม Apply หลังจากนั้นโปรแกรมจะทำการคำนวณค่าต่างๆของ PLL ให้เองครับ

ต่อไปก็คือเป็นการตั้งค่า Configuration bit ของ MCU โดยคลิกที่ Tab Options -> Device & Project Configuration เลือก PIC32MZ1024EFE100 Device configuration แล้วก็เข้าไปตั้งค่าต่างๆให้ตรงกับ Hardware ที่ใช้ เช่นหากต่อ ICD3 กับ PGEx2 ก็ให้ตั้งค่า ICE/ICD Comm Channel Select ให้เป็น ICS_PGx2

ต่อไปก็ไปตั้งค่า System timer เพื่อใช้งาน System delay โดยเข้าไปที่ Tab Options -> Harmony Framework Configuration -> System Service -> Timer แล้วทำเครื่องหมายที่ Use Timer System Service

ขั้นตอนต่อไปคือการตั้งค่า Port ของ MCU ให้คลิกที่ Tab Pin Settings บนบอร์ดที่ใช้งาน LED จะต่อกับขา RD11 ให้เข้าไปตั้งค่า RD11 ให้เป็น Output และ LAT = Low ดังรูปด้านล่าง

เมื่อตั้งค่าต่างๆเสร็จเรียบร้อยแล้ว ให้คลิกที่ปุ่ม Generate Code เพื่อให้ MHC สร้างไฟล์ Source code เข้าไปในโปรเจค

หลังจาก Generate code เสร็จแล้ว ในหน้าต่าง Project จะมีโฟล์เดอร์และไฟล์ต่างๆเพิ่มเข้า ไฟล์ที่เราต้องเขียนโปรแกรมเพิ่มเข้าไปจะอยู่ที่ไฟล์ app.c และ app.h โดยที่ Enum สำหรับกำหนด statement ของโปรแกรมและตัวแปล Global จะประกาศไว้ที่ไฟล์ app.h ส่วนไฟล์ app.c ก็จะเป็นไฟล์ที่ใช้เขียนโปรแกรมเพิ่มเติมครับ

มาเริ่มเขียนโปรแกรมไฟกระพริบโดยใช้ MPLAB Harmony Framework กันเลย โดยเริ่มจากการเปิดไฟล์ app.h ขึ้นมา แล้วเพิ่ม statement ของโปรแกรมเข้าไปดังนี้
โดย Statement ที่เพิ่มเข้ามาจะเป็น APP_STATE_TOGGLE_LED และ APP_STATE_WAIT_DELAY

typedef enum
{
	 /* Application's state machine's initial state. */
	 APP_STATE_INIT=0,
	 APP_STATE_TOGGLE_LED,
	 APP_STATE_WAIT_DELAY,

	 /* TODO: Define states used by the application state machine. */

} APP_STATES;

ในส่วนของ Structure สำหรับประกาศตัวแปลแบบ Global ให้เพิ่มตัวแปล SYS_TMR_HANDLE tmrHandle; เข้าไป

typedef struct
{
	/* The application's current state */
	APP_STATES state;
	SYS_TMR_HANDLE tmrHandle;
} APP_DATA;

หลังจากนั้นให้เปิดไฟล์ app.c ขึ้นมา แล้วแก้ไขและเพิ่ม Source code ในฟังก์ชั่น APP_Tasks() ดังนี้

void APP_Tasks ( void )
{

    /* Check the application's current state. */
    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:
        {
            appData.state = APP_STATE_TOGGLE_LED;
            break;
        }

        case APP_STATE_TOGGLE_LED:
        {
            SYS_PORTS_PinToggle(PORTS_ID_0, PORT_CHANNEL_D, PORTS_BIT_POS_11);
            appData.tmrHandle = SYS_TMR_DelayMS(500);
            appData.state = APP_STATE_WAIT_DELAY;
            break;
        }
        
        case APP_STATE_WAIT_DELAY:
        {
            if(SYS_TMR_DelayStatusGet(appData.tmrHandle) == true)
            {
                appData.state = APP_STATE_TOGGLE_LED;
            }
            break;
        }
        
        /* The default state should never be executed. */
        default:
        {
            /* TODO: Handle error in application's state machine. */
            break;
        }
    }
}

การทำงานของโปรแกรมที่ Harmony สร้างขึ้นมาจะเริ่มต้นที่ไฟล์ main.c โดยเริ่มที่ SYS_Initialize ( NULL ); ฟังก์ชั่นนี้จะเป็นการ Initialize ระบบต่างๆของ Harmony รวมถึงฟังก์ชั่น APP_Initialize(); ที่เราสามารถเขียนเพิ่มได้ว่าจะให้มีการ Initialize อะไรบ้าง หลังจาก Initialize เสร็จแล้ว MCU ก็จะเข้าไปทำงานในฟังก์ชั่น SYS_Tasks ( ); ในฟังก์ชั่นนี้ก็จะเรียกฟังก์ชั่น APP_Tasks(); ที่เราเขียนโปรแกรมเพิ่มเข้าไปอีกทีหนึ่ง

  • การทำงานของโปรแกรมในฟังก์ชั่น APP_Tasks(); จะเริ่มที่ State APP_STATE_INIT ใน state นี้ไม่ได้มีการเขียนโปรแกรมอะไรเพิ่มเข้าไป มีเพียวแค่เปลี่ยน state การทำงานไปเป็น APP_STATE_TOGGLE_LED
  • ในส่วนของ state APP_STATE_TOGGLE_LED จะสั่งให้ LED Toggle ด้วยฟังก์ชั่น SYS_PORTS_PinToggle(PORTS_ID_0, PORT_CHANNEL_D, PORTS_BIT_POS_11); Parameter ที่ส่งไปก็มี PORTS_ID_0 คือ ID ของพอร์ทบน MCU มีแค่ ID เดียวคือ 0, PORT_CHANNEL_D คือพอร์ท D, PORTS_BIT_POS_11 คือตำแหน่งบิทที่ 11 หรือ RD11 ที่ต่ออยู่กับ LED นั่นเอง ฟังก์ชั่นอื่นๆที่เกี่ยวของกับ I/O Port สามารถอ่านดูรายละเอียดเพิ่มเติมในไฟล์ Help ของ Harmony ครับ หลังจากที่สั่งให้ LED Toogle แล้ว ต่อไปก็คือการเริ่มต้นหน่วงเวลาโดยใช้ฟังก์ชั่น appData.tmrHandle = SYS_TMR_DelayMS(500); ตัวแปล appData.tmrHandle จะเป็นตัวเก็บ Handle จากฟังก์ชั่น SYS_TMR_DelayMS(500); เอาไว้ เพื่อตรวจสอบว่าการหน่วงเวลาครบแล้วหรือยัง เสร็จแล้ว MCU ก็จะเปลี่ยน state ไปเป็น APP_STATE_WAIT_DELAY
  • ที่ state APP_STATE_WAIT_DELAY MCU จะตรวจสอบว่าการหน่วงเวลาจาก state ก่อนหน้านี้ครบเวลาแล้วหรือยังโดยใช้ฟังก์ชั่น if(SYS_TMR_DelayStatusGet(appData.tmrHandle) == true) หากหน่วงเวลาจนครบแล้ว จะ Return ค่ากลับมาเป็น 1 แต่ถ้ายังไม่ครบเวลาก็จะส่งค่ากลับมาเป็น 0 หลังจากหน่วงเวลาครบแล้ว MCU ก็จะเปลี่ยน state เป็น APP_STATE_TOGGLE_LED เพื่อ Toggle LED อีกครั้ง การทำงานของโปรแกรมก็จะวนลูปอย่างนี้ไปตลอดเวลาครับ

จากตัวอย่างการเขียนโปรแกรมโดยใช้ State machine จะเห็นว่าเราไม่ต้องรอให้หน่วงเวลาจนครบเวลาแล้วค่อยไปทำงานอย่างอื่น การหน่วงเวลาเราสามารถตรวจสอบได้ว่าครบเวลาแล้วหรือยัง หากยังไม่ครบเราก็สามารถไปทำงานอื่นๆได้ โดยไม่ต้องเสียเวลาไปกับการหน่วงเวลาครับ