Forum: Compiler & IDEs C->Asm (TWI) Fehlersuche


von Sam .. (sam1994)


Angehängte Dateien:

Lesenswert?

Hallo

Die Appnote AVR310 von Atmel beschreibt, wie man das USI Interface als 
I2C/TWI nutzt:
http://www.atmel.com/dyn/resources/prod_documents/doc2561.pdf
http://www.atmel.com/dyn/resources/prod_documents/AVR310.zip

Ich habe das ganze versucht in Assembler zu übersetzen, aber bisher habe 
ich noch nicht alle Fehler gefunden. Mit C-Code mit Assembler 
vergleichen komme ich nicht weiter. Die Hardware ist richtig aufgebaut, 
mit C funktioniert die Ansteuerung tadellos.
Ich habe leider keinen HW-Debugger zur Hand. Ich benutze UART um an die 
Daten zu kommen.

Das Problem ist, dass der µC in einer Schleife festhängt (Z. 229):
1
USI_WAIT1:
2
  sbis PIN_USI, PIN_USI_SCL 
3
  rjmp USI_WAIT1
entspricht der Übersetzung von
1
while( !(PIN_USI & (1<<PIN_USI_SCL)) );// Wait for SCL to go high.

Das ist der erste Aufruf der USI_TWI_Master_Transmit Funktion in der 
Hauptschleife der USI_TWI_Start_Transceiver_With_Data (Z. 162).

Laut AvrSimulator2 ist in dieser Schleife der PIN als Ausgang definiert, 
ist das richtig so (bei TWI)? Normalerweise könnte sich dann PINx gar 
nicht ändern.

Sieht man aus der Verhaltensweise was falsch ist/sein könnte?

