Älskling, jag krympte Arduino: flytta från Arduino till ATtiny och skriva din kod i ren AVR-C (2 / 5 steg)
Steg 2: Vårt första AVR-C projekt: Hej världen, ledde!
"Hej världen" microcontroller programmering är den blinkande lysdioden. Så är detta bara vad vi ska försöka uppnå med vårt första program!
Observera: Du kan få den fullständiga koden för dessa program i min github-repository. Det är också mycket bättre att visa källkoden det eftersom github har korrekt syntax highlighting.
Vi vill inte göra alla steg i sammanställningen, länka, etc. för hand så det första vi behöver är en Makefile. Om du använder AVR Crosspack på OSX, kan du använda kommandot avr-projekt i terminalen som automatiskt skapar en Makefile för oss (och en XCode projekt som vi inte behöver så kan du ta bort det). Annars kan du använda följande mall för din Makefile. Observera att du bör ändra de första raderna i denna mall för dina egna inställningar (dvs anges variablerna enhet och programmerare).
--
#
# Makefile mall för ATtiny45
# Härrör från AVR Crosspack mall
#
DEVICE = attiny45 # se avr-hjälp för alla möjliga enheter
KLOCKAN = 1000000 # 1Mhz
PROGRAMMERARE = - c usbtiny -P usb # för att använda Adafruit USBtiny
OBJEKT = main.o # lägga till fler objekt för varje .c fil här
SÄKRINGAR = - U lfuse:w:0x62:m - U hfuse:w:0xdf:m - U efuse:w:0xff:m # inställningar som hämtats från http://www.engbedded.com/fusecalc/
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
SAMMANSTÄLLA = avr-gcc-vägg -Os-DF_CPU=$(CLOCK)-mmcu=$(DEVICE)
# symboliskt mål:
alla: main.hex
. c.o:
$(COMPILE) - c $< -o $@
. S.o:
$(COMPILE) - x assembler-med-cpp - c $< -o $@
. c.s:
$(COMPILE) -S $< -o $@
Flash: alla
$(AVRDUDE) - U flash:w:main.hex:i
säkring:
$(AVRDUDE) $(FUSES)
installera: flash fuse
# Om du använder en bootloader, ändrar kommandot nedan på lämpligt sätt:
Ladda: alla
bootloadHID main.hex
ren:
RM -f main.hex main.elf $(OBJECTS)
# Fil mål:
Main.Elf: $(OBJECTS)
$(COMPILE) -o main.elf $(OBJECTS)
Main.hex: main.elf
RM -f main.hex
AVR-objcopy - j .text -j .data - O ihex main.elf main.hex
AVR-storlek - format = avr--mcu=$(DEVICE) main.elf
# Om du har ett EEPROM avsnitt, måste du också skapa en hex-fil för den
# EEPROM och lägga till "flash" målet.
# Mål för koden felsökning och analys:
disasm: main.elf
AVR-objdump - d main.elf
cpp:
$(COMPILE) -E main.c
--
Du kanske undrar, vad är det "Säkringar" här? Det kommer att ställa några bitar i chip som är någon form av inledande konfiguration medan programmering din mikrokontroller med en programmerare. Använda denna kalkylator för att ställa rätt bitar. Med den här konfigurationen kan du till exempel berätta mikrokontroller att använda Återställ PIN-koden som normala i/o pin (vilket är bra när du har bara 8 stift!).
Sedan behöver du en minimal main.c-fil som ser ut så här:
--
#include < avr/io.h >
int main(void) {
for(;;) {
huvudloop
}
Return 0; nådde aldrig
}
--
Detta program gör egentligen ingenting, men låt oss försöka sammanställa det ändå. Typ gör i terminalen och det kommer att sammanställa och länka programm. Den visar även du storleken på ditt program och hur mycket den ska upptar på enheten.
gör kommer att kalla alla mål för din Makefile. Det finns andra viktiga mål i din Makefile:
- ren att radera alla genererade binära filer och låter dig sammanställa dem alla på nytt
- installera kommer att överföra programmet till microcontroller med en programmerare. mer om det senare
Låter nu steg för steg skapar ett fullt program för att låta två lysdioder blinkar i sin tur:
Först måste vi inkludera vissa huvuden för vanliga AVR-C funktioner. Vi med redan avr/io.h för I/O hantering, vilket innebär att läsa och skriva från/till våra pins. Vi behöver även en "delay" funktion eftersom vi vill att lysdioderna blinka en viss tid. Denna funktion ingår i util/delay.h. Nästa vi definierar våra pins som vi använder och fördröjningstiden. Så de första raderna i vårt program ser ut så här:
--
#include < avr/io.h >
#include < util/delay.h >
Definiera pins
#define PIN_LED1 PB0
#define PIN_LED2 PB1
Definiera fördröjningstiden i ms
#define DELAY_MS 500
--
Vi behöver några helper makron och funktioner som vi använder ofta (i framtiden). I AVR-C anger du "hög" till en specifik stift, genom att skriva lite "1" till en viss port register med bitvis operationer. I vår ATtiny har vi bara "PORTB" som jag/O-port. När vi vill ange en PIN-kod "PB0" till "hög" vi skulle skriva: PORTB | = (1 << PB0);
Ställa in den på "låg" fungerar på samma sätt med en binär operation genom att biten i detta läget "0" (LOGISKT, inte 1): PORTB & = ~ (1 << PB0);
Vi använder denna information för att skriva två makron för att ange en PIN-kod på en viss port "låg" eller "hög". Vidare definierar vi en funktion som tillåter oss att skapa långa fördröjningstider. Problemet med långa fördröjningstider är, att den interna timer (eller klocka-counter) registrerar kommer overflow mycket snabbt eftersom det är oftast bara en 8-bitars räknare. Därför vi definierar en funktion som delar vår försening i 10ms bitar och vi vänta X gånger 10 MS för att få det långa dröjsmålet.
--
skriva digital "hög" till stift < pn > på port < prt >
#define DIGIWRITE_H (prt, pn) prt | = (1 << pn)
skriva digital "låg" till stift < pn > på port < prt >
#define DIGIWRITE_L (prt, pn) prt & = ~ (1 << pn)
Definiera lång fördröjning funktion
void long_delay_ms (uint16_t ms) {
för (ms = 10; ms > 0; ms--) _delay_ms(10);
}
--
Nu låt oss gå vidare till vår main () funktion. Först kommer vi att ändra "data riktning register" för port B, som ligger i den rörliga DDRB. Detta register berättar chip, vilket stift kan få indata och vilket stift skall producera en utgångsspänning. Som standard är alla stift inställd på "input". När vi vill ange vissa stift till "output" vi måste ange sitt register lite till "1"
Resten är ganska enkel: vi nu bara ringa DIGIWRITE_L() och DIGIWRITE_H() för respektive stift och alternativa status med hjälp av en variabel Växla. Därefter lägger vi till en fördröjning.
Observera användningen av uint8_t för variabeln Växla . När du skriver kod för marker med mycket en liten mängd av flash-minne, är det viktigt att alltid använda minsta möjliga datatypen. Du kan ändra den till, till exempel int32_t och du ser att minnesförbrukningen något stiger.
Detta är den fullständiga koden för våra viktigaste loop:
--
programmet startpunkt
int main(void) {
DDRB är "data riktning register" för port B
ATtinyX5 har endast port B med användbara stift
vi satt båda LED stift att "mata"
DDRB | = (1 << PIN_LED1) | (1 << PIN_LED2);
Inledningsvis stiften till "låg"
DIGIWRITE_L (PORTB, PIN_LED1);
DIGIWRITE_L (PORTB, PIN_LED2);
huvudloop
uint8_t växla = 0;
for(;;) {
växla mellan lamporna att blinka
DIGIWRITE_L (PORTB, (växla == 0? PIN_LED1: PIN_LED2));
DIGIWRITE_H (PORTB, (växla == 0? PIN_LED2: PIN_LED1));
alternave växla variabeln
Toggle =! växla;
gör en lång fördröjning
long_delay_ms(DELAY_MS);
}
Return 0; / * aldrig nått * /
}
--
Skriver gör att kompilera koden. Det kommer att generera en hex-fil. I nästa steg, ska jag förklara hur vi upp detta till våra ATtiny.