Stap 2: Ons eerste AVR-C project: Hallo wereld, geleid!
De "Hello World" van microcontroller programmeren is de knipperende LED. Dus is dit net wat we proberen te bereiken met ons eerste programma!
Opgelet: U kunt de volledige code van deze programma's krijgen in mijn github-repository. Het is ook veel beter te bekijken van de broncode er omdat github heeft het juiste syntaxis benadrukken.
We willen niet te doen alle stappen van het compileren, koppelen, etc. met de hand dus het eerste wat dat we nodig hebben is een Makefile. Als u met de AVR Crosspack op OSX, kunt u de opdracht avr-project in de Terminal die automatisch een Makefile voor ons (en een XCode project dat we niet nodig hebben, zodat u het kunt verwijderen). Anders kunt u de volgende sjabloon gebruiken voor uw Makefile. Let wel op dat u de eerste regels van deze sjabloon voor uw eigen setup moet wijzigen (d.w.z. het apparaat en PROGRAMMEUR variabelen instellen).
--
#
# Makefile sjabloon voor ATtiny45
# Afgeleid van AVR Crosspack sjabloon
#
APPARAAT = attiny45 # Zie avr-hulp voor alle mogelijke toestellen
KLOK = 1000000 # 1Mhz
PROGRAMMEUR = - c usbtiny -P usb # voor het gebruik van Adafruit USBtiny
OBJECTEN main.o = # toevoegen meer objecten voor elk .c bestand hier
ZEKERINGEN = - U lfuse:w:0x62:m - U hfuse:w:0xdf:m - U efuse:w:0xff:m # instellingen zoals ontleend aan http://www.engbedded.com/fusecalc/
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
COMPILEREN = avr-gcc-Wall -Os-DF_CPU=$(CLOCK)-mmcu=$(DEVICE)
# symbolische doelen:
alle: main.hex
. c.o:
$(COMPILE) - c $< -o $@
. S.o:
$(COMPILE) - x assembler-met-cpp - c $< -o $@
. c.s:
$(COMPILE) -S $< -o $@
Flash: alle
$(AVRDUDE) - U flash:w:main.hex:i
zekering:
$(AVRDUDE) $(FUSES)
installeren: flash fuse
# Als u gebruikt een bootloader, wijzigen het bevel hieronder:
laden: alle
bootloadHID main.hex
schoon:
rm -f main.hex main.elf $(OBJECTS)
# bestand doelstellingen:
Main.elf: $(OBJECTS)
$(COMPILE) -o main.elf $(OBJECTS)
Main.hex: main.elf
rm -f main.hex
kunnen worden avr-objcopy - j .text -j .data - O ihex main.elf main.hex
avr-size--formaat = avr--mcu=$(DEVICE) main.elf
# Hebt u een EEPROM sectie, moet u ook een hex bestand voor maken de
# EEPROM en toevoegen aan de "flash"-doelstelling.
# Streefcijfers voor het debuggen van code en analyse:
DISASM: main.elf
avr-objdump - d main.elf
cpp:
$(COMPILE) -E main.c
--
U kunt benieuwd zijn, wat is dit ding "Smelt"? Terwijl uw microcontroller programmeren met een programmeur zal het instellen van sommige bits in de chip die een soort van initiële configuratie. Gebruik deze rekenmachine voor het instellen van de juiste bits. Met deze configuratie kan u bijvoorbeeld vertellen de microcontroller de resetpin gebruiken als normale I/O pin (die is handig wanneer u slechts 8 pinnen!).
Vervolgens moet u een minimale main.c-bestand, dat lijkt op dit:
--
#include < avr/io.h >
int main(void) {}
for(;;) {
hoofdlus
}
keren 0; nooit bereikt
}
--
Dit programma doet eigenlijk niets, maar laten we proberen om het te compileren niettemin. Type maken in de terminal en het zal compileren en koppeling van het programma. Het toont u zelfs het formaat van uw programma en hoeveel het zal bezetten op het apparaat.
maken , noem het alle doelwit van uw Makefile. Er zijn andere belangrijke doelstellingen in je Makefile:
- schone zal Verwijder alle gegenereerde binaire bestanden en laat u ze allemaal opnieuw compileren
- Installeer uw programma de microcontroller met behulp van een programmeur zal doorgeven. meer daarover later
Laat nu stap voor stap een volledige programma voor twee LED's knipperen op zijn beurt heeft laten maken:
Op het eerste moet we aantal headers voor algemene AVR-C-functies opnemen. We zijn reeds opgenomen met avr/io.h voor het afhandelen van I/O waardoor lezen en schrijven van/naar onze pinnen. Ook moeten we de functie van een "vertraging" omdat we willen dat de LED's knipperen gedurende een bepaalde tijd. Deze functie is opgenomen in util/delay.h. Daarna definiëren we onze pinnen die we gebruiken en de wachttijd. Dus de eerste regels van ons programma er als volgt uitzien:
--
#include < avr/io.h >
#include < util/delay.h >
Define pinnen
#define PIN_LED1 PB0
#define PIN_LED2 PB1
Definiëren van de vertragingstijd in ms
#define DELAY_MS 500
--
We moeten sommige helper macro's en functies die we vaak (in de toekomst gebruiken). In AVR-C instellen kunt u "hoog" als u een specifieke pin, het schrijven van een "1" bit aan een specifieke poort register met behulp van bitsgewijze bewerkingen. Wij hebben in onze ATtiny, slechts "PORTB" als I/O-poort. Wanneer we willen een PIN-code "PB0" te "hoog" ingesteld wij zou schrijven: PORTB | = (1 << PB0);
Stellen op "laag" werkt ook met een bitsgewijze bewerking door de bit op deze positie op "0" (logisch-niet 1): PORTB & = ~ (1 << PB0);
We gebruiken deze informatie om te schrijven twee macro's voor het instellen van een pincode op een bepaalde poort "laag" of "hoog". Verder definiëren we een functie dat ons toelaat om het maken van lange vertragingstijden. Het probleem met lange vertragingstijden is, dat de interne timer (of klok-teller) wil registreren overloop zeer snel omdat het is meestal alleen een 8-bits teller. Daarom, definiëren we een functie dat onze vertraging in 10ms brokken splitst en we wachten X keer 10ms voor het verkrijgen van de lange vertraging.
--
op poort < PWT > digitale "hoog" als u pin < pn > schrijven
#define DIGIWRITE_H (PWT, pn) prt | = (1 << pn)
op poort < PWT > digitale "laag" pin < pn > schrijven
#define DIGIWRITE_L (PWT, pn) prt & = ~ (1 << pn)
Langdurige vertraging functie definiëren
VOID long_delay_ms (uint16_t ms) {}
voor (ms / = 10; ms > 0; ms--) _delay_ms(10);
}
--
Nu laten we gaan naar onze main() functie. In eerste instantie zullen we de "richting gegevensregister" voor poort B, die is ingesteld in de variabele DDRB te wijzigen. Dit register vertelt de chip, welke pinnen invoergegevens kunnen krijgen en welke pinnen legt een uitgangsspanning. Alle pinnen zijn standaard ingesteld om "input". Wanneer we willen instellen van bepaalde pinnen te "output" we moeten hun register op '1' bit
De rest is vrij eenvoudig: We nu alleen bellen DIGIWRITE_L() en DIGIWRITE_H() voor de respectieve pinnen en de status worden afgewisseld met behulp van een variabele knevel/uitschakelen. Daarna voegen we een vertragingstijd.
Let op het gebruik van uint8_t voor de toggle -variabele. Bij het schrijven van code voor chips met een zeer kleine hoeveelheid van flash-geheugen, is het van cruciaal belang om altijd gebruik maken van het kleinst mogelijke gegevenstype. U kunt wijzigen, bijvoorbeeld int32_t en u zult zien dat het geheugengebruik iets stijgt.
Dit is de volledige code voor onze hoofdlus:
--
programma ingangspunt
int main(void) {}
DDRB is de "richting gegevensregister" voor poort B
de ATtinyX5 heeft alleen poort B met bruikbare pinnen
Wij stellen beide LED-pins aan "output"
DDRB | = (1 << PIN_LED1) | (1 << PIN_LED2);
in eerste instantie ingesteld de pinnen te "laag"
DIGIWRITE_L (PORTB, PIN_LED1);
DIGIWRITE_L (PORTB, PIN_LED2);
hoofdlus
uint8_t knevel = 0;
for(;;) {
schakelen tussen de LEDs te laten knipperen
DIGIWRITE_L (PORTB, (knevel == 0? PIN_LED1: PIN_LED2));
DIGIWRITE_H (PORTB, (knevel == 0? PIN_LED2: PIN_LED1));
alternave de variabele knevel
Toggle =! in-/ uitschakelen;
Zorg een lange vertraging
long_delay_ms(DELAY_MS);
}
keren 0; / * nooit bereikt * /
}
--
Typ maken om te compileren van de code. Zal het genereren van een .hex-bestand. In de volgende stap, zal ik uitleggen hoe we dit aan onze ATtiny uploaden.