Hallo,
viele Wege führen ja bekanntlich nach Rom, aber irgendwie kommt mir das
Programm welches ich geschrieben habe zu kompliziert für ein solche
einfache Sache vor.
Kann mir jemand einen Tipp geben, wie ich das ganze vereinfachen kann?
PB0, PB1, PB3 als Eingänge
PB2, PB4 als Ausgänge
Programm als Erklärung:
PB0 = high, dann Bit von PB3 zu BP2 und PB1 zu PB4
PB0 = low, dann Bit von PB3 zu PB4
1
.include "tn13adef.inc"
2
.device AtTiny13A
3
4
ldi r16,(1<<PB0)|(1<<PB1)|(1<<PB3) ;Eingang PB3 mit internen PullUp
5
ldi r17,(0<<DDB4)|(0<<DDB4) ;Ausgang PB4 auf LOW
6
out PORTB,r16
7
out DDRB, r17
8
9
Anfang:
10
11
sbis PINB, 0 ;überspringt den nächsten Befehl wenn Bit high
12
rjmp Off
13
14
sbic PINB, 0 ;überspringt den nächsten Befehl wenn Bit low
15
rjmp On
16
17
Off:
18
;PB3 -> PB4
19
sbic PINB, 3 ;überspringt den nächsten Befehl wenn Bit low
20
sbi PORTB, 4 ;setzt Bit
21
sbis PINB, 3 ;überspringt den nächsten Befehl wenn Bit high
22
cbi PORTB, 4 ;löscht Bit
23
24
;Schleife verlassen
25
sbic PINB, 0 ;überspringt den nächsten Befehl wenn Bit low
26
rjmp On
27
28
rjmp Off
29
30
31
On:
32
;PB3 -> PB2
33
sbic PINB, 3 ;überspringt den nächsten Befehl wenn Bit low
34
sbi PORTB, 2 ;setzt Bit
35
sbis PINB, 3 ;überspringt den nächsten Befehl wenn Bit high
36
cbi PORTB, 2 ;löscht bis
37
38
;PB1 -> PB4
39
sbic PINB, 1 ;überspringt den nächsten Befehl wenn Bit low
40
sbi PORTB, 4 ;setzt Bit
41
sbis PINB, 1 ;überspringt den nächsten Befehl wenn Bit high
42
cbi PORTB, 4 ;löscht Bit
43
44
;Schleife verlassen
45
sbis PINB, 0 ;überspringt den nächsten Befehl wenn Bit high
max20 schrieb:> Hallo,> viele Wege führen ja bekanntlich nach Rom, aber irgendwie kommt mir das> Programm welches ich geschrieben habe zu kompliziert für ein solche> einfache Sache vor.>> Kann mir jemand einen Tipp geben, wie ich das ganze vereinfachen kann?
Einfach scharf nachdenken was genau passiert...
> PB0, PB1, PB3 als Eingänge> PB2, PB4 als Ausgänge>> Programm als Erklärung:> PB0 = high, dann Bit von PB3 zu BP2 und PB1 zu PB4> PB0 = low, dann Bit von PB3 zu PB4>> [code]> .include "tn13adef.inc"> .device AtTiny13A>> ldi r16,(1<<PB0)|(1<<PB1)|(1<<PB3) ;Eingang PB3 mit internen PullUp> ldi r17,(0<<DDB4)|(0<<DDB4) ;Ausgang PB4 auf LOW> out PORTB,r16> out DDRB, r17>> Anfang:>> sbis PINB, 0 ;überspringt den nächsten Befehl wenn Bit high> rjmp Off
Die zwei oben weglassen. Hat noch andere Vorteile.
> sbic PINB, 0 ;überspringt den nächsten Befehl wenn Bit low> rjmp On>> Off:> ;PB3 -> PB4> sbic PINB, 3 ;überspringt den nächsten Befehl wenn Bit low> sbi PORTB, 4 ;setzt Bit> sbis PINB, 3 ;überspringt den nächsten Befehl wenn Bit high
Den oben weglassen.
> cbi PORTB, 4 ;löscht Bit>> ;Schleife verlassen> sbic PINB, 0 ;überspringt den nächsten Befehl wenn Bit low> rjmp On>> rjmp Off
Kommt drauf an ob Speicherverbrauch oder Laufzeit wichtiger sind. Wenn
Laufzeit - so lassen. Wenn Speicherverbrauch - immer zu Anfang springen.
Übrigens, wieso hast Du hier sbic richtig benutzt? ;-)
Weiter unten: wie schon gesagt.
> On:> ;PB3 -> PB2> sbic PINB, 3 ;überspringt den nächsten Befehl wenn Bit low> sbi PORTB, 2 ;setzt Bit> sbis PINB, 3 ;überspringt den nächsten Befehl wenn Bit high> cbi PORTB, 2 ;löscht bis>> ;PB1 -> PB4> sbic PINB, 1 ;überspringt den nächsten Befehl wenn Bit low> sbi PORTB, 4 ;setzt Bit> sbis PINB, 1 ;überspringt den nächsten Befehl wenn Bit high> cbi PORTB, 4 ;löscht Bit>> ;Schleife verlassen> sbis PINB, 0 ;überspringt den nächsten Befehl wenn Bit high> rjmp Off> rjmp On>>> rjmp Anfang> [\code]
Keine Garantie für optimale Optimalität.
max20 schrieb:> PB0, PB1, PB3 als Eingänge> PB2, PB4 als Ausgänge>> Programm als Erklärung:> PB0 = high, dann Bit von PB3 zu BP2 und PB1 zu PB4> PB0 = low, dann Bit von PB3 zu PB4
Das ist eigentlich schon das Hauptproblem. Bei etwas intelligenterer
Wahl der Pinbelegung würde sich das Programm von allein massiv
vereinfachen.
max20 schrieb:> viele Wege führen ja bekanntlich nach Rom, aber irgendwie kommt mir das> Programm welches ich geschrieben habe zu kompliziert für ein solche> einfache Sache vor.
Das ist schon ganz o.k. so.
In Assembler muß man eben vieles zu Fuß machen.
In C könnte man schreiben:
In punkto Laufzeit wäre folgende Lösung wahrscheinlich fast die
schnellste ohne zuviel Code zu "verbrauchen":
1
anfang:
2
ldi XL,0x14
3
out DDRB,XL
4
ldi ZH,HIGH(table*2)
5
loop:
6
in ZL,PINB
7
andi ZL,0x0F
8
lpm ZL,Z
9
out PORTB,ZL
10
rjmp loop
11
12
.org 0x100
13
table:
14
.db 0xeb,0xfb,0xeb,0xfb,0xeb,0xfb,0xeb,0xfb
15
.db 0xfb,0xff,0xfb,0xff,0xfb,0xff,0xfb,0xff
Ein Durchlauf braucht dann genau 8 Takte. PB2 bleibt, wenn nicht
benutzt, auf logisch 0 und die unbenutzten Pins auf logisch 1, was
aktivierten Pullup bedeutet. Wenn man das "andi ZL,0x0F" weglässt, kommt
man auf nur 7 Takte, braucht aber eine Tabelle mit 256 Einträgen.
Jörg
Peter Dannegger schrieb:> Mit WINAVR 2010:
Ehe hier die Assembler-Verfechter aufschlagen.
Die IN/OUT Orgien sind höchst wahrscheinlich den Strukturen geschuldet,
die sich hinter den Bit-Bezeichnungen verstecken.
Macht man das ganze klasssisch
1
#include"avr/io.h"
2
3
#define ASSIGN_BIT( TO, FROM ) \
4
if( PINB & (1<<FROM) ) \
5
PORTB |= ( 1 << TO ); \
6
else \
7
PORTB &= ~( 1 << TO );
8
9
intmain()
10
{
11
DDRB=(1<<PB2)|(1<<PB4);
12
13
for(;;){
14
if(PINB&(1<<PB0)){
15
ASSIGN_BIT(PB2,PB3);
16
ASSIGN_BIT(PB4,PB1);
17
}
18
else{
19
ASSIGN_BIT(PB4,PB3);
20
}
21
}
22
}
generiert der AVR-Gcc auch Code, der mittels Bitbefehlen die Logik
abwickelt
1
00000022 <main>:
2
22: 84 e1 ldi r24, 0x14 ; 20
3
24: 87 bb out 0x17, r24 ; 23
4
26: b0 9b sbis 0x16, 0 ; 22
5
28: 08 c0 rjmp .+16 ; 0x3a <__CCP__+0x6>
6
2a: b3 9b sbis 0x16, 3 ; 22
7
2c: 02 c0 rjmp .+4 ; 0x32 <main+0x10>
8
2e: c2 9a sbi 0x18, 2 ; 24
9
30: 01 c0 rjmp .+2 ; 0x34 <__CCP__>
10
32: c2 98 cbi 0x18, 2 ; 24
11
34: b1 9b sbis 0x16, 1 ; 22
12
36: 05 c0 rjmp .+10 ; 0x42 <__SREG__+0x3>
13
38: 02 c0 rjmp .+4 ; 0x3e <__SP_H__>
14
3a: b3 9b sbis 0x16, 3 ; 22
15
3c: 02 c0 rjmp .+4 ; 0x42 <__SREG__+0x3>
16
3e: c4 9a sbi 0x18, 4 ; 24
17
40: f2 cf rjmp .-28 ; 0x26 <main+0x4>
18
42: c4 98 cbi 0x18, 4 ; 24
19
44: f0 cf rjmp .-32 ; 0x26 <main+0x4>
PS: was soll eigentlich mit PB2 geschehen, wenn PB0 auf Low ist? Im
Moment behält der Pin immer den letzten Zustand. Ist das so gewollt?
Karl Heinz schrieb:> Ehe hier die Assembler-Verfechter aufschlagen.> Die IN/OUT Orgien sind höchst wahrscheinlich den Strukturen geschuldet,> die sich hinter den Bit-Bezeichnungen verstecken.>> Macht man das ganze klasssisch
[...]
Solche Optimierungen sollten bei einem guten Compiler eigentlich niemals
nötig sein.
Wenn ich optimieren will, schreibe ich gleich Assembler, da brauch ich
nämlich nicht erst darüber rumrätseln, was der Compiler wohl aus diesem
und jenem Konstrukt gemacht hat und warum er das wohl getan haben mag.
Ich schreibe vielmehr einfach gleich hin, was die CPU tun soll und bin
schon fertig. Und ich brauch' dazu nicht mehr als ein paar Seiten
instruction set reference.
Keine Bücher, die auf vielen hundert Seiten erklären müssen, was die
Sprache tut, bevor ich mich dem widmen kann, was mich eigentlich
intessiert: was nämlich die CPU tun soll.
c-hater schrieb:> Karl Heinz schrieb:>>> Ehe hier die Assembler-Verfechter aufschlagen.>> Die IN/OUT Orgien sind höchst wahrscheinlich den Strukturen geschuldet,>> die sich hinter den Bit-Bezeichnungen verstecken.>>>> Macht man das ganze klasssisch> [...]>> Solche Optimierungen sollten bei einem guten Compiler eigentlich niemals> nötig sein.
Im Prinzip ja.
Nur hat sich anscheinend noch keiner die Mühe gemacht, das ganze für
Bitfelder in Strukturen durchzuziehen.
Ist auch nicht weiter verwunderlich, denn die klassische Methode
arbeitet mit den Bitoperationen direkt.
Die Bitfelder in Strukturen für alle Register hätten enorme Vorteile,
doch leider sind sie in den AVR-Headern nicht durchgezogen. Wären sie
das, würden auch die ganzen Fehler die durch das Setzen von Bits im
falschen Register entstehen, gar nicht erst passieren können.
> Keine Bücher, die auf vielen hundert Seiten erklären müssen, was die> Sprache tut, bevor ich mich dem widmen kann, was mich eigentlich> intessiert: was nämlich die CPU tun soll.
Ja, schon gut.
Während du noch deine 3000 Zeilen Assembler Code schreibst und dich
rumärgerst, welches Register wo fälschlich verändert wird bzw. gesichert
werden muss, bzw. an welcher Stelle wieder mal das Statusregister
verändert wurde, so dass der bedingte Sprung nicht genommen wird,
überlasse ich diese Details dem Compiler und geh dafür lieber auf ein
Bier. Die 5% Laufzeit-Verluste sind es mir wert, wenn ich nicht den
letzten Rest an Laufzeit rausholen muss. Aber das wirst du heute genauso
wenig verstehen, wie all die anderen male zuvor.
ich denke, beide Sprachen absolut Ihre Berechtigung haben.
Bei C gestaltet es sich eben teilweise eben nur so, dass viele
Programmierer (schlimmer noch bei BASIC)keinen Bezug zur Hardware haben
und somit Probleme
bei der efizienten Code Generierung haben.
Das 2. Code Beispiel von Karlheinz ist schon richtig, nur!!! ein reiner
C-Programmierer sieht mit Sicherheit Aufgrund der fehlenden ASM
Kenntnisse
nicht die Notwendigkeit, diesen ebenfalls längeren C-Code zu schreiben.
Assembler Kenntnisse werden C Programmieren auf keinen Fall zum
Nachteil,
eher anders herum.
Über ASM wird viel geschimpft und verdammt.
Gleichzeitig programmieren wir heute noch in einer Sprache (C), die
mittlerweile fast 45 Jahre alt ist und aus einer Epoche stammt, in
der die Anforderungen an ein embedded System wie heutzutage, ganz Andere
waren.
Eigentlich wäre es an der Zeit für eine ganz neue Programmiersprache,
welche vom Syntax nicht mit den heutigen zu tun hat.
Ein Assembler Programm ist so gut und schlecht wie sein Programmierer.
Ein C-Programm ist so gut und schlecht wie sein Programmierer und der
Programmierer des C-Compilers (also Menschen, welche sich trotzdem mit
ASM auseinander setzen müssen)
Karl Heinz schrieb:> Macht man das ganze klasssisch
Das sind dann gerade mal 4 Befehle (8 Byte) gespart.
Optimieren sollte man erst, wenn es auch einen spürbaren Effekt hat.
Dirk schrieb:> ich denke, beide Sprachen absolut Ihre Berechtigung haben.
Ich denke, das sollte jetzt nicht in einen Flamewar ausarten.
Mir ging es nur darum, der Assembler-über-alles Fraktion insofern den
Wind aus den Segeln zu nehmen, weil das von PeDa gepostete Beispiel und
wie es in Object-Code umgesetzt wird, (noch) nicht der Regelfall ist.
> Das 2. Code Beispiel von Karlheinz ist schon richtig, nur!!! ein reiner> C-Programmierer sieht mit Sicherheit Aufgrund der fehlenden ASM> Kenntnisse> nicht die Notwendigkeit, diesen ebenfalls längeren C-Code zu schreiben.
Ein C-Anfänger hätte genau meinen Code geschrieben, eventuell unter
Verzicht auf das Makro. Peters Lösung ist schon die Komfort-Lösung, die
aber (noch) nicht über das Tutorial gelehrt wird.
Das war der springende Punkt. Insofern ist die Umsetzung in
Maschinen-Code mit dem In-maskieren-zurechtschieben-Out Zyklus nicht
repräsentativ dafür, was ein durchschnittlicher Programmierer erhalten
würde. PeDa hat eine Komfort-Variante, die absolut ihre Berechtigung
hat, aber vom Compiler noch nicht so gut unterstützt wird.
Peter Dannegger schrieb:> Karl Heinz schrieb:>> Macht man das ganze klasssisch>> Das sind dann gerade mal 4 Befehle (8 Byte) gespart.> Optimieren sollte man erst, wenn es auch einen spürbaren Effekt hat.
Darum gings mir auch gar nicht.
Als ich deinen Maschinencode Output gesehen habe
1
2c: 88 b3 in r24, 0x18 ; 24
2
2e: 8b 7f andi r24, 0xFB ; 251
3
30: 89 2b or r24, r25
4
32: 88 bb out 0x18, r24 ; 24
da sah ich vor meinem geistigen Auge schon c-hater, der zu einer
erneuten Tirade angesetzt hat, wie ineffizient das nicht gegenüber einer
Assembler-Lösung wäre. Und dem wollte ich zuvor kommen, indem klar
gestellt wird, dass eine 'normale' Lösung, so wie sie jeder Anfänger
aufgrund des Tutorials schreiben würde, in die Bitoperationen-Lösung
mündet.
Dirk schrieb:> Eigentlich wäre es an der Zeit für eine ganz neue Programmiersprache,> welche vom Syntax nicht mit den heutigen zu tun hat.
Gibt es doch schon. Netduino. Programmiert wird in C# mit dem .net micro
framework. Schöner geht es doch schon fast nicht mehr. Oder?
Nur das C# für PC Anwendungen designed wurde und nicht für Embedded
Systeme, das ist ja nur ein Versuch es dafür hinzubiegen. Zudem das auch
schon nicht mehr auf 8-Bitter läuft, sondern auf 32-Bit ARM Maschinen
mit ein paar hundert MHz.
Danke für die vielen Tipps! Das der Zustand von PB2 nicht exakt
definiert ist, sobald von On zu Off Status gewechselt wird war ein
Fehler von mir, sollte wieder auf low beim Wechsel.
Die Lösung in C ist natürlich eine feine Sache, aber ich wollte nach dem
was ich im AVR-Tutorial über Assembler verstanden habe mal die Sache in
Assembler lösen.
Die Vorschläge von Joerg Wolfram und Detlef Kunz kann ich leider nicht
exakt nachvollziehen und machen auch im AVR-Studio nicht das was ich mir
vorgestellt habe. Vielleicht lag das auch an den wenigen Vorgaben die
ich gemacht habe. Ich werde mal am Wochenende testen ob alles läuft.
Danke für das Feedback!
Jasch schrieb:> max20 schrieb:>> Off:>> ;PB3 -> PB4>> sbic PINB, 3 ;überspringt den nächsten Befehl wenn Bit low>> sbi PORTB, 4 ;setzt Bit>> sbis PINB, 3 ;überspringt den nächsten Befehl wenn Bit high>> Den oben weglassen.
Das war natürlich falsch, sozusagen "goto fail" Version 2.0. Wie
peinlich...
Was hatte ich noch geschrieben: scharf nachdenken was genau passiert.
;-)
max20 schrieb:> Die Vorschläge von Joerg Wolfram und Detlef Kunz kann ich leider nicht> exakt nachvollziehen und machen auch im AVR-Studio nicht das was ich mir> vorgestellt habe.
Ups, kleiner Fehler.
1
Anfang:
2
in R16,PORTB
3
in R17,PINB
4
bst R17,PB3
5
sbrs R17,PB0 //muss hier natürlich sbrs anstatt sbis sein ;)
6
rjmp Off
7
8
On:
9
bld R16,PB2
10
bst R17,PB1
11
12
Off:
13
bld R16,PB4
14
out PORTB,R16
15
rjmp Anfang
müsste aber jetzt genau dies machen:
1
PB0 = high, dann Bit von PB3 zu BP2 und PB1 zu PB4
Ja, da ist mir auch ein Fehler bei der Erstellung der Tabelle
unterlaufen. Normalerweise lasse ich solche Tabellen automatisch
erstellen, aber da sollte es halt schnell gehen...
1
.db 0xeb,0xeb,0xeb,0xfb,0xeb,0xeb,0xeb,0xfb
2
.db 0xfb,0xef,0xfb,0xff,0xfb,0xef,0xfb,0xff
Das verwendete Perl-Script sieht folgendermaßen aus, vielleicht kann es
ja jemand gebrauchen:
c-hater schrieb:> Keine Bücher, die auf vielen hundert Seiten erklären müssen, was die> Sprache tut, bevor ich mich dem widmen kann, was mich eigentlich> intessiert: was nämlich die CPU tun soll.
Dann interessiere dich mal dafür.
Hier
Beitrag "AVR Simulator Data or unknown opcode"
kannst du dich beim Helfen austoben soviel du willst.
Aber interessanterweise glänzt du in den Assembler-Threads dann auch
meistens durch Abwesenheit.
Dirk schrieb:> Das 2. Code Beispiel von Karlheinz ist schon richtig, nur!!! ein reiner> C-Programmierer sieht mit Sicherheit Aufgrund der fehlenden ASM> Kenntnisse> nicht die Notwendigkeit, diesen ebenfalls längeren C-Code zu schreiben.
Im Gegenteil, die Variante von Karl Heinz ist sogar eher die bvorzugte.