ความรู้เกี่ยวกับการเขียนโปรแกรมภาษา C ที่นักอิเล็กทรอนิกส์ไม่ค่อยรู้ ตอนที่ 8 ความรู้อื่นๆ เกี่ยวกับการเขียนโปรแกรมภาษา C บน Microcontroller

ความรู้เกี่ยวกับการเขียนโปรแกรมภาษา C ที่นักอิเล็กทรอนิกส์ไม่ค่อยรู้

ตอนที่ 8 ความรู้อื่นๆ เกี่ยวกับการเขียนโปรแกรมภาษา C บน Microcontroller

The C Programming Language logo

ในที่สุดก็ถึงตอนสุดท้ายนะครับ ตอนนี้จะเป็นการรวบรวมเอาความรู้เล็กๆน้อยๆหลายเรื่องมารวมๆกัน บางเรื่องอาจเป็นเรื่องพื้นฐานแต่เชื่อว่ามือใหม่ๆหลายคนน่าจะยังไม่รู้ เพราะผมเองก็เพิ่งจะรู้ตอนที่เขียนโปรแกรมไปได้ 3-4 ปี พอรู้แล้วก็ทำให้เขียนโปรแกรมได้ง่ายขึ้นเยอะครับ

           1. ใช้ return เมื่อไหร่ก็ได้

หลายคนจะเข้าใจว่าใน function ที่มีการ return ค่า เราจะต้อง return ตอนจบ function เท่านั้น แต่จริงๆแล้วเราสามารถจะ return เมื่อไหร่ก็ได้ และใน function ที่ไม่มีการ return ค่า เราก็สามารถใช้ return เฉยๆได้ ซึ่งในหลายๆกรณี การใช้ return จะทำให้เราเขียนโปรแกรมได้ง่ายขึ้น อย่างเช่น ถ้าเรามี function ที่ต้องมีการเช็คเงื่อนไขหลายๆเงื่อนไขและเป็นเงื่อนไขที่มีลำดับความสำคัญ

ยิ่งเรามีเงื่อนไขซ้อนกันหลายข้อ code ก็จะดูซับซ้อนมากขึ้น ลองดู code ด้านล่างว่าดูง่ายขึ้นหรือไม่

จะเห็นว่า code แบบที่สอง เราสามารถเพิ่มเงื่อนไข หรือจะสลับลำดับได้ง่ายกว่า

           2. ใช้ Casting เพื่อเปลี่ยน type ของตัวแปร

ในหลายๆกรณีเราจะต้องเปลี่ยน type ของตัวแปร จากชนิดหนึ่งเป็นอีกชนิดหนึ่ง ที่หลีกเลี่ยงไม่ได้และต้องเจอกันทุกคนก็คือการหารนั่นเอง ลองดู code ต่อไปนี้

ที่เราต้องการคือค่า z ควรจะเป็น 3.3333 แต่ในความเป็นจริงเราจะได้ z เป็น 3

ที่เป็นอย่างนี้เพราะการที่เราเอา integer ซึ่งเป็นจำนวนเต็มมาหารกัน compiler จะจัดการให้เราได้ผลลัพธ์มาเป็น integer นั่นคือพจน์ x/y จะเป็น integer โดยไม่เกี่ยวกับว่าเราใช้ตัวแปรชนิดไหนในการรับค่า

เราสามารถเปลี่ยน type ข้อมูลด้วยการใส่ type ใหม่ในวงเล็บวางไว้หน้าตัวแปร จากตัวอย่างเดิม ถ้าเราต้องการเปลี่ยนเป็น float เราต้องใส่ (float) ไว้ข้างหน้า x/y ซึ่งเรียกว่าการ casting

การใส่วงเล็บให้ถูกต้องมีความสำคัญมากใน code4 บรรทัดต่อไปนี้ มีอยู่ 1 บรรทัดที่ให้ค่า z = 3 ซึ่งผิดส่วนอีก 3 บรรทัดที่เหลือจะให้ค่าที่ถูกต้องคือ 3.33333325 ทราบหรือไม่ว่าเป็นบรรทัดไหน ลอง comment คำตอบ มาใต้ post นี้ได้นะครับ

นอกจากนี้ ถ้าเราต้องการหารตัวแปรด้วยตัวเลข (ไม่ใช่หารด้วยตัวแปร) เราก็สามารถจะเขียนตัวหารให้มีจุดทศนิยม เพื่อให้ compiler ให้ผลลัพธ์มาเป็น float ได้ เช่น แทนที่เราจะเขียนดังนี้

เราสามารถเขียนได้ง่ายๆ เป็นดังนี้

3. ลองปรับเพิ่มขนาดของ stack

ถ้าใครเคยเรียน microcontroller มาก็น่าจะพอรู้จักกับ stack อยู่บ้าง คำว่า stack ในที่นี้หมายถึง ส่วนหนึ่งของ RAM ที่ MCU ใช้ในการเก็บตำแหน่งของโปรแกรมชั่วคราว สำหรับใช้ในการ return กลับมาจาก function และใช้เก็บตัวแปร local (ซึ่งจะมีการเพิ่มและลด ตาม function ที่กระโดดเข้าไปทำงาน) ปกติแล้ว code ที่ให้มากับ chip MCU จะกำหนดขนาดของ stack เอาไว้จำนวนหนึ่ง ซึ่งบางครั้งอาจจะไม่พอกับโปรแกรมที่เราเขียน

