การเขียนโปรแกรม C18 แบบเป็นโครงสร้าง

วันที่โพสต์: Apr 19, 2012 12:31:19 PM

การเขียนโปรแกรม C18 แบบเป็นโครงสร้าง    

โดยทั่วไปแล้ว ถ้าโปรเจคไมโครคอนโทรลเลอร์ของเราไม่ได้มีขนาดใหญ่มากนัก หรือว่าเป็นงานเฉพาะที่ทำครั้งเดียว ไม่ได้ต้องทำกันบ่อย เรามักจะเขียนโปรแกรมของเราให้มีเพียงไฟล์ main.c เพียงไฟล์เดียวแล้วรวมโค๊ดทุกอย่างไว้ใน main.c แต่ในกรณีที่เราต้องการที่จะสร้างโปรเจคของเราให้มีการแยก header file (*.h) และ Source file (*.c)  ออกจากกัน เพื่อประโยชน์ในการแก้ไข หรือเพื่อนำไปใช้กับโปรเจคอื่นๆ ได้ เราจะต้องสร้างและทำการเขียนโปรแกรมแยกออกเป็นโครงสร้าง (Structure Programming) วันนี้เราจะมาเรียนรู้การเขียนโปรแกรมแบบเป็นโครงสร้าง ผ่านตัวอย่างง่ายๆ ต่อไปนี้ 

ตัวอย่างนี้ จะเป็นโปรเจคง่ายๆ เป็นการแสดงไฟติดดับสลับกัน โดยผ่านการกดปุ่ม button 1 ตัว เพื่อเป็นตัวทำหน้าที่ให้หลอด LED ติดดับสลับกัน 

เครื่องมือ 

-MPLAB V8.83 หรือสูงกว่า 

-C18 Complier v3.35 หรือ สูงกว่า

-PIC18F458 

สร้างโปรเจคด้วย MPLAB โดยเปิดโปรแกรม MPLAB ขึ้นมา แล้วไปที่เมนู Project>ProjectWizard

ขั้นที่1 - เลือก PIC18F458

ขั้นที่2 - เลือก C18 complier 

ขั้นที่3 - ตั้งชื่อโปรเจคของเรา Project.mcp

ขั้นที่4 - เพิ่มไฟล์ C:\MCC18\lkr\18f458_g.lkr (บางเวอร์ชั่น 18f458.lkr) เข้าไปในโปรเจค

ขั้นที่5 - เสร็จสมบูรณ์ กด Finish 

จะได้โปรเจคว่างเปล่า หน้าตาโปรแกรม (คล้ายๆดังรูป)

อันดับแรกเลย การที่เราจะเขียนโปรแกรมให้ไมโครคอนโทรลเลอร์ของเราทำงานได้นั้น จะต้องมีการตั้งค่าเริ่มต้น หรือกำหนดค่าเริ่มต้น เช่น กำหนดหน้าที่การทำงานของแต่ละขาของไมโครคอนโทรลเลอร์ ว่าจะให้ทำงานในโหมด เอาท์พุต หรือว่า อินพุต และการกำหนดค่าเริ่มต้นของความถี่ในการทำงาน และการเปิด ปิด โหมดต่างๆ ที่เกี่ยวข้องกับงานของเรา ในที่นี้ เราจะมาสร้างไฟล์ที่ทำหน้าที่เป็นการกำหนดค่าเริ่มต้นให้โปรเจคของเรา เราจะให้ไฟล์ชื่อ system.h เก็บค่าเริ่มต้นเหล่านั้น 

ไปที่เมนู File>New สร้างไฟล์เปล่าๆ ขึ้นมา แล้ว เขียนโปรแกรม เพื่อกำหนดค่าเริ่มต้นให้กับไมโครคอนโทรลเลอร์ ในที่นี้ โค๊ดที่เราสร้าง จะเป็นโค๊ดที่อยู่ข้างนอก void main(void) ซึ่งในตัวอย่างนี้ ค่าที่สามารถสร้างได้ นอก void main(void) ก็คือ มาโคร #pragma config นั่นเอง เพราะฉะนั้นจะได้ โค๊ด ภายในไฟล์ system.h ดังนี้ 

#pragma config OSC = HS,OSCS = OFF

#pragma config PWRT = OFF

#pragma config BOR = OFF

#pragma config WDT = OFF

#pragma config LVP = OFF

หมายเหตุ : ในไฟล์ต่างๆ พยายามเคาะ enter บรรทัดสุดท้าย ให้ว่างๆ ไว้ สักบรรทัดเป็นอย่างน้อย เพราะอาจจะทำให้เรา Complie ไม่ผ่าน แล้วหาไม่เจอ ตรงนี้ให้ระวังด้วยครับ 

