Zunächst mal Hallo an Alle!
Ich beschäftige mich mit der Realisierung eines I²C-Abhörsystems das auf einen ATmega 8 laufen soll.
Dazu habe ich die Clock-Leitung des I²C-Busses auf den externen Interrupt 0 meines AVR-Boards gelegt.
Es stellte sich heraus, dass die 100kHz I²C-Bustakt den ATmega 8 überfordern (läuft bei mir auf 8Mhz).
Denn die Interruptroutine die ich schrieb kann anscheinend nicht in genügend kurzer Zeit abgearbeitet werden.
Um dies nachzuvollziehen wollte ich näher auf den Assembler Code eingehen, der vom AVR-Studio beim compilieren meines C-Codes erzeugt wird.
Allerdings habe ich dabei einpar Verständnis Probleme:
Irgendwann mal habe ich gelernt, dass ein Interrupt folgendermaßen abläuft:
INT 0 -> Register sichern (Push) ->Nachschauen in Interrupt Vektortabelle wo Interrupt routine liegt -> dorthin springen -> ausführen ->zum Programm zurückspringen
(Ich bin sicher ich werde korrigiert wenn ich mit meinen obigen Annahmen falsch liege .)
Folgenden Code habe ich nun dem .lss file im default folder meines Projekt entnehmen können:
das mit push und pop befehlen Registerinhalte auf dem Stack gesichert und nachher wiederhergestellt werden ist mir klar.
Mir ist auch klar, dass reti bedeutet dass wieder zum ursprünglichen Programm zurückgesprungen wird.
Aber wo wird denn hier in einer Interrupttabelle nachgeschaut, bzw. wo erfolgt der Sprung zum Interrupt Programm (jmp 0xinterrupt0)?
Wenn ich im Datenblatt des ATmega8 nachschaue ist ja aufgelistet wieviele Clocks die verschiedenen ASM Befehle benötigen.
stimmt folgende Milchmädchenrechnung?
8Mhz=[8*10^6 Clocks/sec]
Wenn ich nun ca. 20 ASM Befehle â 2 Clocks habe führt das zu 40Clocks
also einem Zeitaufwand von (1/8)*10^-6 [sec/clock]*40 [clocks] =5 *10^-6s=5µs
5µs sind bei einem Bustakt von 100kHz eine gesamte Taktflanke.
Hoffentlich werde ich für folgende Frage nicht gleich gebant :
Kann man evtl. die pushs und pops weglassen wenn einem die Registerinhalte aus der Hauptschleife des Programms wurscht sind?
Oder kann man den Code evtl. anderweitig abspecken?
Eigentlich sollen "nur" 2x9 (Addresse[8]+Ack[1]+Daten[8]+Ack[1]) Bits bei diesem Interrupt nacheinander gesichert werden.
Und das Problem ist, dass das Ding zu lange braucht.
Vielen Dank für euere Hilfe!
Mit freundlichsten Grüßen
Peacefish
Ich beschäftige mich mit der Realisierung eines I²C-Abhörsystems das auf einen ATmega 8 laufen soll.
Dazu habe ich die Clock-Leitung des I²C-Busses auf den externen Interrupt 0 meines AVR-Boards gelegt.
Es stellte sich heraus, dass die 100kHz I²C-Bustakt den ATmega 8 überfordern (läuft bei mir auf 8Mhz).
Denn die Interruptroutine die ich schrieb kann anscheinend nicht in genügend kurzer Zeit abgearbeitet werden.
Um dies nachzuvollziehen wollte ich näher auf den Assembler Code eingehen, der vom AVR-Studio beim compilieren meines C-Codes erzeugt wird.
Allerdings habe ich dabei einpar Verständnis Probleme:
Irgendwann mal habe ich gelernt, dass ein Interrupt folgendermaßen abläuft:
INT 0 -> Register sichern (Push) ->Nachschauen in Interrupt Vektortabelle wo Interrupt routine liegt -> dorthin springen -> ausführen ->zum Programm zurückspringen
(Ich bin sicher ich werde korrigiert wenn ich mit meinen obigen Annahmen falsch liege .)
Folgenden Code habe ich nun dem .lss file im default folder meines Projekt entnehmen können:
Code:
ISR(SCL){//Anmerkung Clock muss mit steigender Flanke getriggert werden!
468: 1f 92 push r1
46a: 0f 92 push r0
46c: 0f b6 in r0, 0x3f ; 63
46e: 0f 92 push r0
470: 11 24 eor r1, r1
472: 2f 93 push r18
474: 3f 93 push r19
476: 4f 93 push r20
478: 5f 93 push r21
47a: 8f 93 push r24
47c: 9f 93 push r25
data|=PINC<<clk;
47e: 23 b3 in r18, 0x13 ; 19
480: 40 91 69 00 lds r20, 0x0069
484: 50 91 6a 00 lds r21, 0x006A
488: 30 e0 ldi r19, 0x00 ; 0
48a: 04 2e mov r0, r20
48c: 02 c0 rjmp .+4 ; 0x492 <__vector_1+0x2a>
48e: 22 0f add r18, r18
490: 33 1f adc r19, r19
492: 0a 94 dec r0
494: e2 f7 brpl .-8 ; 0x48e <__vector_1+0x26>
496: 80 91 6d 00 lds r24, 0x006D
49a: 90 91 6e 00 lds r25, 0x006E
49e: 82 2b or r24, r18
4a0: 93 2b or r25, r19
4a2: 90 93 6e 00 sts 0x006E, r25
4a6: 80 93 6d 00 sts 0x006D, r24
clk++;
4aa: 4f 5f subi r20, 0xFF ; 255
4ac: 5f 4f sbci r21, 0xFF ; 255
4ae: 50 93 6a 00 sts 0x006A, r21
4b2: 40 93 69 00 sts 0x0069, r20
}
4b6: 9f 91 pop r25
4b8: 8f 91 pop r24
4ba: 5f 91 pop r21
4bc: 4f 91 pop r20
4be: 3f 91 pop r19
4c0: 2f 91 pop r18
4c2: 0f 90 pop r0
4c4: 0f be out 0x3f, r0 ; 63
4c6: 0f 90 pop r0
4c8: 1f 90 pop r1
4ca: 18 95 reti
das mit push und pop befehlen Registerinhalte auf dem Stack gesichert und nachher wiederhergestellt werden ist mir klar.
Mir ist auch klar, dass reti bedeutet dass wieder zum ursprünglichen Programm zurückgesprungen wird.
Aber wo wird denn hier in einer Interrupttabelle nachgeschaut, bzw. wo erfolgt der Sprung zum Interrupt Programm (jmp 0xinterrupt0)?
Wenn ich im Datenblatt des ATmega8 nachschaue ist ja aufgelistet wieviele Clocks die verschiedenen ASM Befehle benötigen.
stimmt folgende Milchmädchenrechnung?
8Mhz=[8*10^6 Clocks/sec]
Wenn ich nun ca. 20 ASM Befehle â 2 Clocks habe führt das zu 40Clocks
also einem Zeitaufwand von (1/8)*10^-6 [sec/clock]*40 [clocks] =5 *10^-6s=5µs
5µs sind bei einem Bustakt von 100kHz eine gesamte Taktflanke.
Hoffentlich werde ich für folgende Frage nicht gleich gebant :
Kann man evtl. die pushs und pops weglassen wenn einem die Registerinhalte aus der Hauptschleife des Programms wurscht sind?
Oder kann man den Code evtl. anderweitig abspecken?
Eigentlich sollen "nur" 2x9 (Addresse[8]+Ack[1]+Daten[8]+Ack[1]) Bits bei diesem Interrupt nacheinander gesichert werden.
Und das Problem ist, dass das Ding zu lange braucht.
Vielen Dank für euere Hilfe!
Mit freundlichsten Grüßen
Peacefish