บทความนี้แนะนำการใช้งานซอฟต์แวร์ AVR Studio 7 (for Windows) ในเบื้องต้นสำหรับผู้ที่สนใจจะนำไปใช้เพื่อเขียนโปรแกรมไมโครคอนโทรลเลอร์แบบ Bare-Metal โดยใช้วิธีเข้าถึงรีจิสเตอร์ของ Peripherals ต่างๆ ภายในไมโครคอนโทรลเลอร์ในตระกูล SAMD (เลือกใช้ไมโครคอนโทรลเลอร์ ATSAMD21G18A) และสาธิตการใช้งานร่วมกับอุปกรณ์ SWD Programmer / Debugger (Segger J-Link V9) เพื่อดีบักการทำงานของโปรแกรมในฮาร์ดแวร์
ข้อสังเกต: ถ้าไม่ใช้วิธีการเขียนโค้ดแบบ Bare-Metal ทางบริษัท Microchip / Atmel ก็มี Software Framework ที่เรียกว่า SMART เพื่ออำนวยความสะดวกในการเขียนโค้ด และมี Web-based Software Configuration Tool เพื่อช่วยในการสร้างโปรเจกต์เริ่มต้น และจะต้องใช้งานร่วมกับ Advanced Software Framework
ขั้นตอนการดำเนินการมีดังนี้
- ดาวน์โหลดและติดตั้ง AVR Studio 7 Integrated Development Platform (IDP) ซึ่งมาพร้อมกับ (Atmel) Advanced Software Framework (ASF)+ GNU ARM Toolchains เวอร์ชันที่นำมาใช้คือ AVR Studio 7 Build 2397 (October 2019) + ASF 3.47 (ใช้ร่วมกับ ASF Wizard) เลือกแบบ Web Installer หรือ Offline Installer (file size: 874 MB)
- เปิดใช้งาน AVR Studio 7 และทำขั้นตอนเพื่อสร้างโปรเจกต์ใหม่
- เขียนโค้ดลงในไฟล์ main.c ตามตัวอย่างและทำขั้นตอน Build
- อัปโหลดไฟล์ .hex / .bin ที่ได้จากการคอมไพล์โค้ด ไปยังบอร์ดทดลอง โดยใช้ SWD Programmer / Debugger
- ทำขั้นตอนดีบักการทำงานของโปรแกรมโดยใช้ฮาร์ดแวร์ (เลือกใช้บอร์ด RobotDyn SAMD21 M0-Mini)
ถ้าต้องการจะดาวน์โหลด AVR Studio และ ASF3 ในเวอร์ชันต่าง ๆ ในอดีต ก็สามารถเลือกได้จาก: Downloads Archive for AVR and SAM MCUs/MPUs
การติดตั้ง AVR Studio 7 แบบ Web Installer
ในการติดตั้งซอฟต์แวร์ AVR Studio 7 สำหรับ Windows (x86/x64) ก็ให้ทำตามขั้นตอนไปตามลำดับ
การสร้างโปรเจกต์สำหรับเขียนโค้ดภาษา C (Bare-Metal)
เริ่มต้นด้วยการสร้างโปรเจกต์ใหม่ โดยเลือกใช้แบบ “GCC C Executable Project” เพื่อเขียนโค้ดแบบ Bare-Metal Programming
ตัวอย่างโค้ดสาธิต: Dual LED Blink
ถ้ดไปเป็นโค้ดตัวอย่างสาธิตการทำงานสำหรับ SAMD21 เมื่อโปรแกรมทำงาน จะทำให้ขา PA27 และ PB03 เกิดการสลับสถานะลอจิก (Toggle) ซ้ำไปเรื่อย ๆ
ถ้าใช้บอร์ด RobotDyn SAMD21 M0 Mini จะมีวงจร SMD LEDs จำนวน 2 ดวง ต่อเข้ากับขา I/O ดังกล่าวไว้แล้ว
#include "sam.h"void delay_ms( uint32_t msec ){ // software delay loop
uint32_t cnt = ( 250 * msec );
while ( cnt-- ) { asm volatile("nop"); }
}int main() {
SystemInit(); // system initialization (1MHz CPU freq.) REG_PORT_DIRSET0 = PORT_PA27; // PA27 output direction
REG_PORT_DIRSET1 = PORT_PB03; // PB03 output direction
REG_PORT_OUTSET0 = PORT_PA27; // PA27 high
REG_PORT_OUTCLR1 = PORT_PB03; // PB03 low while (1) {
REG_PORT_OUTTGL0 = PORT_PA27; // PA27 toggle
REG_PORT_OUTTGL1 = PORT_PB03; // PB03 toggle
delay_ms( 100 );
}
}
ข้อสังเกต: โค้ดสาธิตนี้ จะทำงานโดยใช้ความถี่ของ CPU เท่ากับ 1MHz (default) ซึ่งได้จากวงจรสร้างความถี่ภายใน 8MHz (Internal OSC8M) และใช้ตัวหารความถี่ DIV8
การเว้นระยะเวลา (Delay) จะใช้วิธีหน่วงเวลาโดยการวนลูปเพื่อทำคำสั่ง NOP ตามจำนวนครั้งที่กำหนด ระหว่างการสลับสถานะของเอาต์พุตที่ขา GPIO ในแต่ละครั้ง และการหน่วงเวลาโดยการทำคำสั่งในลักษณะนี้ (Software Delay Loop) มีผลต่อความถี่ในการกระพริบของ LEDs และก็ขึ้นอยู่กับการตั้งค่าของ GCC Compiler (Optimization Level) ด้วย ซึ่งแตกต่างจากการทำงานด้วย Hardware Timer
ตัวอย่างแรกนี้ แสดงให้เห็นวิธีการเข้าถึงรีจิสเตอร์โดยอาศัย Macros ในภาษา C ที่ได้มีการกำหนดไว้แล้วสำหรับ SAMD เช่น REG_PORT_DIRSET0
และ REG_PORT_DIRSET1
เกี่ยวข้องกับการกำหนดทิศทาง (I/O Direction) ของขา I/O ของพอร์ต Port A (PA) และ Port B (PB) ตามลำดับ ถ้ากำหนดค่าบิตให้มีค่าเป็น 1 หมายถึง เอาต์พุต แต่ถ้าเป็น 0 หมายถึง อินพุต
รีจิสเตอร์ REG_PORT_OUTTGL0
และ REG_PORT_OUTTGL1
เกี่ยวข้องกับการทำงานเกิดการสลับสถานะบิต (Bit Toggle) ของขา I/O สำหรับพอร์ต PA และ PB ตามลำดับ ถ้าเขียนค่า 1 ลงในตำแหน่งบิตใด จะทำให้เกิดการสลับสถานะหนึ่งครั้ง
การเข้าถึงรีจิสเตอร์ที่เกี่ยวข้องกับ Peripherals ของ MCU นอกจากการใช้ C Macros ยังมีอีกวิธีการหนึ่งคือ เข้าถึงโดยใช้พอยน์เตอร์ (Pointer) ที่ชี้ไปยังโครงสร้างข้อมูลสำหรับรีจิสเตอร์
#include "sam.h"PortGroup * const portA = &(PORT->Group[0]);
PortGroup * const portB = &(PORT->Group[1]);void delay_ms( uint32_t msec ) { // software delay loop
uint32_t cnt = ( 250 * msec );
while ( cnt-- ) { asm volatile("nop"); }
}int main() {
SystemInit(); // system initialization (1MHz CPU freq.) portA->DIRSET.reg = PORT_PA27; // PA27 output direction
portB->DIRSET.reg = PORT_PB03; // PB03 output direction
portA->OUTSET.reg = PORT_PA27; // PA27 output high
portB->OUTCLR.reg = PORT_PB03; // PB03 output low
while (1) {
portA->OUT.reg ^= PORT_PA27; // PA27 output toggle
portB->OUT.reg ^= PORT_PB03; // PB03 output toggle
delay_ms( 100 );
}
}
ถ้าสังเกตดูรายชื่อไฟล์ที่อยู่ในแถบทางขวามือ จะเห็นว่ามีไฟล์ซึ่งเป็นส่วนหนึ่งของ ARM CMSIS (Cortex Microcontroller Software Interface Standard) เช่น
samd21g18a_flash.ld
เป็น Linker Script file สำหรับเขียนลงในหน่วยความจำ Flash ของไมโครคอนโทรลเลอร์startup_samd21.c
เป็น Startup file สำหรับไมโครคอนโทรลเลอร์ เช่น การกำหนดค่าเริ่มต้นใน Interrupt Vector Table สำหรับ Interrupt Handlers และเรียกฟังก์ชันmain()
เป็นต้นsystem_samd21.c
เป็นไฟล์ที่มีฟังก์ชัน เช่นSystemInit()
สำหรับกำหนดความถี่ในการทำงานของระบบ
การตั้งค่าใช้งานสำหรับ SWD Programmer: Segger J-Link
เราจะใช้อุปกรณ์ Segger J-Link สำหรับการอัปโหลดโปรแกรมไปยังบอร์ดทดลอง และทำขั้นตอนดีบักในฮาร์ดแวร์ ถัดไปเป็นขั้นตอนการตั้งค่าก่อนใช้งาน
เมื่อทำการเชื่อมต่อกับอุปกรณ์ SEGGER J-Link อาจมีการทำขั้นตอนเพิ่ม เพื่ออัปเดตเฟิร์มแวร์ใหม่ให้อุปกรณ์ เป็นเวอร์ชันล่าสุด
เมื่อเชื่อมต่อกับอุปกรณ์ J-Link ได้แล้ว ให้กดปุ่ม Apply เพื่ออ่านค่าจากอุปกรณ์ เช่น Read Device Signature และ Read Target Voltage ถ้าสามารถอ่านค่าได้ แสดงว่า อุปกรณ์ J-Link สามารถเชื่อมต่อกับบอร์ดไมโครคอนโทรลเลอร์ได้อย่างถูกต้องและพร้อมใช้งาน
กลับเข้าสู่หน้าหลักของ IDE แล้วทำคำสั่งจาก Debug > Start Debugging and Break เพื่อเริ่มต้นขั้นตอนดีบัก
การดีบักโปรแกรมโดยใช้อุปกรณ์ฮาร์ดแวร์
เมื่อเข้าสู่ Debug Session จะมีการอัปโหลดไฟล์ .hex ไปยังบอร์ด (Device Programming) แล้วต่อด้วยการทำดีบักในฮาร์ดแวร์ (In-Circuit Debugging) และสามารถสั่งให้รันไปทีละประโยคคำสั่งในโค้ด main.c ได้ (Step-Over) หรือ รันต่อเนื่อง (Continue)
ในขณะที่ทำขั้นตอนดีบัก ผู้ใช้สามารถดูการเปลี่ยนแปลง เช่น ในรีจิสเตอร์ที่เกี่ยวข้องกับ GPIO ได้
ผู้ใช้สามารถเลือกบรรทัดในโค้ดเพื่อเพิ่ม Breakpoint (จะมีวงกลมสีแดงปรากฏในบรรทัดที่ได้เลือก) เมื่อรันโค้ดมาถึงบรรทัดดังกล่าว จะมีการหยุดชั่วคราวโดยอัตโนมัติ
ข้อสังเกต: ถ้าต้องการแค่อัปโหลดโปรแกรมไปยังบอร์ดทดลอง ให้ทำคำสั่ง Debug > Start Without Debugging
ตัวอย่างโค้ดสาธิต: SysTick-based LED Blink
โค้ดตัวอย่างถัดไป สาธิตการเลือกใช้ความถี่ 8MHz (Internal OSC8M) จากเดิม 1MHz (เปลี่ยนจากตัวหาร DIV8 เป็น DIV1) และเปลี่ยนไปใช้ขา PA17 เป็นเอาต์พุต นอกจากนั้นยังได้มีการเปิดใช้งานวงจร SysTick Timer (Hardware Timer) ภายใน CPU ซึ่งเป็นวงจรตัวนับถอยหลังขนาด 24 บิต
เมื่อวงจร SysTick Timer ทำงานจะเริ่มนับจากค่าเริ่มต้น (Initial Load Value) แล้วนับถอยหลังจนถึง 0 จากนั้นจะเกิดอินเทอร์รัพท์ (Interrupt) ก่อนที่จะเริ่มต้นนับใหม่
เมื่อเกิดเหตุการณ์ดังกลา่ว จะมีการเรียกฟังก์ชัน SysTick_Handler()
ซึ่งเป็น ISR (Interrupt Service Routine) ที่เกี่ยวข้องกับการทำงานของ SysTick Timer และจะทำให้เกิดการสลับสถานะลอจิกที่ขา PA17
#include "sam.h"void SysTickInit( uint32_t usec ) { // assume 8MHz system clock
SysTick->CTRL = 0; // disable SysTick
SysTick->LOAD = (8*usec)-1; // initial start value (24-bit)
SysTick->VAL = 0; // reset counter value
NVIC_SetPriority( SysTick_IRQn, 3 ); // set interrupt priority
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk // use system clock
| SysTick_CTRL_TICKINT_Msk // enable TICKINT interrupt
| SysTick_CTRL_ENABLE_Msk; // enable SysTick
NVIC_EnableIRQ( SysTick_IRQn ); // enable SysTick interrupt
}int main() {
SystemInit(); // 1MHz CPU frequency (default)
REG_SYSCTRL_OSC8M &= ~SYSCTRL_OSC8M_PRESC_Msk; // 8MHz
while (!(REG_SYSCTRL_PCLKSR & SYSCTRL_PCLKSR_OSC8MRDY)){}
SysTickInit( 100000UL );
REG_PORT_DIRSET0 = PORT_PA17; // use PA17 as output pin
while (1) {}
}// The interrupt handler for SysTick timer underflow
void SysTick_Handler(void) {
REG_PORT_OUTTGL0 = PORT_PA17; // toggle PA17 every 100 msec
}
เมื่อได้ทดลองใช้ซอฟต์แวร์และฮาร์ดแวร์ตามที่นำเสนอไป ถัดไปผู้อ่านคงมีพื้นฐานในระดับหนึ่งและสามารถศึกษาเรียนรู้การเขียนโปรแกรมสำหรับ ATSAMD21 ในหัวข้อต่าง ๆ เป็นลำดับต่อไปได้