Ich schreibe gerade an einem C-Programm für einen AVR.
Dort habe ich eine unsigned char Variable Befehl.
Das werte ich mit switch case aus.
Hier nur ein Beispiel!
case1:if(PORTB&~(1<<7))PINB=(1<<7);break;// SET PB7
5
case2:if(PORTB&(1<<6))PINB=(1<<6);break;
6
case3:if(PORTB&~(1<<6))PINB=(1<<6);break;
7
case4:if(PORTB&(1<<5))PINB=(1<<5);break;
8
case5:if(PORTB&~(1<<5))PINB=(1<<5);break;
9
case6:if(PORTB&(1<<4))PINB=(1<<4);break;
10
case7:if(PORTB&~(1<<4))PINB=(1<<4);break;
11
// Hier kommen noch 93 andere Befehle !!!
12
}
Bei der switch-case-Anweisung stört mich, das das Programm bei sehr
langen Listen jeden einzelnen case testen muß.
Gibt es eventuel noch eine andere Möglichkeit?
Blitzenblitz schrieb:> // Hier kommen noch 93 andere Befehle !!!
kannst du noch ein paar mehr zeigen? die Frage ist wie es weiter geht ob
man das nicht komplett berechnen kann. Es wird zwar dadurch vermutlich
nicht schneller aber es spart viel code.
Der Compiler sollte sotwas schön als Sprungtabelle ablegen, eventuell
sollte man mal suchen warum er sotwas nicht macht.
Es würde im übrigen helfen, wenn du schreibst was du vor hast.
Das Programmstück sieht so aus, als wolltest du die Möglichkeit
implementieren über Befehle alle verfügbaren Pins zu steuern. Dafür gibt
es in der Tat bessere Ansätze.
dr.no schrieb:> Ansonsten mit Funktionspointern arbeiten.
Sowas hatte ich im Sinn.
Leider bin ich mit Pointern in C noch nicht so fit...
Wo würde ich die ganzen Befehls-sequenzen hinschreiben?
Kannst du mir da mal ein geben?
Blitzenblitz schrieb:> Wo würde ich die ganzen Befehls-sequenzen hinschreiben?
In diesem Fall in eine separate Funktion.
1
typedef void (* FUNC_PTR)(void);
2
3
static void Befehl_0(void);
4
static void Befehl_1(void);
5
6
const FUNC_PTR befehle[] =
7
{
8
Befehl_0,
9
Befehl_1
10
// ... weitere Befehle
11
};
12
13
14
static void Befehl_0(void)
15
{
16
// ... code Befehl 0
17
}
18
19
static void Befehl_1(void)
20
{
21
// ... code Befehl 1
22
}
Zugriff dann mit:
1
befehle[Befehl]();
Es empfiehlt sich zu prüfen ob "Befehl" innerhalb des gültigen Bereichs
liegt bevor man das tut.
Nochmal: Es würde helfen wenn du erklärst was du vor hast.
Blitzenblitz schrieb:> Bei der switch-case-Anweisung stört mich, das das Programm bei sehr> langen Listen jeden einzelnen case testen muß.
Nein. Muss es eben nicht. Praktisch jeder C-Compiler weiß, wie's besser
geht. Anhängig von der Anzahl/Dichte der Fälle sind ALternativen zB
- binärer Entscheidungsbaum
- Sprungtabelle
> Gibt es eventuel noch eine andere Möglichkeit?
Wie sieht der Code denn überhaupt aus? Wieso werden keine Sprungtabellen
erzeugt?
Funktionspointer sind hier auch nicht der Weisheit letzter Schluss. Das
artet in Unmengen von Funktionen aus.
Am Besten:
Zurücklehenen, erst mal erzählen was die AUfgabenstellung aus einer
höheren Werte gesehen ist und dann kann man entscheiden welche Technik
angebracht ist.
So wie es jetzt aussieht, hast du einen verkorksten Ansatz. Den rettet
aber auch eine Änderung auf tiefster Ebene nicht mehr.
So sind zum Beispiel in deinem Beispiel alle ungeraden Befehle Tests ob
am PortB ein Bit gelöscht ist, und alle geraden Befehle sind Tests ob am
Port (B?) ein bestimmtes Bit gesetzt ist. Alleine mit dieser
Unterscheideung kann man schon mal 2 Gruppen bilden, bei der dann in
weiterer Folge nur noch die Hälfte der Befehle geprüft werden muss.
wenn es wirklich so sein muss (was ich auch noch bezweifle) dann kann
man immer noch zu ASM ausweichen. Da die Befehle alle gleich lang sind.
Kann man einfach den
Adresszeiger = Adresszeiger + Befehltslänge * Befehl
rechnen und schon steht man an der richtigen stelle.
Peter schrieb:> wenn es wirklich so sein muss (was ich auch noch bezweifle) dann kann> man immer noch zu ASM ausweichen.
ARGL, nö. Hier geht's weiter:
Karl heinz Buchegger schrieb:> So wie es jetzt aussieht, hast du einen verkorksten Ansatz. Den rettet> aber auch eine Änderung auf tiefster Ebene nicht mehr.
In der Überschrift steht: "switch-case-Anweisung zu langsam"
Wie kommst Du überhaupt darauf? Hast Du irgendetwas gemessen?
Ich schätze mal: nein.
Und falls es wider Erwarten doch zu langsam abgearbeitet werden sollte,
einfach den internen Takt von 1MHz auf 8MHz stellen, wenn wir vom AVR
reden.
> Bei der switch-case-Anweisung stört mich, das das Programm bei> sehr langen Listen jeden einzelnen case testen muß.
Kaum.
Also mich würde eher stören, daß der ganze Code nichts taugt.
case 0: if(PORTB & (1 << 7)) PINB = (1 << 7); break; // CLR PB7
wird mitnichten ein Bit löschen
case 1: if(PORTB & ~(1 << 7)) PINB = (1 << 7); break; // SET PB7
was willst du an PIN auch setzen ?
Warum schreibst du nicht funktionierende Programme ?
if(befehl&1) // Setzen
{
PORTB|=1<<(befehl>>1);
}
else // löschen
{
PORTB&=~(1<<(befehl>>1));
}
erledigt deine 16 ersten Befehle, und es funktioniert damit sogar.
Ersetzt man PORTB noch alle 16 befehle durch das Richtige
int PORT[5]={PORTB,PORTA,PORTC,PORTD,PORTE};
if(befehl&1) // Setzen
{
*PORT[befehl>>4]|=1<<(befehl>>1);
}
else // löschen
{
*PORT[befehl>>4]&=~(1<<(befehl>>1));
}
erschlägt der Code gar alle deine 100 Befehle (funktioniert aber so auf
einem AVR wohl nicht weil das nicht deinen Befehlen entspricht).
MaWin schrieb:> Ersetzt man PORTB noch alle 16 befehle durch das Richtige>> int PORT[5]={PORTB,PORTA,PORTC,PORTD,PORTE};>> if(befehl&1) // Setzen> {> *PORT[befehl>>4]|=1<<(befehl>>1);> }> else // löschen> {> *PORT[befehl>>4]&=~(1<<(befehl>>1));> }
Ja, so ungefähr hätte ich mir das auch vorgestellt. Daher die
Aufforderung an den TO, mal etwas mehr zu erzählen. Dann könnte man
entscheiden ob die Stossrichtung stimmt.
Auch Hybridmethoden wären denkbar (er wird ja doch nicht für jedes Bit
an jedem Port einen eigenen Befehl eingeführt haben. Oder doch?), wenn
es ausser dem gezeigten 'Befehlsschema' noch andere gibt.
> erschlägt der Code gar alle deine 100 Befehle (funktioniert aber so auf> einem AVR wohl nicht weil das nicht deinen Befehlen entspricht).
Liese sich aber leicht erreichen. Das wäre nicht das Problem.
bekomme ich die Meldung: Build succeeded with 0 Warnings.
Ohne das volatile bekomme ich allerdings die Warnung:
initialisation discards qualfiers from pointer target type
> bekomme ich die Meldung: Build succeeded with 0 Warnings.
Mit anderen Worten: alles in Ordnung.
Auch wenn ich manchmal Fehler mache, ich bin ja auch nicht auf der
Nudelsuppe dahergeschwommen gekommen.
> Ohne das volatile bekomme ich allerdings die Warnung:> *initialisation discards qualfiers from pointer target type*
Mit anderen Worten: das volatile muss da sein. Und genau deswegen habe
ich es hingeschrieben. qed
Blitzenblitz schrieb:> Wird diese von einem Interrupt unterbrochen, der ebenfalls PORTB> verfummelt dann kann das in die Hose gehn...
du kannst ja immer noch ein sei/cli drum herum machen :-)
Von Interrupts war ja bisher nicht die Rede.
> bekomme leider die Fehlermeldungen
Drum schrieb ich "funktioniert aber so auf einem AVR wohl
nicht" (weil mir die Definitonsweise des PORTB dort
unbekannt ist).
Also nicht stumpf abtippen, sondern selber denken,
hat Karl heinz ja auch geschafft und sogar das
fehlende &0x0F nachgerüstet, obwohl ich &7 genommen hätte:
if(Befehl&1) // Setzen
{
*PORT[Befehl>>4] |= (1<<((Befehl>>1)&7));
}
else // löschen
{
*PORT[Befehl>>4] &= ~(1<<((Befehl>>1)&7));
}
Karl heinz Buchegger schrieb:> du kannst ja immer noch ein sei/cli drum herum machen :-)
Ah ja sei ist ein Assemblerbefehl und heißt SEt Interrupt
und cli heißt CLear Interrupt.
Ich schreibe in C:
1
sei();// Interrupts freigeben
2
PORTB|=(1<<7);// SET PB7
3
cli();// Interrupts sperren
Wobei zu testen ist, ob das länger dauer als mein
1
if(PORTB&~(1<<7))PINB=(1<<7);break;// SET PB7
und ob ich mir damit nicht noch andere Probleme einhandle...
MaWin schrieb:> case 0: if(PORTB & (1 << 7)) PINB = (1 << 7); break; // CLR PB7> wird mitnichten ein Bit löschen
Doch. Nicht auf Mega8/32, wohl aber auf Mega88/324.
1 auf PINx schreiben hat da Toggle-Funktion.
Besser wär aber:
case 0: PINB = PORTB & (1 << 7);
Blitzenblitz schrieb:> Ah ja sei ist ein Assemblerbefehl und heißt SEt Interrupt> und cli heißt CLear Interrupt.> Ich schreibe in C:sei(); // Interrupts freigeben> PORTB |= (1 << 7); // SET PB7> cli(); // Interrupts sperren> Wobei zu testen ist, ob das länger dauer als meinif(PORTB & ~(1 << 7)) PINB = (1
<< 7); break; // SET PB7
> und ob ich mir damit nicht noch andere Probleme einhandle...
Du kannst auch einfach dem GCC das Optimieren erlauben. Aus
PORTB |= (1<<7);
wird dann:
256: c7 9a sbi 0x18, 7 ; 24
Also ein einzelner "sbi"-Befehl. Der ist Atomar, kein sei/cli nötig.
Bei der Tabellen-Version oben, da brauchst du das ggfs.
Εrnst B✶ schrieb:> Also ein einzelner "sbi"-Befehl. Der ist Atomar, kein sei/cli nötig.
Aber Vorsicht! Nicht alle Ports aller AVRs liegen in von SBI/CBI
erfassbaren Adressraum.
Danke an alle fleißigen Helfer,
habe dieses jetzt mal programmiert und simuliert:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
//***********************************************
4
typedefvoid(*FUNC_PTR)(void);
5
voidBefehl_0(void);
6
voidBefehl_1(void);
7
voidBefehl_2(void);
8
voidBefehl_3(void);
9
voidBefehl_4(void);
10
voidBefehl_5(void);
11
voidBefehl_6(void);
12
voidBefehl_7(void);
13
voidBefehl_8(void);
14
voidBefehl_9(void);
15
//***********************************************
16
constFUNC_PTRbefehle[]=
17
{
18
Befehl_0,
19
Befehl_1,
20
Befehl_2,
21
Befehl_3,
22
Befehl_4,
23
Befehl_5,
24
Befehl_6,
25
Befehl_7,
26
Befehl_8,
27
Befehl_9
28
};
29
//***********************************************
30
intmain(void)
31
{
32
unsignedcharBefehl=0;
33
DDRB=0xFF;
34
while(1)
35
{
36
befehle[Befehl++]();
37
if(Befehl==10)Befehl=0;
38
}
39
}
40
//***********************************************
41
voidBefehl_0(void){if(~PORTB&(1<<4))PINB=(1<<4);}
42
voidBefehl_1(void){if(~PORTB&(1<<3))PINB=(1<<3);}
43
voidBefehl_2(void){if(~PORTB&(1<<2))PINB=(1<<2);}
44
voidBefehl_3(void){if(~PORTB&(1<<1))PINB=(1<<1);}
45
voidBefehl_4(void){if(~PORTB&(1<<0))PINB=(1<<0);}
46
voidBefehl_5(void){if(PORTB&(1<<4))PINB=(1<<4);}
47
voidBefehl_6(void){if(PORTB&(1<<3))PINB=(1<<3);}
48
voidBefehl_7(void){if(PORTB&(1<<2))PINB=(1<<2);}
49
voidBefehl_8(void){if(PORTB&(1<<1))PINB=(1<<1);}
50
voidBefehl_9(void){if(PORTB&(1<<0))PINB=(1<<0);}
Scheint zu funktionieren.
Der tiefe Sinn dahinter, den ich bisher verschwiegen habe, war:
1. Meine C-Kenntnisse zu verbessern...
2. Eine Möglichkeit zu finden, historische oder selbsterdachte µC's auf
einem AVR in Software nachbilden zu können...
>Der tiefe Sinn dahinter>"switch-case-Anweisung zu langsam"
Die "Lösung" vom Datum: 12.01.2011 16:21 wird NIEMALS schneller sein
als der Anfang dises Threads.
Sie ist nur
= umständlicher
= schlechter lesbar
= schlechter wart- und erweiterbar (aufwendiger)
Erich schrieb:> Die "Lösung" vom Datum: 12.01.2011 16:21 wird NIEMALS schneller sein> als der Anfang dises Threads.> Sie ist nur> = umständlicher> = schlechter lesbar> = schlechter wart- und erweiterbar (aufwendiger)
Nö, eigentlich bin ich ganz zufrieden.
Ich würde im weiteren Verlauf den Befehlen dann natürlich sinnvollere
Namen als Befehl_123 geben.
Mein eigentliches Programm würde dann hier stehen:
1
constFUNC_PTRbefehle[]=
2
{
3
SET_PB1,
4
SET_PB3,
5
SET_PB5,
6
SWAP_A_at_R0,// Befehl wie beim 8051, nur Gedankenspiel
7
CLR_PB2,
8
CLR_PB4,
9
// usw.
10
};
Kritiker werden einwenden können ich hätte das ja auch so machen können:
@ Blitzenblitz (Gast)
>Mein eigentliches Programm würde dann hier stehen:>const FUNC_PTR befehle[] =
Und auf dem Besten weg, ein langsames, akademisches Monster zu werden.
Diese ganzen Structs und Datenzugriffe kosten Zeit, auch auf einem
schnellen Prozessor. Zeit, die du angeblich nicht hast.
>Allerdings würde der Preprozzessor dann für jeden Befehl den kompletten>Code einfügen
Das ist der Sinn der Sache, wenn es schnell sein soll. Lies mal den
Betreff DEINES Beitrags . . .
> und ruckzuck wär mein Speicher voll.
Nimm einen größeren Prozessor. Und vor allem musst du schon VERDAMMT
viele solche Sachen machen, um mit ein paar IO Klimpereien einen
normalen uC vollzustopfen.
Ergo: Thema verfehlt, sechs.
MFG
Falk