Ich habe eine Funktion, die eine LED einschaltet, indem PB1 auf HIGH gesetzt wird. Das Licht der LED ist kaum sichtbar. Wenn ich PB1 in meiner Hauptfunktion auf HIGH stelle, ist das Licht der LED so hell, wie es sein sollte.
Es macht für mich keinen Sinn, da es nur einen Wert ändert, der entweder 0 oder 1 ist. Ich muss etwas sehr Offensichtliches übersehen, aber was könnte es sein?
Hier einige Hintergrundinformationen:
Bearbeiten: Weitere Informationen und Tags hinzugefügt
Hier ist der Code mit dem Pin-Set in main:
#include <avr/io.h>
int main(void) {
DDRB |= (1 << PB1);
PORTB |= (1 << PB1);
while(1) {
}
return 0;
}
Und hier ist der Code, wo der Pin in einer Funktion gesetzt wird:
#include <avr/io.h>
void turn_on_led();
int main(void) {
DDRB |= (1 << PB1);
turn_on_led();
while(1) {
}
return 0;
}
void turn_on_led()
{
PORTB |= (1 << PB1);
}
Hier ist das Makefile:
main:
avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c
elf:
avr-gcc example.o -o example.elf
hex:
avr-objcopy -O ihex example.elf 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
Sicherungseinstellung:
avrdude: Device signature = 0x1e9514
avrdude: safemode: lfuse reads as E2
avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as 7
Eingabe der Sicherungseinstellung in einen Sicherungsrechner, um einzelne Bits anzuzeigen:
Demontage des Einstellstiftes im Wesentlichen:
example.o: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 0000003a 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 0000003a 2**0
ALLOC
3 .debug_abbrev 0000004e 00000000 00000000 0000003a 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_info 00000082 00000000 00000000 00000088 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
5 .debug_line 000000c2 00000000 00000000 0000010a 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
6 .debug_frame 00000020 00000000 00000000 000001cc 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
7 .debug_pubnames 0000001b 00000000 00000000 000001ec 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
8 .debug_pubtypes 0000001e 00000000 00000000 00000207 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
9 .debug_aranges 00000020 00000000 00000000 00000225 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
10 .debug_str 000000d9 00000000 00000000 00000245 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <main>:
#include <avr/io.h>
int main(void) {
DDRB |= (1 << PB0);
0: 20 9a sbi 0x04, 0 ; 4
PORTB |= (1 << PB0);
2: 28 9a sbi 0x05, 0 ; 5
4: 00 c0 rjmp .+0 ; 0x6 <__zero_reg__+0x5>
Demontage Einstellstift in Funktion:
example.o: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000000c 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000040 2**0
ALLOC
3 .debug_abbrev 00000061 00000000 00000000 00000040 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_info 00000096 00000000 00000000 000000a1 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
5 .debug_line 000000dc 00000000 00000000 00000137 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
6 .debug_frame 00000030 00000000 00000000 00000214 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
7 .debug_pubnames 0000002b 00000000 00000000 00000244 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
8 .debug_pubtypes 0000001e 00000000 00000000 0000026f 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
9 .debug_aranges 00000020 00000000 00000000 0000028d 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
10 .debug_str 000000e5 00000000 00000000 000002ad 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <turn_on_led>:
return 0;
}
void turn_on_led()
{
PORTB |= (1 << PB1);
0: 29 9a sbi 0x05, 1 ; 5
}
2: 08 95 ret
00000004 <main>:
#include <avr/io.h>
void turn_on_led();
int main(void) {
DDRB |= (1 << PB1);
4: 21 9a sbi 0x04, 1 ; 4
turn_on_led();
6: 0e 94 00 00 call 0 ; 0x0 <turn_on_led>
a: 00 c0 rjmp .+0 ; 0xc <main+0x8>
Wow, das ist ziemlich verrückt. Diese Programme sind fast identisch. Nur zum einfacheren Vergleich hat das erste Programm mit der Zuweisung in main
die Assembly (mit Kommentaren):
0: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
2: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
4: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
Das zweite Programm mit der Zuweisung in einer Funktion hat die Assembly:
0: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
2: 08 95 ret ; Return to the address on the call stack
4: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
6: 0e 94 00 00 call 0 ; Call the function at address 0 (at top)
a: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
Das dritte Programm führt eine interessante Optimierung durch - es entfernt den Funktionsaufruf vollständig, fügt ihn ein und macht dieses Programm mit dem ersten identisch. Sie könnten wahrscheinlich einen identischen Effekt mit erzielen inline void turn_on_led()
. Der Vollständigkeit halber ist die Baugruppe:
0: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
2: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
4: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
Die Adressen 0
bis a
sind Adressen innerhalb des .text
Abschnitts, nicht im Programmspeicher. Der .text-Abschnitt beginnt bei Offset 0x34 gemäß der File off[set]
Direktive:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000034 2**0
Was sich wirklich an den Adressen 0-34 befindet, ist (normalerweise) die Interrupt-Vektortabelle. Wenn Sie das BOOTRST
Flag ausgewählt hätten, wäre es am Start des Bootloaders, aber Sie haben es nicht getan, also ist es nicht. Das erste Element in der Unterbrechungsvektortabelle teilt dem Prozessor mit, wohin er beim Zurücksetzen oder Hochfahren gehen soll.
Diese Stelle sollte die erste Anweisung main
für diese Programme sein ( 0
für den ersten Fall, 4
für den zweiten Fall), aber es ist möglich, dass sie 0
im zweiten Programm standardmäßig adressiert wird, was den Ausgang hoch setzen würde, während das Datenrichtungsregister noch gesetzt war zur Eingabe gemäß den Standardeinstellungen. Dies würde den schwachen Pullup einschalten, und die spätere Änderung des Datenrichtungsregisters hätte keine Wirkung.
Ich würde vermuten, dass dies passiert. Um zu testen, ob Ihr Problem darin besteht, dass Ihre Ausgabe nur den schwachen Pullup verwendet, können Sie die DDRB-Zuweisung vollständig aus beiden Programmen entfernen. Das Ergebnis sollte eine schwach leuchtende LED sein. Wenn es nicht die gleiche Helligkeit ist, dann ist dies nicht Ihr Problem. Wenn es die gleiche Helligkeit ist, würde ich vermuten, dass dies tatsächlich Ihr Problem ist.
Ein weiteres Problem könnte die Erwähnung in Abschnitt 11.2 des Datenblatts „Ports as General Digital I/O“ sein, dass:
Wie in Abbildung 11-2 gezeigt, bilden das PINxn-Registerbit und das vorhergehende Latch einen Synchronisierer. Dies ist erforderlich, um Metastabilität zu vermeiden, wenn der physische Pin den Wert nahe der Flanke des internen Takts ändert, führt aber auch zu einer Verzögerung. Abbildung 11-3 zeigt ein Zeitdiagramm der Synchronisation beim Lesen eines extern angelegten Pinwerts.
Daher gibt es in jedem Beispiel des Lesens eines Pins eine Nulloperation, während der diese Synchronisationsverzögerung berücksichtigt wird. Verwenden Sie __no_operation();
für diesen Effekt in C. Diese Notwendigkeit einer Verzögerung ist bei der Programmierung eingebetteter Systeme sehr verbreitet; Es ist billiger, den Programmierer eine Verzögerung in seinen Code einbauen zu lassen, als einige Dinge in einem einzigen Taktzyklus passieren zu lassen.
In Ihrem ersten Programm haben Sie keine solche Verzögerung. In Ihrem zweiten Programm haben Sie eine Verzögerung. Das sollte dazu führen, dass das erste Programm nicht funktioniert, aber das zweite Programm sollte funktionieren. Dies ist nicht der Fall, und es gibt keine solche Verzögerung an den Ausgängen, daher bezweifle ich, dass dies das Problem ist.
Ihre Assembly zeigt, dass dies nicht der Fall ist, aber ein häufiger Fehler besteht darin, die while(1)
. Dies kann dazu führen, dass der Prozessor sofort nach dem Einschalten der LED zurückgesetzt wird. Während der Prozessor sich selbst zurücksetzt und das DDRB-Register gesetzt wird, ist die LED aus. Dann ist es kurz an und der Reset beginnt von vorne. Dies bildet bei einem Unfall ein rudimentäres PWM-System , wodurch die LED schwach erscheint.
Sie haben jedoch eine while(1)
(beachten Sie, dass dies for(;;)
ein beliebtes Äquivalent ist), und es wird in der Assembly als angezeigt rjmp .+0
, sodass dies nicht Ihr Problem zu sein scheint . Ich bin ein wenig verwirrt von der 0
, rjmp
ändert den Programmzähler in PC + k + 1. Normalerweise verwenden wir dafür Labels, wenn wir in Assembler schreiben, und dies sollte daher a k
von -1 ausgeben, aber es scheint vernünftig, darauf zu vertrauen, dass der Compiler tut hier das Richtige.
Schauen wir uns jedoch die Codierung genauer an. Der Hex-Code für die Anweisung ist 00 c0
. Gemäß dem AVR Instruction Set-Handbuch ist der Opcode für rjmp 1100 kkkk kkkk kkkk
, oder, in Hex, 0xCK KK, wobei die Verkettung von K
k ist, unser relativer Sprung. Der AVR, den wir verwenden, ist Little-Endian, also ist, 00 C0
wie im Programm zu sehen, ein relativer Sprung (C) zu einer Position 0 Bytes entfernt.
Gemäß der Operationsbeschreibung führt dies die Operation PC < – PC + 0 + 1 durch oder erhöht den Programmzähler über diese Adresse hinaus. Es kann jedoch sein, dass dies nicht die korrekte Interpretation dieser Operation ist, ich habe bei der Arbeit mit dieser Anweisung immer Labels verwendet, daher wurde die tatsächlich verwendete Zahl vom Assembler abstrahiert.
Den Compiler beschuldigen, die Zeilen falsch interpretiert zu haben
while(1) {
}
ist aber ziemlich extrem. Ich glaube nicht, dass dies das Problem ist.
Hoffe das hilft!
Nachdem ich die Informationen gelesen hatte, die Kevin in seiner Antwort bereitgestellt hatte, las ich ein Dokument mit dem Titel „AVR32006: Erste Schritte mit GCC für AVR32“. In diesem Dokument gab es einen bestimmten Abschnitt, der mich darüber nachdenken ließ, was Kevin über die Vektortabelle gesagt hat.
Der Verknüpfungsprozess benötigt Informationen über Code und Datenspeicherort. Die Verwendung eines Linker-Skripts bietet dies. Wenn Sie angeben, für welches Gerät Sie Code kompilieren, indem Sie das ?-mpart=? Option in avr32-gcc, wird ein Standard-Linker-Skript für dieses Gerät verwendet.
Also habe ich versucht, das mmcu
Flag zum Verknüpfungsbefehl hinzuzufügen, sodass es geändert wurde von:
elf:
avr-gcc example.o -o example.elf
Zu:
elf:
avr-gcc -mmcu=atmega328 example.o -o example.elf
Dies stellte sich als Problem heraus, der Vektortisch war nicht richtig eingerichtet und viele andere Teile fehlten.
Hier ist die Demontage nach dem Hinzufügen der mmcu
Flagge:
example.elf: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000090 00000000 00000000 00000054 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .stab 000006cc 00000000 00000000 000000e4 2**2
CONTENTS, READONLY, DEBUGGING
2 .stabstr 00000081 00000000 00000000 000007b0 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
74: 0e 94 42 00 call 0x84 ; 0x84 <main>
78: 0c 94 46 00 jmp 0x8c ; 0x8c <_exit>
0000007c <__bad_interrupt>:
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000080 <turn_on_pb>:
return 0;
}
void turn_on_pb(void)
{
PORTB |= (1 << PB0);
80: 28 9a sbi 0x05, 0 ; 5
}
82: 08 95 ret
00000084 <main>:
void turn_on_pb(void);
int main(void)
{
DDRB |= (1 << PB0);
84: 20 9a sbi 0x04, 0 ; 4
turn_on_pb();
86: 0e 94 40 00 call 0x80 ; 0x80 <turn_on_pb>
8a: ff cf rjmp .-2 ; 0x8a <main+0x6>
0000008c <_exit>:
8c: f8 94 cli
0000008e <__stop_program>:
8e: ff cf rjmp .-2 ; 0x8e <__stop_program>
Ich stelle mir vor, dass Ihr Problem darin besteht, dass der Stapel nicht richtig konfiguriert ist, sodass die call
Anweisung fehlschlägt, wenn versucht wird, die Rücksendeadresse zu speichern. Dies führt wahrscheinlich zu einem Prozessor-Reset, was zu einem versehentlichen PWM führt, wie von Kevin beschrieben, mit der Ausnahme, dass DDRB von PWM betroffen ist und PORTB überhaupt nie eingestellt ist.
Klatsch
#define
s verwenden, um die LED-Ausgänge umzubenennen und „magische Zahlen“ zu vermeiden, da dies zu einem saubereren und besser lesbaren Code führt.rzetterberg
Toby Jaffey
rzetterberg
Flucht
Vicatcu
rzetterberg
rzetterberg