Hallo zusammen, ich habe ein Verständnisproblem und hoffe hier Hilfe zu erhalten. Wenn ich ein Probgramm in Assembler schreibe und gucke mir den Code mit der Disassembler Funktion unter AVR Studio an, dann sieht das wunderbar aus. (Ganz einfaches Programm!) Wenn ich es aber in C schreibe (gleicher Sinn) und mir dann den Assembler code ansehe fängt der schon gar nicht bei Adresse Null an! Das sieht irgendwie total durcheinander aus. Kann das vielleicht mit der Optimierung zu tuen haben? Wobei die auf -Os steht. Hab mal die ersten Zeilen eingefügt ----UNKNOWN_FILE-------------------------------------------------------- ----- 0: File not found +00000000: 940C0038 JMP 0x00000038 Jump Kann mir jemand was dazu sagen? Vielen Dank im voraus.
Vermutlich verwendest Du keine Interrupts, dann beginnt Dein Code wahrscheinlich bei 0x0000. Der Compiler setzt bei Adresse 0x0000 einen Sprung der über die Interrupt-Sprungtabelle hinwegführt. (Mache bei Asm-Programmen aus Prinzip schon immer so...) Bei unbenutzen Interrupts steht dann halt ein Reti.
Habe ein ganz einfaches Programm geschrieben
1 | .include "m162def.inc" |
2 | .def temp = r16 |
3 | |
4 | ldi temp, HIGH(RAMEND) |
5 | out SPH, temp |
6 | ldi temp, LOW(RAMEND) |
7 | out SPL, temp |
8 | ldi temp, 0xff |
9 | out DDRB, temp |
10 | ldi R17, 0b10101010 |
11 | rcall sub1 |
12 | out PORTB, R17 |
13 | loop: rjmp loop |
14 | sub1: |
15 | push R17 |
16 | ldi R17, 0 |
17 | pop R17 |
18 | ret |
Nur für verständnis. Es stimmt es sind keine Interrupts vorhanden. Heißt das, dass in C der Compiler gleich einen Offset für eventuelle Interrupts einfügt?
Keinen Offset, sondern eine Sprungtabelle, also einen Block von Code, der aus jmps besteht, für jeden Interruptvektor einen. Auch danach beginnt nicht Dein Programm, sondern es folgen die Flash-Konstanten (Dinge, die im Programm mit PROGMEM festgelegt wurden). Dann kommt der C-Startup-Code, und erst danach folgt Dein Programm.
Okay! Hab jetzt mal in C das hier geschrieben:
1 | #include<avr/io.h> |
2 | |
3 | int main() |
4 | {
|
5 | DDRB=0xff; |
6 | PORTB=0x55; |
7 | |
8 | while(1) |
9 | {
|
10 | |
11 | }
|
12 | }
|
Im Disassembler sieht das so aus: 0: File not found +00000000: 940C0038 JMP 0x00000038 Jump +00000002: 940C0055 JMP 0x00000055 Jump +00000004: 940C0055 JMP 0x00000055 Jump +00000006: 940C0055 JMP 0x00000055 Jump +00000008: 940C0055 JMP 0x00000055 Jump +0000000A: 940C0055 JMP 0x00000055 Jump +0000000C: 940C0055 JMP 0x00000055 Jump +0000000E: 940C0055 JMP 0x00000055 Jump +00000010: 940C0055 JMP 0x00000055 Jump +00000012: 940C0055 JMP 0x00000055 Jump +00000014: 940C0055 JMP 0x00000055 Jump +00000016: 940C0000 JMP 0x00000000 Jump +00000018: 940C0055 JMP 0x00000055 Jump +0000001A: 940C0055 JMP 0x00000055 Jump +0000001C: 940C0055 JMP 0x00000055 Jump +0000001E: 940C0055 JMP 0x00000055 Jump +00000020: 940C0055 JMP 0x00000055 Jump +00000022: 940C0055 JMP 0x00000055 Jump +00000024: 940C0055 JMP 0x00000055 Jump +00000026: 940C0055 JMP 0x00000055 Jump +00000028: 940C0055 JMP 0x00000055 Jump +0000002A: 940C0055 JMP 0x00000055 Jump +0000002C: 940C0055 JMP 0x00000055 Jump +0000002E: 940C0055 JMP 0x00000055 Jump +00000030: 940C0055 JMP 0x00000055 Jump +00000032: 940C0055 JMP 0x00000055 Jump +00000034: 940C0055 JMP 0x00000055 Jump +00000036: 940C0055 JMP 0x00000055 Jump +00000038: 2411 CLR R1 Clear Register +00000039: BE1F OUT 0x3F,R1 Out to I/O location +0000003A: EFCF SER R28 Set Register +0000003B: E0D4 LDI R29,0x04 Load immediate +0000003C: BFDE OUT 0x3E,R29 Out to I/O location +0000003D: BFCD OUT 0x3D,R28 Out to I/O location +0000003E: E011 LDI R17,0x01 Load immediate +0000003F: E0A0 LDI R26,0x00 Load immediate +00000040: E0B1 LDI R27,0x01 Load immediate +00000041: EBEC LDI R30,0xBC Load immediate +00000042: E0F0 LDI R31,0x00 Load immediate +00000043: C002 RJMP PC+0x0003 Relative jump +00000044: 9005 LPM R0,Z+ Load program memory and postincrement +00000045: 920D ST X+,R0 Store indirect and postincrement +00000046: 30A0 CPI R26,0x00 Compare with immediate +00000047: 07B1 CPC R27,R17 Compare with carry +00000048: F7D9 BRNE PC-0x04 Branch if not equal +00000049: E011 LDI R17,0x01 Load immediate +0000004A: E0A0 LDI R26,0x00 Load immediate +0000004B: E0B1 LDI R27,0x01 Load immediate +0000004C: C001 RJMP PC+0x0002 Relative jump +0000004D: 921D ST X+,R1 Store indirect and postincrement +0000004E: 30A0 CPI R26,0x00 Compare with immediate +0000004F: 07B1 CPC R27,R17 Compare with carry +00000050: F7E1 BRNE PC-0x03 Branch if not equal +00000051: 940E0057 CALL 0x00000057 Call subroutine +00000053: 940C005C JMP 0x0000005C Jump +00000055: 940C0000 JMP 0x00000000 Jump @00000057: main ---- 2010-05-26_jetzt_c.c ------------------------------------------------------------------------ - 4: { +00000057: EF8F SER R24 Set Register +00000058: BB87 OUT 0x17,R24 Out to I/O location 6: PORTB=0x55; +00000059: E585 LDI R24,0x55 Load immediate +0000005A: BB88 OUT 0x18,R24 Out to I/O location +0000005B: CFFF RJMP PC-0x0000 Relative jump 6: PORTB=0x55; +0000005C: 94F8 CLI Global Interrupt Disable +0000005D: CFFF RJMP PC-0x0000 Relative jump Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu springen! Warum macht er das denn?
> Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu > springen! Warum macht er das denn? Interrupts, die im Programm nicht belegt sind, werden mit einem Sprung zu einem Default-Handler vorbelegt, um nicht in der Wüste zu landen. Der Default-Handler macht nichts als einen Soft-Reset (Sprung zu dem Vektor auf Adresse 0 -- den Default-Handler hast Du bei Dir auf Adresse 0x55).
Tommy schrieb: > Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu > springen! Warum macht er das denn? Er überspringt die Interupt Tabelle.
Okay ich glaube so langsam schnall ichs! Die Interrupteinspring Tabelle steht ja am Anfang des Flash und wird im C Programm übersprungen. Das ist doch soweit richtig. Aber was ich jetzt nicht verstehe, ist das in der Assemblerversion das Programm keine Adressen am Anfang überspringt. Also doch nicht geschnalt. Oder ist das so, dass die Assembler Version gar nicht die Interrupt Tabelle einfügt, wenn sie nicht genutzt wird. Kann man sich das so vorstellen? Wenn das so ist, wäre für kleine Programme Assembler immer platzsparender als C oder?
Nein. In der Assembler-Version musst Du die Interrupttabelle schreiben (so Du eine haben willst), denn die Laufzeitumgebung bestimmst alleine Du. In Assembler wird kein vorgefertigter Code hinzugefügt, solange Du es nicht befiehlst.
Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann muss ich darauf achten, wo er im Flash hingeschrieben werden muss, da sie an fest definierten Adressen stehen müssen.
Tommy schrieb: > Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann > muss ich darauf achten, wo er im Flash hingeschrieben werden muss, Richtig > da > sie an fest definierten Adressen stehen müssen. Genau. Und diese Adressen sind quasi eine Vereinbarung zwischen Controller-Hersteller und Programmierer (oder Compilerbauer). Genaue Informationen dazu findest Du im Datenblatt des betreffenden Controllers, denn jeder AVR ist anders mit (interruptauslösenden) Features ausgestattet, braucht also eine andere Sprungtabelle. MfG, Consulter
Und ich hätte noch eine Frage: Mein Default Handler springt ja an Adresse 0x0000000. Ist das dann nicht eine Endlosschleife? +00000000: 940C0038 JMP 0x00000038 Jump +00000002: 940C0055 JMP 0x00000055 Jump +00000004: 940C0055 JMP 0x00000055 Jump Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von vorne an?
Vielen Dank an alle! Jetzt hab ichs geschnallt! Wirklich letzte Frage: Dann kann ich doch anhand der Junps am Anfang feststellen, wie viele Interrupts der MC hat, oder? Ich weiß zwar nicht was für welche aber die Anzahl weiß ich.
Hc Zimmerer schrieb: > Oder ist das > so, dass die Assembler Version gar nicht die Interrupt Tabelle einfügt, > wenn sie nicht genutzt wird. Das ist dem Aessemler egal, ob Du die Interrupt Tabelle benutzt oder nicht. Er schreibt sie so wie Du sie schreibst, schreibst Du keine schreibt er auch keine. Tommy schrieb: > Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann > muss ich darauf achten, wo er im Flash hingeschrieben werden muss, da > sie an fest definierten Adressen stehen müssen. Wenn ein bestimmter Interrupt ausgelöst wird springt der Programmcounter an die bestimmte Adresse von diesem Interrupt, da muß die Sprungadresse stehen, wo der Code für diesen Interrupt steht. Tommy schrieb: > Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die > ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von > vorne an? Ja! und dann nach 0x00000038 wo dein Programm anfängt.
> Hc Zimmerer schrieb:
Das schrieb ich nicht. Bitte korrekt zitieren.
Hc Zimmerer schrieb: >> Hc Zimmerer schrieb: > Das schrieb ich nicht. Bitte korrekt zitieren. Muß natürlich heißen "Tommy schrieb:" (Wenn man Text markiert und dann rechts klickt "Markierten Text Zitieren" und man erwischt einen anderen passiert dies.) war keine Absicht. Tommy schrieb: > Dann kann ich doch anhand der Junps am Anfang feststellen, wie viele > Interrupts der MC hat, oder? Ich weiß zwar nicht was für welche aber die > Anzahl weiß ich. Sehe mal hier mit dem Editor ganz unten C:\Programme\Atmel\AVR Tools\AvrAssembler\Appnotes\m162def.inc
Ja ja so landsam durchschau ich es! Doch keine Zauberei! Noch mal vielen Dank an allen.
MarioT schrieb: > Tommy schrieb: >> Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die >> ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von >> vorne an? > > Ja! und dann nach 0x00000038 wo dein Programm anfängt. @Tommy Ganz genau. Das Problem ist: Auf einem µC muss immer irgendwas passieren. Ein Aufruf eines Interrupts, für den es keinen Handler gibt, ist natürlich ein Fehler. Aber was soll denn der Default Handler machen? Er weiß ja nichts von deiner Hardware. Also sind die gcc Leute den Weg des geringsten Widerstands gegangen und haben das einzig Sinnvolle getan, was sie tun können: Sie lassen das komplette Programm neu starten. Das gibt dir zum einen die Chance, den Fehler während der Entwicklung festzustellen und führt auf der andern Seite dazu dass in einer Produktionsumgebung zumindest die Chance besteht, dass dein µC nach dem Fehler wieder seine Arbeit aufnimmt. Denkbar wäre auch gewesen: den µC in einer Endlosschleife gefangen zu halten. Das hat aber den Nachteil, dass dann mit Sicherheit dein µC hängt. den Interrupt einfach zu ignorieren, gar nichts zu tun und zu returnen. Das hat aber wieder den Nachteil, dass du in der Entwicklungsphase es nicht bemerkst, dass du einen Interrupt freigegeben hast, der nicht behandelt wird. Zu guter letzt: Es gibt auch noch die Möglichkeit sich selbst einen Default-Handler zu schreiben, der sich dann so verhält, wie du dir das vorstellst.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.