เมนู

วันอังคารที่ 5 กรกฎาคม พ.ศ. 2559

Pinguino สื่อสารผ่าน USB และ Com Port

               บทความนี้จะเขียนถึงการสื่อสารระหว่าง Pinguino บอร์ดกับคอมพิวเตอร์ โดยเรามีตัวเลือกว่าจะใช้ USB โมดูลในตัว PIC18F45K50 emulate เป็น com port สื่อสารกับคอมพิวเตอร์ผ่านทางโปรแกรมเทอร์มินอลต่างๆ  หรือจะใช้โมดูล USART ในตัวไมโครคอนโทลเลอร์สื่อสารแทนก็ทำได้ นอกจากนี้หากต้องการ USART มากกว่าหนึ่งโมดูลก็สามารถใช้ Software USART ที่มีเตรียมไว้ให้กับขา I/O อื่นๆได้อีก เดี๋ยวเรามาลองดูกันทีละโมดูลกันนะครับ

                USB CDC (Communication Device Class) คือการให้ USB ทำตัวเป็นตัวเชื่อมต่อสื่อสารแบบต่างๆ(https://en.wikipedia.org/wiki/USB_communications_device_class) แต่ที่ส่วนใหญ่เข้าใจกันก็คือ USB จำลองเป็น com port เมื่อเชื่อมต่อกับคอมพิวเตอร์ โดยใน Pinguino จะมีฟังก์ชันที่ช่วยในการใช้งาน USB CDC อยู่พอสมควร สิ่งที่เราต้องทำคือแค่เรียกใช้งานเท่านั้น ลองดูตัวอย่างโค้ดดังต่อไปนี้นะครับ

1:  void setup() {  
2:    // put your setup code here, to run once:  
3:    pinMode(USERLED,OUTPUT);    
4:  }  

5:  void loop() {  
6:    // put your main code here, to run repeatedly:  
7:     digitalWrite(USERLED, HIGH);  
8:     CDC.printf("On\n\r");  
9:     delay(1000);  

10:    digitalWrite(USERLED,LOW);  
11:    CDC.printf("Off\n\r");  
12:    delay(1000);  
13:  }  

               ตัวอย่างโค้ดนี้จะเหมือนกับในบทความที่ผ่านมาโดยสั่งให้ USER LED ติด และ ดับ ทุกๆ 1 วินาทีแล้วผมทำการเพิ่มให้มีการส่งข้อความว่า “On” เมื่อ USER LED ติดสว่าง และคำว่า “Off” เมื่อ USER LED ดับ จากบอร์ด Pinguino ไปยังคอมพิวเตอร์ผ่านทางสาย USB ที่เชื่อต่อนะแหละครับ โดยทางฝั่งคอมพิวเตอร์ผมก็ใช้โปรแกรมเทอร์มินอลมาอ่านหรือรับค่าแสดงข้อความ โดยสามารถกำหนด baud rate เท่าไหร่ก็ได้เพราะตัว USB จะจัดการให้เอง สังเกตนะครับว่าผมเพียงแต่เรียกใช้ฟังกชัน CDC.printf() เท่านั้นไม่จำเป็นต้องเซ็ทอัพโมดูล USB ใดๆก่อนเลยค่อนข้างสะดวกมากครับ ลองดูวิดีโอประกอบด้านล่างได้ครับ



               USART ( Universal Synchronous Asynchronous Receiver Transmitter ) เป็นโมดูลสื่อสารแบบอนุกรมที่นิยมมากแบบหนึ่งสำหรับไมโครคอนโทรลเลอร์ ใช้ติดต่อสื่อสารกับอุปกรณ์หรือเครื่องมือต่างๆได้มากมาย เช่น Bluetooth, RF, และอื่นๆ รวมถึงใช้สื่อสารกับคอมพิวเตอร์ได้อีกด้วย โดยก่อนเริ่มสื่อสารนั้นจำเป็นต้องกำหนด baud rate หรือความเร็วในการสื่อสารให้ตรงกันทั้งสองฝ่ายเสียก่อน โดยในตัวอย่างต่อไปนี้จะเซ็ทอัพให้ Pinguino สื่อสารกับคอมพิวเตอร์ที่ baud rate เท่ากับ 9600 bits per second ไม่มี hardware flow control ดาต้าบิทเท่ากับ 8 ไม่มี parity และมีเพียง 1 stop bit. โดยสามารถดูได้จากตัวอย่างข้างล่างนี้ครับ


1:  void setup() {  
2:    // put your setup code here, to run once:  
3:    pinMode(USERLED, OUTPUT);  
4:    Serial.begin(9600);  
5:  }  
6:  void loop() {  
7:    // put your main code here, to run repeatedly:  
8:     digitalWrite(USERLED, HIGH);  
9:     Serial.printf("LED On\n\r");  
10:    delay(1000);  

11:    digitalWrite(USERLED, LOW);  
12:    Serial.printf("LED Off\n\r");  
13:    delay(1000);  
14:  }  
              
              จากโค้ดตัวอย่างจะเห็นว่าเราเพียงจำเป็นต้องเซ็ท baud rate ที่เราต้องการในส่วน setup() ก่อนเริ่มใช้งาน USART โดยเรียกใช้ฟังก์ชัน Serial.begin() หลังจากนั้นเราก็สามารถใช้งานฟังก์ชัน Serial.printf() ในการส่งข้อความขึ้นไปยังคอมพิวเตอร์ผ่านทาง USART ได้แล้ว ตัวอย่างนี้จะเหมือนกับตัวอย่างแรกเกือบทั้งหมดเพียงเปลี่ยนมาใช้ USART แทน CDC เท่านั้น  ในการทดสอบเราจำเป็นต้องมี usb-to-serial มาต่อเข้ากับขา Tx และ Rx ของบอร์ด Pinguino ดังแสดงในรูปข้างล่างนี้นะครับ แล้วเราก็ใช้โปรแกรมเทอร์มินอล connect เข้ากับ com port ของ usb-to-serial นั้นๆก็จะสามารถเห็นข้อความ “LED On” และ “LED Off” สลับกันตามจังหวะการติด ดับ ของ USER LED  ลองดูวิดีโอดูครับ


รูป บอร์ด Pinguino เชื่อมต่อกับ USB-to-Serial



               Software Serial เป็นการใช้ขา I/O ของไมโครคอนโทลเลอร์มาทำงานให้เหมือนขา Tx และ Rx ของ USART บางครั้งก็เรียกว่า Bit Bang Serial  โดยทั่วไปในไมโครคอนโทลเลอร์ตัวเล็กๆ หรือไมโครคอนโทลเลอร์รุ่นเก่าๆ จะมี USART เพียงแค่ตัวเดียว แต่บางครั้งงานที่เรากำลังทำอยู่นั้นจำเป็นต้องใช้ USART มากกว่าหนึ่ง จะเปลี่ยนไมโครคอนโทลเลอร์เป็นตัวที่ใหญ่ขึ้นก็เปลืองและไม่ง่ายนัก Software Serial จึงเป็นทางออกที่น่าจะเหมาะสมกว่า แต่มันก็มีข้อจำกัดของมันเช่น ใช้กับการสื่อสารที่ baud rate สูงมากไม่ค่อยได้ ต้องคำนวณคาบเวลาของแต่ละบิต ครึ่งบิต ให้แม่นยำและเขียนโปรแกรมให้ถูกต้องด้วย ซึ่งก็ค่อนข้างยากและเสียเวลามากพอสมควร
                ใน Pinguino นี้ได้จัดเตรียมไลบรารี่ Software Serial ไว้ให้เราเรียกใช้งานได้อย่างง่ายดาย เราเพียงแค่ต้องกำหนดว่าจะใช้ PORT ใด และขาไหนให้เป็นขา Tx และ Rx รวมถึงค่า baud rate ที่ต้องการไว้ที่ส่วนบนของโปรแกรมที่เรากำลังจะเขียนดังตัวอย่างต่อไปนี้ ที่จะใช้ PORTB ขา RB4 เป็น Tx และขา RB5 เป็น Rx โดยกำหนด baud rate ไว้ที่ 9600 bits per second ดังตัวอย่างข้างล่างนี้

 #define SWBAUDRATE 9600// or 57600, 38400, 19200, 9600, 4800  
 // other baudrates 115200 2400 1200 were tried but did not work perfectly   
 #define _SW_PORT _PORTB // Transmit pin port and pin  
 #define TXDpin 4   
 #define RXDpin 5   
 #define SW_PORT_TXDpin PORTBbits.RB4   
 #define SW_TRIS_TXDD TRISBbits.TRISB4   
 #define SW_PORT_RXDpin PORTBbits.RB5   
 #define SW_TRIS_RXDD TRISBbits.TRISB5   
               
              ถัดมาก็จำเป็นต้องกำหนดให้ขา Tx เป็นเอ้าพุตเพื่อให้สามารถ on/off (bit bang) pin เพื่อให้สามารถส่งดาต้าออกไปจากไมโครคอนโทลเลอร์ได้ และกำหนดค่าเริ่มต้นของทั้งขา Tx และ Rx ให้เป็น high( 1 ) ก่อนดังตัวอย่างข้างล่างนี้ครับ ซึ่งเซ็ทอยู่ใน setup() ฟังก์ชัน


 SW_TRIS_TXDD = 0;  
 SW_PORT_TXDpin = 1;  
 SW_TRIS_RXDD = 1;  

หลังจากนั้นเราก็สามารถเรียกใช้งานฟังก์ชันของ Software Serial ได้อย่างง่ายดายเหมือนฟังก์ชัน Serial ในตัวอย่างที่สองที่ผ่านมาได้ ลองดูตัวอย่างโค้ดทั้งหมดได้ครับ


1:  #define SWBAUDRATE 9600// or 57600, 38400, 19200, 9600, 4800  
2:  // other baudrates 115200 2400 1200 were tried but did not work perfectly   
3:  #define _SW_PORT   _PORTB      // Transmit pin port and pin  
4:  #define TXDpin   4  // or 0, 2, 6  
5:  #define RXDpin   5  // or 1, 3, 7  
6:  #define SW_PORT_TXDpin   PORTBbits.RB4  // or 0, 2, 6  
7:  #define SW_TRIS_TXDD    TRISBbits.TRISB4 // or 0, 2, 6  
8:  #define SW_PORT_RXDpin   PORTBbits.RB5  // or 1, 3, 7  
9:  #define SW_TRIS_RXDD    TRISBbits.TRISB5 // or 1, 3, 7  

10:  void setup() {  
11:    SW_TRIS_TXDD = 0;  
12:    SW_PORT_TXDpin = 1;  
13:    SW_TRIS_RXDD = 1;  
14:    pinMode(USERLED, OUTPUT);  
15:  }  

16:  void loop() {   
17:    digitalWrite(USERLED, HIGH);  
18:    SwSerial.printf("USER LED On\n\r");  
19:    delay(1000);  
20:    digitalWrite(USERLED, LOW);  
21:    SwSerial.printf("USER LED Off\n\r");  
22:    delay(1000);  
23:  }  


USB-to-Serial ต่อเข้ากับขา 4,5 (Pinguino) หรือขา 8,9(Arduino)







วันพุธที่ 22 มิถุนายน พ.ศ. 2559

เริ่มต้นกับ Pinguino



วันนี้ผมจะมาแนะนำ platform สำหรับพัฒนาไมโครคอนโทรลเลอร์ของ PIC mcu ที่มีความง่ายเหมือนกับ Arduino นะครับ ซึ่งเจ้า platform ตัวนี้มีชื่อว่า Pinguino ซึ่งใช้ PIC mcu ที่มีโมดูล USB ภายในมาเป็นตัวสื่อสารกับ PC เพื่อโปรแกรม .hex ไฟล์ลงไปในตัว mcu โดยที่ตัว PIC mcu เองจะมี bootloader อยู่ภายในคอยสื่อสารและรับข้อมูลมาเขียนลง Flash เอง ก็เหมือนกับ Arduino นั่นแหละครับเพียงแต่ไม่ต้องมี usb-to-serial อย่างพวก FTDI หรือ CH3404 นั่นเองทำให้ออกแบบง่ายขึ้นเยอะ

โดย Pinguino จะมีทั้ง mcu 8 bit และ 32 bit ทางด้าน 8 bit จะเป็นเบอร์ PIC18F2550, PIC18F4550, PIC18F45K50 และอีกหลายๆเบอร์  ส่วนทางด้าน 32 bit จะมีเบอร์ PIC32MX250F128B, PIC32MX270F128B, PIC32MX795F512L และอีกหลายตัวเลือก ในบทความนี้ผมเลือกใช้ PIC18F45K50 เพราะใช้ไฟ 5 V. , มี internal oscillator ที่มีความแม่นยำสูง ทำให้สามารถทำ application USB โดยไม่ต้องต่อ crystal ข้างนอก ซึ่งก็จะประหยัดไปได้อีก และขา I/O ที่ปกติต้องต่ออยู่กับ crystal ก็จะว่างสามารถนำมาใช้งานเพิ่มได้อีก และสุดท้ายเจ้า PIC18F45K50 นั้นจะใหม่กว่า PIC18F4550 แถมยังราคาถูกกว่าอีกด้วยครับ


            สำหรับ hardware ผมได้ออกแบบให้คล้ายกับ Arduino UNO มากที่สุดเพื่อที่จะได้ใช้กับ shield ต่างๆที่มีขายอยู่มากมาย แต่ด้วยข้อจำกัด และความแตกต่างระหว่าง PIC18F45K50 กับ ATMEGA328P จึงทำได้แค่ใกล้เคียงไม่สามารถแทนกันได้ 100% แล้วเดี๋ยวเราจะค่อยๆมาดูกันครับว่า Pinguino สามารถทำได้ขนาดไหน

รูปที่ 1 schematic
ข้างบนนี้คือ schematic ของ board Pinguino 8 ครับ

รูปที 2 Board Pinguino 8

ทางด้านซ้ายนี้คือหน้าตาของ board ครับ
ใช้ PIC18F45K50 ตัว 40 ขาครับ หลังจากเชื่อมต่อ USB แล้วยังมีขา I/O อีกเหลือเฟือเลยครับ
ราคาก็ใกล้เคียงกับตัว 28 ขาอีกด้วยครับ











รูปที่ 3 อีกมุมนึง
         Crystal ไม่ได้ใช้ครับ ใช้ internal RC แทนครับ แต่สามารถเพิ่ม Crystal ภายหลังได้กรณีต้องการใช้กับ mcu ตัวเก่าเช่น PIC18F4550

         ทางด้าน software พัฒนาเราสามารถใช้ตัว IDE ของ Pinguino เองในการเขียน firmware ส่วนตัว compiler นั้นจะเป็น SDCC สำหรับ 8 bit mcu ซึ่งเราสามารถไปโหลดได้ที่ www.pinguino.cc ครับ แล้วเลือกไปที่แท็บ download จากนั้นก็โหลดตัวติดตั้งสำหรับ windows มา

หมายเหตุ 1: ในขณะที่เขียนบทความนี้ทาง pinguino.cc มี IDE V.11 ที่เป็นตัว stable อยู่ และกำลังทดสอบ IDE V.12 ครับ ตัว installer ที่โหลดมานั้น เมื่อจะติดตั้งต้องต่อ internet ด้วยนะครับ เมื่อคลิ้กติดตั้งไปแล้ว สามารถเลือกได้ว่าจะติดตั้ง IDE V.11 หรือ IDE V.12 หรือทั้งสองตัวก็ได้ โดยตัวติดตั้งจะไปโหลดไฟล์ IDE, Library, compiler, Python 2.7 มาไว้ในคอมพิวเตอร์ของเราแล้วติดตั้งให้ครับ

หมายเหตุ 2: โดยส่วนตัวผมลองติดตั้งตาม หมายเหตุ 1 แล้วแต่ไม่สำเหร็จ ทั้ง IDE V.11 และ IDE V.12 เลย ทดลองเปลี่ยนเครื่องคอมพิวเตอร์ที่มี Windows 7 32 bit ก็ฟลุกติดตั้ง IDE V.11 ได้ครั้งเดียว แต่ตัว IDE V.12 นี่ไม่ได้เลยครับ :<  แล้วตัวติดตั้งเองทุกครั้งที่ลองติดตั้งใหม่ มันก็พยายามไปโหลดไฟล์ต่างๆมาใหม่ ทั้งๆที่ไฟล์ทุกอย่างก็โหลดมาหมดแล้ว ค่อนข้างเสียเวลาครับ

            อีกทางเลือกในการติดตั้ง Pinguino IDE ที่ผมลองแล้วใช้ได้ ง่ายด้วย คือใช้ตัว installer เวอร์ชั่นเก่าที่ตอนนี้ทาง pinguino.cc เอาออกไปไว้ไหนแล้วก็ไม่รู้ แต่ผมมีเซพเก็บไว้แล้วผู้อ่านสามารถโหลดไปติดตั้งได้เลยครับจากลิ้งค์ข้างล่างนี้นะครับ


โดยตัว installer นี้ก็ต้องต่อ internet ในตอนติดตั้งนะครับแล้วมันจะไปโหลดไฟล์ที่จำเป็นมาให้ ผมทดลองติดตั้งกับคอมพิวเตอร์หลายเครื่องแล้วใช้งานได้ทุกครั้งครับ และถ้ามีไฟล์ต่างๆอยู่ในโฟลเดอร์เดียวกันกับตัวติดตั้งอยู่แล้วมันก็รู้และไม่ไปโหลดมาใหม่อีกครับ ง่ายมากเลย
            หลังจากติดตั้งตัว IDE เสร็จแล้ว เมื่อเราเปิดโปรแกรมขึ้นมาก็จะมีหน้าตาสวยงามดังรูปที่ 4 จากนั้นคลิ้กเลือก New ก็จะมีไฟล์ที่มีฟังก์ชั่น setup() และ loop() มาให้เราเสร็จสรรพพร้อมเริ่มต้นเขียน
โปรแกรมกันเลยครับ
รูปที่ 4 หน้าตา IDE


            และโปรแกรมแรกจะเป็นอะไรไปไม่ได้นอกจากไฟกระพริบ ใช่ไม๊ครับ โดยเราเริ่มจากกำหนดขาที่ต่ออยู่กับ LED ให้เป็น output ซึ่งเรากำหนดเพียงครั้งเดียวเราจึงควรกำหนดในฟังก์ชัน setup() จากนั้นเราก็จะสั่งให้ขาเอ้าพุตนั้นเป็น high เพื่อให้ LED ติดสว่างและต้องหน่วงเวลาไว้เพื่อให้เราสามารถมองเห็นการติดดับได้ จากนั้นจึงสั่งขาเอ้าพุตเดิมให้เป็น low เพื่อให้ LED ดับและก็ต้องหน่วงเวลาไว้เช่นกัน แล้วจึงวนกลับไปสั่งให้ LED ติดสว่างอีก วนทำงานอยู่อย่างนี้ไปเรื่อยๆ ดังนั้น code ส่วนนี้จึงควรเขียนในฟังก์ชัน loop() ตัวอย่างโค้ดอยู่ข้างล่างนี้ครับ



 void setup() {  
   // put your setup code here, to run once:  
   pinMode(USERLED,OUTPUT);  
 }  
 void loop() {  
   // put your main code here, to run repeatedly:  
   digitalWrite(USERLED, HIGH);  
   delay(300);  
   digitalWrite(USERLED,LOW);  
   delay(300);  
 }  

         หลังจากเขียนโค้ดเสร็จแล้วควรจะ save เก็บให้เรียบร้อยซะก่อน จากนั้นเราก็มาเลือก mcu ว่าเป็น 8 bit หรือ 32 bit ใช้ bootloader ใช่ไม๊ version ไหน และ mcu เบอร์อะไรแน่ สำหรับ board Pinguino 8 นั้น จะเป็น 8 bit mcu เบอร์ PIC18F45K50 และใช้ bootloader version 4.15  เมื่อเราเลือกถูกต้องแล้วก็สามารถกด compile โค้ดได้ซึ่ง Pinguino จะ compile เร็วกว่า Arduino มาก( อันนี้ชอบมาก ) หลังจาก compile เสร็จแล้วก็จะมี pop-up ขึ้นมาถามว่าจะโปรแกรมลงบอร์ดเลยหรือเปล่า
ถ้าต่อบอร์ดไว้อยู่แล้ว ให้กดปุ่ม Reset บนบอร์ดก่อนครับ RUN LED ก็จะกระพริบถี่ๆเพื่อบอกให้เรารู้ว่า mcu กำลังอยู่ในโหมด bootloader( ตอนเริ่มจ่ายไฟให้กับบอร์ด mcu จะไม่เข้าโหมด bootloader นะครับ จะเริ่มทำงานที่ user program ทันที ถ้าไม่มี user program อยู่ก็จะไม่มีอะไรเกิดขึ้นให้เราเห็น ต้องกดปุ่ม RESET ก่อน mcu ถึงจะเข้าสู่โหมด bootloader ) เราก็สามารถกดโปรแกรมบนคอมพิวเตอร์ได้เลยครับ ( ไม่ต้องรีบร้อนนะครับ mcu จะยังคงอยู่ในโหมด bootloader ตราบเท่าที่เรายังไม่โปรแกรมลงไป ) เมื่อเราโปรแกรมสำเร็จแล้วก็จะเห็น RUN LED( ดวงเดียวกับ USER LED ครับ เมื่อเวอร์ชันก่อนใช้ชื่อ RUN LED ตอนหลังเปลี่ยนมาเป็น USER LED แต่ RUN LED ก็ยังใช้ได้นะ ) กระพริบช้าๆตามที่เราเขียนเอาไว้เลยครับ ลองดูวิดีโอกันก่อนครับ


   
           โค้ดตัวอย่างข้างบนเขียนในแบบ Arduino Style ใน Pinguino สามารถเขียนให้ง่ายกว่านี้อีกครับลองดูโค้ดต่อจากนี้เทียบกันดูครับ


 void setup()  
 {  
   // initialize the digital pin USERLED as an output.  
   pinMode(USERLED, OUTPUT);     
 }  
 void loop()  
 {  
   toggle(USERLED);             // alternate ON and OFF  
   delay(300);                     // wait for 300ms  
 }  

         อีกแบบหนึ่งสำหรับคนที่คุ้นเคยกับการเขียนแบบกำหนดค่าใน register โดยตรงก็สามารถทำได้แบบนี้ครับ

 void setup()  
 {  
   // initialize the digital pin USERLED as an output.  
   TRISAbits.TRISA4 = 0;     
 }  
 void loop()  
 {  
   LATAbits.LATA4 = 1;             // Turn LED on  
   delay(300);                 // wait for 300ms  
      LATAbits.LATA4 = 0;             // Turn LED off  
   delay(300);                 // wait for 300ms  
 }  

          ทั้งสามแบบให้ผลลัพธ์เหมือนกันครับ เพียงแต่แบบที่สามจะมีขนาดโค้ดเล็กกว่าพอสมควร ลองสไตร์ที่ชอบดูครับ นอกจากนี้ยังสามารถดูเพิ่มเติมได้จากลิ้งข้างล่างนี้ได้นะครับ ตอนนี้ขอจบก่อนครับ ขอบคุณครับ

http://wiki.pinguino.cc/index.php/6_ways_to_blink_the_built-in_Led

วันพฤหัสบดีที่ 31 มีนาคม พ.ศ. 2559

อ่านค่าจากสวิตซ์ ด้วย MPLABX และ MCC



              ในบทความนี้ผมตั้งใจว่าจะเขียนถึงการอ่านค่าจากสวิตซ์ หรือปุ่มกด เพราะสวิตซ์เป็นอีกหนึ่งอุปกรณ์สำคัญในงานไมโครคอนโทรลเลอร์ ที่เป็นเหมือกับ user interface อีกประเภทหนึ่งที่ทำให้เราสามารถควบคุมหรือสั่งงานไปยังไมโครคอนโทรลเลอร์อีกทางหนึ่ง  โดยสวิตซ์เป็นอีกหนึ่งอุปกรณ์พื้นฐานที่ผู้เริ่มต้นเรียนไมโครคอนโทรลเลอร์จะต้องใช้เป็นประจำต่อจาก LED, และ USART เรามาลองดูกันซิว่าจะเขียนโปรแกรมเพื่ออ่านค่าจากมันได้อย่างไรบ้าง

Experiment Shield
            ก่อนจะเริ่มเขียนโปรแกรมอ่านค่าจากสวิตซ์ ผมอยากจะแนะนำ Experiment Shield ที่ผมออกแบบขึ้นมาเพื่อใช้เริ่มต้นเรียนคู่กับ PIC Get Start 8 บอร์ด โดยผมได้เอาแบบอย่างมาจากที่นี่ครับ Demo Shield แล้วปรับแก้เล็กน้อยพร้อมกับเพิ่มจุดเชื่อมต่อจอ LCD 16x2 หรือ 20x4 เข้าไป เพื่อให้เกิดประโยชน์มากยิ่งขึ้น โดยในส่วนของสวิตซ์จะมีอยู่สองตัวซึ่งต่ออยู่กับขา RB0 และ RB1 ซึ่งสวิตซ์ทั้งสองตัวนี้จะไม่มีตัวต้านทานต่อพูลอัพไว้ เพราะตั้งใจจะใช้ weak-pull up ในตัวไมโครคอนโทรลเลอร์ ซึ่งความสามารถนี้จะมีอยู่บน PORTB ของ PIC16F1938 นี้  นอกจากนี้ยังมี LED อีกสี่ดวงซึ่งจากในรูปที่ 1 จะเห็นว่าต่ออยู่กับ PORTB ขา RB<2:5> ซึ่งเราจะใช้ในการแสดงผลลัพธ์ของการกดสวิตซ์

                                                 รูปที่ 1 schematic of Experiment shield

เริ่มต้นสร้างโปรเจ็ค
            ในการเขียนโปรแกรมเราจะสร้างโปรเจ็คใหม่ใน MPLABX ก่อนแล้วใช้ MCC ในการ config และ generate code เริ่มต้นให้กับเรา โดยสามารถดูได้จากวิดีโอด้านล่างนี้ครับ  สำหรับคนที่เพิ่งมาเริ่มต้นกับบทความนี้สามารถดูวิธีการสร้างโปรเจ็คกับ MPLABX และ MCC รวมถึงการใช้งานโปรแกรม ds30loaderGUI ได้จากบทความก่อนหน้านี้ครับ



จากวิดีโอจะเห็นว่าเราได้ทำการเขียนโค้ดเพิ่มเติมเพื่อให้สามารถอ่านค่าจากสวิตซ์ได้ดังนี้  

     LED4_Toggle();  
     __delay_ms(400);  
     if(S1_GetValue()==LOW){  
       __delay_ms(20);     //debounce  
       while(S1_GetValue()==LOW);   //wait for S1 released  
       __delay_ms(20);     //debounce  
       LED1_Toggle();  
     }  
     if(S2_GetValue()==LOW){  
       __delay_ms(20);     //debounce  
       while(S2_GetValue()==LOW);   //wait for S1 released  
       __delay_ms(20);     //debounce  
       LED2_Toggle();  
     }  
                   สองบรรทัดแรกการสั่งให้ LED4 กระพริบทุกๆ 400 ms จากนั้นจึงมาอ่านค่าจาก สวิตซ์ S1 โดยเมื่อยังไม่ได้กดสวิตซ์ S1 จะมีค่าเป็น logic 1 หรือ HIGH เพราะมี internal weak-pull up resistor ต่ออยู่กับ Vdd
แต่เมื่อกดสวิตซ์ค่าที่อ่านได้จะมีค่าเป็น logic 0 หรือ LOW เพราะถูกช็อตลงกราวด์ ดังนั้นเมื่อเราเขียนโค้ดอ่านค่าแล้วได้เท่ากับ LOW หมายความว่ามีการกดสวิตซ์ เราก็จะเข้ามาสั่ง Toggle LED ดวงที่เราต้องการ เพื่อเป็นสิ่งแสดงให้รู้ว่าไมโครคอนโทรลเลอร์สามารถอ่านค่าจากสวิตซ์ได้ถูกต้อง  ส่วนคำสั่ง __delay_ms(20); เราจำเป็นต้องใส่ไว้เพื่อป้องกัน bounce ที่เกิดขึ้นจากหน้าสัมผัสของสวิตซ์แตะกันตอนเรากด หรือปล่อย สวิตซ์ หากไม่มีการ de-bounce ไมโครคอนโทรลเลอร์จะอ่านค่าจากสวิตซ์ผิดพลาดได้ นอกจากนี้ผมยังได้เพิ่มคำสั่ง while(S2_GetValue()==LOW); ลงไปเพื่อให้แน่ใจว่าได้ถูกกดและปล่อยเรียบร้อยแล้วจึงจะทำการ Toggle LED
            สำหรับสวิตซ์ S2 ก็จะมีการทำงานในลักษณะเดียวกันกับสวิตซ์ S1 ตัวอย่างโค้ดอันนี้เป็นการอ่านค่าจากสวิตซ์แบบง่ายๆนะครับ ยังไม่เหมาะเอาไปใช้ในงานจริงต่างๆเพราะยังมีจุดอ่อนหลายอย่าง  อย่างเช่นถ้าเรากดสวิตซ์ใดสวิตซ์หนึ่งค้างไว้ LED1 จะไม่กระพริบ เพราะไมโครคอนโทรลเลอร์จะค้างอยู่ที่คำสั่งรอให้สวิตซ์ปล่อยอยู่ หรือถ้าลองกดสวิตซ์หลายๆครั้งเราจะสังเกตุเห็นว่าสวิตซ์ตอบสนองช้าในบางครั้งหรืออาจไม่ตอบสนองต่อการกดครั้งนั้นๆก็ได้ นี่เพราะเราใช้ __delay_ms(400); สำหรับ Toggle LED4 นั่นเอง   

อ่านค่าสวิตซ์ในอินเตอร์รัพของไทม์เมอร์
            จากปัญหาในตัวอย่างที่ผ่านมาผมจึงอยากนำเสนออีกหนึ่งทางแก้ไขคือการอ่านค่าของสวิตซ์ในอินเตอร์รัพของไทม์เมอร์ ในที่นี้จะใช้ Timer0 ให้ over flow แล้วเกิด interrupt ทุกๆ 1ms. ใน interrupt service routine ของ Timer0 ก็จะทำการอ่านค่าสวิตซ์พร้อมทั้งมีการ de-bounce สวิตซ์ด้วย เมื่ออ่านค่าได้แล้วจึงส่งผ่านตัวแปรโกลบอลให้โปรแกรม main สั่งงาน Toggle LED ตามที่กดสวิตซ์ต่อไป  ส่วน LED4 ก็ให้กระพริบทุกๆ 400 ms. ใน interrupt ของ Timer0 เช่นกัน ลองมาดูตัวอย่างในวิดีโอข้างล่างนี้ได้เลยครับ


                  เราเริ่มต้นโดย copy โปรเจ็คเดิมแล้วตั้งชื่อใหม่ จากนั้นก็ใช้ MCC ในการคอนฟิค Timer0 ให้ได้อย่างที่เราต้องการ หลังจากได้โค้ดที่ generated ใหม่แล้วเราก็ลบโค้ด polling เดิมทิ้งซะ แล้วไปเขียนโค้ดในส่วนของ Timer0 interrupt ซึ่งอยู่ในไฟล์ tmr0.c  โดยเราจะประกาศตัวแปรที่จำเป็นต้องใช้ตามตัวอย่างข้างล่างนี้
 #define Key_Delay    20  
 /**  
  Section: Global Variables Definitions  
 */  
 volatile uint8_t timer0ReloadVal, KEYPAD_Debounce_Counter;  
 volatile uint8_t KeyReturn;  
 volatile enum Key{ NoPressed, Debounce1, Pressed, StillPressed, Debounce2} KeyState = NoPressed;  


และไปเพิ่มเติม state-machine code ในฟังก์ชัน TMR0_ISR(void) ซึ่งหากลองไล่ทำความเข้าใจจะพบว่าไม่ยากเลย ลองดูครับ ตัวอย่างโค้ดตามนี้ครับ
 switch( KeyState ){  
     case NoPressed:  
       if( !RB0 | !RB1 ){     //any keys is pressed  
         KEYPAD_Debounce_Counter = 0;  
         KeyState = Debounce1;  
       }  
       break;  
     case Debounce1:  
       KEYPAD_Debounce_Counter++;  
       if( KEYPAD_Debounce_Counter >= Key_Delay ){  
         KeyState = Pressed;  
       }  
       break;  
     case Pressed:  
       if( !RB0 ){           //S1 is pressed  
         KeyReturn = 1;  
         KEYPAD_Debounce_Counter = 0;  
         KeyState = StillPressed;  
       }  
       else if( !RB1 ){        //S2 is pressed  
         KeyReturn = 2;  
         KEYPAD_Debounce_Counter = 0;  
         KeyState = StillPressed;  
       }  
       else{              //no key pressed  
         KeyReturn = 0;  
         KeyState = NoPressed;  
       }  
       break;  
     case StillPressed:  
       if( !RB0 | !RB1 ){      //still pressed  
       }  
       else if( RB0 | RB1){    //already released  
         KeyState = Debounce2;  
       }  
       break;  
     case Debounce2:  
       KEYPAD_Debounce_Counter++;  
       if( KEYPAD_Debounce_Counter >= Key_Delay ){  
         KeyState = NoPressed;  
       }  
       break;  
     default: break;  
   }//switch( KeyState )  


                     ในตัวอย่างผมจะอ่านค่าสวิตซ์จากขาของไมโครคอนโทรลเลอร์โดยตรง ไม่ได้ใช้มาโคร ‘S1_GetValue()’ เหมือนในตัวอย่างแรกครับ เพราะมันสั้นดี แต่มีความหมายเหมือนกันนะครับ ทำงานได้เหมือนกันเลย


สุดท้ายเราก็มาเขียนโค้ดใน main เช็คว่าสวิตซ์ตัวไหรถูกกดก็ Toggle LED ที่สัมพันธ์กัน แต่เราจำเป็นต้องบอก compiler ว่าตัวแปร KeyReturn ที่เราจะเช็คนั้นอยู่ภายนอกไฟล์ main ไม่เช่นนั้นเวลาคอมไพล์แล้วจะ error ไม่ผ่านครับ  อย่างนี้ครับ

 extern volatile uint8_t KeyReturn;  

อ้อแล้วอย่าลืมเคลียร์ค่าของ KeyReturn ด้วยนะครับ ตัวอย่างโค้ดใน main ครับ

 switch( KeyReturn )  
     {  
       case 1: LED1_Toggle(); KeyReturn = 0; break;  
       case 2: LED2_Toggle(); KeyReturn = 0; break;  
       default: break;  
     }  

ผลการทดสอบจะเห็นว่าเวลากดสวิตซ์มีการตอบสนองที่รวดเร็ว ถึงแม้จะกดค้างไว้ LED4 ก็ยังกระพริบตามจังหวะ แต่จะมีปัญหาหากกดสวิตซ์พร้อมกันทั้ง 2 ตัว ทางแก้อาจจะต้องเขียน state-machine แยกของแต่ละสวิตซ์รวมถึงใช้ตัวแปรแยกจากกันด้วย ลองคิดกันดูนะครับ

วันพฤหัสบดีที่ 24 มีนาคม พ.ศ. 2559

MPLABX MCC UART

             
                 ห่างหายไปนานสำหรับบล็อกนี้ พึ่งจะได้มีเวลาว่างมาเขียนอีกครั้ง /(^_^)\
สำหรับบทความนี้ตั้งใจจะเขียนถึงโมดูล USART ในตัว PIC เพราะค่อนข้างมีประโยชน์มากสำหรับงานทางด้าน embedded อย่างที่ผมทำอยู่ อย่างแรกคือมันเป็น user interface ที่สามารถสื่อสารกับตัวเราได้ เพราะสำหรับ micro controller ซึ่งไม่มีหน้าจอแล้ว มันค่อนข้างลำบากที่จะรู้ว่าตอนนี้เจ้า micro controller กำลังทำอะไรอยู่ แต่เราสามารถที่จะใช้โมดูล USART นี้เป็น user interface สื่อสารกับเราผ่านทางหน้าจอคอมพิวเตอร์ โดยอาศัยเจ้า usb-to-serial module ที่มาพร้อมกับบอร์ด PIC Get Start 8 และโปรแกรม terminal บนคอมพิวเตอร์ของเราเป็นตัวกลางและตัวช่วย เราก็จะสามารถสื่อสารกับ PIC ด้วยภาษาอังกฤษแล้ว นอกจากนี้เจ้าโมดูล USART ยังเป็นการสื่อสารแบบ serial ที่นิยมกันมาก สามารถใช้กับโมดูลต่างๆได้มากมาย ไม่ว่าจะเป็น Bluetooth-to-serial ที่เอาไว้ติดต่อกับ android phone ผ่านทาง Bluetooth เพื่อสื่อสารหรือทำหุ่นยนต์ไว้เล่นก็ได้ หรือจะเป็น WiFi-to-serial ที่เอาไว้ติดต่อทาง WiFi ก็เห็นมีใช้กันอยู่มากมาย ดังนั้น USART จึงน่าที่จะเรียนรู้ไว้ใช้งานเป็นอันดับแรก

                โมดูล USART นั้นมาจากคำว่า Universal Synchronous Asynchronous Receiver Transmitter ซึ่งหมายถึงว่ามันสามารถสื่อสารได้ทั้งแบบ Synchronous และแบบ Asynchronous โดยแบบ Synchronous จะมีขาสำหรับส่ง(transmit), ขาสำหรับรับ(receive), และขาสัญญานนาฬิกา(clock) เพื่อ sync กับข้อมูล  ส่วนแบบ Asynchronous จะมีเฉพาะขาส่ง กับขารับ เท่านั้น ซึ่งที่ผมจะใช้ก็จะเป็นแบบ Asynchronous ที่มีเพียง 2 ขาเท่านั้นและโมดูลส่วนใหญ่ก็จะใช้แบบ Asynchronous ทั้งนั้นเท่าที่ผมเคยใช้

          ในการเขียนโปรแกรมเพื่อใช้งานโมดูล USART นั้นจำเป็นที่จะต้องเข้าไปคอนฟิค รีจิสเตอร์ หลายตัวของโมดูลและยังจะต้องกำหนดว่าจะให้โมดูลสื่อสารกับคอมพิวเตอร์ที่ความเร็วเท่าไหร่ หรือก็คือการกำหนด baud rate นั่นเองโดยทั่วไปจะใช้งานที่ baud rate = 9600 แล้วใช้ data bits = 8 ไม่มี Parity bit ใช้ stop bit = 1 และไม่มี hardware flow control คราวนี้ถ้าไปดูจาก datasheet จะเห็นว่าค่อนข้างวุ่นวายพอควรเลยทีเดียวสำหรับมือใหม่ หรือถึงแม้มือเก่า (แก่) อย่างผมยังพลาดออกบ่อยไปเพราะ mcu ตัวใหม่ๆจะทำงานที่สปีดสูงๆ โมดูล USART ก็สามารถสื่อสารที่ baud rate สูงๆก็เลยต้องมีรีจิสเตอร์สำหรับคอนฟิคเพิ่มขึ้น นอกจากนี้ขา Tx กับ Rx ก็อาจจะ multiplex กับโมดูลอื่นอีกก็ต้องคอยเช็ค คอยเลือกให้ถูกต้อง เฮ้อเหนื่อยกว่าจะทำให้มันสื่อสารได้  แต่หลังจากมี MCC มาช่วยในการคอนฟิคแล้ว ทุกอย่างมันง่ายขึ้นเยอะเลยทีเดียวครับ แค่เลือก clock สำหรับ mcu ให้ถูกต้องจากนั้นก็มาคอนฟิคโมดูล USART ว่าจะใช้ baud rate เท่าไหร่ มันก็จะคำนวณค่าที่จะใช้ให้เสร็จสรรพ แถมคำนวณค่าผิดพลาดให้เรารู้ด้วยว่าอยู่ในช่วงที่ยอมรับได้หรือไม่ นอกจากนี้ก็ยังมีออปชั่นให้เลือกว่าจะใช้เป็นแบบ polling หรือแบบ interrupt ทั้งฝั่งส่งและรับ ถ้าเลือกเป็นแบบ interrupt มันจะสร้างเป็นแบบ circular buffer ให้เราเสร็จสรรพทั้งฝั่งส่งและรับ สามารถกำหนดขนาดของ buffer ได้ด้วยนะครับ  เมื่อก่อนนี้จะทำ circular buffer สำหรับ USART นี่ผมต้องหาจากเน็ทให้วุ่นวายไปหมด แล้วส่วนใหญ่ก็ไม่ค่อยเวิคส์เพราะไม่เข้าใจที่หามาได้   อันสุดท้ายสำหรับใครที่อยากใช้ printf กับ USART ก็สามารถเลือกให้ MCC สร้างฟังก์ชัน putch() ที่ printf จะเรียกใช้งานได้ด้วย สะดวกมากมายเลยครับ

                ในวีดีโอได้แสดงการสร้างโปรเจ็คและใช้ MCC เช็ทค่าโมดูล Timer0 ให้เกิด interrupt ทุกๆ 500 ms เพื่อ toggle LED จากนั้นจึงคอนฟิคโมดูล USART  ให้สามรถรับค่าและส่งค่ากับ PC ได้ โดยกำหนด baud rate ที่ 9600 และใช้ polling นอกจากนี้ก็เลือกให้สามารถใช้งาน printf() ได้เพื่อความสะดวก จากนั้นจึงโปรแกรม hex file ลง PIC16F1938 ผ่านทาง ds30loaderGUI โดยตัว ds30loaderGUI นี้มีออปชั่นให้เลือกว่าหลังจาก upload ไฟล์เสร็จแล้วก็จะออโตเมติคเปิดแท็ป terminal ให้เราเลย ซึ่งถ้าเรากำหนด baud rate ที่ถูกต้องไว้ก่อนแล้วมันก็จะแสดงข้อความที่เราโปรแกรมไว้ขึ้นมาให้เห็นทันที ไม่ต้องไปหาโปรแกรม terminal ตัวอื่นมาเปิด มาคอนเน็ค ให้เสียเวลากันอีกแล้วครับ เมื่อโปรแกรมเริ่มทำงานเราก็จะเห็นข้อความที่ PIC ส่งมาให้เราพิมพ์ตัวอักษรอะไรก็ได้ลงไป จากนั้นตัว PIC มันจะ echo กลับมาบอกว่าเราพิมพ์ตัวอะไรลงไป ในขณะที่ LED ก็กระพริบช้าๆ เพื่อบอกให้เรารู้ว่าตัว PIC มันกำลังทำงานอยู่นั่นเอง ลองทดลองกันดูนะครับ  ถ้าต้องการแนะนำอะไรก็คอมเม้นมาได้นะครับ

วันศุกร์ที่ 18 กันยายน พ.ศ. 2558

ดีบัคโค๊ดใน MPLABX, Debug Code with MPLABX

           ในบทความที่แล้วเราได้ทำการเขียนโค้ดกระพริบ LED ง่ายๆแล้วใช้โปรแกรม ds30loaderGUI โปรแกรม .hex ไฟล์ลงไปในไมโครคอนโทรลเลอร์ผ่านทาง bootloader มาแล้ว คราวนี้เราจะมาใช้เครื่องมือที่เรียกว่า programmer/debugger มาทำการดีบัคโค้ดกันครับ            ตัว programmer/debugger ที่รองรับไมโครคอนโทรลเลอร์ของไมโครชิพจะมีอยู่ด้วยกันหลายตัว เริ่มต้นจากตัวเล็กที่สุดและราคาถูกที่สุดก็คือ PICkit3 ซึ่งรองรับการดีบัคแบบพื้นฐาน และเพียงพอกับการใช้งานดีบัคทั่วไป
        ถัดมาจะเป็น ICD3 ซึ่งมีความสามารถสูงกว่า PICkit3 เช่นรองรับ software breakpoint แบบ unlimited ทำให้สามารถดีบัคโค้ดที่ซับซ้อนได้ง่ายขึ้น อีกทั้งยังมีความเร็วในการโปรแกรมสูงกว่าอีกด้วย


        ตัวสุดท้ายคือ Real ICE ซึ่งก็รองรับการดีบัคทั้งหมดที่ PICkit3 และ ICD3 รองรับ แต่ความสามารถที่เหนือไปยิ่งกว่าคือ trace และ real time monitor ค่าในตัวแปรต่างๆได้แบบ real time ไม่ต้อง pause ก่อนเพื่อจะดูค่าของรีจิสเตอร์ หรือตัวแปร เหมือนอย่างที่ PICkit3 และ ICD3 ต้องทำ


หลังจากรู้จักกันแล้วว่าตัว programmer/debugger มีตัวไหนกันบ้างก็มาถึงการใช้งานเพื่อดีบัคโค้ดของเรากันแล้วครับ โดยเราจะให้โค้ดไฟกระพริบจากตัวอย่างบทความที่ผ่านมา แล้วเมื่อเรากดปุ่มดีบัค ตัว MPLABX จะโปรแกรมไฟล์ .elf/.coff ลงไปในตัวไมโครคอนโทรลเลอร์เพื่อทำหน้าที่ในการรวบรวมข้อมูลและสื่อสารกับ MPLABX ผ่านทางตัว programmer/debugger ที่เราเลือกใช้ ซึ่งในที่นี้คือ PICkit3  
                หลังจากที่เราเปิดโปรเจ็คของเราขึ้นมาแล้ว ให้ต่อ PICkit3 เข้ากับบอร์ด PIC Get Start 8 ดังรูป



จากนั้นกดปุ่มดีบัคดังแสดงในวิดีโอ MPLABX จะทำการคอมไพล์โปรเจ็คของเราอีกครั้ง แล้วจะเชื่อมต่อ กับ PICkit3 หากเป็นการเชื่อมต่อครั้งแรก อาจจะมีการอัพเดท firmware ของ PICkit3 ที่เหมาะสมสำหรับไมโครคอนโทรลเลอร์ของเรา การอัพเดทอาจใช้เวลาซัก 1 – 2 นาที อดทนรอแป็ปนึง จากนั้น MPLABX จะแจ้งว่า ไม่สามารถติดต่อกับไมโครคอนโทรลเลอร์ของเราได้ นั้นเพราะเรายังไม่ได้จ่ายไฟเลี้ยงให้กับตัวไมโครคอนโทรลเลอร์นั่นเอง  ในการทดลองครั้งนี้ผมจะใช้ PICkit3 จ่ายไฟให้กับตัวไมโครคอนโทรลเลอร์ ขั้นตอนก็ดูได้จากในวิดีโอนะครับ พอเราสามารถโปรแกรมไมโครคอนโทรลเลอร์ เราจะเห็นเมนูดีบัคแอ็คทีพขึ้นมา ซึ่งเราสามารถสั่ง RUN, Pause, Step by Step, etc. ซึ่งปุ่มคำสั่งต่างๆเหล่านี้จะใช้คู่กับการเซ็ท breakpoint ตรงตำแหน่งต่างๆที่เราต้องการให้โปรแกรมไปหยุด ร่วมกับหน้าต่าง watch ที่ใช้ดูค่าของตัวแปรต่างๆ รวมไปถึงรีจิสเตอร์ที่เราสนใจ