AVR

Compile AVR source code with CMake

Xin chào,
Dạo này tự nhiên mình không còn thích dùng IDE để lập trình các loại vi điều khiển nữa. Bản thân mình thấy rằng học cách sử dụng một tá IDE (Atmel Studio, Code Compse Studio, uKeil, Eclipse, …) để lập trình cho nhiều loại MCU khác nhau (AVR, ARM Cortex, Embedded Linux,…) thật rắc rối. Ngoài ra IDE còn khá nặng nề, chậm chạp và một số thậm chí còn không hỗ trợ Linux!

Để giải quyết các vấn đề trên, mình quyết định sử dụng combo: Sublime Text 3, CMake và toolchains của từng loại MCU. Như vậy mình chỉ cần học cách sử dụng Sublime Text và CMake là mọi chuyện sẽ ổn (mong là như vậy).


CÀI ĐẶT AVR TOOLCHAIN

Để cài đặt AVR toolchain cho Ubuntu, chúng ta dùng lệnh sau:

$ sudo apt-get install gcc-avr binutils-avr gdb-avr avr-libc avrdude

Trong đó:
+ gcc-avr: trình biên dịch C cho AVR;
+binutils-avr: assembler, linker và các công cụ dùng để tạo file binary;
+ gdb-avr: debugger;
+ avr-libc: các thư viện C cho AVR MCUs;
+ avrdude: dùng để nạp code cho AVR MCUs.


CẤU TRÚC CỦA PROJECT

Project demo của chúng ta sẽ có cấu trúc như sau:

DemoProject
|—- build
|—- main.c
|—- CMakeLists.txt

Trong đó, thư mục build được dùng để chứa các build files và binary file.

Viết code cho MCU ATmega328P

Đoạn code dưới đây sẽ blink 2 con LED ở chân PD5 và PD6 với tần số khác nhau, trong đó Timer0 được dùng để tính thời gian kể từ khi MCU bắt đầu hoạt động (đơn vị là millisecond).

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>

typedef struct {
	volatile uint8_t *port;
	uint8_t pin;
	uint8_t state;

	uint32_t on;
	uint32_t off;
	uint32_t lastUpdate;
} LED;


void update(LED*);

volatile uint32_t millis;

int main(int argc, char* argv[]) {
	LED led1 = {&PORTD, PD5, 0, 500, 200, 0};
	LED led2 = {&PORTD, PD6, 0, 300, 1000, 0};

	DDRD = (1 << PD5) | (1 << PD6);

	TCCR0A = (1 << WGM01);
	TCCR0B = (1 << CS01) | (1 << CS00);
	TIMSK0 = (1 << OCIE0A);
	OCR0A = 124;

	sei();
	millis = 0;

	while (1) {
		update(&led1);
		update(&led2);
	}

	return 0;
}

ISR(TIMER0_COMPA_vect) {
	millis++;
}


void update(LED* d) {

	if ((d->state == 1) && (millis - d->lastUpdate >= d->on)) {
		d->state = 0;
		d->lastUpdate = millis;
		*(d->port) &= ~(1 << d->pin);
	}

	if ((d->state == 0) && (millis - d->lastUpdate >= d->off)) {
		d->state = 1;
		d->lastUpdate = millis;
		*(d->port) |= (1 << d->pin);
	}
}

Chuẩn bị file CMakeLists.txt

Các bạn có thể tải template của file CMakeLists.txt từ github của mình.

##############################################################################
##                           START USER CONFIGURATION                       ##
##############################################################################

cmake_minimum_required(VERSION 3.5)                                        

# Project name                                                                            
    project(BlinkLED)                                              
   
# Target filename                                                         
    set(TARGET_FILE blink)                                     
                                       
# Load all source files in project folder                                                         
    set(SOURCES main.c)                                 
                             
# Set F_CPU, MCU name and UART Baudrate                                                     
    set(MCU atmega328p)                                                         
    set(F_CPU 8000000UL)
                                                      
##############################################################################
##                           END USER CONFIGURATION                         ##
##############################################################################

# Use AVR GCC toolchain
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_CXX_COMPILER avr-g++)
set(CMAKE_C_COMPILER avr-gcc)
set(CMAKE_ASM_COMPILER avr-gcc)

# mmcu MUST be passed to bot the compiler and linker, this handle the linker
set(CMAKE_EXE_LINKER_FLAGS -mmcu=${MCU})

add_compile_options(
    -mmcu=${MCU}
    -std=gnu99
    -O3
    -Wall
    -g
)

# Create one target
add_executable(${TARGET_FILE} ${SOURCES})

# Rename the output to .elf as we will create multiple files
set_target_properties(${TARGET_FILE} PROPERTIES OUTPUT_NAME ${TARGET_FILE}.elf)

# Strip binary for upload
add_custom_target(strip ALL avr-strip ${TARGET_FILE}.elf DEPENDS ${TARGET_FILE})

# Transform binary into hex file, we ignore the eeprom segments in the step
add_custom_target(hex ALL avr-objcopy -R .eeprom -O ihex ${TARGET_FILE}.elf ${TARGET_FILE}.hex DEPENDS strip)

# Transform binary into hex file, this is the eeprom part (empty if you don't use eeprom static variables)
add_custom_target(eeprom avr-objcopy -j .eeprom  --set-section-flags=.eeprom="alloc,load"  --change-section-lma .eeprom=0 -O ihex ${TARGET_FILE}.elf ${TARGET_FILE}.eep DEPENDS strip)

# Clean extra files
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${TARGET_FILE}.hex;${TARGET_FILE}.eeprom;${TARGET_FILE}.lst")

Chúng ta chỉ cần tùy chỉnh phần USER CONFIGURATION, trong đó: dòng 17 – chỉ ra dòng MCU được sử dụng, dòng 18 – quy định tần số mà MCU hoạt động.


Biên dịch source code

$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make

Nếu mọi chuyện đều ổn, chúng ta sẽ nhận được kết quả như sau:

Hình 1 – lệnh cmake và kết quả
Hình 2 – các build files đã được tạo
Hình 3 – lệnh make và kết quả
Hình 4 – các binary files đã được tạo (blink.elf và blink.hex)

Nạp code vào MCU

Để MCU có thể hoạt động, chúng ta cần nạp file blink.hex vào MCU. Mình đã có một bài viết về cách flash AVR MCUs với avrdude và Arduino, các bạn có thể tham khảo.


KẾT LUẬN

Như vậy chỉ với CMake và AVR toolchain, chúng ta đã có thể biên dịch source code và flash MCU. Đã đến lúc “say goodbye” với các IDE rồi, ahihi.

Cảm ơn các bạn đã theo dõi bài viết.
Thân ái và quyết thắng.

References:
[1] The AVR GCC Toolchain.
[2] Using CMake to deploy to avr microcontrollers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s