Hallo liebe Leute!
Habe vor, mit meinem ATmega16 im AVR-Studio mit dem GCC ein LCD
anzusteuern. Und zwar funktioniert dieses nach dem Schieberegister
Prinzip.
Man schiebt in Summe 48 Bits (für die 48 Segmente) nacheinander ins LCD,
gibt nach jedem Bit einen Clock und Lädt abschließend das
Schieberegister über eine logische "1" am Load-Eingang in das Display.
Nun meine Frage. Wie kann ich in C eine "Vereinfachung" schreiben. Ich
muss ja schließlich für jede Veränderung des Displays die 48 Bits
schieben,clocken und loaden. Wenn beispielsweise von bit20 bis bit48
keine ungleichen werte sind, dann kann ich eine schleife machen....
Danke!
@Johannes
>Man schiebt in Summe 48 Bits (für die 48 Segmente) nacheinander ins LCD,>gibt nach jedem Bit einen Clock und Lädt abschließend das>Schieberegister über eine logische "1" am Load-Eingang in das Display.>Nun meine Frage. Wie kann ich in C eine "Vereinfachung" schreiben. Ich
Was soll denn daran noch gross vereinfacht werden? Deine 48 Bits legst
du sinnvoll in einem Array ab. Als Anfänger wollen wir mal ein
uint_8t daten[48];
gelten lassen, als Fortgeschrittener wirst du platzsparend ein
uint_8t daten[6];
nehmen. Das kannst du in einer einfachen Schleife in dein
Schieberegister takten. Sind eine handvoll Zeilen Code. Wenig
Optimierungspotential.
>muss ja schließlich für jede Veränderung des Displays die 48 Bits>schieben,clocken und loaden.
Und?
>Wenn beispielsweise von bit20 bis bit48>keine ungleichen werte sind, dann kann ich eine schleife machen....
Ich liebe die doppelte Verneinung ;-)
Du solltest so oder so eine Schleife machen.
MfG
Falk
P.S. Die von Jörg vorgeschlagene SPI ist nicht die Lösung deines
Problem. Du hast noch ein grundlegendes Verständnisproblem wie man
solche Sachen prinzipiell angeht.
Sobald Du auch nur einmal schiebst, ist das ganze Bitmuster verwurstelt.
Du mußt also immer alle Bits schieben, auch wenn sich nur ein Bit
ändert.
Ob nun HW-SPI oder SW-SPI macht in der Codegröße kaum einen Unterschied,
das HW-SPI ist nur etwas schneller.
Vereinfachen muß man der CPU nichts, der ist egal, wie oft sie was
wiederholen soll.
Mach Dir ne Schleife für 8 Bits, die dann 6 * aufgerufen wird.
Peter
wie peter zuletzt schrieb, muss ich bei jeder Änderung aufs Neue ALLE 48
Bits schieben.
Und meine Frage war eben, wie ich beispielsweise den Zählerstand (aus
dem Zählerregister) "direkt" auf ein LCD des von mir genannten Typs
ausgeben kann. Ich kann natürlich eine Funktion schreiben, in der ich
für jede einzelne Zahl auf dem Display von 1-1000 jeweils 48 Bitmuster
schreibe. Dann lasse ich in meiner main() abfragen in welchem Bereich
der Zählerstand steht, und dann gebe ich beispielsweise im 100ms-Raster
die Zahl am Display aus, dazuaddiert mit der von mir bereitgestellten
Overflow-Variablen, wenn der Zähler von FF auf 00 geht.
Aber wie kann ich das problem eben vereinfachen?
und noch eine kleine Frage:
der Takt muss eine bestimmte Zeit auf H-pegel sein. Kann ich da einfach
eine Schleife in einer Funktion hochzählen lassen, um damit eine
Verzögerung hinzukriegen?
Bspl:
int Delay(laenge)
{
for (i=0;i<laenge;i++)
{
}
}
int main (void)
{
DDRA=0b00000001; //Bit schieben ("1")
DDRA=0b00000010; //clocken von L nach H
Delay(10000); //Aufruf der Fkt. "Delay"
DDRA=0b00000000; //clocken von H nach L
// das ganze 48x mit verschiedenen Schiebebits
DDRA=0b00000100; //abschließender Load-Befehl
}
so müsste es doch klappen oder?
Willst du nun seriell etwas ausgeben oder innerhalb des Port schieben.
Irgendwie ist mir das Ganze unklar.
Mal unterstellt du suchst eine Funktion die dir seriell etwas mit Clock
raustakten soll dann wäre es so etwas:
1
voidsend_char(uint8_tdsp_char_out){
2
uint8_tn;
3
4
for(n=0;n<8;n++){// Sende 8 BIT
5
if(dsp_char_out&(0x80>>n))// Daten an Display ausgeben, MSB first
6
{
7
PORTB|=(1<<DATA);
8
}
9
else
10
{
11
PORTB&=~(1<<DATA);
12
}
13
PORTB&=~(1<<CLOCK);// Ausgabe des Schiebetakt, CLOCK = 0
@Johannes
>der Takt muss eine bestimmte Zeit auf H-pegel sein. Kann ich da einfach>eine Schleife in einer Funktion hochzählen lassen, um damit eine>Verzögerung hinzukriegen?
Nutzt für Verzögerungen die Funktionen der AVR libc, die funktionieren
garantiert.
Ausserdem wirst du wahrscheinlich keine Delays brauche, denn die
Schieberegister sind im allgemeinen wesentlich schneller, als du es per
Software ansteuern kannst.
>so müsste es doch klappen oder?
Nein, du musst ein Schleife programmieren.
Hier mal die einfache Variante.
uint8_t data[48]; // Datenarray, ein Byte pro Bit
for (i=0; i<48; i++)
{
PORTA = PORTA & data[i]; // Daten auf PA0
PORTA = PORTA & 0x02; // Takt auf PA1
}
PORTA = 0; // Takt und Daten löschen
PORTA = 0x04; // LOAD am Schieberegister ist PA2
PORTA = 0; // Takt, Daten und LOAD löschen
MFG
Falk
Johannes wrote:
> Ich kann natürlich eine Funktion schreiben, in der ich> für jede einzelne Zahl auf dem Display von 1-1000 jeweils 48 Bitmuster> schreibe.
Sag bloß, Du hast in der Schule alle Zahlen 0...1000 auswendig gelernt.
Ich hab nur die Ziffern 0..9 gelernt und genau so macht man es eben in
der CPU:
- Zahl in Ziffern zerlegen
- Bitmuster pro Ziffer aus Tabelle lesen
- alle Bitmuster in die 6 Bytes reinschreiben
- die 6 Bytes rausschieben.
Peter
Falk wrote:
> uint8_t data[48]; // Datenarray, ein Byte pro Bit
Was soll denn daran einfach sein. ?
Da muß man ja erst die Bitmuster auf die einzelnen Bit-Bytes aufteilen.
Das ist nicht einfach, sondern sehr aufwendig.
Peter
Danke Falk, deine Löstung habe ich kurz nach deinem Thread auch
geschrieben :)
Und zwar muss ich aber vorher noch die Array initialisieren....
array[48] {0,1,.........}
ansonsten eben die schleife, wie du es geschrieben hast.
Danke!
Peter, deine Lösung ist wahrscheinlich die Luxus-Effizienz-Super-Kanone,
aber der OP steht glaub ich programmier-und C-technisch auf ner ganz
anderen Stufe. Da ist Einfachheit erstmal wichtiger als Effizienz.
MfG
Falk
Falk wrote:
> Peter, deine Lösung ist wahrscheinlich die Luxus-Effizienz-Super-Kanone,> aber der OP steht glaub ich programmier-und C-technisch auf ner ganz> anderen Stufe. Da ist Einfachheit erstmal wichtiger als Effizienz.
Ich denke eigentlich, daß Anfänger nie früh genug damit anfangen können,
den Code in kleine verdaubare (verstehbare) Häppchen aufzuteilen.
Darin liegt meine Einfachheit.
Ich halte es im Gegenteil für komplizierter, die 48 Bits als monolitisch
verschweißten Block zu betrachten. Spätestens, wenn er dann die
einzelnen Ziffern drauf aufteilen will, wirds haarig.
Peter
P.S.:
Die Bitmacros hab ich hier geklaut:
Beitrag "sbit macro für avr-gcc"
Besten Dank an Volker, da kommt ja richtig 8051-Feeling auf.
@Peter Dannegger
>Ich denke eigentlich, daß Anfänger nie früh genug damit anfangen können,>den Code in kleine verdaubare (verstehbare) Häppchen aufzuteilen.
Ja. Auf jeden Fall.
Aber dein Code ist ausgewachsener C-Code, bei solchen Sachen wie
__attribute__((_packed_));
wird auch mir erstmal etwas mulmig.
Und auch sowas ist nicht sonderlich für Anfänger verdaulich
for( i = 6; i; i-- ){ // 6 byte
shift_out( *--dat );
}
LCD_CE = 1; // CE = 1 (latch data)
MfG
Falk
Falk wrote:
> wird auch mir erstmal etwas mulmig.
Dann sollte man sich nicht scheuen, den Autor zu fragen.
> __attribute__((_packed_));
Ist nur ein Bestandteil des Macros zur bequemen Bitdefinition (alle Bits
werden zusammen in ein Byte "gepackt").
> for( i = 6; i; i-- ){ // 6 byte
Ist quasi die Standardsyntax für eine Schleife ( i = 6, also 6 mal).
> dat += 6; // last byte first
...
> shift_out( *--dat );
Das ist zugegeben die einzige etwas verzwickte Zeile:
dat ist der übergebene Zeiger auf das Feld mit den 6 Bytes.
Damit nun mit dem höchstwertigen angefangen wird, wurde 6 addiert und er
zeigt hinter das Feld.
Bei jedem Schleifendurchlauf wird vor dem Zugriff wieder 1 abgezogen
"--dat", so daß erst Byte 5, 4, 3, 2, 1 und 0 ausgegeben werden.
Man will nun aber nicht den Zeiger (Adresse) ausgeben, sondern das Byte,
welches unter dieser Adresse abgelegt wurde und das macht der *
Operator.
Man kann diese Zeile in die einzelnen Operationen zerlegen:
1
uint8_tval;
2
3
dat=dat-1;// Zeiger runterzählen
4
val=*dat;// Inhalt holen
5
shift_out(val);// und ausgeben
> LCD_CE = 1; // CE = 1 (latch data)
Das ist wieder einfach: Pin CE auf 1 (high) setzen
Peter
@ Peter Dannegger
>> __attribute__((_packed_));>Ist nur ein Bestandteil des Macros zur bequemen Bitdefinition (alle Bits>werden zusammen in ein Byte "gepackt").
Das war MIR zu 99% klar. Einen Anfänger irritiert es massiv.
>> for( i = 6; i; i-- ){ // 6 byte>Ist quasi die Standardsyntax für eine Schleife ( i = 6, also 6 mal).
Dito. Aber z.B. i als solches ist nicht sonderlich anschaulich. Jaja,
die liebe C-Syntax. Oh wie vermisse ich Pascal seufz
ICH hab das Programm schon verstanden, aber wie gesagt. Didaktisch halte
ich es für absolut untauglich.
Ich hoffe du verstehst mich nicht falsch. Du bist sicher absolut fit in
der Materie, aber du kannst nur schwer auf Anfängernivau runterschalten.
Ich hatte mal nen Prof im Studium, der war genauso. Für den war FFT und
Laplace wie 1+1. Seine Vorlesung hab ich erst ein paar Semester später
WIRKLICH kapiert ;-)
Es ist eine Kunst, schwierige Dinge einfach und verständlich zu
erklären!
>Man kann diese Zeile in die einzelnen Operationen zerlegen:> uint8_t val;> dat = dat - 1; // Zeiger runterzählen> val = *dat; // Inhalt holen> shift_out( val ); // und ausgeben
Genau DAS würde ich machen. Der entstehende Code ist sowieso der
gleiche. Aber die Lesbarkeit (für die wir ja als C-Programmierer
plädieren) steigt enorm. Ausserdem verringert sich das
"indenFusschiessenRisiko", wenn man bei diesen tierisch verketteten
Operatoren mal wieder die Prioritäten nicht beachtet hat. ;-)
MfG
Falk