Die Delay Funktion ist die aus dem Wiki ( 
http://www.mikrocontroller.net/articles/AVR_Assembler_Makros#Delay ).

von Sam .. (sam1994)


Lesenswert?

Ist es möglich die C-Routinen von gcc übersetzen zu lassen (Saubere 
lss-Datei)?

von Scott Vitalw (Gast)


Lesenswert?

Hello:
If you pull the I2C specification, you'll see that the SCL line is the 
master clock line, which is driven by the master.  From what you state, 
I believe you are operating your AVR as a TWI master, yes?

What AVR?  Does it have a dedicated TWI controller or a USI emulating a 
TWI?

Also, be sure that you have 4K7 resistors on the SCL amd SDA lines.  You 
can use the weak pullups on the AVR I/O pins if only driving one or two 
slaves on short lines but safer to use the external 4K7 resistors.

On porting from C to ASM.  I could not find any asm code for the newer 
AVR's with the TWI controller (bit-banging only).  They all are written 
in C now.  So, I compiled Peter Fluery's TWI driver for my Mega32U4 and 
just massaged the resulting ASM code contained in the LSS file.  The 
code uses the TWI module but it is polled, not interrupt driven.

Hope that helps!

Peace,
Scott

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Die Hardware ist richtig aufgebaut,
> mit C funktioniert die Ansteuerung tadellos.

Warum nimmst du dann nicht einfach den C-Code?

von Sam .. (sam1994)


Lesenswert?

Jörg Wunsch schrieb:
> Warum nimmst du dann nicht einfach den C-Code?

Weil ich die Software aus Platztechnischen Gründen in Assembler 
schreiben möchte. Da ich das Problem bisher nicht lösen konnte (selbst 
die BitBang-Methoden funktionieren nicht), habe ich in C mit einem 
haufen Assembler kombiniert. Das erscheint mir jedoch noch schwerer als 
reines Asm.

Scott Vitalw schrieb:
> Hello:
> If you pull the I2C specification, you'll see that the SCL line is the
> master clock line, which is driven by the master.  From what you state,
> I believe you are operating your AVR as a TWI master, yes?
I use an Attiny2313 with USI-Interface as master.
> What AVR?  Does it have a dedicated TWI controller or a USI emulating a
> TWI?
Attiny2313
> Also, be sure that you have 4K7 resistors on the SCL amd SDA lines.  You
> can use the weak pullups on the AVR I/O pins if only driving one or two
> slaves on short lines but safer to use the external 4K7 resistors.
In the circuit are 10k pullups. It doesn't work without them.
> On porting from C to ASM.  I could not find any asm code for the newer
> AVR's with the TWI controller (bit-banging only).  They all are written
> in C now.  So, I compiled Peter Fluery's TWI driver for my Mega32U4 and
> just massaged the resulting ASM code contained in the LSS file.  The
> code uses the TWI module but it is polled, not interrupt driven.

Something in my Asm-code had to be wrong, because it never works 
(bitbanging, too).
In addition, the I2C-Slave (PCF8577) is very slowly (max. 100kHz). In C 
I have to reduce the frequency (100kHz), if the clock of attiny2313  is 
less than 500kHz. I don't know why.

sry for my poor english.

von Sam .. (sam1994)


Lesenswert?

Das sturre Übersetzen der der .lss hat geholfen. Danke für die 
Anregungen.

von Peter D. (peda)


Lesenswert?

Samuel K. schrieb:
> Weil ich die Software aus Platztechnischen Gründen in Assembler
> schreiben möchte.

Naja, Du darfst nicht jede Variable als int definieren, dann braucht C 
auch nur unwesentlich mehr als Assembler.
Wenn unsigned char (= uint8_t) reicht, dann nimm es auch.
Und nimm Unterfunktionen anstatt Spaghetticode.
Bei Atmel, AVRFreaks usw. findet man weitere Tips zum Vermeiden von 
C-Sünden.

Typisch braucht C nur etwa 5 .. 20% mehr.
Bei größeren Programmen kann C oft sogar kompakteren Code erzeugen als 
viele Assemblerprogrammierer.
Der AVR-GCC kann sogar eine über alles Optimierung vornehmen:
1
-Wl,--relax --combine -fwhole-program


Samuel K. schrieb:
> (selbst
> die BitBang-Methoden funktionieren nicht)

Die funktionieren sehr gut und brauchen auch nicht mehr Flash, als 
HW-I2C.
Ich nehme sie sehr gerne, da sie portabel sind und jeden Pin nutzen 
können.
Ein großer Vorteil von SW-I2C ist auch, daß es sich nicht verklemmen 
kann bei Störungen auf dem Bus.
HW-I2C braucht immer auch einen Timeout-Handler, der es kurz mal 
disabled, den Bus freitaktet und wieder enabled.

HW-I2C braucht man nur für Multimaster/Slave.

Samuel K. schrieb:
> habe ich in C mit einem
> haufen Assembler kombiniert. Das erscheint mir jedoch noch schwerer als
> reines Asm.

Den Fehler habe ich auch ganz zu Anfang mal gemacht.
Es kostet viel Arbeit, der Nutzen ist dagegen lächerlich gering.
Seitdem kommt mir in C keine Assemblerzeile mehr rein.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Ein großer Vorteil von SW-I2C ist auch, daß es sich nicht verklemmen
> kann bei Störungen auf dem Bus.

Das bekommst du bei Hardware-I²C aber auch hin.  Du darfst nur nicht
stur überall in festen Schleifen warten (vor allem nicht auf das
Interruptflag), ohne die Wartezeit zu begrenzen.  Was anderes macht
dein Software-I²C letztlich ja auch nicht in so einer Situation.

von Sam .. (sam1994)


Lesenswert?

Peter Dannegger schrieb:
> Naja, Du darfst nicht jede Variable als int definieren, dann braucht C
> auch nur unwesentlich mehr als Assembler.
Die kleinste Variablengröße nehme ich immer,  int kommt bei mir gar 
nicht vor (außer als 4Byte Feld zur Argumentübergabe zu Asm).
> Wenn unsigned char (= uint8_t) reicht, dann nimm es auch.
> Und nimm Unterfunktionen anstatt Spaghetticode.
Wobei Spaghetticode in Assembler meist kleiner ist.
> Bei Atmel, AVRFreaks usw. findet man weitere Tips zum Vermeiden von
> C-Sünden.
Ich hab in dem C-Programm schon dauernd in die lss-Datei geschaut, viel 
zu Optimieren hab ich nicht gesehen.

> Typisch braucht C nur etwa 5 .. 20% mehr.
Das Assemblerprogramm hat inzwischen dieselbe Funktionalität wie das 
C-Programm braucht aber nur 1KB während C 1,5KB nutzt. Die Hauptroutinen 
sind jedoch wirklich zum Spaghetticode geworden und dadurch auch sehr 
kurz im Vergleich zum C-Code wo avrgcc dauernd seine Register 
verschiebt.

Deutlich kürzer sind auch die ISRs. Während avrgcc haufenweise Register 
sichert einschließlich r0 und r1, komme ich in Asm in der Timerisr mit 5 
Registern aus. In 2 anderen Interrupts wird SREG nicht einmal gesichert, 
da kein Befehl es verändert.

Durch ein globalen Pointer (Y) der auf SRAM_START zeigt, kann man sich 
so ziemlich alle lds und sts Befehle sparen (diese nutzt avrgcc auch 
sehr gerne).

Allgemein sind gerade komplexe Funktionen in Asm viel kleiner - 
vielleicht programmiere ich auch unverständlich für den Optimierer. In 
Assembler weiß man halt am Ende was man hat, wo viel Code verbraucht 
wird, wie viel Ram man braucht, was langsam ist... man hat den Überblick 
über sein Programm.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Samuel K. schrieb:

> In 2 anderen Interrupts wird SREG nicht einmal gesichert,
> da kein Befehl es verändert.

Ja, sowas hat dem C-Compiler niemand beigebracht, ist aber auch
ein ziemlicher Sonderfall, für den man bequem mit einer Assembler-
ISR arbeiten kann (denn die ISR ist dann zwangsläufig sowieso
sehr übersichtlich ;-).

> Durch ein globalen Pointer (Y) der auf SRAM_START zeigt, kann man sich
> so ziemlich alle lds und sts Befehle sparen (diese nutzt avrgcc auch
> sehr gerne).

Sowas erreicht man in C, indem man eine globale struct für die
Daten benutzt.

von Clemens M. (panko)


Lesenswert?

Mich würde die bit bang Lösung von Peter interessieren. Wenn die nicht 
unter Verschlusssache fällt würde ich mich über die Präsentation 
derselben freuen.
Eine ziemlich robust funktionierende Lösung hat etwas. Die freie Pinwahl 
ist ein gewichtiges Argument finde ich.
Bei der Verwendung der avr Hardware polle ich z.B. sowiso nur, bis die 
entsprechenden irq kommen - da hat man dann ja auch nichts gewonnen.
Und - die könnte der Autor auch auf asm übersetzen um Platz zu sparen.

von Sam .. (sam1994)


Lesenswert?

Hier noch ein kleines Beispiel, das in Asm ziemlich klein ist, in C 
aufgebläht wird. Die Funktion zeigt 2 Zeiten auf je ein Lcd an. Dabei 
werden die Zeiten (in sec) erst in Stunden:Minuten bzw. Minuten:Sekunden 
umgerechnet, zu BCD konvertiert und in den Displaybuffer geladen, der am 
Ende mit dem Lcd-Treiber aktualisiert wird. Alles in nur ~40 Befehlen:
1
  //Daten aus dem Ram laden und mit Displaybuffer aktualisieren
2
  ldi temp, 60    //Divisor laden
3
  mov r0, temp
4
  ldi r17, 2      //=lcd: lcd1=2, lcd2=3
5
SetTime:
6
  ld r24, X+      //Zeit in Sekunden laden
7
  ld r25, X+
8
  movw r22:r23, r0:r1  //Divisor laden
9
  rcall div16u    //Division ausführen
10
  cpi r24, 20      //Wenn die Zeit größer als 19 Minuten ist
11
  cpc r25, zero    //muss nochmals durch 60 geteilt werden
12
  brlo SetTime1
13
  movw r22:r23, r0:r1  //Divisor laden
14
  rcall div16u    //Division ausführen
15
  ldi r23, C_COL-2
16
  rjmp SetTime1_2
17
SetTime1:
18
  ldi r23, C_DOT2-2
19
SetTime1_2:
20
  mov r15, r22    //Rest sichern
21
  in r16, state_reg
22
  eor r16, r17
23
  sbic Icounter, 7
24
  sbrc r16, 0
25
  rjmp SetTime1_3
26
  ldi r23, -2
27
SetTime1_3:
28
  add r23, r17
29
  ldi r22, ~(C_DOT2 | C_COL)
30
  rcall SetChars
31
SetTime2:
32
  rcall bin2bcd8    //In BCD konvertieren
33
  mov r16, r25    //Zehner sichern
34
  mov r25, r17    //anzuzeigendes Digit in r25 laden
35
  rcall SetDigitFont  //Einer anzeigen
36
  subi r17, 2      //Nächstes Digit = Zehner -> digit-2
37
  movw r24:r25, r16:r17//gesicherte Wert laden:  Zehner, digit
38
  rcall SetDigitFont  //Zehner anzeigen
39
  mov r24, r15    //Rest laden
40
  subi r17, -6    //Beim nächsten Durchgang werden Minuten angezeigt, digit+6
41
  cpi r17, 10      //Wenn digit >= 10 beenden
42
  brlo SetTime2
43
  adiw X, PLAYER_LEN-2
44
  subi r17, 7      //Ersten Durchgang: r17=10 -> r17=3: lcd2
45
  cpi r17, 4      //Fertig wenn digit==4
46
  brne SetTime  
47
48
  rcall Refresh    //Lcd aktualisieren
49
  sbiw X, PLAYER_LEN*2//X auf Startwert setzen

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Clemens M. schrieb:
> Mich würde die bit bang Lösung von Peter interessieren.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Hier noch ein kleines Beispiel, das in Asm ziemlich klein ist, in C
> aufgebläht wird.

Du kannst ja vieles behaupten.  Dein Assemblerspaghetti ist nicht
so auf die Schnelle zu verstehen, dass man die äquivalenten Zeilen
in C hinschreiben könnte, um das überhaupt zu vergleichen.
Außerdem ist es natürlich immer einfach, sich erstmal einen händisch
gefeilten Kontext zu schaffen (diverse globale Variablen, Funktionen
mit beliebiger Aufrufsequenz) und dann festzustellen, dass ein
C-Compiler, der ja viel formaler und allgemeiner an die Lösung
rangeht, da gar nicht mithalten könne.

Wirklich vergleichen kann man folglich nur das Gesamtprojekt.

von Peter D. (peda)


Lesenswert?

Samuel K. schrieb:
> Hier noch ein kleines Beispiel, das in Asm ziemlich klein ist

Das alleine macht garnichts.
Da sind 7 Calls drin und deren Code mußt Du noch dazu zählen.

Es ist aber trotzdem ein schönes Beispiel, wie schwer Assembler zu 
durchschauen und zu erweitern ist.
Schon allein die nichts sagenden Registernamen r0..r31 sind 
undurchdringlich.
Ich hatte in Assembler den Registern wenigstens Namen gegeben, damit man 
erkennt, was sind welche Variablen, was sind Parameter, was sind 
Returnwerte, was sind Scratchpad usw.


Peter

von Sam .. (sam1994)


Lesenswert?

Peter Dannegger schrieb:
> Das alleine macht garnichts.
> Da sind 7 Calls drin und deren Code mußt Du noch dazu zählen.

Die Calls sind in C genauso vorhanden. Die Divisionsroutinen sind in C 
auch Asm-routinen.

Peter Dannegger schrieb:
> Es ist aber trotzdem ein schönes Beispiel, wie schwer Assembler zu
> durchschauen und zu erweitern ist.
> Schon allein die nichts sagenden Registernamen r0..r31 sind
> undurchdringlich.
Die Register zu numerieren hat den Vortei, dass man keine übereinander 
legen kann. Bei Namen passiert das unweigerlich. Tatsächlich verwende 
ich es in gemischter Form. Im Hauptprogramm verwende ich r0-r31 
großteils, in Funktionen benenne ich sie.
> Ich hatte in Assembler den Registern wenigstens Namen gegeben, damit man
> erkennt, was sind welche Variablen, was sind Parameter, was sind
> Returnwerte, was sind Scratchpad usw.
Das hätte ich vielleicht dazusagen sollen. r24 stellt den ersten 
Übergabeparameter dar, danach r25, r22 und r23. Ausnahme bildet die 
Funktion SetChars, weil man dadurch ein paar Instruktionen spart. Die 
Funktionen dürfen r25-r18 und Z zerstören. Davon gehe ich aber nicht 
stur (wie avrgcc) aus. Wenn ein Register bei Funktion x nicht zerstört 
wird sichere ich es auch nicht.

Ich denke am schwierigsten zu verstehen ist der nicht kommentierte Teil. 
Um diesen zu verstehen müsste ich schon viel weiter ausholen. Allgemein 
lässt er den Trennpunkt bzw. Doppelpunkt zw. den Minuten:Stunden / 
Minuten:Sekunden blinken, und zwar nur bei einem bestimmten lcd.

Jörg Wunsch schrieb:
> Wirklich vergleichen kann man folglich nur das Gesamtprojekt.

Ich habe derzeit in Asm mehr Funktionalitäten als im C-Projekt und 
trotzdem 30% Codeersparnis. Bin ich deswegen ein miserabler 
C-Programmierer?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Samuel K. schrieb:

> Ich denke am schwierigsten zu verstehen ist der nicht kommentierte Teil.

Nein, wenn man als Außenstehender draufguckt, versteht man da rein
gar nichts.  Die Mikro-Kommentare helfen da nicht viel, denn die
beschreiben nur das, was man in C bereits im Quellcode lesen
könnte.  Was komplett fehlt ist das, was man im C-Code kommentieren
würde, also eine Überblicksbeschreibung.

> Ich habe derzeit in Asm mehr Funktionalitäten als im C-Projekt und
> trotzdem 30% Codeersparnis. Bin ich deswegen ein miserabler
> C-Programmierer?

Möglich, vielleicht hast du dich auch nur irgendwo ungeschickt
ausgedrückt, vielleicht sind auch die Optimierungsmöglichkeiten des
Compilers gar nicht ausgenutzt worden, kann man so nicht sagen.  Was
man aber sagen kann ist, dass du einfach zu viel Zeit hast, wenn du
dir sowas noch leisten kannst. ;-)

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
Noch kein Account? Hier anmelden.