Makefile místo Arduino IDE

Vývojová deska Arduino Uno s mikrokontrolérem ATMega328 je primárně zaměřena na začátečníky, kteří elektronice nebo mikrokontrolérům vůbec nerozumí. Internetový odborníci na slovo vzatí :) se ale různě po diskusích dohadují, že je Arduino nepoužitelné, že neučí začátečníka ty správné návyky™, že je vývojář až příliš odříznut od hardawe a podobně. Dva hlavní argumenty těchto diskutujících jsou — Arduino používá vlastní jazyk  a ne standardní C. A že vývojové prostředí je pro pokročilejší věci nepoužitelné.

V tomto článku bych chtěl oba tyto argumenty vyvrátit a dokázat, že Arduino je pouze ATMega328 a že lze Arduino IDE obejít.

Na následujících řádcích se budu odkazovat na nástroje make, avr-gcc, avr-objcopy, avrdude a stty. Předpokládám alespoň základní znalost práce v Unixovém terminálu a znalost jazyka C pro mikrokontroléry Atmel. Pokud nerozumíte co to všechno znamená, pak mám pro vás dobrou zprávu — tento článek ještě číst nemusíte, vystačíte si s Arduino IDE :)

Jak to dělá Arduino IDE

Arduino není nic jiného, že deska s ATMega328 (mluvíme o Arduino Uno). Tento mikrokontrolér má již naprogramovaný bootloader, který umí programovat flash paměť skrz rozhraní UART. Aby bylo možné Arduino programovat pomocí avrdude, obsahuje deska i převodník USB-UART, který je realizován mikrokontrolérem ATMega8U2 (nebo ATMega16U2).

Po připojení k PC se Arduino hlásí jako sériový port, na Unixových systémech většinou /dev/tty.usbmodem*, /dev/tty.usbserial*, /dev/ttyACM* nebo /dev/ttyUSB*. Záleží na operačním systému a verzi Arduina.

Pokud spustíte Arduino IDE a přeložíte libovolný kód, na pozadí se nejdříve spustí překladač avr-gcc. Jakmile chcete kód do Arduina nahrát, na pozadí nejdříve ATMega328 restartuje a pak se spustí avrdude.

Tímto víme vše potřebné, abychom obešli vývojové prostředí a napsali si vlastní makefile a kód přímo v jazyce C.

Kód blink.c

Nejdříve jednoduchý kód, který bude blikat LEDkou na pinu číslo 13. V Arduino IDE by nám stačilo napsat něco takového:

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

My ale nemáme k dispozici žádné Arduino funkce, vše si musíme napsat sami.

Při pohledu do schématu Una zjistíme, že pin číslo 13 je připojen na PB5. Náš kód bude vypadat třeba takto:

#include <avr/io.h>
#include <util/delay.h>

// Nastaví bit 'b' bajtu 'B' na log. nulu.
#define LOW(B, b)   (B &= ~_BV(b))
// Nastaví bit 'b' bajtu 'B' na log. jedničku.
#define HIGH(B, b)  (B |= _BV(b))

// Pin 13 je na 5. bitu portu B.
#define P13  (5)

void setup() {
  // Všechny piny portu B budou výstupní.
  DDRB = 0xFF;
}

/**
 * Hlavní smyčka programu
 */
static void loop() {
  LOW(PORTB, P13);
  _delay_ms(500);
  HIGH(PORTB, P13);
  _delay_ms(500);
}

int main(void) {
  setup();

  for (;;)
    loop();

  return 0;
}

Programování Arduina

Pro překlad .c do .hex použijeme avr-gcc:

$ avr-gcc -g -Os -mmcu=atmega328p -DF_CPU=16000000 -c blink.c
$ avr-gcc -g -mmcu=atmega328p -o blink.elf blink.o
$ avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex

Nejdříve přeložíme do objektového kódu. Musíme definovat frekvenci krystalu (Arduino používá 16MHz) a cílový mikrokontrolér (ATMega328P). Jelikož v kódu používáme funkci _delay_ms(), musíme kód optimalizovat pomocí přepínače -Os. Pokud bychom tuto optimalizaci nezapli, zmíněná funkce by nebyla přesná a blikání by mělo jinou frekvenci.

Teď již zbývá .hex nahrát do Arduina. To provedeme např.:

$ stty -f /dev/tty.usbmodem441 hupcl
$ avrdude -p atmega328p -P /dev/tty.usbmodem441 -c arduino -U flash:w:blink.hex

Prvním příkazem Arduino restartujeme. Přepínač -f funguje pouze pro BSD verzi stty, pokud máte GNU verzi, použijte -F. Pokud z nějakého důvodu nechcete použít stty, lze Arduino restartovat i tímto jednoduchým kódem v Pythonu:

import serial, time

s = serial.Serial('/dev/tty.usbmodem441', 57600)
s.setDTR(True)
time.sleep(1)
s.setDTR(False)

Druhým příkazem se Arduino naprogramuje. Avrdude lze použít i pro přečtení obsahu paměti. V případě, že chcete přečíst celou flash paměť Arduina, můžete použít toto:

$ avrdude -p atmega328p -P /dev/tty.usbmodem441 -c arduino -U flash:r:blink.hex:i

Pokud byste chtěli měnit i fuses a bootloader, již budete potřebovat externí programátor.

Makefile

A takto nějak může vypadat náš Makefile:

MCU = atmega328p
F_CPU = 16000000
FORMAT = ihex

CC = avr-gcc
FLAGS = -std=c99 -g -mmcu=$(MCU)
OBJCOPY = avr-objcopy
FILE = blink
PORT ?= /dev/tty.usbmodem*
STTY = stty
AVRDUDE = avrdude
PROGRAMMER = arduino

programme: $(FILE).hex
    $(STTY) -f $(PORT) hupcl
    $(AVRDUDE) -p $(MCU) -P $(PORT) -c $(PROGRAMMER) -U flash:w:$(FILE).hex

$(FILE).hex: $(FILE).elf
    $(OBJCOPY) -j .text -j .data -O $(FORMAT) $(FILE).elf $(FILE).hex

$(FILE).elf: $(FILE).o
    $(CC) $(FLAGS) -o $(FILE).elf $(FILE).o

$(FILE).o: $(FILE).c
    $(CC) $(FLAGS) -Os -DF_CPU=$(F_CPU) -c $(FILE).c

clean:
    rm -f *.elf *.o *.hex

A to je vše. Teď již pro programování Arduina stačí spustit make:

$ ls
blink.c Makefile
$ make

Závěr

V tomto článku jsem chtěl připomenout, že Arduino není nic jiného, než ATMega328 a pár součástek okolo. Pokud vám z nějakého důvodu vadí Arduino IDE, není nic jednoduššího, než jej obejít a využívat překladač gcc a avrdude.