Uwolnij swoje Arduino – programowanie Arduino bez bibliotek od Arduino

Uwolnij swoje arduino

Arduino – platforma programistyczna dla systemów wbudowanych opracowana w 2005 roku we Włoszech, oparta na Open Hardware. Celem projektu Arduino było stworzenie samowystarczalnej platformy umożliwiającej tworzenie projektów bez dodatkowych narzędzi. Obecnie Arduino dostarcza zarówno płytkę (np. Arduino Uno, z mikroprocesorem ATmega328P) jak i wieloplatformowe środowisko programistyczne tzw. Arduino IDE. Mikroprocesor umieszczony na płytce jest domyślnie wyposażony w bootloader który dostarcza możliwość programowania mikrokontrolera z wykorzystaniem portu szeregowego (UART). Jest to znaczne ułatwienie, dzięki temu aby zaprogramować mikroprocesor nie potrzebujemy zewnętrznego programatora.

 

Biblioteka Arduino

Arduino dostarcza również wysokopoziomową bibliotekę do kontrolowania peryferiów mikroprocesora. Użytkownik nie musi zatem zagłębiać się w wewnętrzną architekturę procesora, wystarczy umiejętność posługiwania się funkcjami dostarczonymi przez Arduino. Obecna wersja Arduino IDE nie daje użytkownikowi opcji rezygnacji z korzystania z wysokopoziomowej biblioteki.

Przykład realizacji prostego zadania (mruganie diody LED) z wykorzystaniem biblioteki Arduino:

int led = 13;
 
void setup() {                
	pinMode(led, OUTPUT);    
}
 
void loop() {
	digitalWrite(led, HIGH);
	delay(1000);
	digitalWrite(led, LOW);
	delay(1000);
}

Warto zwrócić uwagę, że powyższy kod nie zawiera definicji funkcji int main(). Funkcja ta jest zaimplementowana wewnątrz biblioteki Arduino:

int main(void)
{
	init();
 
#if defined(USBCON)
	USBDevice.attach();
#endif
 
	setup();
 
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
 
	return 0;
}

Widać wyraźnie, że zadaniem funkcji main() jest zainicjalizowanie biblioteki (uruchomienie funkcji init()), jednorazowe uruchomienie zdefiniowanej przez użytkownika funkcji setup() oraz nieskończone wykonywanie funkcji użytkownika loop().

A co z funkcją init()? Jej zadaniem jest konfiguracja timerów oraz ADC mikroprocesora. Funkcja ta dekonfiguruje również UART zainicjalizowany wcześniej przez bootloader.

 

Alternatywa – programowanie Arduino bez wysokopoziomowej biblioteki

Skąd w ogóle ten pomysł? Odpowiedź jest prosta. Ręczna konfiguracja peryferiów pozwala na lepsze zrozumienie zasad działania mikroprocesora. Zdobyta wiedza jest uniwersalna – umożliwia programowanie dowolnego mikroprocesora (nawet bardziej rozbudowanego np. ARM Cortex). Oczywiście zyskujemy również większą kontrolę nad sprzętem oraz szerokie pole do popisu jak chodzi o optymalizację.

Ile wart jest programista nierozumiejący urządzenia, które programuje?

Na powyższe pytanie odpowiedzieć powinien sobie każdy, kto poświęca swój czas na zrozumienie wysokopoziomowej biblioteki zamiast na zrozumienie urządzenia. Rzecz jasna nie jest to zawsze priorytetem danego projektu, w pewnych sytuacjach zagłębianie się w działanie mikroprocesora jest niewskazane (np. w przypadku kursów dla dzieci w wieku do 10 lat).

 

Do dzieła – przykład bez biblioteki Arduino

#include <avr/io.h>
#include <util/delay.h>
 
int main() {
	DDRB |= _BV(PB5);
	while (1) {
		PORTB ^= _BV(PB5);
		_delay_ms(1000);
	}
}

Powyższy kod realizuje dokładnie to samo zadanie, co ten wykorzystujący bibliotekę Arduino. Efektem wykonania kodu powinno być mruganie diody mieszonej na płytce Arduino UNO co jedną sekundę.

Kompilacja przykładu

Zakładając, że kod powyższego przykładu zapisaliśmy w pliku main.c, możemy użyć następującego polecenia:

avr-gcc -c -mmcu=atmega328p -Wall -Werror -Os -DF_CPU=16000000UL main.c -o main.o

Opis argumentów:

  • -c – tylko kompilacja, bez linkowania
  • -mmcu=atmega328p – model procesora docelowego (w przypadku Arduino UNO będzie to ATmega328p)
  • -Wall – włączenie raportowania wszystkich ostrzeżeń
  • -Werror – traktowanie wszystkich ostrzeżeń jako błędy (wymuszenie przerwania kompilacji w przypadku dowolnego ostrzeżenia)
  • -Os – włączenie optymalizacji pod kątem rozmiaru
  • -DF_CPU=16000000UL – definiujemy wymaganą stałą o nazwie F_CPU określającą taktowanie procesora w Hz. Przyrostek UL wymusza traktowanie liczby jako unsigned long (liczba 32bit bez znaku). Definicja ta jest wymagana do poprawnego działania pętli opóźniających realizowanych np. przez funkcje _delay_ms().
  • main.c – plik wejściowy
  • -o main.o – plik wyjściowy (skompilowany kod będzie umieszczony w pliku main.o)

Powyższe polecenie należy uruchomić dla każdego pliku ze źródłami projektu. W przypadku kompilacji plików napisanych w języku C++ zamiast avr-gcc należy użyć avr-g++.

Linkowanie

