->> หมวดหมู่และหัวข้อบทความ << คลิ๊ก
บันทึกช่วยจำ และความเข้าใจส่วนตัว ภาษาC++ , Arduino ide , Atmel studio เพื่อเขียน atmega328p
--กำลังศึกษาอยู่ครับ มันเป็นแค่บันทึกช่วยจำส่วนตัวครับ ผมสนใจอะไรก็จะบันทึกลงไป อาจไม่มีประโยชน์อะไรแก่ผู้อ่าน และไม่ได้ครอบคลุมทั้งหมด เนื่องจากแต่เดิมผมไม่เคยเรียนภาษาC++ , ตัวเขียนโปรแกรมของ Arduino IDE , และไม่เคยใช้ Atmel studio ทั้งสามอันไม่ได้แตะหรือรู้เรื่องอะไรกับเขาเลยและผมก็จบเครื่องกล แต่ก่อนเคยเล่นอยู่ก็เป็นMCS51 เบอร์ที่เล่นก็เล่นอยู่เบอร์เดียว คือ AT89LP4051 และเขียนด้วยภาษาชุดคำสั่งเครื่องเพียวๆ หรือภาษาassembly จึงจำเป็นต้องบันทึกความรู้ที่จะขุดนี้เอาไว้ใช้ส่วนตัวเท่านั้น--
ความรู้ของผมแต่เดิมก็ไม่ได้รู้เรื่องอะไรกับเขามากนัก แต่เรื่องเขียนโปรแกรมนี่ก็พอได้ครับ พอรู้ภาษา เช่น ปาสคาล เบสิก PHP ถ้าอื่นๆก็พอรู้แบบคุ้ยๆได้ ก็ จาวาสคริ๊ป mysql VB ปัจจุบันภาษาในการเขียนโปรแกรมแต่ละภาษามันพัฒนาฟังก์ชั่นใหม่ๆตลอดเวลาเป็นไลบารี่ ซึ่งมีจำนวนมากเกินไปที่จะจดจำทั้งหมด จะใช้อะไรก็ไปค้นหากันเอาเอง โปรแกรมที่เขียนด้วยPHPบางตัวที่เขาเขียนกันเชิงพาณิชย์ มันซับซ้อนซ่อนเงื่อนแทบแกะไม่ออก น่าจะพอเปรียบได้กับภาษาC++เช่นในArduino IDE ที่ออกแบบฟังก์ชั่นและโมดูลแต่ละอันแยกกันคนละที่อีกต่างหาก แล้วที่ผมอุตส่าห์เตรียมตัวอ่านชุดคำสั่งของATmega328Pเอาไว้ก่อนแล้วมันไปอยู่ตรงไหนในภาษาCในIDEหว่า เริ่มต้นผมก็ไปไม่เป็นเหมือนกัน อาศัยเพื่อนๆพี่ๆที่ทดสอบเล่นไปแล้วในยูทูปก็ค่อยแกะกันไป
โดยส่วนตัวแล้วผมคงไม่ลงไปลึกในรายละเอียดทุกขั้นตอน อาศัยคุ้ยๆแล้วจดบันทึกเอาไว้ให้พอเป็นแนวทาง เนื่องจากผมต้องการใช้ hardware ของ Arduino และชิลของมัน ตลอดจนอุปกรณ์ต่อพ่วงสั่งการที่มีราคาถูกมาใช้กับงานส่วนตัว อาศัยแกะๆคุ้ยๆโปรแกรมบางส่วนในภาษาซีArduinoมาศึกษา ดัดแปลง และที่เป็นส่วนหลักๆคงเขียนใหม่
1 ภาษา C++ ในArduino คงบันทึกเอาไว้ให้พอเป็นคอนเซ็บเพราะในเว็บมันมีให้คนคว้าเยอะแยะมากมาย แต่พยายามจะหาจุดเชื่อมที่ลงไปลึกถึงระดับ I/O register การกำหนดค่าจำเป็นต่างๆเช่น watchdog ค่าอินเตอร์รัพ ไทม์เมอร์ เพื่อจะนำมาต่อยอดใช้เขียนคำสั่งแบบชุดคำสั่งเครื่องโดยตรงผ่านภาษาC++ เข้าใจว่าใช้ได้นะเดาๆเอา ส่วนภาษาC++ในAtmel ดูเหมือนว่าจะเป็นต้นตำหรับมากกว่าและเข้าใจว่าสามารถสร้างฟังก์ชั่นภาษาCจากภาษาแอสเซมบลี้ได้โดยตรง
2 ส่วนตัวเขียนโปรแกรมหรืออาจจะเรียกว่าคอมไพล์เลอร์ เช่น Arduino IDE หรือ Atmel studio ก็แค่บันทึกเอาไว้เป็นคอนเซ็บเพื่อให้คุ้ยต่อได้ก็เท่านั้น เพราะตอนนี้ก็ไปไม่เป็นเหมือนกัน
การเรียนภาษาในการเขียนโปรแกรมต้องใช้หลัก อ่าน(ดู)(คลำ) คิดจน เข้าใจ(คลิ๊ก) แล้วลงมือทำ(ลองเขียน) ปัจจุบันมีคอมไพล์เลอร์ออนไลน์ให้เราสามารถลงมือเขียนได้ทันทีไม่ต้องลงโปรแกรมใดๆ เราควรอ่านทีละคำสั่งเมื่อคิดแล้วเข้าใจว่าไงก็ลงมือเขียนทันทีจะง่ายและสำเร็จมากกว่า การอ่านเพื่อจำทีเดียวทั้งเล่มแล้วค่อยกลับมาเขียนซึ่งส่วนใหญ่จะไม่ได้ผล วิชานี้เน้นความเข้าใจและภาคปฎิบัติเป็หลักไม่ได้เน้นความจำที่เลิศ ถ้าเปรียบก็ต้องอาศัยหลักวิปัสสนาหรือสร้างปัญญาเป็นส่วนใหญ่ไม่ใช่หลักสมถะซึ่งเป็นหลักของจิตนิ่งความจำจึงดี แต่ต้องใช้สองหลักอาศัยกันแต่เน้นปัญญามากกว่าความจำ
..
ภาษาC++ ในArduino
พอดีได้ซื้อหนังสือภาษาไทยมา2เล่ม คือ
1 เรียนรู้ เข้าใจ ใช้งาน ไมโครคอนโทรลเลอร์ตระกูล AVR ด้วย Arduino
,เอกชัย มะการ , ETT ,บริษัท อีทีที
2 พื้นฐานภาษาC สำหรับ Arduino , ประจิน พลังสันติกุล , บริษัท แอพซอฟต์เทค
เท่าที่เริ่มอ่านดูพบว่าคงต้องกลับไปทำความเข้าใจภาษาC ให้พอมีพื้นความรู้ก่อน เนื่องจากไปค้นๆดูว่าภาษาCต่างกับC++ มากแค่ไหน มีผู้นิยามว่าภาษาC ตัวภาษาเกือบทั้งหมดอยู่ใน C++ เนื่องจาก C++ถูกพัฒนามาจากC จึงจำเป็นต้องเรียนรู้Cให้มากพอก่อนจะใช้C++ ภาพข้างล่างแนะนำเว็บไซต์ที่วิเคราะห์ความแตกต่างระหว่างภาษาCและC++ ซึ่งฟีเจอร์ต่างๆของc แชร์ใช้ในc++ เกินกว่า95เปอร์เซนต์ดังนี้รูป ลองหาอ่านดู ใน techwelkin.com/difference-between-c-and-c-plus-plus เปรียบเทียบภาษาทั้งสองดูจะเห็นภาพรวมขึ้นว่าทำไมต้องเรียนc++จากcด้วย และข้อแตกต่างกันในภาษาแตกต่างกันอย่างไร
..dd
ANSI- C programing language ความเข้าใจเบื้องต้น
ภาษาCแต่เดิมได้ถูกพัฒนาขึ้นตั้งแต่ปี1972 ซึ่งเป็นหนึ่งในภาษาระดับสูง คือ มนุษย์สามารถอ่านแล้วตีความได้ง่าย การเขียนโปรแกรมและการทำความเข้าใจก็ทำได้ง่าย เมื่อนำมาใช้เขียนโปรแกรมสำหรับไมโครคอนโทรลเลอร์หรือระบบปฏิบัติกการอื่นๆสามารถเปลี่ยนไปใช้ฮาร์ดแวร์ตัวอื่นๆหรือซอฟแวร์เวอชั่นสูงขึ้นได้โดยผู้พัฒนาซอฟแวร์หรือฮาร์ดแวร์เป็นคนกำหนดระเบียบข้อปลีกย่อยใหม่ของฮาร์ดแวร์หรือซอฟท์แวร์ ทำให้ผู้เขียนโปรแกรมC เมื่อนำชุดโค๊ดเดิมมาใช้ใหม่ก็สามารถทำได้ทันที โดยต้องปรับปรุงแก้ไขหรือปรับข้อปลีกย่อยให้เหมาะกับซอฟแวร์หรือฮาร์ดแวร์นั้นๆโดยไม่ต้องเปลี่ยนโค๊ดหลักทั้งหมด
แต่เมื่อเปรียบเทียบกับชุดคำสั่งที่รวมกันเป็นโปรแกรมให้ซีพียูทำงานโดยตรง หรือชุดคำสั่งภาษาเครื่องที่เรียกว่าภาษาแอสเซมบลี้นั้น เขากำหนดว่าเป็นภาษาระดับต่ำ หมายถึงคนทั่วๆไปไม่สามารถทำความเข้าใจได้โดยง่าย โดยปรกติเป็นชุดตัวเลขฐานสิบหกทั้งนั้นยาวเป็นหางว่าว แม้ตัวเขียนโปรแกรมหรือคอมไพล์เลอร์หรืออีดิตเตอร์สำหรับภาษาแอสเซมบลี้นั้น สามารถใส่คอมเม้นเพื่อให้มนุษย์อ่านค่าต่างๆให้พอทำความเข้าใจได้แต่ใช้เวลาการเรียนรู้ค่อนข้างมาก การเปลี่ยนไมโครคอนโทรลเลอร์ใหม่เช่นย้ายค่ายไมโครคอนโทรลเลอร์ ก็ต้องไปเรียนภาษาแอสเซมบลี้ของแต่ละตัวใหม่แล้วเขียนใหม่ทั้งหมด (แต่โดยเนื้อแท้ตามความเข้าใจนะ..คนที่จะใช้ภาษาซีมาเขียนไมโครคอนโทรลเลอร์ถ้าต้องการเขียนละเอียดๆก็ต้องเรียนแอสเซมบลี้อยู่ดีหรือต้องเข้าใจหรือเคยอ่านชุดคำสั่งของคอนโทรลเลอร์นั้นอย่างน้อย1รอบอยู่ดี)
การนำภาษาC ที่เป็นภาษามาตรฐานเดิมมาใช้กับงานใดๆ เช่นมาเขียนสั่งไมโครคอนโทรลเลอร์ ภาษาC ดังกล่าวจึงถูกพัฒนาขึ้นให้เหมาะสมเพื่อจะมีวิธีการแปลภาษาจากCเป็นASMได้ตรงจุดขึ้นเรียกว่าภาษาC++ ซึ่งมีคำสั่งหรือฟังก์ชั่น หรือข้อกำหนดต่างๆแตกต่างไปจากภาษาCดั้งเดิมที่เรียกว่า ANSI-C
แต่อย่างไรก็ตามภาษาC++ ก็ยังใช้คำกุญแจหรือคีย์เวิร์ดส่วนใหญ่ในภาษาCซึ่งเป็นคำที่เป็นแกนกลางภาษาหรือแก่นภาษาที่ต้องเรียนรู้ให้เข้าใจ ไม่งั้นไปต่อไม่ถูก ซึ่งมีคำสำคัญเดิมหรือkeyword 32 ตัว ตั้งแต่ยุคพัฒนาในต้นๆ จริงๆมีรายละเอียดเยอะถ้าจะไปไล่ประวัติดูให้ดูที่นี่ [?Wiki ] C progaming language , [?Wiki ] ANSI-C
แนะนำที่เรียนภาษา C บางที่มีคอมไพล์เลอร์ออนไลน์ด้วย (Compiler แปลว่า"ผู้รวบรวม" ปรกติจะหน้าตาเหมือนเท็กอีดิเตอร์เช่นโน๊ตแพ็ด แต่มีหน้าที่รวบรวม แปล และสามารถติดต่อส่งให้ซีพียู execute แปลว่า "กระทำการ" ตามคำสั่งหรือตามโปรแกรมที่เขียน)
1 www.tutorialspoint.com/cprogramming/index.htm
2 www.tutorialspoint.com/compile_c_online.php
3 www.programiz.com/c-programming
4 tigcc.ticalc.org/doc/ โปรแกรมเครื่องคิดเลขที่ใช้ภาษาCเขียน
5 www.c-programming-simple-steps.com คำจำกัดความค่อนข้างครอบคลุมดี
C90--Keywords หรือคำที่เป็นแก่นภาษาC ใน ANSI-C 32 ตัว (C90)-
auto | double | int | struct |
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
const | float | short | unsigned |
continue | for | signed | void |
default | goto | sizeof | volatile |
do | if | static | while |
ANSI-C90 Keywords ชนิดตัวแปร type & modifier | ||
1. | char | ชนิดตัวแปร character type (ตัวอักษร สัญลักษณ์ และการดำเนินการตัวอักษร) ค่าตัวเลขหรือรหัสขนาด1ไบท์(8บิท) ที่มีค่า signed(คิดบวกลบ)หรือไม่ได้กำหนด**deflault -128 to 127 ,unsigned 0-255 ถ้าเป็นรหัสตัวอักษรจะหมายถึงค่า 0-255 ในASCII code เช่น char alphabet ="A"; ตัวคอมไพล์เลอร์จะแปลงAให้เป็นตัวเลขทีหลัง |
2. | int | ชนิดตัวแปร integer type (เลขจำนวนเต็ม) ขนาด2ไบท์(16บิท) **default or signed -32768 to 32767 , unsigned 0-65535 (int มีขนาด2ไบท์ใน cpu,mcu8-16bit --และมีขนาด4ไบท์ในcpu32bit) |
3. | float | ชนิดตัวแปร float type (เลขทศนิยมคิดเครื่องหมาย) ขนาด4ไบท์(32บิท) +-1.175494e-38 to +-3.402823e+38 (**ค่าตัวเลขนี้ไม่ชัวร์นะ) ถ้ายึดตามIEEE754 ประกอบด้วย( 1 sign bit *1ลบ*0บวก , 23 bits of significant , 8 bits of exponent) IEEE754 calculator ข้างล่างเป็นวีดีโออธิบายการแปลงเลขทศนิยมฐานสิบเป็นเลขทศนิยมฐานสอง vdo by Wannik Akademy อาจารย์ วรเศรษฐ สุวรรณิก ![]() |
4. | double | ชนิดตัวแปร double type (เลขทศนิยมคิดเครื่องหมาย) ขนาด8ไบท์(64บิท) 1.7E-308 to 1.7E+308 |
5. | signed - | ตัวโมชนิดตัวแปร signed Type modifiers (คิดเครื่องหมาย) สามารถนำไปใส่ข้างหน้า char ,int , short , long ในกรณีที่ไม่ใส่ ค่าดีฟอลท์จะคิดเครื่องหมาย |
6. | unsigned- | ตัวโมชนิดตัวแปร unsigned (ไม่คิดเครื่องหมาย) ใส่นำหน้า char , int , short ,long |
7. | short - | โมชนิดตัวแปร short จำนวนเต็มที่มีค่าน้อยกว่าหรือเท่ากับint เอาใส่นำหน้า int มีขนาด2ไบท์(cpu8-32bit) ถ้าไม่ระบุเพิ่มเติมว่าอันไซน์ จะมีค่าอยู่ -32768 to 32767 , unsigned 0-65535 |
8. | long - | ตัวโมชนิดตัวแปร long จำนวนเต็มที่มีขนาด4ไบท์(32บิท) ใส่นำหน้า int ดีฟอลท์ signed -2147483648 to +2147483647 unsigned 0 -4,294,967,295 |
9. | - const - | ใส่นำหน้าหรือต่อหลังตัวแปร เพื่อระบุว่าเป็นค่าคงที่ เช่น const int LENGTH = 10; หรือจะใช้ #define LENGTH 10; แทนก็ได้ |
10. | enum | enumerated lists ชนิดชุด(set,list)ค่าคงที่ที่เป็นint , การกำหนดชุดค่าคงที่ในลิสต์สามารถกำหนดเป็นชื่อตัวอักษรใดๆ(name)ก็ได้และตั้งกฎใหม่ว่าให้เท่ากับตัวเลขเท่าใด แต่ถ้าตัวอักษรไม่ได้กำหนดค่าตัวเลข คอมไพล์เลอร์จะตั้งค่าintเสมือนให้ โดยเริ่มต้นตั้งแต่ค่า0 จากลิสต์ด้านซ้ายไปขวา เช่น enum [tag] {name [=value], ..} variable_1, variable_2,..; enum weekday {Mo, Tu, We, Th, Fr, Sat, Su}; ในที่นี้ weekday เป็น new type variables ซึ่งอาจกำหนดเพิ่มเป็น enum weekday today , tomorrow; หรือ enum weekday today = Tu; หรือ=1 ความหมายเดียวกัน enum {Mo, Tu, We, Th, Fr, Sat, Su} today, tomorrow; การนำค่าตัวอักษรในลิสต์ มาใช้ในลักษณะ int เช่น today = Mo+2; สามารถจ้า |
11. | struct | ชนิดชุดข้อมูลกลุ่มโครงสร้างstructมีตัวแปรข้อมูลหลายๆชนิดรวมกัน และถูกจองพื้นที่ในแรมแบบขยายความ(memory padding)และคำนวนสะสมเกลี่ยเฉพาะ ทำให้กินพื้นที่มาก จำนวนไบท์ในแรมที่จองอาจมีค่าจะน้อยลงได้ถ้าชนิดตัวแปรที่มีจำนวนน้อยถูกเรียงไปหามาก ไม่ควรเรียงมากไปหาน้อย struct [ชื่อชนิดสตรัคท์] //[struct-type-name] { [ชนิดตัวแปร และชื่อตัวแปร];.. [type and variable name]; } [รายชื่อตัวแปรสตรัคท์ กั้นด้วยลูกน้ำ]; struct employee // employee เป็น new struct type { long q1; char id1; int q2; char id2; // long 4 ,char 1 ,int 2 byte //จองแรม= 12 byte =4+(1+ว่าง1+2)+(1+ว่าง3) } MisterA ,MisterB; // แต่ถ้าเรียงอย่างนี้จะจองแรมแค่ 8 ไบท์ // long q1; char id1; char id2; int q2; // 8 = 4+(1+1+2) // struct employee MisterS; ประกาศเพิ่มทีหลัง // MisterS.q1=500; ใช้.q1 เป็นนาสกุลระบุชนิดย่อย |
12. | union | ชนิดชุดข้อมูลกลุ่มโครงสร้างunionมีตัวแปรข้อมูลหลายๆชนิด เช่นเดียวกับ struct แต่กินแรมน้อยกว่า(no memory padding)คือจองแรมเท่ากับขนาดตัวแปรที่มีจำนวนไบท์ใหญ่ที่สุด เมื่อมีการใช้งานเขียนข้อมูลเช่นใส่ค่าจะถูกเขียนทับข้อมูลเดิมจึงอาจต้องมีตัวแปรช่วยในการเก็บข้อมูลเก่า ส่วนใหญ่จะเป็นการใช้งานตัวแปรในช่วงสั้นๆแบบไม่พร้อมกันเท่านั้นจึงเขียนทับซ้ำที่เดิมได้ ด้วยคุณสมบัตินี้ที่ทำให้ประหยัดแรมได้มากในกรณีมีแรมน้อยๆ union [ชื่อชนิดยูเนียน] { [ชนิดตัวแปร และชื่อตัวแปร];.. [type and variable name]; } [รายชื่อตัวแปรยูเนี่ยน กั้นด้วยลูกน้ำ]; union employee { long q1; char id1; int q2; char id2;} //จองแรม= 4 byte |
13. | typedef - | type definition ให้นิยามชนิดตัวแปรในชื่อชนิดใหม่ เป็นการนิยามชื่อเท่านั้นเพื่อความสะดวกในการใช้งานไม่ได้เป็นตัวแปรชนิดใหม่จริงๆ ใส่นำหน้า type หรือ modifier ตามด้วยชื่อชนิดที่ต้องการนิยาม typedef typedef unsigned char byte; // สามารถใช้ใหม่เป็น byte m, n; // แต่เพียงแค่สมมูลกับ unsigned char m,m; ไม่ใช่ชนิดใหม่แต่อย่างใด typedef char str40[41]; //ตัวแปรอาเรย์มีใช้ตั้งแต่C99 ขึ้นไป typedef struct {float re, im;}complex; //นิยามชื่อชนิด complex typedef char *byteptr; // นิยามชื่อชนิด byteptr |
ANSI-C90 Keywords หลูป เงื่อนไข กระโดด คืน | ||
14. | if | if ถ้า..เป็นจริงแล้ว..จงกระทำ ปรกติใช้คู่กับ else ถ้าเข้าเงื่อนไขก็กระทำการในวงเล็บ{} ถ้ามีประโยคให้ทำอันเดียวก็ไม่ต้องมี{}ปีกกา หรือเขียนอยู่บรรทัดเดียวกับ if เลยก็ได้ if(condition) { block to execute ; } |
15. | else | ในกรณีที่ประโยคหลังif ไม่ได้ถูกกระทำ เนื่องจากถ้า...ไม่เป็นความจริง ถ้า..ไม่เป็นจริงอย่างนั้นelse..จงกระทำ ต้องใช้กับ if ก่อนจึงจะเป็นประโยคที่สมบูรณ์ if(condition) { block to execute; } else { alternative code block; } |
16. 17. |
switch case |
switchจงจับคู่ค่า(int, long, char, enum)ในเงื่อนไขcaseต่อไปนี้..แล้วจงกระทำ ถ้าไม่ตรงกับเงื่อนไขใดให้กระทำในdefaultแทน switch(expression) { case case case [default: default statement;] } switch(today) //ดูในenum { case sat : printf("Happy Saturday"); break; case su : printf("Holiday Sunday"); break; default : printf("Work day"); } เมื่อเข้าcaseใด statementสามารถมีมากกว่า1ประโยคได้โดยไม่ต้องใช้ปีกกา แต่เวลาออกต้องใช้ break; คือคำสั่งการออกเงื่อนไขจับคู่ค่าโดยไม่ข้องแวะเคสอื่นๆอีก |
18. | break | เบรก break หยุดแล้วออกหลูปหรือเงื่อนไข while ,do ,for ,switch เท่านั้น |
19. | default | ดีฟอลท์ default ประโยคเงื่อนไขหลังให้กระทำตามค่าเริ่มต้น ต้องใช้กับประโยคก่อนหน้าจึงทำงานได้สมบูรณ์ เช่น switch... case |
20. | goto | กระโดด goto ไปที่.. กระโดดได้เฉพาะในฟังก์ชั่น โดยตั้งชื่อตำแหน่งปลายทางที่ต้องการกระโดดเอาไว้ เมื่อเข้าประโยคสั่งกระโดดก็จะกระโดดทันที สามารถนำไปใช้สร้างหลูปโดยผนวกเงื่อนไขในการกระโดดด้วย if ก็ได้ จะกระโดดออกจากหลูปก็ได้ หรือไปทำคำสั่งซ้ำๆจนกว่าจะพอใจก็ได้ ***goto กระโดดออกจากหลูปfor,while,doได้ แต่กระโดดเข้าไปไม่ได้ *** |
21. | return | กลับคืนหรือจบreturn ฟังก์ชั่นกลับไปอยู่ที่ตำแหน่งหลังเรียกและคืนค่าบางอย่างหรือไม่ก็ได้ ใช้ภายในฟังก์ชั่นเท่านั้น ส่วนการคืนค่าอยู่ที่การกำหนดฟังก์ชั่น return [expression]; // การคืนค่าเป็นออฟชั่น -------------------------------- int sqr (int x) { return (x*x); } |
22. | for | forสำหรับ ค่า(เริ่มต้น)ต่อไปนี้ ถ้ายังอยู่ในเงื่อนไข ให้กระทำ... แล้วจึงอัพเดทค่า วนทำไปเรื่อยๆจนพ้นเงื่อนไข ในภาษาc หลูปfor เป็นหลูปที่เขียนได้สั้นมากแต่ตำแหน่งภาษามนุษย์อ่านแล้วทำให้สับสน ขั้นตอนจริงๆ ค่าเริ่มต้น -> (เงื่อนไข -> ทำ -> อัพเดทค่า) แล้ววนหลูปเช็กเงื่อนไข for(ค่าเริ่มต้น ; ตรวจสอบเงื่อนไข; อัพเดทค่า) { กระทำการ;} for ([expr1]; [expr2]; [expr3]) statement; แต่ละ expr สามารถใส่หลายๆค่าได้โดยมีลูกน้ำกัน เช่น for (i=1 ,x=10; หลายเงื่อนไข; หลายอัพเดท) ---------------สมมูลกับหลูบนี้----------------- expr1; while (expr2) { statement; expr3; } ---------------------------------------------- int i; for(i = 1; i <= 10; i++) //มนุษย์อ่าน ให้i=1 ,<=10,ให้บวกi แล้วปริ๊น?? ซึ่งผิดนะ { printf("%d ", i); } //คำถามคือสั่งปริ๊นกี่ครั้ง คำตอบคือ10ครั้ง ภาษาCตั้งแต่C99 สามารถประกาศชนิดตัวแปรใน for(int i =1;i<=10;i++){} |
23. 24. |
while do |
1. ในขณะWhile ที่ยังอยู่ในเงื่อนไข ให้กระทำ.... จนกว่าจะพ้นเงื่อนไข สรุปคือให้ตรวจสอบเงื่อนไขก่อน แล้วค่อยกระทำ while(Condition) { ... กระทำ statements;} 2. ให้ทำdo กระทำ.... แล้วในขณะนี้whileยังอยู่ในเงื่อนไขก็จงกระทำต่อไป สรุปคือให้ทำก่อน1ครั้งแล้วค่อยตรวจสอบเงื่อนไข do {... กระทำ Statements; }while(condition); |
25. | continue | ไปต่อcontinue โดยออกจาก{กระทำ;}ทันทีแล้วไปต่อเพื่อวนหลูปอีกครั้ง การคอนทีนิวนี้เป็นการกำหนดเงื่อนไขเช่น if เพื่อให้งดกระทำการบางอย่างแล้วทำการต่อแถววนหลูปอีกครั้งทันที่ เช่น ในfor , while ,do-while บางทีเรียกว่า ขัดจังหวะการกระทำเพื่อไปต่อ (The continue statement interrupts) while(...) { for(...) {if(...) continue; //ifจริงแล้วจะงดกระทำ2 แต่ยังอัพเดทค่า แล้ววนหลูปforต่อ กระทำการ2; } } |
ANSI-C90 Keywords ที่เหลือ | ||
26. | void | ว่างเปล่าvoid.... ไม่มีค่าใดๆ no value, ไม่ต้องใช้พารามิเตอร์ใดๆ no parameter ,ไม่ระบุชนิดตัวแปร no type 1. เมื่อvoidนำหน้าฟังก์ชั่นจะแสดงว่าฟังก์ชั่นนั้นไม่มีการคืนค่า เป็นฟังก์ชั่นให้เข้าทำอย่างเดียว void hello (char name) { printf("ascii code = %d", name); } 2. เมื่อvoidอยู่ในวงเล็บของฟังก์ชั่น แสดงว่าไม่ต้องส่งค่าใดๆเข้ามาใช้ในฟังก์ชั่น int init (void) { return 1; } 3. ใช้voidในการประกาศตัวแปรพ้อยเตอร์ชนิดที่ไม่กำหนดชนิดตัวแปรว่าข้อมูลที่เก็บกี่ไบท์(คือไม่กำหนดว่าเป็น char int float เป็นต้น) เมื่อต้องการใส่เข้าหรือนำข้อมูลออกจากตำแหน่งดังกล่าวให้กำหนดที่อยู่ของพ้อยเตอร์นั้นแล้ว กำหนดชนิดของข้อมูลที่จะใส่เข้าหรือนำออกมาหน้าพ้อยเตอร์นั้น เพื่อให้คอมไพล์เลอร์ตัดสินใจว่าข้อมูลนั้นยาวกี่ไบท์ int x; float r; void *p = &x; // p points to x , &เมื่อใส่หน้าxจะให้ที่อยู่ *(int*)p = 2; // ใส่ข้อมูล2 เข้าไปที่ตำแหน่งชี้x และใส่2ไบท์ printf("The number is %d.\n", *(int *)p); //2 p = &r; // p points to r *(float*)p = 1.1; // ใส่ข้อมูล1.1 ที่&r โดยใส่เป็นfloat 4ไบท์ printf("The number is %f.\n", *(float*)p); //1.100000 |
27. | volatile - | ใส่หน้าชนิดตัวแปร(เช่นหน้า int ,struc) เป็นการประกาศให้คอมไพล์เลอร์รู้ว่า ตัวแปรนี้จะมีการเปลี่ยนค่าได้โดยฮาร์ดแวร์หรือโดยเบื้องหลังที่อาจไม่ได้เกิดจากโปรแกรมของเรา ทำให้ต้องอ่านข้อมูลสดจากรีจิสเตอร์ก่อนนำมาใช้งานไม่ใช่อ่านจากแรมแคชทั่วไปที่จองพื้นที่เอาไว้ |
28. | sizeof | คืนค่าขนาดข้อมูล(type) (variable name) หรือ expression ว่ามีขนาดกี่ไบท์ sizeof(type or name); // printf("%u\n", sizeof(double));ได้ 8ไบท์ sizeof expression; // printf("%u\n", sizeof 5); ได้2ไบท์ |
29. | extern - | external(global) variables เป็นการบอกคอมไพล์เลอร์ให้เรียกใช้ตัวแปรโกบอลเคยที่ประกาศไว้แล้วเอาไว้ในที่อื่นๆ(ไฟล์อื่น)เพื่อเอามาใช้ โดยไม่ได้เป็นการประกาศตัวแปรใหม่ โดยการระบุ extern นำหน้า--type และ ชื่อตัวแปร-- extern data-definition; extern function-prototype; int callCount = 0; // ประกาศเป็นตัวแปรโกบอลนอก เมน()หรืออยู่ในไฟล์ xxx.h extern int callCount; // เป็นการบอกคอมไพล์เลอร์ว่ามันเคยประกาศเป็นโกบอลแล้วให้ไปหามาใช้ จะพิมพ์กี่บรรทัดที่มีคำว่า extern ก็ไม่เกิดเออเร่อขึ้น อาจใช้การผูกโยงไฟล์ เพื่อให้คอมไพล์เลอร์เข้าไปค้นหาไฟล์ที่เกี่ยวข้องจนกว่าจะเจอตัวแปรโกบอล เช่นใช้ #include "xxx.h" |
30. | static - | ให้รักษาค่าตัวแปรเอาไว้หลังจากผ่านการใช้งาน staticใส่นำหน้าtypeหรือชื่อตัวแปรก็ได้ หรือใส่หน้าชื่อฟังก์ชั่นหรือหน้าtypeก็ได้ 1. เมื่อมีการประกาศตัวแปรstaticและกำหนดค่าภายในฟังก์ชั่นเช่น static int callCount = 0; ขณะexecuteเรียกฟังก์ชั่นนั้นระบบจะกำหนดให้ประกาศ callCount=0เพียงครั้งเดียว และเมื่อค่าเปลี่ยนไปจะจำค่านั้นไว้หลังออกฟังก์ชั่น เมื่อเรียกฟังก์ชั่นเดิมใหม่ก็จะเอาค่าเดิมที่เกิดขึ้นตอนออกจากฟังก์ชั่นคราวก่อนมาใช้โดยไม่ประกาศtypeอีกครั้ง 2. เมื่อมีการประกาศ static ทั้งตัวแปรหรือฟังก์ชั่นในไฟล์.cอื่น เมื่อมันถูก#include เข้าไปในไฟล์หลัก ไฟล์เมนจะไม่สามารถใช้หรืออ่านค่าได้ เหมือนกับตัวแปรหรือฟังก์ชั่นนั้นถูกห่อหุ้มปกป้องเอาไว้ไม่สามารถแกะค่าออกมาได้ต้องให้ภายในเรียกใช้งานกันเอง (คล้ายๆคำว่า private funciton ในphp) **สรุปคือยังไม่ได้ลองใช้จริงจึงไม่แน่ใจเท่าไหร่ |
31. | register - | ใช้นำหน้า type เพื่อบอกคอมไพล์เลอร์ว่าตัวแปรนี้ให้เก็บค่าในรีจิสเตอร์ ในC ต่างจาก C++lang ไม่สามารถอ้างอิงตัวแปรโดยอ้อมด้วย&ได้ กลับกันC++ทำได้ register data-definition; register int i; |
32. | auto - | ใส่นำหน้า type หมายถึงให้เก็บค่าระหว่างที่ทำงานอัตโนมัติตอนประกาศอยู่ในฟังก์ชั่นและค่าจะสิ้นสุดลงเมื่อจบฟังก์ชั่น เรียกอีกอย่างว่า local variable ซึ่ง ใส่หรือไม่ใส่มีค่าเท่ากัน เนื่องจากc ตั้งให้เป็นตัวแปรโลคอลโดยเริ่มต้นอยู่แล้วตอนประกาศในฟังก์ชั่นถ้าไม่ได้ใส่ค่าอื่นเช่น static |
C99(1999) และ C11 (2011) keywords
--ที่น่าสนใจและเกี่ยวข้องน่าจะเป็น _Bool ซึ่งสามารถประกาศตัวแปรชนิดใหม่คือ Boolean (หมายถึงตัวแปรจริงเท็จ) ซึ่งในC99 ถูกเพิ่มเติมใน
#define false 0 [keyword in C++]
#define true 1 [keyword in C++]
C99 | |||
_Bool | _Imaginary | restrict | |
_Complex | inline | ||
C11 |
|||
_Alignas | _Atomic | _Noreturn | _Thread_local |
_Alignof | _Generic | _Static_assert |
--หน้าตาปรกติเมื่อเราเริ่มเขียนโปรแกรมภาษาซี
--
#include
int main() {
/* my first program in C */
printf("Hello, World! \n");
return 0;
}--เรามักจะพบคำว่า #include ซึ่งมันไม่ใช่คำสั่งในภาษาซี แต่มีความความหมายบอกให้คอมไพล์เลอร์รู้ว่าให้รวมคำสั่งหรือข้อความหรือข้อกำหนดในไฟล์ที่ต่อหลังinclude เช่น
ซึ่งหลายๆครั้งมักมีไอ้ stdio.h ไฟล์นี้มันคืออะไรหว่า? คอนเซ็บคือ
ไฟล์นามสกุล.h หมายถึงเฮดเดอร์ไฟล์ แปลว่าไฟล์เฮดเดอร์ หรือไฟล์หัวๆที่ควรต้องไปรวบรวมมาก่อนก่อนอ่านบรรทัดต่อไป เมื่ออ่านในที่ www.tutorialspoint.com/c_standard_library/stdio_h.htm และที่ tigcc.ticalc.org/doc/stdio.html พบว่ามีฟังก์ชั่นหรือค่ากำหนดบางอย่างเหมือนกันและบางอย่างไม่เหมือนกัน
stdio.h เป็นไฟล์เฮดเดอร์ที่เอาไว้เก็บ ค่าคงที ฟังก์ชั่น และค่าการประกาศตัวแปรต่างๆ ในการติดต่อกับอะไรสักอย่างหนึ่งด้วยเพื่อให้ส่งค่าinputหรือเพื่อให้รับค่าได้output ด้วยวิธีการมาตรฐาน(stdio น่าจะมาจากคำว่า standard input output) และถูกเขียนด้วยคำสั่งมาตรฐานของ ANSI-C ซึ่งอะไรสักอย่างหนึ่งนั้น ทำให้ค่าในไฟล์stdio.hในเครื่องคิดเลข กับ stdio.hในภาษาC หรือ stdio.hในArduino ไม่เหมือนกัน เพราะอะไรสักอย่างหนึ่งเช่น cpu mcu ฮาร์ดแวร์ ไม่เหมือนกันเป็นต้น --
-- ต่อมาพบว่า printf() ไม่ได้อยู่ในkeywordมาตรฐาน 32 ตัว จากการทำงานพิมพ์ได้แสดงว่าเป็น ฟังก์ชั่น และเป็นฟังก์ชั่นที่อยู่ใน header file ที่ชื่อว่า stdio.h ซึ่งการจะเข้าใจว่า printf ใช้งานอย่างไร ก็ต้องไปเปิดใน stdio.h ดูเท่านั้น
ซึ่ง printf() เป็นฟังก์ชั่นที่เขียนด้วยANSI-Cและมีความเป็นมาตรฐาน คือในเครื่องคิดเลขและในภาษาC และC++ในArduinน ก็เป็นฟังก์ชั่นมาตรฐานที่เหมือนกัน ลองศึกษาใน
tigcc.ticalc.org/doc/stdio.html#printf และใน www.tutorialspoint.com/c_standard_library/c_function_printf.htm คำจำกัดความและการใช้งานของprintf() ก็เหมือนกันแทบทั้งหมด--
-- ส่วนบรรทัด return 0; หมายความว่าออกจากฟังก์ชั่นmian() และให้ค่าหรือคือค่า 0 ออกมา ซึ่ง return เป็น1ในคีย์เวิร์ดมาตรฐานของ ANSI-C
--ยังก็ตามยังต้องเรียนรู้ข้อกำหนดอื่นๆอีกเยอะจนกว่าจะเขียนเป็น อีกทั้งยังต้องเรียนรู้ไลบารี่และฟังก์ชั่นซึ่งมีมากมายเยอะแยะและอาจไม่จำเป็นกับการใช้งาน ถ้าอันไหนจำเป็นผมจะกลับมาเขียน
แต่ตอนนี้ ผมคงได้แต่ทำลิงค์เอาไว้ให้คุ้ยได้ต่อไปเท่านั้น เพราะมันนอกเนื้อหาหัวข้อที่ผมกำลังจะเขียน
เรียน C www.tutorialspoint.com/cprogramming/index.htm
เรียน C++ www.tutorialspoint.com/cplusplus/index.htm
--
--131--
--131--
--131--
---
---
---
กำลังเขียนอยู่ครับ 8/3/2560
Arduno IDE คือ? , Atmel studio คือ?
ผมเรียกตัวเขียนโปรแกรม Arduino IDE คือเรียกตามแบบชาวบ้านที่ยังไม่รู้เรื่องอะไรเลย เมื่ออ่านหนังสือของETTดูพบว่าเขาบอกว่า Arduino IDE ตัวมันเองไม่ใช่C-Complierที่สร้างขึ้นมาใหม่ ประมาณว่าตัวที่เรียกว่าSketch เป็นเท็กอิดิเตอร์(คล้ายๆnotepad)ซึ่งเป็นฉากบังหน้า ส่วนเบื้องหลังArduinoจะไปเรียกตัวคอมไพล์เลอร์C และยูทีลิตี้อื่นๆ --
-- Arduino IDE เลือกใช้ C-Compiler ของ GNU AVR-GCC Toolchain ร่วมกับ library function ของ avr-libc ส่วน Utility ที่ใช้ในการอัพโหลด(หรือดาวโหลดโค๊ด)ให้avr นั้นจะใช้ซอฟแวร์ของ AVRDude --
เท่าที่ลองติดตั้ง ARDUINO 1.8.1 - 2017.01.09 แล้วหาไฟล์ revision.txt พบว่า ARDUINO 1.6.10 - 2016.07.26 ได้อัพเดทไฟล์
[core]
* avr: toolchain updated to latest Atmel release 3.5.3 http://distribute.atmel.no/tools/opensource/Atmel-AVR-GNU-Toolchain/3.5.3/
The updated tools are now binutils-2.26, gcc-4.9.2, avr-libc-2.0.0, gdb-7.8
* avr: avrdude updated to version 6.3
เอาล่ะครับมีศัพท์เพิ่มอีกมากมายที่เรายังไม่รู้ อันนี้เราพูดถึงเรื่องC++ที่จะเอามาเขียนโปรแกรมatmega328p นะครับ แต่คงต้องทำความเข้าใจคำพวกนี้ก่อน --
IDE คือ? ย่อมาจาก Integrated Development Environment แปลแบบบ้านๆก็คือ สภาพแวดล้อมพัฒนาที่เพิ่มขึ้นมา จากภาษาC++ จากฟังก์ชั่นและห้องสมุดภาษาCสำหรับ...และAVR เพื่อนำใช้งาน....อะไรสักอย่างหนึ่ง...
แสดงว่า Arduino IDE ก็คือ สภาพแวดล้อมพัฒนาที่เพิ่มขึ้นมาจาก C++และห้องสมุด...และavrเดิม เพื่อให้มีฟังก์ชั่นเพิ่มขึ้นจากห้องสมุดCเดิมโดยสร้างขึ้นเพื่อให้ผู้ใช้งานใช้ง่ายขึ้นกับการเขียนโปรแกรมลงไปในบอร์ดของยี่ห้อ Arduino ซึ่งที่พูดในบทความนี้เป็นmcuในตระกูลavr(ซึ่ง Arduino ก็แตกไปอีกหลายตระกูล) โดยสภาพแวดล้อมพัฒนาฟังก์ชั่นที่เพิ่มขึ้นจะสามารถให้ผู้ใช้งานเขียนสั่งให้mcuทำงานภายในได้ง่ายไม่กี่บรรทัด
เช่น ฟังก์ชั่นการติดต่อกับEEPROMของATmega328p จากห้องสมุดเดิมเป็นการกำหนดวิธีการหลักการทั่วไปในการติดต่อEEPROMของmcuตระกูลAVR ส่วนในArduinoก็เขียนไลบารี่หรือฟังก์ชั่นCเพิ่มขึ้นเพื่อกำหนดวิธีการและตัวแปรจริงที่ติดต่อกับรีจิสเตอร์ที่เกี่ยวกับEEPROMของATmega328p รวมถึงฟังก์ชั่นต่างๆที่เพิ่มขึ้นให้ผู้ใช้งานง่ายกับการเขียนโปรแกรมสำหรับการอ่านเขียนอีอีพรอมของMCUเอง
เช่น ฟังก์ชั่นการสั่งงานservo ในห้องสมุดเดิมไม่มีการกล่าวถึงใดๆ เพราะไม่มีอะไรที่เกี่ยวข้องกับหลักการภายในของmcuเอวีอาร์ แต่เมื่อเขียนโปรแกรมเพิ่มขึ้นก็มีไลบารี่สำหรับservo ให้เขียนสั่งใช้งานได้ง่ายไม่กี่บรรทัด โดยไม่ต้องไปเขียนฟังก์ชั่นหลักการวิธีการสั่งงานเซอร์โว เพราะ arduino เขียนฟังก์ชั่นให้แล้ว
แสดงว่าถ้ามีผู้มีความรู้ทางไลบารี่avr ก็สามารถพัฒนาฟังก์ชั่นใหม่ๆใส่เข้าไปในarduino ide เมื่อพัฒนาฮาร์ดแวร์ชิลด์ควบคู่กันไป จึงเป็นลักษณะ โอเพ่นซอร์ส คือโค๊ดที่ให้เอาไปพัฒนาต่อยอดได้นั่นเอง ซึ่งผู้ผลิตบอร์ดยี่ห้อใดๆที่มีmcu avr ตระกูลที่เหมือนกับ arduino ส่วนใหญ่มักแนะนำผู้ใช้งานซึ่งก็สามารถนำโค๊ดพวกนี้ไปใช้งานได้ แต่ไม่ได้เป็นจุดประสงค์หลักของผู้พัฒนาซอร์สโค๊ดของarduinoเพราะเขาทำให้ซอร์ฟแวร์ฟรีเพื่อที่จะขายฮาร์ดแวร์ได้ดีซึ่งมันก็ขายได้ดีมากจริงๆครับ
-- arduino IDE เท่าที่ผมเข้าใจส่วนตัวครับ --
C-Complier และ ห้องสมุดCที่เกี่ยวกับMCU เท่าที่อ่านดูตามความเข้าใจของผมนะ แบ่งออกเป็น2ฝั่ง ---
1 เป็นฝั่งโอเพ่นซอร์ส ซึ่งเปิดให้ใช้ฟรีและไม่มีค่าใช้จ่ายอะไร ซึ่งเป็นโปรเจ็คของ กนู GNU ซึ่งมีหลักการให้ผู้ใช้งานซอฟแวร์เองเป็นผู้คุมซอฟท์แวร์เพื่อให้ซอฟแวร์ดีขึ้นตามความต้องการของผู้ใช้ ไม่ใช่ซอฟแวร์หรือบริษัทผลิตซอฟแวร์เป็นผู้คุมคนใช้งานหรือป้อนชักจูงความต้องการให้แก่ผู้ใช้งาน โดยเปิดโอกาสให้ผู้มีความรู้ความสามารถมาช่วยพัฒนาซอฟแวร์ต่างๆ ซึ่งหลักการดังกล่าวก็เกิดโปรเจคโอเพ่นซอร์สมากมายจนถึงทุกวันนี้ ข้อดีกล่าวไปแล้วส่วนข้อเสียคืออาจเกิดบั๊กที่ไม่คาดหมาย และต้องรอเขาอัพเดทตามแก้
ฉะนั้นแล้ว กนู หรือ GNU คือ [?wiki GNU] [?site GNU] ก็คือ ระบบปฏิบัติการและคอลเล็กชั่นส่วนขยายของซอฟแวร์คอมพิวเตอร์ที่ให้ใช้ฟรี ภายใต้ใบอนุญาตให้ใช้ของ GNU's project ซึ่งผู้ก่อตั้งคือ Richard Stallman[?wiki] เรามองลองดูคำอุทิศแก่โลกนี้ของเขาในเรื่องซอฟแวร์ vdo Free software, free society: Richard Stallman at TEDxGeneva 2014 CC-BY-ND 3.0 by TEDx Talks---
2 เป็นฝั่งที่ต้องจ่ายเงินซื้อซอฟแวร์ ซึ่งซึ่งมีทั้งให้ทดลองใช้บางส่วน และเสียเงินถ้าต้องการใช้งานให้เต็มประสิทธิภาพ ซึ่งอาจมีหลายค่าย หลายบริษัทแต่ค่ายที่ใหญ่ๆที่เปิดมานานคือ Keil [?wiki ] [?site] ซึ่งเป็นบริษัทค่ายเยอรมัน ที่สร้าง C-Complier และห้องสมุดCของเขาเอง ซึ่งข้อดีที่คนยังเลือกใช้คือเขาเขียนโค๊ดได้อย่างมีประสิทธิภาพในการลดขนาดโปรแกรมที่จะอัดลงmcu ในการนี้ผู้ต้องการลดขนาดจำเป็นต้องใช้งานในฝั่งค่ายเสียเงินนี้ เพราะเขาพัฒนาเพื่อให้ขนาดโปรแกรมลดลง ส่วนการอัพเดทแก้บั๊กเป็นส่วนบริการที่เขาคงให้ความสำคัญเป็นอันดับต้นๆ แต่ถ้าเราต้องการจะลดขนาดของโปรแกรมลงอีกให้ไปเขียนด้วยภาษาแอสเซมบลี้จะดีกว่าไม่มีค่าใช้จ่ายอะไรด้วย (เท่าที่ดูใน support list ของ keil จะไม่ซัพพอร์ท AVRถึงอยากใช้ก็ไม่มี ส่วนใหญ่ซัพพอร์ทตระกูล 8051 และ ARM ซะส่วนใหญ่) ---
---
---
---
---
---
---
-เรากลับมาลองดู Atmel studio 2017 คือ ตัวเขียนโปรแกรม อยู่ในกลุ่มไหน ต้องทราบก่อนว่า Atmel เป็นผู้ผลิตฮาร์ดแวร์ เช่น Atmega328p และmcuอื่นๆในตระกูล51 avr arm หรืออื่นๆ(พอดีรู้จักแค่นี้) เมื่อได้โอกาสโดยจึงนำส่วนโอเพ่นซอร์สของกนูมาเป็นหลักในการสร้างระบบตัวเขียนโปรแกรม ซึ่งผนวกเอาทั้งภาษาC/C++ หรือจะเขียนด้วยภาษาแอสเซมบลี้ด้วยก็ได้ แล้วก็มีความสามารถในเรื่องปลั๊กอิน ยูทิลิตี้ต่างๆ ในการจำลองการใช้งาน รวมทั้งการอัดโปรแกรมลงในmcu และที่สำคัญคือความสามารถในการติดตั้งIDEต่างของค่ายอื่นๆที่ทั้งเป็นโอเพ่นซอร์สอย่างArduino หรือ Keil ก็ได้อีกด้วย แต่เท่าที่อ่านเนื้อหาหลักใน www.atmel.com/webdoc/GUID-ECD8A826-B1DA-44FC-BE0B-5A53418A47BD/ ถ้าไม่ได้โหลดปลั๊กอินอะไรเพิ่มเติมก็สามารถใช้ภาษาC ในการเขียนโปรแกรมได้เลย โดยมาจาก
- AVR8 GCC Toolchain 3.5.0 with upstream versions
gcc 4.9.2 เข้าใจว่าใช้ C++ compiler ของกนู ซึ่งเป็นบางส่วน
ของ GCC (The GNU Compiler Collection)
4.9.2 เป็นเวอชั่น ปี2014
Binutils 2.25 คำจำกัดความของ GNU Binutils คือผมก็ไม่เข้าใจ
เท่าไหร่ แต่น่าจะเป็นโปรแกรมเบื้องหลังที่เกี่ยวข้อง
เมื่อนำ gcc มาใช้
avr-libc 1.8.0svn [?atmel] คือไลบารี่C ที่เกี่ยวข้องกับการเขียนAVR
ลอง avr-libc 1.8.0 manual pdf แต่ปัจจุบันเป็น
Standard C library for AVR-GCC
avr-libc 2.0[?site]
gdb 7.8 น่าจะเป็น debuger ของ GNU
สรุปแล้วคงพอเข้าใจคำแล้วนะครับว่า Arduino IDE , กะ Atmel studio ต่างก็ใช้ C-compilerของ GNU ซึ่งอยู่ในส่วน GCC และใช้ avr-libc ก็เหมือนกัน แต่ Atmel studio อาจมีฟังก์ชันสภาพแวดล้อมที่เขียนเพิ่มขึ้นหรืออาจไม่มี(เด๋วขอลองใช้ดูก่อน) คือถ้าใช้ Atmel studio โดดๆ ก็ต้องไปเขียนเพิ่มเอาเอง แต่มันเพิ่ม IDEของ Arduino ได้ด้วยนี่สิ แล้วก็มีดีบักเกอร์ส่วนที่atmelเขียนเอง แล้วก็ติดตั้งยูทิลิตี้อื่นๆได้อีกเยอะ ใช้ซอฟแวร์อัดโปรแกรมของ avrdude ก็ทำได้ คือทำได้เหมือน Arduino คำถามคือทำไมไม่ใช้ Arduino IDE ล่ะ เท่าที่ดูวีดีโอข้างล่าง เขาพบข้อผิดพลาดหรือบั๊กของIDE Arduino เมื่อเขียนด้วยโค๊ดง่ายๆตามที่Arduinoกำหนด เนื่องจากทางArduinoใส่ค่าเบื้องหลังที่จำเป็นสำหรับโปรเจ็กพื้นฐานทั้งหมดให้หมดแล้ว เมื่อต้องการปรับปรุงต้องศึกษาและปรับโค๊ดในคอนโทรลรีจิสเตอร์เพิ่มเติม ซึ่งต้องไปค้นคว้าแกะเอาเองว่ามันต้องแก้ไขอะไรตรงไหน เช่นในวีดีโอของท่านนี้พบว่า เมื่อต้องการเขียนให้สลับเปิดและปิดขา 13 โดยรอเวลาสลับ 50us ด้วยฟังก์ชั่น delayMicroseconds(50); กลับได้50usกว่าๆแล้วไม่นิ่งด้วย เอาสโคบจับดู มีค่าแกว่งอยู่ประมาณ3.5us เขาก็พยายามค้นอยู่นานพบว่า ในโค๊ดมีอินเตอรัพไทม์เมอร์แอบไปทำงานอยู่ข้างหลัง จึงจำเป็นต้องใส่โค๊ดเพิ่มเติมที่ไม่ใช่Arduinoจัดมาเพื่อปิดอินเตอรัพนั้นเสีย
Why I’m switching over from the awesome Arduino IDE to Atmel Studio : by Joop Brokking กดลิงค์ไปดูที่youtube เลยครับ
https://www.youtube.com/watch?v=648Tx5N9Zoc
ในวีดีโอเขาแนะนำเชิงสรุปว่า
บางโปรเจ็ก(ที่ต้องลงลึกไปในระดับคอนโทรลรีจีสเตอร์)ไม่สามารถใช้ Arduino IDE ในส่วนของ function และ library มาตรฐานที่จัดไว้ให้ใช้ จำเป็นต้องใส่โค๊ดเพิ่มเติมเพื่อคุมรีจิสเตอร์คอนโทรลให้ทำงานได้ตามความต้องการ
และเขาก็พบว่าด้วยการสั่งงานแบบเดียวกันนี้เมื่อเขาเขียนโค๊ดใน Arduino IDE ด้วยการเพิ่มคำสั่งที่ปิดอินเตอรัพที่ไม่จำเป็นออกไป ได้ขนาดโค๊ด506byte แต่เมื่อเขียนด้วย Atmel studio กลับใช้โค๊ด(program memory)ไปเพียง 164 byte แต่การเขียนด้วย Atmel studio จะต้องศึกษาเพิ่มเติมซึ่งมันไม่ง่ายนัก เท่ากับการใช้ Arduino IDE ยังไงเขาก็แนะนำ Arduino IDE สำหรับผู้เริ่มต้นใหม่ แต่เขาย้ายไปทำโปรเจ็คของเขาใน Atmel studio แล้ว
ผม
---
---
-ถ้าเราศึกษาภาษาC จากลิงค์ข้างต้นเราก็พอจะรู้วิธีการเขียนภาษาCเบื้องต้นแล้ว
ก่อนจะศึกษา C++ ใน arduino เราจะรู้ได้อย่างไรว่า ไลบารี่ต่างๆ ฟังก์ชั่นต่างๆ ที่ C ใน Arduino ide เตรียมเอาไว้ให้เราใช้งานมีอะไรบ้าง หรือ Arduino ide เรียกใช้ไลบารี่ต่างๆ รวมถึง ซอร์สโค๊ดฟรีต่างๆหรือฟังก์ชั่นต่างๆที่เขียนเอาไว้สำหรับชิลล์แต่ละตัวมันใช้งานหรือมันเรียกใช้งานอย่างไรผ่านตัวแปรอะไรบ้าง และ มันมีการเชื่อมโยงไฟล์ต่างๆที่ซ่อนเอาไว้อย่างไร บางทีมันซ่อนกันแบบลึกลับซับซ้อน มือใหม่อย่างผมก็งงครับ --
-ลองใช้ doxygen--
---
---
---
---
---
บันทึกช่วยจำ เมื่อแปลงภาษา C++ เป็น Assembly ทำใน Atmel Studio IDE
-เนื่องจากมือใหม่ลองเขียนโค๊ดด้วยC แล้วรู้สึกสับสนว่า C++ ที่เรียบเรียงโดย GNU AVR-GCC Toolchain ร่วมกับ library function ของ avr-libc จะมีความฉลาดแค่ไหนและสามารถแปลเป็นภาษาAsm ได้โค๊ดอะไรกันบ้าง แต่เท่าที่ลองเรื่องเกี่ยวกับการให้ค่าพอร์ท พบว่าผู้สร้างสรรค์ระบบของ GNU ในC++เก่งมากครับแปลโค๊ดเป็นASMได้ดีแต่จะได้ความยาวโค๊ดต่างๆกันไปตามโอเปอร์เตอร์ในภาษาC จึงขอบันทึกเอาไว้เนื่องจากมีความสนใจในความเก่งกาจของผู้สร้างสรรค์ AVR-GCC เป็นอย่างมาก -
--
ตัวอย่างการให้ค่าPORTB, DDRB เมื่อแปลง C เป็น asm | ||
คำจำกัดความoperatorในภาษา C เมื่อนำมาใช้ระบุค่าในportb เป็นภาษาที่ชวนให้สับสนเป็นอย่างมากสำหรับมือใหม่ใน bitwise เนื่องจากลองอ่านเรื่องนี้ใน tutorialspoint.com/cplusplus/cpp_operators.htm โดยเฉพาะ operator << , >> มีความหมายว่าเลื่อยบิทไปทางซ้าย ,หรือขวา ตย. กำหนดให้ A = 0011 1100 , B = 0000 1101 ใส่ A<<2 2="" a="1111" 0000="" br=""> ใส่ A>>2 จะหมายถึง เลื่อนบิทวนไปทางขวา2ครั้ง A= 0000 1111 แต่เมื่อมาลองตีความในการให้ค่าพอร์ทจะสับสนมากๆ จึงไม่ควรตีความว่าเป็นการวนบิทในขั้นต้น แต่ผมขอนิยามในแบบของผมก่อนดังนี้ จะผิดถูกเดี่ยวทดลองแปลแล้วค่อยว่ากันไป 1<<PORTB5 ควรหมายถึง ให้ PORTB5 = 1 = high บิทที่เหลือในไบท์เป็น0 หรือ ให้บิทที่5 ใน PORTB=1 บิทที่เหลือในไบท์เป็น0 0<<PORTB5 ควรหมายถึง PORTB5 =0 และบิทที่เหลือในไบท์PORTB เป็น0 (0< ควรตีความได้แค่นี้ก็เท่านั้น เนื่องจากเมื่อนำไปประกอบกับประโยคอื่นๆ จะทำความเข้าใจยาก ทำให้เกิดเออเร่อในเชิงการตีความภาษามนุษย์ ซึ่งผมจะสรุปดังข้างล่าง ****สรุปคือมือใหม่ไม่ควรใช้โอเปอเรเตอร์ 1<<บิทที่x ในแอดเดรสI/O ที่มักจะระบุว่าให้บิทที่เท่าไหร่เป็น1หรือ0 เพราะอาจเกิดเออเร่อเนื่องความเข้าใจคลาดเคลื่อนในภาษาในบางกรณี**** เพราะผมลองทำการ blink ขา13 ใน Arduino Uno พบว่ามีคำสั่งเหล่านี้บางทีเกิดเออเร่อขึ้นแบบหาแมนน่วลที่มาไม่ได้ เข้าใจว่าเป็นเออเร่อวิธีการใช้งาน<<,>>ของ GNU avr gcc จากการแปลความของเราๆที่ใช้มันอย่างผิดๆ ควรใช้เป็นคำสั่งสีน้ำเงินแทน*** |
||
C++ | ASM | |
1. | *ให้ค่าบิท5=1ในบิทโดยไม่กระทบบิทอื่น* DDRB |= (1<<DDB5); PORTB |= (1<<PORTB5); (PORTB |= 0b00100000;) |
**C แปลว่า เอา ไบท์DDRB มา or กับ 0b0010000 ** DDRB และ PORTB เป็นตำแหน่งI/O ที่สามารถใช้การแอ็กเซส ระดับบิทได้โดยตรงแบบลัดสั้นที่สุด ผ่านคำสั่ง SBI 2ไบท์/2clk SBI 0x04,5 ;Set bit in I/O register ;^หรือ SBI DDRB,5 บิทที่5=1 SBI 0x05,5 ;Set bit in I/O register ; ^0x05 คือตำแหน่ง PORTB ผลคือ กำหนดให้ DDRB5 เป็น 1 เอ้าพุท และบรรทัดถัดมากำหนดให้ PB5 (ขา13ใน uno) เป็น1 แสดง LED (SBI 0x05,5 ;ผลเหมือนกันจ้า) |
2. | **ให้ค่าบิท5=1 กระทบบิทอื่น** PORTB = (1<<PORTB5); (PORTB = 0b00100000;) |
**C แปลตรงตัวคือ PORTB = 0b0010000 ** เมื่อแปลง จะได้ชุด LDI 2บ/1clk , OUT 2บ1clk LDI R24,0x20 ;Load immediate ;0x20=0b00100000 OUT 0x05,R24 ;Out to I/O location ; ^0x05 คือตำแหน่ง PORTB ผลคือ ให้ R24=0b0010000 แล้วก็เอาไปเก็บ ในPORTB มีผลให้ PORTB = 0b00100000 (ผลเหมือนกันจ้า) |
3. | *ให้บิท5=0โดยไม่กระทบบิทอื่น* PORTB &= ~(1<<PORTB5); (PORTB & = 0b11101111) |
**C ให้ นำไบท์ PORTB มา โลจิกแอนด์ กับ 0b11011111 โดยบิทที่ห้าหรือPB5=0 การคอมไพล์เป็น CBI ถือว่าตรงและลัดสั้นที่สุด CBI 0x05,5 ;Clear bit in I/O register (CBI 0x05,5 ;ผลเหมือนกัน ) |
4. | **ให้บิท5=0 กระทบบิทอื่น** PORTB = (0<<PORTB5); (PORTB = 0b00000000;) |
**C แปลตรงตัวตือ PORTB=0b00000000 ความฉลาดของการแปลงคือดูที่ข้างล่าง คอมไพล์เลอร์Cจะหาดูว่ามี รีจิสเตอร์ตัวไหนที่มีค่า0 เช่นR1=0 แล้วจัดคำสั่ง OUT ให้ทันที1เดียวจบ OUT 0x05,R1 ;Out to I/O location (ผลเหมือนกัน) |
5. | **??ให้บิท0 ไม่กระทบบิทอื่น ตย. ยังไม่ชัด***** PORTB &= (0<<PORTB5); |
**C อยากให้เหมือน นำ PORTB แอนด์กับ 0 แต่ตอนคอมไพล์กลับฉลาดแบบครึ่งๆกลางๆ เพราะมีคำสั่งส่วนเกินมา1บรรทัด เนื่องจากมีR1=0 จึงตัดสินใจกลางทางเป็น **ASM มีคำสั่ง IN , OUT ซึ่งเป็นคำสั่ง2ไบท์ใช้1clock IN R24,0x05 ;In from I/O location ส่วนเกินที่ไม่ได้ใช้ ;^ซึ่ง ให้ R24= ค่าของPORTB5(0x05) ซึ่งเตรียมกระทำการ แอนด์ ANDI แต่กลับใจกลางลำ OUT 0x05,R1 ;Out to I/O location R1=0 คืนค่ากลับPORTB |
6 | ;ลองเซ็ทให้เป็น1ทั้งหมด.. PORTB = 0b11111111; **จะให้บิท5=0 ไม่กระทบบิทอื่น แต่กลับกลายเป็นเออเร่อ** PORTB &= (0<<PORTB5); ----------------------------- PORTB = 0b11111111; **ลองให้บิท5=0** PORTB &= 0b11011111; |
SER R24 ;Set Register R24=0b11111111 OUT 0x05,R24 ; PORTB(0x05)=R24 **C to ASM ถูกแปลงควรมีคำสั่ง IN, ANDI ,OUT 3คำสั่ง แต่กลับเกิดเออเร่อขึ้นทำให้ PB5 และบิทอื่นๆใน PORTB กลายเป็น 0 หมด ซึ่งเป็นเออเร่อของ GNU AVR-GCC หรือาจเป็นเพราะคำสั่งภาษาCที่เราเขียนไม่ถูกต้องหรือเข้าใจไม่ถูกต้อง คิดว่าเป็นการให้ค่าบิทโดยตรงได้ IN R24,0x05 ; R24 ← I/O (0x05) PORTB OUT 0x05,R1 ; I/O (0x05) ← R1(R1=0) คือแทนที่PORTB จะเท่ากับ 0b11011111 กลับกลายเป็น PORTB =0 ---------------------------------------------- SER R24 ; R24=0xFF OUT 0x05,R24 ; PORTB=R24 CBI 0x05,5 ; ให้บิทที่5 ในPORTB=0 |
7 | ;ลองเซ็ทก่อน PORTB =0b00100000; **คิดว่าPB5 แอนด์ กับ1 กลับกลายเป็นเออเร่อ** PORTB &= (1<<PORTB5); ----------------------------- **PB5 แอนด์กับ1 ที่ถูกควรใช้เป็น*** PORTB =0b00100000; PORTB &= 0b11111111 -----ให้บิท4บิท6=0 ไม่กระทบบิทอื่นๆ-------- PORTB &= 0b10101111; |
LDI R24,0x20 ;Load immediate OUT 0x05,R24 ;Out to I/O location **เมื่อแปลแล้วเกิดเออเร่อคือมันไม่เป็นไปตามที่สมมติเอาไว้เลย IN R24,0x05 ; In from I/O ANDI R24,0x20 ;Logical AND with immediate ;^ เอาค่าไบท์ในPORTB แอนด์ กับ 0b00100000 ซึ่งจะกลับกลายเป็นเกิดเออเร่อ เพราะต้องการให้แอนด์แค่ PB5 OUT 0x05,R24 ;Out to I/O location ---------------------------------------------------- LDI R24,0x20 OUT 0x05,R24 IN R24,0x05 ; R24=PORTB=0b00100000 OUT 0x05,R24 ; Out to I/O location ดูเหมือนคอมไพล์เลอร์C++จะให้ดาวโหลดค่าportbมาก่อน แล้วจึงเช็กค่าแอนด์ ถ้าเท่ากับ0xFF ก็อัพโหลดคืนค่าเหมือนเดิม ซึ่งไม่มีอะไรผิดแต่มันดันมีคำสั่งที่ไม่ต้องมีก็ได้เพราะดูเหมือนครึ่งๆกลางๆแต่ช่วยลดโค๊ดไปได้1เวิร์ด ---------ให้บิท4บิท6=0 ถูกต้อง-------------- IN R24,0x05 ;R24=PORTB=0b00100000 ANDI R24,0xAF ;Logical AND with immediate ;^ ให้ R24 แอนด์ กับ 0b10101111 (0xAF) OUT 0x05,R24 |
สรุป การใช้ operator << (bitwise) สำหรับการกำหนดค่าบิทในPORTB หรือค่ารีจิสเตอร์I/O อื่นๆ สามารถกระทำได้ดังนี้ ทำความเข้าใจใหม่อีกครั้ง ตามลำดับดังนี้ การให้ค่าบิทในportb ก็คือนิยามการวนบิทในภาษาC อยู่ดีนั่นเอง ถ้าไม่ใช่มือใหม่ก็บอกว่าดีอ่านง่ายสบายตาไฮโซกูรู้เรื่องคนเดียวพอ แต่มือใหม่บอกว่ามันคืออะไรของมึงหว่า 1. PORTB =1; // สมมูลกับ PORTB=0b00000001; หรือ PORTB=0x01; 2. PORTB =(1<<5); /* ให้ PORTB =0b0000 0001 แล้ววนไปทางซ้าย5ครั้ง ฉะนั้น PORTB =0b0010 0000 เมื่อภาษาแมคโครหรือภาษาที่สร้าง คำจำกัดความ ขั้นตอน วิธีการ ก่อนที่ ภาษาC จะทำการคอมไพล์ เมื่อ (1<<5) โดยให้ตัวเลขด้านหลังสามารถใช้ คำจำกัดความว่า เป็น PB5 , PORTB5 ,5 มีความหมายเดียวกัน */ 3. PORTB =(1<<PORTB5); /* มีความหมายเหมือนกับในข้อ2. สามารถนิยามเป็น ให้บิทที่5=1 นอกนั้นบิทอื่นๆในไบท์portbเป็น0หมดก็ถูกต้อง */ 4. PORTB = 0b00100001; /* ให้บิทที่ 5 และบิทที่0 เป็น1 นอกนั้นเท่ากับ0 สมมูลกับ PORTB = 0b00100000 | 0b00000001; portb มีค่าได้จาก2ไบท์ที่ทำ logic or gate ร่วมกัน สมมูลกับ PORTB =(1<<5)|(1<<0); */ 5. PORTB &=~(1<<5); /* หมายถึง เราต้องการให้ บิท5 เป็น0 อย่างดียวโดย ไม่กระทบกับบิทอื่นๆ ในการนี้ เราไม่นิยาม PORTB = (0<<5) หริอ PORTB &=(0<<5) เพราะมันจะให้ค่า PORTB =0 แทน เนื่องจาก มีความหมายว่าให้วน0จากบิทที่0ไปทางซ้าย5ครั้งบิทที่เหลือในไบท์เป็น0 ขั้นตอนเป็นดังนี้ PORTB = (0010 0001 ข้อ4.) logic and กับ ~(0010 0000) PORB = (0010 0001) & (1101 11111) ฉะนั้น PORTB = 0000 0001 */ // ข้อ5. สมมูลกับ PORTB &=0b00100000 ; ใจความชัดเจนพอๆกัน ไม่ต้องตีความกลับบิทจาก0เป็น1และ1เป็น0ไปมาให้ดูไฮโซปวดหัว 6. PORTB |=(1<<6)|(1<<PORTB4) ; /*ความหมายคือให้ บิทที่6และบิทที่4ใน PORTB เท่ากับ1 โดยไม่กระทบกับบิทอื่นๆ PORTB= (0000 0001 ข้อ5.) or กับ (0100 0000 or 0001 0000) PORTB= 0000 00001 | 0101 0000 ฉะนั้น PORTB=0101 0001 */ 7. PORTB &=~(1<<0); /*สมมูลกับ PORTB |=0b11111110; ฉะนั้นจากข้อ6. PORTB=0101 0000 */ 8. PORTB &=~((1<<PORTB6)|(1<<PORTB4)) /* ให้บิท6บิท4=0 อย่างเดียว จะเห็นว่าถ้าใช้ยิ่งยาวก็ยิ่งยากและยิ่งงง เราใช้ให้สั้นๆจะสบายกว่าด้วย สมมูลกับ PORTB &= 0b10101111; สั้นกว่ามากมายมีความเหมือนกันด้วย ไม่รู้จะนิยามมาทำไมให้เยอะแยะปวดหัว ภาษาCที่ใช้ bitwise ในการกำหนดบิท จะใช้ดีก็ต่อเมื่อไม่เกิน1บิท ฉะนั้น PORTB = (0101 0000 ข้อ7.) logic & ~((0100 0000| 0001 0000)) PORTB = 0101 0000 & 1010 1111 PORTB =0 */ |
||
ตัวอย่างการใช้ function _delay ในC แปลงเป็น asm | ||
C (Atmel studio) | ASM | |
1 | _delay_us(1); 1-47 us (asm 1byte value loop) |
***C function นี้ก่อนใช้ต้อง ใส่ภาษาแมคโครว่า #define F_CPU 16000000UL /*ค่าสัญญาณนาฬิกา ในที่นี้ ใช้ Arduino Uno 1คล็อก [1c] MCU ใช้ เวลาทำงาน =1/16Mhz = 0.0000000625 s [1c] ,1clock = 62.5ns */ #include <util/delay.h> //ฟังก์ชั่นดีเลย์ ในห้องสมุด ***asm จะได้โค๊ดข้างล่าง โดยปรกติ ถ้าต้องการดีเลย์ 1 us ต้องหาอะไรให้ mcu ทำ เป็นจำนวนกี่คล็อก? หาได้จาก /*เวลา 62.5 ns (นาโนวินาที) = mcuใช้เวลา 1c เวลา 1000 us (ไมโครวินาที)= 1000/62.5 =16c =16clocks */ LDI R24,0x05 /*[1c] Load immediate โหลดค่า5เข้าR24 โดย 1 us , R24=0x05 /..../ 47 us, R24=0xFA */ DEC R24 /*[1c] Decrement ลดค่าR24ลง1ค่า คำสั่ง DEC จะมีผลกับแฟลก Z หรือซีโร่ (zero) คือ 1. R24-1 แล้วไม่เท่ากับ0 ,Z=0 ทุกครั้ง 2. R24-1 แล้วR24เท่ากับ0 ,Z=1 ทันที */ BRNE PC-0x01 /*[2c],[1c] Branch if not equal if (Z = 0) then PC ←PC-1 else PC+1 1. [2c] ถ้าZ=0 คือR24ยังไม่0 จะโดดย้อนไปที่ DEC อีกรอบ 2. [1c] ถ้าZ=1 คือR24=0แล้ว จะอ่านบรรทัดต่อไป NOP ;[1c] No operation สรุปได้ 16 clocks = 1us พอดีดังนี้ ldi[1c]r24=5 // dec[1c]r24=4 // brne[2c] // dec[1c]=3 // brne[2c] // dec[1c]=2 // brne[2c] // dec[1c]=1 // brne[2c] // dec[1c]=0 // brne[1c] // nop[1c] |
_delay_us(1-47us) ต่อ เมื่อแปลงเป็น asm จะได้ฟังก์ชั่นดังนี้ เมื่อเขียนตามหลักภาษา asm ทุกๆ1 us ต้องกินclock 16 รอบ เศษ=1 ดูด้านล่างหลักการคำนวนคล็อก จะมีรูปแบบอยู่ 3 แบบ เศษ=0,1,2 เท่านั้น ( .EQU us_va1 =5 ;1us=16clk LDI R24, us_va1 ;[1c] r24=5 1us_notzero: DEC R24 ;----below BRNE 1us_notzero ;----[14c] = dec&brne[(5*3c)-1c] NOP ;[1c] ) เศษ=2 ( .EQU us_va2 =10 ;2us=32clk LDI R24, us_va2 ;[1c] r24=10 2us_nz: DEC R24 ;----ดูข้างล่าง BRNE 2us_nz ;----[29c] = [(10*3c)-1c] RJMP next ;[2c] next: ; ) เศษ=0 ( .EQU us_va3 =16 ;3us=48clk LDI R24, us_va3 ;[1c] r24=16 3us_nz: DEC R24 ;----ดูข้างล่าง BRNE 3us_nz ;----[47c] = [(16*3c)-1c] ) ;หลักการคำนวนของ _delay(1-47us) เมื่อแปลงเป็น asm เพื่อคำนวนคล็อก จะต้องได้ค่าdif 0,1,2 อย่างใดอย่างหนึ่ง เพื่อกำหนดรูปแบบตอนจบฟังก์ชั่นในasm โดยอาศัยแกนคำสั่งวนหลูป DECและฺBRNE ซึ่งต้องมีการวนหลูปเป็นจำนวนvครั้งแล้ว MCUจะกินคล็อกดังนี้ รูปแบบ 1us*16c/us= ldi[1c] +dec&brne((va*[3c])-[1c])+[เศษ0,1,2] 16 = 1 +3v-1 +เศษจากการหาร v = 16 div 3 =5 ;--div คือหารแล้วเอาแต่เลขจำนวนเต็มที่หารลงตัว เศษ = 16 mod 3 =1 ;--mod คือหารแล้วเอาเศษที่เหลือที่หารไม่ลงตัว ;1us*16 =16clk , va=(16)/3 =5 เศษ1 // 5*3 =15 , เศษ 16-15=1 ;2us*16 =32clk , va=(32)/3 =10 เศษ2 // 10*3 =30 , เศษ 32-30=2 ;3us*16 =48 , va=(48)/3 =16 เศษ0 // 16*3 =48 , เศษ 48-48=0 ;4us*16 =64 , va=(64)/3 =21 เศษ1 // ;5us*16 =80 , va=(80)/3 =26 เศษ2 // ;6us*16 =96 , va=(96)/3 =32 เศษ0 // ;47us*16=752 , va=(752)/3=250 เศษ2 // 250*3 =750 ,เศษ 2 |
||
_delay_us(48) 48us ถึง 16,384us (asm 2byte value loop) ------------------------- _delay_us(16385) 16,385us to 5,242,880 us (asm 3byte value loop) ------------------------- _delay_us(5242881) สามารถใส่ค่าได้มากถึง 5,242,881us to ... ..ไปคำนวนเอาเอง ประมาณ..268,435,000us (asm 4 byte max value loop) -------------------------- มากกว่านี้ทำไม่ได้ครับ ต้องสร้างเอาเอง |
**C _delay_us(48) to asm LDI R30,0xBF ;[1c] 48us, R30=191 LDI R31,0x00 ;[1c] lp1: SBIW R30,0x01 ;---[2c] (R31:R30)-1 BRNE lp1 ;---[2c] ไม่0 วนlp1 ;[1c] เมื่อ R31:R30=0 RJMP nex ;[2c] nex: NOP ;[1c] หลักการคำนวนหาค่าva หรือค่าที่จะใส่ในr31:r30 ก็เหมือนเดิมแต่เปลี่ยน rjmp หรือ nop เป็นอย่างอื่น เพื่อให้คล็อกได้ลงตัวเท่าที่ต้องการคือ 48us*16c/us= [768c] [768c] =[1c]+[1c]+([4c]*va-[1c])+[clkที่ขาด] ซึ่ง[clkที่ขาด]เข้าใจว่าอยู่ระหว่าง 0-3clk ในตัวอย่างด้านบนคือ เศษ3c ที่ได้จำนวนclkจาก rjmp+nop ซึ่งจะเป็นอย่างอื่นก็ได้ตามต้องการ **16384us ค่าva ที่ได้จะเป็น R31:30=65535=0xffff ----------------------------------------------- **C _delay_us(16385) to asm LDI R18,0xCF ;[1c] R18=low(52431) LDI R19,0xCC ;[1c] R19=high(52431) LDI R25,0x00 ;[1c] lp3: SUBI R18,1 ;---[1c] R18-1 SBCI R19,0 ;---[1c] R19-0-carry SBCI R25,0 ;---[1c] R25-0-carry BRNE lp3 ;---[2c] va<>0 ไป lp3 ;[1c] R25:R19:R18=0 RJMP nx ;[2c] nx: NOP ;[1c] จะสังเกตว่า SBCI R25,0 โดยที่ R25=0 เมื่อตัวตั้งเป็น0 ลบด้วย0 จะไม่ทำให้เกิดflagZ และไม่ก่อความเปลี่ยนแปลงใดๆกับ flag Z ก่อนหน้านี้ โดยถ้าก่อนหน้านั้น เกิด Z=1 ที่ SBCI R19,0 ก็ตาม จากโค๊ด R25:19:18 =0x00CCCF =52431 รูปแบบการคำนวนคือ 16385us*16c/us=3 +(5va-1)+(เศษที่เหลือ=3) va = (262160-5)/5 = 52431 เข้าใจว่าเศษที่เหลือถ้าเป็นusตัวเลขอื่นอาจมีเศษตั้งแต่ 0-5 |
|
_delay_ms(1) 1ms - 16ms (asm 2byte value loop) ------------------------- _delay_ms(17) 17ms - 5242ms (asm 3byte value loop) --------------------------- _delay_ms(5243) 5243ms - 268,435 ms (asm 4byte max value loop) -------------------------- มากกว่าค่า 268,435ms จะใช้ไม่ได้ครับ ต้องสร้าง ฟังก์ชั่นเอาเองครับ |
**C _delay_ms(1) to asm LDI R30,0x9F ;[1c] 1ms, R30=191 LDI R31,0x0F ;[1c] lp2: SBIW R30,0x01 ;---[2c] (R31:R30)-1 BRNE lp2 ;---[2c] ไม่0 วนlp1 ;[1c] เมื่อ R31:R30=0 RJMP n_x ;[2c] n_x: NOP ;[1c] 1ms*16,000c/ms= [16,000c] 16,000 =2c+(4*va-1c)+[clkที่ขาด 3c] va = ที่ใส่ใน R31:30 = 3999 = 0x0f9f ---------------------------------------------- **C _delay_ms(17) to asm LDI R18,0xCF ;[1c] R18=low(52431) LDI R19,0xCC ;[1c] R19=high(52431) LDI R25,0x00 ;[1c] lp4: SUBI R18,1 ;---[1c] R18-1 SBCI R19,0 ;---[1c] R19-0-carry SBCI R25,0 ;---[1c] R25-0-carry BRNE lp4 ;---[2c] va<>0 ไป lp3 ;[1c] R25:R19:R18=0 RJMP nx3 ;[2c] nx3: NOP ;[1c] 17ms*16,000c/ms =3 +(5va-1)+(เศษที่เหลือ=3) va = (272000-5)/5 = 54399 ---------------------------------------------- **C _delay_ms(5243) to asm LDI R18,0x95 ;[1c] LDI R19,0x56 ;[1c] LDI R20,0xD5 ;[1c] LDI R25,0x00 ;[1c] lp6: SUBI R18,1 ;---[1c] R18-1 SBCI R19,0 ;---[1c] R19-0-carry SBCI R20,0 ;---[1c] R20-0-carry SBCI R25,0 ;---[1c] R25-0-carry BRNE lp6 ;---[2c] va<>0 ไป lp3 ;[1c] R25:R19:R18=0 RJMP n1 ;[2c] n1: RJMP n2 ;[2c] n2: NOP ;[1c] 5243 ms*16,000c/ms = = 4 +(6va-1)+(เศษที่เหลือ=5) va = (83,888,000-8)/6 =13,981,332 =0x00D55694 |
---
---
---
---
---
---
-บันทึกช่วยจำ โครงสร้างการเขียนภาษาc arduino
เริ่มต้นด้วย header >setup() >loop()
1 header คือภาษาแมคโคร และการประกาศตัวแปรและค่าคงที่
2 setup() เป็นฟังก์ชั่นบังคับที่ทำงานครั้งเดียวในการเซทอัพเช่นการตั้งพินโหมด การตั้งค่าต่างๆในไมโครคอนโทรลเลอร์เป็นต้น
3 loop() เป็นฟังก์ชั่นบังคับที่เมื่อทำงานจบบบรรทัดสุดท้ายก็จะกลับมาเริ่มใหม่ไม่จบสิ้น เพื่อให้สอดคล้องกับหลักการทำงานแบบไมโครคอนโทรลเลอร์ที่จะต้องหางานอะไรทำซ้ำๆกันไปเรื่อยๆ
--
---
-753--
---
-บันทึกช่วยจำ การเขียนโปรแกรมด้วยC++ อ่านและสรุปความมาจากหนังสือ เริ่มต้นการเขียนโปรแกรมด้วยภาษาC++ ,ยุทธนา ลีลาศวัฒนกุล ,บริษัทดวงกมลสมัย หนังสือเล่มนี้ถ้าเกี่ยวกับการเขียนโปรแกรมด้วยภาษาC++ เป็นหนังสือที่มีมีลีลาเป็นเอกลักษณ์ที่ผมชอบมาก โดยเฉพาะการยกตัวอย่างที่ผิดและถูกในการเขียนโปรแกรม โดยเฉพาะไวยากรณ์และวิธีการใช้งาน ซึ่งจะหาจากตำราเล่มอื่นๆไม่ค่อยได้ ถือว่าเป็นหนังสือภาษาไทยที่ผมชอบและอ่านเข้าใจได้ดีที่สุด ซึ่งผู้เขียนมีประสบการณ์เขียนโปรแกรมภาษานี้จริงไม่ใช่แปลหรือดัดแปลงจากtextbook อย่างเดียว สรุปเป็นตำราที่ดีที่ควรมีไว้ใช้อย่างจริงจัง ผู้เขียนในตำราบอกว่าภาษาC++ เป็นภาษาที่มีไวยากรณ์เข้มงวดมากและมากกว่าภาษาC ซึ่งเท่าที่ผมได้อ่านไปซักพักพบกว่ามันมีรายละเอียดของไวยากรณ์ซับซ้อนเข้มงวดมากพอสมควร เพราะมันสามารถติดต่อกับเมมโมรีในCPUต่างๆได้และเอาไปปรับแต่งและเอาไปรันกับกับCPUต่างค่ายกันได้ทัน การอ่านและทำความเข้าใจภาษาถ้าไม่เข้าใจในไวยากรณ์ของมันก็ไปต่อไม่ได้ ซึ่งไวยากรณ์ของมันเป็นไวยากรณ์ที่เขียนได้ลัดสั้นมากและบางทีก็สื่อความหมายได้หลายรูปแบบแล้วแต่จะนำไปประกอบกับอะไรแต่ถ้าเข้าใจแล้วมันเป็นไวยากรณ์ที่พิมพ์ได้สั้นและอ่านแล้วให้เข้าใจได้อย่างทันที
ผมจะดัดแปลงและเพิ่มความรู้ความเข้าใจส่วนตัวเข้าไปโดยเฉพาะเพื่อนำไปเขียนกับไมโครคอนโทรเลอร์โดยตรง
บทความส่วนนี้ผมจะเน้นสรุปเนื้อหาที่เกี่ยวกับ อาร์เรย์ พ้อยเตอร์ ซึ่งเกี่ยวข้องกับการเขียนMCU โดยตรงและเน้นใช้เป็นอย่างมาก ส่วนไวยากรณ์หลักๆ เช่น ชนิดข้อมูลส่วนใหญ่ให้ศึกษาจากภาษาซีที่กล่าวไปแล้ว ส่วนโอเปอเรเตอร์ต่างๆให้ศึกษาจากเว็บลิงค์ถ้ามีเวลาจะมาเขียน ส่วนคำสั่งเงื่อนไขและการวนรอบก็กล่าวไปแล้วในCเช่นกัน ส่วนอื่นๆถ้ามีเวลาว่างคงกลับมาเขียนหลังจากใช้ภาษาเก่งแล้ว
รูปแบบการเขียนC++ ต้องมี
1 header หรือส่วนประกาศการใช้ฟังก์ชั่นและตัวแปร
2 ฟังก์ชั่น main() เป็นทางเข้าของโปรแกรมที่จำเป็นต้องมีเป็นภาคบังคับให้เขียนใส่เข้าไป การที่ทำให้ใช้งานได้สอดคล้องกับไมโครคอนโทรลเลอร์ที่ต้องทำงานซ้ำๆกันไม่รู้จบจึงต้องผนวกเงื่อนไขการกระโดดให้กลับสู่จุดเริ่มต้นในฟังก์ชั่น main เช่นใช้ goto ในการกระโดดเป็นต้น
--
-คำสงวนใน ANSI C++ (รวมC++98)
asm | auto | bool | break | auto |
catch | char | class | const | const_cast |
continue | default | delete | do | double |
dynamic_cast | else | enum | explicit | export |
extern | false | float | for | friend |
goto | if | inline | int | long |
mutable | namespace | new | operator | private |
protected | public | register | reinterpret_cast | return |
short | signed | sizeof | static | static_cast |
struct | switch | template | this | throw |
true | try | typedef | typeid | typename |
union | unsigned | using | virtual | void |
volatile | wchar_t | while | ||
ความหมาย สี | C | อยู่ในภาษา C เดิม | ||
C++ | อยู่ในภาษา C++ | |||
C++ 98 | อยู่ในภาษา C++ เพิ่มเติม ปี98 |
----
ANSI/ISO C++ สรุปบทเรียนเรื่อง Array , Pointer แล้วลองเขียนผ่าน C-Atmel |
||
1. | Array คือ |
เป็นโครงสร้างที่เก็บข้อมูลชนิดเดียวกันได้หลายๆจำนวนในตัวแปรเดียว ซึ่งแต่ละ1ข้อมูลจะถูกเก็บอยู่ใน1อีลีเม้นท์(ช่องเก็บข้อมูลหรือหน่วยความจำ) ซึ่งเราสามารถเรียกใช้ผ่านอินเด็กซ์ในภาษาC++ซึ่งคอมไพล์เลอร์จะจัดสรรตำแหน่งในหน่วยความจำให้อัตโนมัติ และจัดสรรขนาดหน่วยความจำให้เองตามชนิดข้อมูลที่เราประกาศใช้ อาร์เรย์สามารถสร้างเป็นโครงสร้างได้หลายมิติ ซึ่งส่วนใหญ่ใช้กันอยู่1,2,3 มิติ โครงสร้างข้อมูล1มิติ เปรียบได้กับ โปรแกรมเอ็กเซลซึ่งมีช่องเก็บข้อมูล1แถวซึ่งมีอิลีเม้นท์คือช่องเก็บข้อมูลเรียงยาวกันไปทางขวามือซึ่งเราสนใจแต่เพียงคอลัมว่านี่เป็นคอมลัมหรือข่องที่1(อีลีเมนท์ที่1),...,ช่องที่N(อีลีเมนท์ที่N) แล้วแต่เราสร้างขึ้นว่ามีกี่อีลีเมนท์ ![]() ตัวอย่างของอาร์เรย์ 1 มิติในรูป ประกอบด้วยอีลีเมนท์ที่มีเลขช่องเก็บในภาษาคนเริ่มที่หมายเลข1 ส่วนในภาษาC ใช้คำว่า อินเด็กซ์ เริ่มต้นตั้งแต่ค่า0 ส่วนเมมโมรีแอดเดรสหรือตำแหน่งที่อยู่ของชุดข้อมูลอาร์เรย์จะเริ่มต้นที่ออโต้หรือภาษาCกำหนดให้เองอัตโนมัติในอินเด็กซ์ที่0 ถัดมาที่อยู่ข้อมูลของx[1]จะเท่ากับค่าออโต้+ด้วยขนาดชนิดข้อมูลที่ประกาศ ในที่นี้คือint ซึ่ง MCU มีขนาดเพียง2ไบท์ |
2. | Array รูปแบบ |
การประกาศตัวแปรอาเรย์ ทำได้หลายแบบ โดยหลักต้องระบุชนิดข้อมูล-type และชื่อตัวแปรอาร์เรย์ ส่วนขนาดจำนวนช่อง(จ.น.element)และค่าเริ่มต้นมีวิธีต่างๆกันไป type name [size]; //รูปแบบทั่วไปของการประกาศอาเรย์ int x [5] ; //ประกาศตัวแปรarray ชนิดint 5 ช่อง int a [5] = {1,2} // ให้a[0]=1,b[1]=2 ที่เหลือคือ0 int b [ ] = {3,6,12}// คอมไพล์เลอร์จะคำนวนให้ว่า size=3 |
3. | ||
4. | ||
5. | ||
6. | ||
7. | ||
8. | ||
9. | ||
10. | ||
11. | ||
12. |
-753--
-753--
-753--
-753--
-753--
-
คลิ๊ก> หมวดหมู่ความรู้
Email :
. . pui108108@yahoo.com
Line ID :
. . pui108diy
โทร: .. 089-797-9411
blog word press :
. . . pui108diy.com/wp/
รูป Flickr :
. . . ./photos/pui108diy/
รูป Wiki commons :
. . ./wiki/User:Pui108108
Pinterest :
. . . ./pui108108/
Youtube :
. . . ./user/p108108
หน้าที่เข้าชม | 214,168 ครั้ง |
ผู้ชมทั้งหมด | 167,913 ครั้ง |
เปิดร้าน | 17 ก.ย. 2558 |
ร้านค้าอัพเดท | 15 ก.ย. 2568 |