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!
> Wie kann ich in C eine "Vereinfachung" schreiben.
Indem du SPI nimmst.
@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?
Das ändern des DataDirectionRegisters wird dir aber nicht viel helfen...
lol ups DDRA/DDRB ist mit PORTA/PORTB zu ersetzen ;) das kommt vom kopieren!
Wenn du beim Übersetzen schon die Zeitdauer für das Delay kennst ("10000"), dann nimm die delay-Funktionen aus der avr-libc. http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html
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 | void send_char (uint8_t dsp_char_out) { |
2 | uint8_t n; |
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 |
14 | PORTB |= (1 << CLOCK); // CLOCK = 1 |
15 | }
|
16 | wait (delay_irgendwas); |
17 | }
|
Das geht natürlich auch für deine 48 BIT.
@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
ja, die würde ich gerne nehmen, wenn sie nicht 20 % meines Speichers fressen würde ;)
Die obigen Beispiele sind ja nicht so der Brüller (bezüglich Code, SRAM, Laufzeit), besser gehts so:
1 | #include <avr\io.h> |
2 | #include <inttypes.h> |
3 | |
4 | |
5 | struct bits { |
6 | uint8_t b0:1; |
7 | uint8_t b1:1; |
8 | uint8_t b2:1; |
9 | uint8_t b3:1; |
10 | uint8_t b4:1; |
11 | uint8_t b5:1; |
12 | uint8_t b6:1; |
13 | uint8_t b7:1; |
14 | } __attribute__((__packed__)); |
15 | |
16 | |
17 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
|
18 | |
19 | |
20 | #define LCD_CLK SBIT(PORTD, 0) // clock
|
21 | #define LCD_CLK_DIR SBIT(DDRD, 0)
|
22 | #define LCD_DAT SBIT(PORTD, 1) // data
|
23 | #define LCD_DAT_DIR SBIT(DDRD, 1)
|
24 | #define LCD_CE SBIT(PORTD, 2) // chip enable
|
25 | #define LCD_CE_DIR SBIT(DDRD, 2)
|
26 | |
27 | |
28 | void shift_out( uint8_t b ) // send byte |
29 | {
|
30 | uint8_t i; |
31 | |
32 | for( i = 8; i; i-- ){ // 8 bits |
33 | LCD_DAT = 0; |
34 | if(b & 0x80 ) // high bit first |
35 | LCD_DAT = 1; |
36 | b += b; |
37 | LCD_CLK = 0; |
38 | LCD_CLK = 1; |
39 | }
|
40 | }
|
41 | |
42 | |
43 | void lcd_out48( uint8_t * dat ) // send 48 bits (= 6 byte) |
44 | {
|
45 | uint8_t i; |
46 | |
47 | LCD_DAT_DIR = 1; // output |
48 | LCD_CLK_DIR = 1; // output |
49 | LCD_CE_DIR = 1; // output |
50 | LCD_CE = 1; |
51 | LCD_CLK = 1; |
52 | |
53 | LCD_CE = 0; // CE = 0 (enable shift register) |
54 | dat += 6; // last byte first |
55 | |
56 | for( i = 6; i; i-- ){ // 6 byte |
57 | shift_out( *--dat ); |
58 | }
|
59 | LCD_CE = 1; // CE = 1 (latch data) |
60 | }
|
Und für Delays dann noch die Funktionen aus der delay.h nehmen. 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.
> da kommt ja richtig 8051-Feeling auf
Ja, die sind genial und bei mir (8051 Fan) ebenso eingepflegt ;-))
@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_t val; |
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.