ทำการบันทึกไฟล์ system.h ไว้ในโฟลเดอร์เดียวกันกับที่เราสร้างโปรเจค (ไว้ที่เดียวกันกับไฟล์ Project.mcp นั่นแหละ) 

ที่เมนู File>New สร้างไฟล์เปล่าๆ ขึ้นมา เราจะให้ไฟล์นี้เป็นไฟล์ ที่ฟังก์ชั่น main( ) เริ่มต้นทำงาน โดยเขียนโค๊ดเริ่มต้นดังนี้ 

#include <p18cxxx.h>

#include "system.h"

void main(void)

{

while(1)

{


}

}

จากนั้นบันทึกไว้เป็นไฟล์ main.c (เพื่อสื่อความหมายว่า main ฟังก์ชั่น อยู่ที่ไฟล์นี้) จุดสังเกต เราจะพบว่าในไฟล์ main.c นี้ จะมีการ  นำไฟล์ system.h ( ดัวยคำสั่ง #include "system.h" )เข้ามาร่วมในไฟล์ main.c ด้วย นั่นหมายความว่า โค๊ดที่เราเขียนไว้ที่ไฟล์ system.h จะถูกนำมาแปะไว้ที่ไฟล์ main.c ก่อนขึ้นฟังก์ชั่น void main(void) และอยู่หลัง #include <p18cxxx.h>

ที่ Source Files ให้ทำการ Add File main.c เข้าไป แล้วให้ทำการ build project ดูก่อน เพื่อให้มั่นใจว่า ไม่ได้เกิด Error ขึ้นในขั้นตอนนี้ ถ้าทุกอย่างถูกต้อง เพื่อนๆ จะต้องได้คำว่า BUILD SUCCEEDED ที่หน้าต่าง build แท๊ป output (ถ้าเกิด BUILD FAILED ให้ทำการแก้ไข ตาม บรรทัดที่แจ้งเตือน) 

ถ้าทุกอย่างเรียบร้อย เราจะมาเริ่มทำการเขียนโค๊ดไฟกระพริบ ก่อน โดยเราจะให้ ขา PB0 ทำหน้าที่เป็นเอาท์พุต และให้หลอด LED ที่ต่ออยู่ที่ขานี้ กระพริบติดดับสลับกัน เมื่อเราได้แนวคิดแล้ว ให้ทำการสร้างไฟล์ขึ้นมา เพื่อทำหน้าที่ควบคุมการติดดับของหลอด LED  และเพื่อให้สื่อความหมายของไฟล์นี้ เราจะตั้งชื่อไฟล์ว่า LED ให้เพื่อนๆ สร้างไฟล์ LED.c ขึ้นมาโดยไปที่เมนู File>New แล้วเขียนโค๊ด 

#include <p18cxxx.h>

void InitialiseLED(void)

{

TRISB = TRISB & 0xFE;  // PB0 as output pin

}

void LED_Off(void)

{

PORTB &= (~0x01); // Turn off LED

}

void LED_On(void)

{

PORTB |= 0x01;  // Turn on LED

}

จากนั้น เราจะทำการสร้างไฟล์ ที่เป็น header file ชื่อ LED.h ของไฟล์ LED.c เพื่อทำการประกาศฟังก์ชั่นโปรโตไทป์ ก่อนการเรียกใช้งานฟังก์ชั่น ที่เมนู File>New สร้างไฟล์ขึ้นมา แล้วทำการ ก๊อปปี้ชื่อฟังก์ชั่นไปไว้ในไฟล์ LED.h โดยมีคำว่า extern นำหน้า ซึ่งบอกให้คอมไพล์เลอร์ทราบว่า ชื่อฟังก์ชั่นนี้อยู่ที่ไฟล์อื่น ไม่ได้อยู่ที่ไฟล์ LED.h จริง ฉะนั้นในไฟล์ LED.h จะต้องเขียนโค๊ดเป็นดังนี้

extern void InitialiseLED(void);

extern void LED_Off(void);

extern void LED_On(void);

ทำการบันทึกไฟล์ LED.h จากนั้นกลับไปที่ไฟล์ main.c ทำการเพิ่ม #include "LED.h" ไว้ที่หัวของไฟล์ เพื่อบอกให้คอมไพล์เลอร์ทราบว่าให้นำ LED.h เข้ามาร่วมด้วยที่ไฟล์ main.c และทำการเรียกฟังก์ชั่นที่เราสร้างไว้ที่ไฟล์ LED.c ซึ่งได้แก่ฟังก์ชั่น 

InitialiseLED();

LED_Off();

LED_On();

เข้ามาทำงานในไฟล์ main.c ดังนั้นที่ไฟล์ main.c เราจะได้ว่า 

#include <p18cxxx.h>

#include "system.h"

#include "LED.h"

void main(void)

{

InitialiseLED();

LED_Off();

LED_On();


while(1)

{


}

}

ลองทำการกดปุ่ม Build All เพื่อนๆ จะพบว่า เกิด Error ขึ้น เพราะ Linker ไม่สามารถที่จะหาตัวฟังก์ชั่นที่เราเรียกมาใช้ได้ เราจะต้องทำการนำไฟล์ที่เป็นที่อยู่ของฟังก์ชั่นดังกล่าวเข้ามาในโปรเจคเราซะก่อน โดยการทำการคลิกขวาที่ Source Files แล้ว Add File แล้ว เลือกไฟล์ LED.c กด OK เราจะได้ Source File เพิ่ม ดังภาพ แล้วลอง Build All อีกครั้ง จะได้ BUILD SUCCEEDED แสดงว่าทุกอย่างถูกต้องแล้ว 

หากเราพิจารณา โดยที่ยังไม่ต้องเบิร์นโค๊ดเรา ลงไปที่ไมโครคอนโทรลเลอร์ เราก็อาจจะพิจารณาได้ว่า หลอด LED ที่ต่ออยู่ที่ขา PB0 ของไมโครคอนโทรลเลอร์ จะติดค้างอยู่ ที่ PB0 หากเราต้องการให้เกิดการติดดับสลับกัน เราก็แค่ให้ฟังก์ชั่น LED_On() และ LED_Off() วางสลับกัน ก็น่าจะทำให้หลอดไฟ LED ติดดับสลับกันได้ แต่ ด้วยความถี่ที่สูงมาก ทำให้สายตาของเราไม่สามารถแยกความแตกต่างได้ สิ่งที่เราต้องการก็คือ ควรจะมีการหน่วงเวลา เพื่อทำให้สายตาของเราแยกแยะความแตกต่างได้ ดังนั้น เราจึงควรที่จะสร้างฟังก์ชั่นหน่วงเวลาขึ้นมา หรือที่นิยมเรียกกันว่า delay ขึ้นมาใช้งาน ในการสร้างฟังก์ชั่น delay เราก็จะทำคล้ายๆ กับที่เราสร้างฟังก์ชั่น LED เหมือนกัน ให้เพื่อนๆ ลองสร้างไฟล์ delay.h และ delay.c ขึ้นมา (เรื่องการคำนวณ delay สามารถดูได้จากตอนที่ผ่านไปแล้ว ได้ครับ คลิก

โค๊ดของ delay.c

#include <delays.h>

void delay_ms(unsigned int ms)

{

    while(ms--)

    {

        Delay1KTCYx(5); 

    }

}

และ delay.h

extern void delay_ms(unsigned int ms);

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

#include <p18cxxx.h>

void InitialiseButton(void)

{

    TRISB = TRISB & 0xFF;  // PB1 as input

}

char PushButton(void)

{

    if(PORTB & 0x02){

        return 1;

    }else{

        return 0;    

    }

}

และเช่นเดิม ผมต้องแยกไฟล์ button.h เพื่อเก็บฟังก์ชั่นโปรโตไทป์ ออกมา จะได้ไฟล์ button.h ที่มีโค๊ดดังนี้

extern void InitialiseButton(void);

extern char PushButton(void);

แล้วก็อย่าลืม เพิ่มไฟล์ *.h เข้าไปที่หัวไฟล์ของ main.c ในขณะที่ *.c เราจะ add file เข้าที่ source files ดังภาพ 

ทำการ Build All โปรเจคอีกครั้ง ต้อง BUILD SUCCEEDED ถ้าไม่ได้ ให้ทำการแก้ไข อย่าลืมเรื่องการเคาะ Enter ไว้ที่บรรทัดสุดท้าย ในแต่ละไฟล์ด้วย (สงสัยจะเป็น bug ของ IDE หรือเปล่าไม่แน่ใจ )

ทำการต่อวงจรในโปรแกรมจำลองการทำงาน Proteus โดยให้ Processor Clock Frequency มีค่าเท่ากับ 20MHz ทดลองกดปุ่ม ดูผลการทำงานของโปรแกรม 

ปล. บางรูปอาจจะไม่สัมพันธ์กับคำบรรยายในบางรูป แต่ขอให้ทำตามคำแนะนำที่ให้ไว้ครับ