Do połączenia wszystkich plików projektu, w jeden plik „wykonywalny” należy użyć następującego polecenia:

avr-gcc -mmcu=atmega328p main.o -o main.elf

Proces linkowania polega na łączeniu plików obiektowych w jeden plik finalny ze wszystkimi danymi programu. Podczas tego procesu ustalane jest również ostateczne miejsce kodu oraz zmiennych w pamięci procesora. Jako pliki wejściowe należy podać wszystkie pliki obiektowe (*.o).

Nagrywanie programu

Przed nagraniem programu do pamięci mikroprocesora, musimy przekonwertować plik wynikowy w formacie ELF main.elf do formatu Intel Hex rozumianego przez program obsługujący programator. Można to zrobić następującym poleceniem:

avr-objcopy -O ihex -R .eeprom main.elf main.hex

Opis argumentów:

  • -O ihex – określamy format wyjściowy jako Intel Hex
  • -R .eeprom – usuwamy sekcje .eeprom (nie będziemy modyfikować pamięci EEPROM)
  • main.elf – plik wejściowy
  • main.hex – plik wyjściowy

Tak przygotowany plik main.hex nadaje się już do nagrania na procesor:

avrdude -p m328p -c arduino -b 115200 -P /dev/ttyACM0 -U flash:w:main.hex

Opis argumentów:

  • -p m328p – model procesora, który programujemy
  • -c arduino – typ programatora (korzystamy z protokołu bootloadera Arduino)
  • -b 115200 – baud rate interfejsu szeregowego (w przypadku bootloadera Arduino UNO jest to 115200 bodów)
  • -P /dev/ttyACM0 – ścieżka do urządzenia portu szeregowego (dokładną ścieżkę należy sprawdzić w logach jądra po podłączeniu Arduino do portu USB (np. wykonując polecenie dmesg))
  • -U flash:w:main.hex – aktualizujemy pamięć flash zapisując dane z pliku main.hex

Automatyzacja procesu kompilacji, linkowania i nagrywania

Cały proces możemy zautomatyzować wykorzystując narzędzie GNU/Make. W katalogu projektu należy utworzyć plik Makefile z następującą zawartością:

#
# Copyright (C) Patryk Jaworski <regalis@regalis.com.pl>
# 
# Makefile for Arduino projects wihout high-level library.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# 
 
# Makefile for Arduino UNO
 
AVRDUDE_UPLOAD_SPEED=115200
AVRDUDE_COM_PORT=/dev/ttyACM0
AVRDUDE_PROGRAMMER=arduino
AVRDUDE_MCU=m328p
 
MCU=atmega328p
CC_FLAGS=-Os -Wall -Werror -mmcu=$(MCU) -DF_CPU=$(F_CPU) -c
LD_FLAGS=-mmcu=$(MCU)
 
F_CPU=16000000UL
CC=avr-gcc
CXX=avr-g++
LD=avr-gcc
OBJ_COPY=avr-objcopy
 
OBJS=$(patsubst %.c, %.o, $(wildcard *.c))
 
main.elf: $(OBJS)
	$(LD) $(LD_FLAGS) $^ -o $@
 
main.hex: main.elf
	$(OBJ_COPY) -O ihex -R .eeprom $< $@
 
%.o: %.c
	$(CC) $(CC_FLAGS) $< -o $@
 
%.o: %.c %.h
	$(CC) $(CC_FLAGS) $< -o $@
 
%.o: %.cpp
	$(CXX) $(CC_FLAGS) $< -o $@
 
%.o: %.cpp %.h
	$(CXX) $(CC_FLAGS) $< -o $@
 
upload: main.hex
	avrdude -c $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -b $(AVRDUDE_UPLOAD_SPEED) -P $(AVRDUDE_COM_PORT) -U flash:w:$<
 
clean:
	rm -rvf *.o main.elf main.hex
 
.PHONY: upload clean

Wykorzystując powyższy plik Makefile, kompilacja i nagranie programu na płytkę Arduino UNO sprowadza się do wykonania jednego polecenia (wewnątrz katalogu projektu):

$ make upload

To wszystko. Happy hacking!

Share:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Blip
  • Blogger.com
  • Gadu-Gadu Live
  • Google Buzz
  • LinkedIn
  • MySpace
  • Twitter
  • Wykop
  • Śledzik

3 thoughts on “Uwolnij swoje Arduino – programowanie Arduino bez bibliotek od Arduino

  1. Arduino Polska pisze:

    Świetni poradnik, polecam i zapraszam do nas! :)

  2. rad pisze:

    świetne, dzięki! dłuugo szukałem takiego poradnika

  3. Arkadiusz Wernicki pisze:

    Cieszę się, że jeszcze są osoby, którym niestraszne jest pisanie w „żywym C”. Może pójdźmy krok dalej i nie używajmy bibliotek C a wręcz nie używajmy C i napiszmy to w ASM. Będzie to wymagało jeszcze lepszego poznania budowy procesora i jego działania. Pętla while nagle zmieni się w coś zupełnie innego. _delay nagle okaże się blokiem instrukcji. Dopiero tutaj na poziomie ASM da się pokazać co oznacza optymalizacja i poznanie procesora. Nagle się okaże, że zużycie pamięci RAM spadło, FLASH świeci pustkami. Jedno się tylko zmieni: więcej czasu spędzimy na pisaniu. Najlepiej gdyby środowisko wysokiego poziomu pozwalało w prosty sposób wstawiać fragmenty w języku niskiego poziomu.

Odpowiedz na „Arkadiusz WernickiAnuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Please type the characters of this captcha image in the input box

Udowodnij, że jesteś człowiekiem - przepisz tekst z obrazka

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>