Ich habe einen ATmega328-PU-Chip, der so eingerichtet ist, dass er den internen Oszillator verwendet, ohne die Taktrate durch 8 zu teilen. Das erste, was ich tat, war, ein wirklich einfaches Programm zu verwenden, das ich hochgeladen hatte; ein Programm, das alle Pins auf PORTB ein- und ausschaltet. So:
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/delay.h>
int main(void) {
DDRB = 0xFF;
PORTB = 0xFF;
while(1) {
PORTB = 0x00;
_delay_ms(50);
PORTB = 0xFF;
_delay_ms(50);
}
return 0;
}
Update 4: Ich habe simulavr und avr-gdb angeschlossen, um zu sehen, ob ich etwas gefunden habe, dies war die Ausgabe:
memory.c:267: WARNUNG: * * Versuch, ungültige IO-Registrierung zu schreiben: ADCL bei 0x0024 memory.c:267: WARNUNG: * * Versuch, ungültige io-Registrierung zu schreiben: ADCH bei 0x0025 memory.c:267: WARNUNG: * * Versuch, eine ungültige IO-Registrierung zu schreiben: ADCH bei 0x0025 decoder.h:59: WARNUNG: Unbekannter Opcode: 0xffff
Dann wird der unbekannte Opcode für immer wiederholt.
Wenn ich das Programm jedoch hochgeladen habe, erreicht es die zweite Anweisung in main und friert dann ein. Lassen Sie alle PORTB-Pins auf HIGH. Bisher habe ich versucht:
-DF_CPU=8000000
Warum ich hier frage und nicht stackoverflow.com, ist, weil ich denke, ich sollte anfangen, Fehler auf der niedrigsten Abstraktionsebene zu beseitigen, das heißt; Hardware.
Was könnte also das Problem sein?
Hier ein paar Informationen zu meinem Setup:
Sicherungseinstellungen:
avrdude: safemode: lfuse reads as E2
avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as 7
Eingabe in den AVR-Sicherungsrechner :
makefile:
main:
avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c
hex:
avr-objcopy -j .text -j .data -O ihex example.o example.hex
dump:
avr-objdump -h -S example.o > example.lst
upload:
avrdude -p m328 -c avrispmkII -P usb -U flash:w:example.hex
clean:
rm -f *.o
rm -f *.hex
rm -f *.lst
Auf dem Chip verwendete Pins:
Ich vermute, das Problem liegt nicht an Ihrem C-Code, sondern an Ihrem Makefile.
Die folgenden Zeilen in Ihrem Makefile erzeugen eine example.o
Objektdatei.
main:
avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c
Die erstellte .o
Datei enthält nur die Symbole und den Code von example.c
, nicht die zusätzliche Quelle, die erforderlich ist, um sie tatsächlich auf einem Zielsystem auszuführen, wie z. B. Interrupt-Vektorsprungtabellen und Code zum Initialisieren des BSS-RAM-Segments auf Nullen und zum Laden Ihrer initialisierten Datenabschnitte.
Sie müssen eine zusätzliche Zeile wie diese hinzufügen, um den Linker auszuführen und ein Ausgabeobjekt zu erzeugen, das zum Herunterladen in den AVR-Teil geeignet ist. Verwenden Sie alternativ avr-ld
, aber Sie müssen alle erforderlichen Linkeroptionen ausarbeiten.
main.elf: example.o
avr-gcc example.o -o main.elf
Sie können avr-objdump --disassemble-all <filename>
beide example.o
und main.elf
sich selbst verwenden, um den unterschiedlichen Inhalt jeder Datei zu überprüfen.
Es ist immer eine gute Idee, zu versuchen, Ihr Problem schrittweise auf ein möglichst einfaches Beispiel zu reduzieren. In diesem Fall würde es wahrscheinlich bedeuten, in die AVR Studio-Software zu wechseln und ein Projekt zu erstellen, das auf dem Simulator mit ihrem verwalteten Build-Prozess ausgeführt wird. Von dort aus können sie das Makefile exportieren, das von ihrem Build-Prozess verwendet wird, indem sie die Menüoption „Makefile exportieren“ verwenden. Das generierte Makefile könnte dann mit Ihrer Version verglichen werden.
Tatsächlich ist es wahrscheinlich eine gute Idee, ein Makefile zu verwenden, das dem von AVR Studio generierten ähnlich ist, da die richtigen Regeln bereits definiert sind. Sie müssen nur einige Variablen in Bezug auf die zu generierenden Objekte und die endgültige Zieldatei einrichten Name.
Für Ihre CPU-Geschwindigkeit:
#define F_CPU 80000000UL
Hast du da extra 0
drauf? Das liest 80 MHz. Ich denke, Sie wollen 8 MHz.
BEARBEITEN: Nachdem Sie das Datenblatt überprüft haben, scheint Ihr lfuse
Wert in Ordnung zu sein, wenn Sie den internen Oszillator mit deaktiviertem CKDIV8 verwenden möchten.
../src/example.c
der richtige Ort ist und dass Sie alle Ihre Objektdateien bei jedem Versuch bereinigt haben? Vielleicht lassen Sie das Makefile einfach auf sein eigenes Verzeichnis verweisen, nur um es vorerst zu vereinfachen. Will nur Unfälle ausschließen...Ihr Programm sollte in der Lage sein _delay_ms()
, , aber es gibt andere Möglichkeiten, eine Verzögerung zu erreichen.
_delay_ms()
ruft nur auf _delay_us()
, was delay_basic.h_delay_loop_1()
aufruft . , wie der Name schon sagt, einfach Schleifen. Es verwendet 3 Anweisungen pro Schleife (für Vergleiche, Inkremente und Sprünge), und die Zeit, die für jede Anweisung benötigt wird, ist konstant und bekannt, sodass es für eine bestimmte Zeit verzögert werden kann, indem es einfach in einer Schleife ausgeführt wird._delay_loop_1()
Um jegliche Verwirrung über Ihre Programmstruktur und Ihren F_CPU
Suchpfad zu beseitigen, könnten Sie die Funktion (ganz grob) in der Hauptfunktion wie folgt neu erstellen:
#include <stdint.h> // Typedefs `unsigned long` or something to `uint16_t`,
// a 16-bit unsigned number
#include <avr/io.h>
int main(void) {
uint16_t counter1, counter2;
DDRB = 0xFF;
PORTB = 0xFF;
while(1) {
PORTB ^= 0; // Use XOR to toggle
for (counter1 = 0; counter1 < 10; counter1++) {
for (counter2 = 0; counter2 < 50000; counter2++) { /* Do nothing */ }
}
}
return 0;
}
Die andere Methode besteht darin, die integrierten Timer-Peripheriegeräte zu verwenden, anstatt einen Timer mit einer Schleife zu implementieren. Sie können diese Peripheriegeräte auf viele Arten verwenden, aber ich werde die zwei einfachsten Möglichkeiten unten beschreiben. Erstens können Sie den Timer aus einer Schleife abfragen und etwas tun, wenn der Wert einen bestimmten Punkt erreicht. Zweitens können Sie den Timer einen Interrupt ausgeben lassen, wenn er einen bestimmten Punkt erreicht, und Ihren Code nichts tun lassen.
Um aus einer Schleife abzufragen, würden Sie etwa so vorgehen:
#include <avr/io.h>
int main (void)
{
DDRB = 0xFF; // Set port as output
TCCR1B |= _BV(CS10); // Set up timer
while(1) {
// Check timer value in if statement, true when count matches 1/20th of a second
if (TCNT1 >= 49999)
{
PORTB ^= 0xFF; // Toggle the port
TCNT1 = 0; // Reset timer value
}
}
}
So geben Sie einen Interrupt aus:
#include <avr/io.h>
#include <avr/interrupt.h>
int main (void)
{
DDRB = 0xFF; // Set LED as output
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TIMSK |= (1 << OCIE1A); // Enable CTC interrupt
sei(); // Enable global interrupts
OCR1A = 10000; // Set CTC compare value, about 50ms.
TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64
while(1) {
// Do nothing, the interrupt is taking care of it.
}
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= 0xFF; // Toggle the LED
}
Die obigen Codebeispiele wurden aus dem exzellenten Timer-Tutorial zu AVR Freaks übernommen , das auch auf Deans Website hier im PDF-Format verfügbar ist .
Darüber hinaus schaltet Ihr Code (und jedes der obigen Beispiele) den gesamten Port um. Ihre LED ist auf PB0; Sie können nur diesen Pin umschalten, indem Sie jede Zuweisung zu PORTB durch ersetzen
PORTB ^= _BV(PB0);
was PORTB nur mit vergleicht 0x0000 0001
.
Vicatcu
rzetterberg
main:
, wo Sie sehen können, dass das-Os
verwendet wird.