ลองจินตนาการว่า MCU มี stack อยู่จำนวนหนึ่ง เวลากระโดดเข้าไปใน function จะมีการสร้างตัวแปร local ขึ้นมา แล้วใน function นั้นหากมีการกระโดดเข้าไปใน function อื่นอีก ก็ต้องสร้างตัวแปร local เพิ่มขึ้นอีก ทีนี้ลองคิดดูว่า compiler จะรู้ได้อย่างไร ว่า code ของเราจะต้องเข้าไปลึกที่สุดตอนไหน และจะต้องสร้างตัวแปร local ขึ้นมามากที่สุดโดยใช้พื้นที่เท่าไหร่ ใช่แล้วครับ compiler เองก็ไม่รู้ว่า code ของเราจะซับซ้อนแค่ไหน จึงไม่รู้ว่าจะต้องใช้ stack เท่าไหร่ ดังนั้นจึงไม่สามารถจะเตือนเราได้ว่าเรามี stack ไม่พอ ถ้าเราเขียน code โดยใช้ตัวแปร local มากๆ และมีการเรียก function ซ้อนเข้าไปลึกๆ stack จึงอาจจะไม่พอได้ ซึ่งอาการที่เคยเห็นมาก็คือ MCU จะค้างในการทำงานบาง function โดยที่หาสาเหตุจาก code ไม่ได้ แต่เมื่อลองเพิ่มขนาด stack ดูอาการค้างก็หายไป ส่วนใหญ่แล้วถ้าเราเขียน code แล้วก็ทดสอบไปด้วย พอเขียนเพิ่มไปอีกนิดนึงเกิดค้างขึ้นมา การเพิ่ม stack เพียงเล็กน้อยก็น่าจะแก้ปัญหาได้ แต่ถ้าลองเพิ่มไปพอสมควรแล้วไม่หาย ก็น่าจะเป็นที่อย่างอื่น

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

แล้วเราจะเพิ่มขนาด stack ตรงไหน อันนี้ไม่สามารถจะระบุได้ เพราะ code ของ MCU แต่ละตระกูลไม่เหมือนกัน แต่โดยทั่วไปจะอยู่ที่ไฟล์ startup(.s) เช่นไฟล์ startup ของ Nuvoton NUC140 จะมีการกำหนดขนาดของ stack ไว้ดังนี้

0x400 คือ 0100 0000 0000 ซึ่งเท่ากับ 1,024 byte เราสามารถเพิ่มให้เป็น 0x00000500 (1,280) หรือมากกว่านี้ได้ ทั้งนี้ใน MCU อื่นๆอาจจะเขียนเป็นอย่างอื่น จึงควรใช้การค้นหาคำว่า stack ดู

4. การใช้ volatile

เราคงเคยเห็นคำว่า non-volatilememory มาแล้ว ซึ่งก็คือ memory ที่สามารถเก็บข้อมูลได้ แม้ว่าจะไม่มีไฟเลี้ยง ดังนั้นคำว่า volatilememory ก็คือ memory ที่ข้อมูลจะลบเลือนได้ เราจะใช้คำว่า volatile นี้ ใส่ไว้นำหน้าการประกาศตัวแปร เพื่อบอก compiler ว่าตัวแปรตัวนั้นอาจมีการเปลี่ยนแปลงค่าได้ เวลาอ่านให้ไปอ่านจาก memory ที่ตำแหน่งนั้นตรงๆ

หลายคนคงสงสัยว่าแล้วปกติ MCU มันไม่ไปอ่านค่าตรงๆจาก memory ตรงนั้นเหรอ คำตอบก็คือในการทำงานจริงของ MCU จะมี cache memory(เหมือนใน CPU ของ PC) คอยทำหน้าที่เป็น memory ที่อยู่ใกล้ตัว MCU ที่สุด และสามารถเข้าถึงได้รวดเร็วที่สุด ค่าของตัวแปรต่างๆที่ถูกใช้งาน จะถูกนำมาเก็บไว้ใน cache ก่อน และในบางกรณีที่เราคอยเช็คค่าตัวแปรนี้อยู่ตลอดเวลา MCU จะอ่านค่าของตัวแปรนี้จาก cache ซึ่งจะเป็นค่าเดิมที่ยังไม่ถูก update การที่เราบอกกับ MCU ว่าตัวแปรนี้อาจมีการเปลี่ยนแปลงค่าได้ตลอดเวลา ทำให้เวลาอ่านค่าตัวแปร จะไม่ไปอ่านจาก cache ตามปกติ แต่จะไปอ่านมาจาก memory นั้นตรงๆ จึงได้ค่าที่ update เสมอ

ตัวอย่าง code ที่เรามักจะต้องใช้ volatile ก็คือพวก flag ต่างๆที่จะถูกวนอ่านอยู่ตลอดเวลา เช่นถ้าเราสร้างตัวแปร flag เป็น unsigned char ใช้ตรวจสอบว่ามี external interrupt เข้ามาหรือยัง โดยให้ชื่อว่า waiting_done

โดยเมื่อมีการ interrupt เราจะ set ให้ flag นี้เป็น 1

 ทีนี้ใน code ปกติ (ที่ไม่ใช่ใน interrupt) จะมีช่วงที่เรารอให้ flag ตัวนี้เป็น 1 จึงจะทำงานต่อไป

ถ้าเราเขียน code แบบนี้ จะมีโอกาสที่จะค้างที่ loop while แม้ว่าจะเกิด interrupt เข้ามาแล้ว ทั้งนี้เป็นเพราะ MCU ไม่ได้ไปอ่านค่าจาก memory ของ waiting_done จริงๆ เราจึงต้องใส่ volatile ซึ่งจะเขียนได้ดังนี้

เท่านี้ก็เป็นอันเรียบร้อย

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

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

<< กลับไปสู่หน้าแรกสารบัญ