ผลการเรียนรู้ (Learning Outcomes)
- อธิบายลำดับภาษา (high→assembly→machine) และ toolchain (compiler/assembler/linker/loader)
- อธิบาย program memory layout: Flash(code), RAM(Data/BSS, Stack, Heap) และ stack/heap ชนกัน
- อธิบายทำไม global address คงที่จึง watch real-time ได้ แต่ local ไม่ได้
- อธิบาย register Cortex-M (13 GPR + SP/LR/PC + PSR N Z C V)
- เปรียบเทียบ Von Neumann vs Harvard; Cortex-M=Harvard
- จำแนก Cortex-A/R/M และ M0/M3/M4/M7
- เปรียบเทียบ SRAM/DRAM, ROM/PROM/EPROM/EEPROM/Flash
§2.1 ระดับภาษาและ Toolchain
ภาษาโปรแกรมมีหลายระดับไล่จากใกล้คนไปใกล้เครื่อง. ระดับบนสุดคือ High-level language (ภาษาระดับสูง เช่น C/C++) ที่เขียนใกล้ตัวปัญหาและคนอ่านเข้าใจง่าย. ถัดลงมาคือ Assembly ซึ่งเป็นรูปข้อความของคำสั่งเครื่องแต่ละคำสั่ง. ล่างสุดคือ Machine code ที่เป็นเลขฐานสอง จัดเป็น word ขนาด 8/16/32/64 บิต โดยแต่ละบิตแทนด้วยระดับแรงดันไฟฟ้า.
Toolchain คือชุดเครื่องมือที่แปลงภาษาระดับสูงให้เป็นไฟล์ที่ชิปรันได้. Compiler แปลงไฟล์ .c/.h เป็น assembly (.s). Assembler แปลง assembly เป็น object file โดยตั้งชื่อ symbol เป็นเลขลำดับภายใน. Linker รวม object หลายไฟล์เข้ากับ libraries แล้วจัด address ใหม่ให้เป็น executable (ELF/.bin/.hex) ซึ่ง loader ต้องรู้รูปแบบไฟล์จึงจะโหลดได้.
บนบอร์ด Nucleo เราข้ามขั้น loader ที่ซับซ้อนได้ เพราะลากไฟล์ .bin ไปวางลงไดรฟ์ของ ST-LINK ได้โดยตรง ไม่ต้องมี loader แยกต่างหาก.
โค้ดภาษา C/C++ (ใช้ bare-metal หรือ HAL)
คลิกแต่ละสเตจเพื่อดูว่าได้ไฟล์อะไรและทำอะไร. บน CubeIDE กด Build = รัน compiler→assembler→linker ให้อัตโนมัติ.
§2.2 Program Memory Layout
โค้ดโปรแกรมถูกเก็บใน Flash ซึ่งเป็นหน่วยความจำแบบ non-volatile (ข้อมูลไม่หายเมื่อปิดไฟ). ส่วนตัวแปรทั้งหลายอยู่ใน RAM ซึ่งถูกแบ่งเป็นหลายส่วน.
RAM แบ่งเป็นส่วนหลัก ๆ ดังนี้. Data segment เก็บตัวแปร global ที่มีค่าเริ่มต้น เช่น int A=5. BSS เก็บตัวแปร global ที่ไม่ได้กำหนดค่า ซึ่ง compiler จะตั้งค่าเริ่มต้นเป็น 0 ให้. Stack เก็บตัวแปร local และข้อมูลการเรียกฟังก์ชัน. Heap เก็บหน่วยความจำที่จองแบบ dynamic ด้วย malloc.
Stack มีพฤติกรรมพิเศษคือโตจาก address สูงลงไปหา address ต่ำ. เมื่อเข้าฟังก์ชันจะลดค่า SP (Stack Pointer) ลงเพื่อจองพื้นที่ เมื่อออกจากฟังก์ชันก็บวกค่า SP คืน ทั้งหมดนี้ compiler จัดการให้เอง. ส่วน Heap ใช้เมื่อเราไม่รู้ขนาดข้อมูลตอน compile เช่นการสร้าง linked list.
จุดที่ต้องระวังคือ Stack (โตลง) กับ Heap (โตขึ้น) โตเข้าหากัน ถ้าทั้งคู่ใช้พื้นที่มากจนมาชนกันโปรแกรมจะพัง จึงอาจต้องกำหนดขนาด stack/heap ใน linker. ค่าคงที่เล็ก ๆ เก็บฝังไว้ในโค้ด (Flash) ได้ แต่ตัวแปรที่ต้องเปลี่ยนค่าต้องจองพื้นที่ใน RAM.
addr สูง ┌──────────┐
│ Stack │ ↓ โตลง (local, ฟังก์ชัน)
│ ↓ │
│ (ว่าง) │ ← เสี่ยงชนกัน
│ ↑ │
│ Heap │ ↑ โตขึ้น (malloc)
├──────────┤
│ BSS (=0) │
│Data(init)│
addr ต่ำ └──────────┘ (RAM)
Flash: [ code + ค่าคงที่ ] non-volatileFlash (แยกต่างหาก, non-volatile): เก็บ code + ค่าคงที่
Stack โตจาก address สูงลงล่าง เมื่อเรียกฟังก์ชัน; Heap โตจาก address ต่ำขึ้นบนเมื่อ malloc. ถ้าโตเข้าหากันจนพื้นที่ว่างหมด จะเกิด stack/heap collision.
§2.3 Global vs Local Address
ตัวแปร global มี address ที่ compiler รู้แน่นอนตั้งแต่ตอน compile จึงอยู่ตำแหน่งคงที่. เพราะ address ไม่เปลี่ยน เราจึงอ่านค่าจาก address นั้นได้ตลอดเวลา ทำให้ watch หรือ monitor ค่าแบบ real-time ได้.
ตัวแปร local อยู่บน stack ซึ่ง address ขึ้นกับเส้นทางการเรียกฟังก์ชัน (call path) จึงไม่คงที่ แต่ละครั้งที่เรียกอาจอยู่คนละที่ จึง watch ค่าแบบ real-time ไม่ได้.
- address
- 0x2000_0010
- คงที่?
- คงที่เสมอ
- watch real-time
- ทำได้
อยู่ Data/BSS — compiler รู้ address แน่นอน
- address
- ยังไม่ถูกสร้าง
- คงที่?
- —
- watch real-time
- N/A
อยู่บน stack — ลองกด call/return แล้วดู address ขยับ
Global มี address คงที่เสมอ จึง watch real-time ได้. Local อยู่บน stack — address เปลี่ยนตาม call path จึง watch real-time ไม่ได้.
§2.4 Registers ใน Cortex-M
Register คือหน่วยเก็บบิตชั่วคราวความเร็วสูงที่อยู่ภายใน CPU สร้างขึ้นจากวงจร D flip-flop. Cortex-M มี register ทั้งหมด 16 ตัว แบ่งเป็น general-purpose 13 ตัว (R0–R12) และ special register 3 ตัว.
special register 3 ตัวได้แก่ SP (Stack Pointer) ที่ชี้ยอด stack, LR (Link Register) ที่เก็บ address สำหรับกลับจากฟังก์ชัน, และ PC (Program Counter) ที่ชี้คำสั่งถัดไป. นอกจากนี้ยังมี PSR ที่เก็บ flag สถานะ 4 ตัว ได้แก่ N (Negative), Z (Zero), C (Carry) และ V (oVerflow).
ขั้นตอนการคำนวณของ ALU: โหลด operand จาก memory เข้า register → ให้ ALU คำนวณ → เก็บผลลัพธ์กลับ register แล้วจึงเขียนกลับ memory (หรือส่งตรงออก I/O).
คำสั่ง (instruction) ของ ARM ขนาด 32 บิต แบ่งบิตเป็นสองส่วน: บิตแรก ๆ เป็น opcode (บอกว่าทำอะไร) ส่วนที่เหลือเป็น operand (บอกว่าทำกับข้อมูลตัวไหน). วงจร decoder จะแปลง opcode ให้กลายเป็นสัญญาณควบคุมภายใน.
LDR R0, [x] ; โหลด x เข้า R0
LDR R1, [y] ; โหลด y เข้า R1
ADD R2, R0, R1 ; ALU: R2 = R0 + R1
STR R2, [z] ; เก็บผลกลับตัวแปร zเดินทีละคำสั่ง: load operand เข้า register → ALU บวก → store กลับ memory. Register ที่เปลี่ยนจะถูกไฮไลต์.
§2.5 Von Neumann vs Harvard
สถาปัตยกรรมแบบ Von Neumann ใช้บัสเส้นเดียวร่วมกันทั้งการดึงคำสั่งและการเข้าถึงข้อมูล จึงดึงคำสั่งกับข้อมูลพร้อมกันไม่ได้.
สถาปัตยกรรมแบบ Harvard แยกบัสคำสั่งออกจากบัสข้อมูล จึงดึงทั้งสองอย่างพร้อมกันได้และทำงานเร็วกว่า แลกกับวงจรที่ซับซ้อนกว่า. Cortex-M เป็นแบบ Harvard (ภายใต้ ARMv7-M).
นอกจากนี้บัสยังแบ่งตามหน้าที่เป็น address bus, data bus และ control bus (ส่งสัญญาณ enable/read/write). สำหรับงานที่ต้องการราคาถูกและใช้พื้นที่น้อยก็มี ARMv6-M เป็นทางเลือก.
| หัวข้อ | Von Neumann | Harvard |
|---|---|---|
| บัส | รวม | แยก (คำสั่ง/ข้อมูล) |
| ดึงพร้อมกัน | ไม่ได้ | ได้ |
| ความเร็ว | ช้ากว่า | เร็วกว่า |
| ความซับซ้อน | ง่ายกว่า | ซับซ้อนกว่า |
| ใช้ใน | ระบบทั่วไปบางแบบ | Cortex-M (ARMv7-M) |
Von Neumann ใช้บัสเดียวร่วมกัน คำสั่งและข้อมูลต้องผลัดกันวิ่ง (คอขวด). Harvard แยกบัส ดึงคำสั่งและข้อมูลพร้อมกันได้ จึงเร็วกว่า. Cortex-M เป็น Harvard.
§2.6 ตระกูล Cortex: A/R/M และ M0–M7
ARM แบ่งตระกูล Cortex เป็น 3 โปรไฟล์ตามลักษณะงาน: Cortex-A สำหรับงานสมรรถนะสูงที่รัน OS, Cortex-R สำหรับงาน real-time ที่ต้องตอบสนองแน่นอน และ Cortex-M สำหรับงานฝังตัวที่เน้นประหยัดพลังงาน. ภายในตระกูล M ยังแบ่งย่อยเป็นรุ่น M0, M3, M4, M7 ตามตารางด้านล่าง.
| โปรไฟล์ | ชื่อ | เด่น | ตัวอย่าง | สถาปัตยกรรม |
|---|---|---|---|---|
| Cortex-A | Application | สมรรถนะสูง, รัน OS | มือถือ, Raspberry Pi | ARMv7-A |
| Cortex-R | Real-time | ตอบสนองเร็ว/แน่นอน | ABS/airbag (หลัก ms), หัวอ่าน HDD | ARMv7-R |
| Cortex-M | Microcontroller | ประหยัดพลังงาน, งานฝังตัว | บอร์ดเรา (STM32) | ARMv7-M |
| รุ่น M | จุดเด่น | สถาปัตยกรรม |
|---|---|---|
| M0 | gate น้อยมาก (<12K), 3µW/MHz | ARMv6-M |
| M3 | กระแสหลัก, pipeline 3-stage | ARMv7-M |
| M4 | DSP + FPU | ARMv7E-M |
| M7 | รุ่นของบอร์ดเรา | ARMv7-M |
รุ่นของบอร์ดเรา (NUCLEO-F767ZI)
คลิกโหนดเพื่อดูการ์ดรายละเอียด. ไฮไลต์ M7 = แกนของบอร์ดในวิชานี้.
§2.7 เทคโนโลยีหน่วยความจำ
หน่วยความจำแบ่งเป็นสองกลุ่มใหญ่ตามการเก็บข้อมูล. RAM เป็นแบบ volatile เก็บข้อมูลชั่วคราว ข้อมูลหายเมื่อปิดไฟ. ส่วน ROM หรือหน่วยความจำ non-volatile เก็บ code และค่าคงที่ไว้ได้แม้ปิดไฟ.
ตัวอย่างการใช้งานจริง: DRAM ใช้เป็น main store หรือหน่วยความจำหลัก, SRAM ใช้เป็น cache ที่เร็วแต่แพง, ส่วน Flash ใช้เก็บ BIOS, firmware หรือเพลง MP3.
| SRAM | DRAM | |
|---|---|---|
| เซลล์ | MOS/MOSFET | capacitor (ต้อง refresh) |
| ความเร็ว | เร็ว (~2ns) | ช้ากว่า (~10ns) |
| ใช้เป็น | cache, local variable | main store |
| ชนิด | เขียน/ลบ | หมายเหตุ |
|---|---|---|
| Masked ROM | กำหนดตอนผลิต | แก้ไม่ได้ |
| PROM | เขียนครั้งเดียว | — |
| EPROM | ลบด้วยแสง UV (~20 นาที) | หน้าต่างควอตซ์ |
| EEPROM | ลบด้วยไฟฟ้า ทีละไบต์ | ช้า ~10s/byte, 10k–100k รอบ, มัก external |
| Flash | ลบเป็นบล็อก, เร็วกว่า ~1s/byte | เก็บ code ใน MCU/mass storage; NOR/NAND |
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); // เปิด clock GPIOB, ตั้ง PB0 output push-pull
while (1) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // LD1 ติด (3.3V)
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // LD1 ดับ (0V)
HAL_Delay(500);
}
}ลบเป็นบล็อก, เร็วกว่า ~1s/byte; เก็บ code ใน MCU; NOR/NAND
กรองตามชนิด แล้วคลิกดูรายละเอียด. เดโมด้านล่างเทียบการลบ: Flash ลบทั้งบล็อกทีเดียว vs EEPROM ลบทีละไบต์.
ลำดับการสั่ง GPIO: เปิด clock พอร์ต → ตั้งโหมด pin → เขียนค่า. ถ้ายังไม่เปิด clock การเขียนจะไม่ทำงาน. SET=logic 1 (3.3V, ไฟติด), RESET=logic 0 (0V, ไฟดับ).
แบบฝึกหัด
12 ข้อ — แต่ละข้อมีชนิดและระดับความยากกำกับ กดปุ่ม "ดูเฉลย" เพื่อดูคำตอบและคำอธิบาย
- 1.ปรนัยง่าย
เรียงลำดับภาษาจากใกล้คนไปใกล้เครื่องได้อย่างไร
- 2.ปรนัยกลาง
หน้าที่หลักของ linker คืออะไร
- 3.ปรนัยง่าย
code ของโปรแกรมถูกเก็บไว้ที่ใด (ไม่หายเมื่อปิดไฟ)
- 4.ปรนัยกลาง
ตัวแปร local อยู่ในส่วนใดของ RAM และโตไปทางใด
- 5.ถูก/ผิดกลาง
ตัวแปร global มี address คงที่ จึง watch ค่าแบบ real-time ตอน debug ได้
- 6.ถูก/ผิดกลาง
ตัวแปร local สามารถ watch ค่าแบบ real-time ได้เหมือนตัวแปร global
- 7.เติมคำง่าย
Cortex-M มี register รวม 16 ตัว เป็น general-purpose ____ ตัว และ special ____ ตัว (SP, LR, PC)
- 8.เติมคำกลาง
flag ใน PSR ของ Cortex-M มีสี่ตัวคือ N, Z, ____ และ ____
- 9.ปรนัยกลาง
Cortex-M ใช้สถาปัตยกรรมบัสแบบใด
- 10.ปรนัยกลาง
โปรไฟล์ Cortex ใดออกแบบมาสำหรับงาน real-time เช่น ABS/airbag
- 11.ปรนัยกลาง
ข้อใดอธิบายความต่างของ EEPROM กับ Flash ได้ถูกต้อง
- 12.วิเคราะห์ยาก
อธิบายว่าทำไม Lab01 ใช้ global num, led0 โดยโยงกับเรื่อง address และหน่วยความจำในบทนี้