Hallo, ich bin gerade dabei ein Programm zu schreiben, welches mir je nach dem eine gewisse Zahl anzeigen soll (tut ja mal nix zur sache). Ich nutze AVR Studio mit GCC. Eingesetzt : Mega8 ( 1Mhz Takt ) Als Anzeige habe ich ein 2x7 Segment LCD. Ich habe es bereits geschafft ein PWM Signal mit ca. 32 Hz zu kreieren. Laut der Application Note Nr. AVR241 "Direct driving of LCD display using general I/O" ( http://www.atmel.com/dyn/resources/prod_documents/doc2569.pdf ) ist es möglich das LCD mit dem Mega8 ohne spezielle LCD Treiber Bausteine zu betreiben. Laut der Application Note braucht jedes Segment, damit es "leuchtet", ein 32Hz Rechtecksignal am gemeinsamen anschluss "common Line", und am Segment Anschluss das gleiche Rechtecksignal allerdings invertiert. Mein Problem liegt jetzt darin, dass ich an PB3(OC2) mein Rechtecksignal anliegen habe, ich aber nicht weis wie ich das Signal an einem anderen Pin invertiert ausgeben kann. Laut beigefügtem code, scheint es irgendwas damit auf sich zu haben: PORTB = PORTB ^ 0Xff; //Inverts levels on segement lines PORTD = PORTD ^ 0Xff; // Inverts levels on segement lines Ich werde aber aus dem ganzen nicht schlau. Kann mir vielleicht irgendwie irgendjemand erklären was an einem LCD genau anliegen muss, damit ich so ein Segment "EIN" und auch wieder "AUS" schalten kann, und wie ich dieses PWM Signal an einem anderen PIN invertiert ausgeben kann ?????? Danke und Gruß, Thomas
Ein bitweises Exklusiv-ODER ("^") eines Portregisters mit 0xFF macht nichts anderes als jedes einzelne Bit dieses Registers zu invertieren (jedes Bit wird mit "1" Exklusiv-ODER-verknüpft, es ändert also seinen Zustand unabhängig vom Ausgangswert; aus 0 wird 1 und umgekehrt). Wenn in PORTB vorher 0xBB (binär 10101010) steht, dann steht nach dem "PORTB ^= 0xFF" 0x55 (binär 01010101) in PORTB. Was noch ziemlich wichtig bei der Ansteuerung von LCDs ist (und was auch in der verlinkten Appnote implizit steht) ist, dass die Rechteckspannung für das LCD gleichspannungsfrei, also absolut symmetrisch sein muss, da sonst auf Dauer das Display geschädigt wird. Nimm also keine PWM, sondern lass den betreffenden Timer im CTC-Modus laufen. Du musst schließlich ein Tastverhältnis von exakt 50% erreichen.
Ich habe die Appnote und die dazugehörige Software nicht durchgelesen, aber das Prinzip der Steuerung ist, dass ein Takt am Common-Anschluss des Displays (bzw. der Display-Stelle, falls jede Stelle einen eigenen Common-Anschluss hat) anliegt. Wenn man diesen Takt jetzt invertiert auf ein Segment gibt, dann liegt zwischen der Segment-Elektrode und der Backplane (Common) die Rechteckspannung an und das Segment wird "schwarz". Wenn man den Takt nichtinvertiert an ein Segment anlegt, dann ist die Spannung zwischen Segmentelektrode und Backplane null und das Segment ist nicht sichtbar. Man kann jetzt z.B. die 7 Segmente einer Display-Stelle an 7 Pins eines µC-Ports anschließen und den Common-(Backplane-) Anschluss an den 8. Pin. Wenn man jetzt mit der entsprechenden Frequenz den kompletten Port jeweils invertiert, dann werden alle Segmente schwarz, deren Steuerpins gegenüber dem Common-Pin invertiert sind. Diejenigen Segmente, deren Steuerpins am µC den gleichen Zustand haben, wie der Common-Pin, bleiben aus. Will man eine Ziffer auf dem Display ausgeben, muss man lediglich dafür sorgen, dass die Controller-Pins, an denen die für die Darstellung der Ziffer benötigten Segmente hängen, jeweils gegenphasig in Bezug auf Common sind. Wenn der Pin, der mit Common verbunden ist also "0" ist, müssen die entsprechenden Segment-Pins "1" sein und umgekehrt. Hoffe das war einigermaßen verständlich...
Hey schonmal vielen Dank an dich. Okay dann muss ich also nicht das PWM nehmen, sondern den CTC Modus. aber auch in diesem Modus habe ich ja an dem OC2 Pin das Rechtecksignal. Wie mach ich das nun am geschicktesten, dass ich z.b.: an PORTD jeweils auch dieses Rechtecksignal zur Ansteuerung der Segmente habe ? Ich bin ja noch nicht so der Programmier-Held. Wäre super, wenn mir jemand schematisch den Ablauf der Software erklären könnte. Also z.b.: du brauchst einen Timer, der bei Überlauf dies und das macht...usw. Ich brauch ja im Endeffekt diese LCD Ansteuerung, und ein Programm das gleichzeitig abläuft. --> Wenn z.b. ein Eingang gesetzt wird, soll von 0 auf 1 hochgezählt werden (LCD zeigt dann die 1 an). Bei nochmaligem Eingangssignal wird dann die 2 angezeigt, usw. Also müsste die Sache mit der LCD Ansteuerung ja irgendwie seperat nebenher laufen. Wie du wahrscheinlich raushörst bin ich noch nicht so tief in der ganzen Sache drin. Aber wenn man nix macht, kann man es auch nicht lernen. Deshalb dieses Projekt g. Gruß, Thomas
Naja ich denk ich muss es ja nicht gleich überteiben. Außerdem möchte ich das eben noch auf ner Lochrasterplat. aufbauen können.
Du musst im Prinzip nur das, was ich oben versucht habe zu erklären, in Software umsetzen. Als kleiner Anstoß: Du brauchst einen Timer, der mit der doppelten Display-Frequenz Interrupts generiert. Im betreffenden Interrupt-Handler werden jetzt, wie oben beschrieben, die Controller-Ports, an denen das LCD angeschlossen ist, invertiert. Wenn ein neuer Wert ausgegeben werden soll, muss das entsprechende Bitmuster für die Segmente in die Portregister geschrieben werden, und zwar immer so, dass die aktiven Segmente gegenüber dem Taktausgang für die Backplane invertiert sind. Das sollte vergleichsweise einfach in Software umsetzbar sein....
Super.... Danke ... werde mal versuchen mich an deine Tippsp zu halten, mal schauen ob ich das hinbekomme :) Danke ung Gruß, Thomas
Hi, ich habe nun mal zum Test den Timer/Counter so eingestellt, das der Takt noch etwas langsamer ist, so ist es zum simulieren einfacher. Bei mir wird nun der PINB3 getaktet. Nur wie bekomme ich nun diesen Takt z.b. auf den gesamten Port B, sodass ich da dann das LCD anschließen kann ???? Ich habe den Takt ja wirklich nur an PINB3.
Wie weiter oben schon (sogar von Dir selbst) angesprochen: Du musst in der ISR den betreffenden Port "verexklusivODERn". Dazu musst Du natürlich den betreffenden Interrupt freigeben und eine ISR schreiben. Das sind aber absolute Grundlagen. Du solltest vielleicht mal einen Blick ins Tutorial werfen. Da steht wie's geht...
Alles klar. Ist ja meine erste berührung mit Interrupts. Ich werde nachlesen. Danke
Hi, Habe es nun soweit geschafft, das er Interrupt ausgelöst wird, und in die Interrupt Routine gesprungen wird. Das mit dem Invertieren des kompletten Ports funktioniert auch. Nun steh ich aber seit einiger Zeit auf dem Schlauch, wie ich es nun machen kann, das sich standartmäßig der komplette PortB so verhält wie PINB3 (also getaktet - phasengleich sozusagen). Nur gewisse PINS sollen dann später gegenphasig getaktet werden um das jeweilige LCD segment zu "schwärzen" ( welcher pin das ist, sollte doch dann in der Main festgelegt werden, oder ? )
PORTB ^= 0xFF; Guck dir am besten mal logische Funktionen an; z.B. das XOR-Gatter: http://de.wikipedia.org/wiki/Xor
PORTB ^= 0xFF; Habe ich doch so gemacht, wie du im Code siehst, mein Problem ist nun wie ich die anderen PINS des PORTB dazu kriege, damit sie eben nicht invertiert sind, sondern phasengleich zu dem einen PIN takten.
Wenn Du am Anfang (also z.B. in der main()) in das Portregister ein 0xFF oder 0x00 reinschreibst (also alle Pins den gleichen Zustand haben) und dann in der ISR ein Exklusiv-ODER darauf losläßt, dann wird immer der komplette Port 0 oder 1...
>Habe ich doch so gemacht, wie du im Code siehst
Guck ich mir Code an? (oder ganzen Thread?) ;-)
Dann veroderst du halt nicht mit 0xFF, sondern einer anderen Bitmaske.
0 xor 0 = 0
1 xor 0 = 1
Gibt es zu dem "Problem" auch eine Pinbelegung? (wie gesagt: Ich habe
mir nicht den ganzen Thread angeguckt...)
Pinbelegung vorerst : LCD Commonline : PB3 LCD Segmente : PB0-2 und PB4-7 Habs jetzt aber hinbekommen. Dazu habe ich wie ich es brauche das Portregister in der Main mit meinen Werten geladen. Nur komisch das das in der While schleife innerhalt der main nicht funktioniert. Später soll ja in der while schleife innerhalb der main eingegeben werden welches segment leuchten soll. daher müsste das Laden des Portregisters ja auch innerhalb der while schleife funtionieren. anbei der aktuelle stand.
Mach das nicht so. Wenn Du an PB3 einen Pin hardwaremäßig toggelst und den Rest dann per Software, dann gibts nen Zeitversatz zwischen PB3 und den anderen, weil der OCx-Pin sofort geschaltet wird, der Rest aber erst, wenn die entsprechende Anweisung in der ISR auftaucht. Außerdem müsstest Du den Common-Pin dann beim Exklusiv-ODER ausmaskieren, weil er sonst sofort wieder umgeschaltet wird. Wenn, dann (wie ich es oben schon mal geschrieben habe) alle Pins per Software toggeln. Dann gibts auch übersichtlichere Bitmuster für die Segmente, weil der Taktpin nicht mittendrin liegt. Lege den Backplane-Anschluss ("Common") z.B. auf PB7 und die Segmente auf PB6...0. Wichtig ist auch, dass ein neuer Anzeigewert synchron übernommen wird, also möglichst in der ISR. Sonst kommst Du eventuell ins Schleudern mit der Portinvertierung. Du musst vor einer Aktualisierung jeweils das Common-Bit abfragen und entsprechend entweder die Bitmaske oder die invertierte Bitmaske an (z.B.) PB6...0 ausgeben. Da darf dann eben kein Interrupt dazwischenhauen. Und genau das ist innerhalb der ISR gewährleistet.
Noch so eine Frage aus interesse: Gibts einen speziellen Grund, warum du LC-Displays nutzt?? LED-Anzeigen sind viel besser abzulesen und einfacher anzusteuern. Zudem wirst du Probleme bekommen wenn du mehr als eine Common Plane hast,da lohnen sich LCD-Controller dann wirklich. Gruß, SIGINT
@Sigint: Ich glaube, mich erinnern zu können, dass LCDs ein bisschen weniger Strom brauchen als LEDs. Thomas hat zwar nichts über einen konkreten Anwendungszweck gesagt, aber für Batteriebetriebene Gerätschaften würde ich persönlich auch ein LCD nehmen. Außerdem ist es als Anfänger (der Thomas augenscheinlich ist) als Lerneffekt gar nicht verkehrt, sich mit solchen Sachen auseinanderzusetzen. Und auch bei mehr als einer Backplane sehe ich keinerlei Probleme (außer dass man einen Taktausgang mehr braucht), eher im Gegenteil, da in dem Fall jeder Ausgabeport unabhängig geschaltet werden kann und es keine Synchronisationsprobleme gibt.
@Siginit: du sagst LED - Anzeigen sind viel besser abzulesen. Hast du schon mal ein LCD Display bei Tageslicht im freien abgelesen und im vergleich dazu eine LED 7 Segment Anzeige ? Dann wird dir mit Sicherheit genau das Gegenteil auffallen :) @johnny.m : Richtg ... außerdem : blutiger Anfänger g Du sagst ich solle den Common-Anschluss auf PB7 legen. das würde ich gerne tun, allerdings dachte ich bisher immer, wie ich es im Datenblatt gelesen habe, das der OC2 pin hardwaremäßig eben PB3 ist. Wie kann ich den denn auf einen anderen Pin umlegen? Ich habe den Taktausgang PB3 ja nicht selbst gewählt.
Du musst dafür sorgen, dass die Umschalterei synchron erfolgt (also Taktpin für Backplane und Segment-Pins gleichzeitig umgeschaltet werden). Das geht nunmal in Hardware nicht, weil die Hardware nur einen einzelnen Pin umschalten kann. Du musst das komplette Umschalten in Software machen. Der OCx-Pin darf nicht als Compare-Ausgang konfiguriert sein, sondern es muss in der ISR jeweils der komplette Port invertiert werden.
Mittlerweile erfolgt die Umschalterei synchron. Jetzt muss ich nur noch PB7 (common-line) invertiert laufen lassen. Also wenn PB0-6 = 1 dann PB7 = 0. Nur die Frage wie ich das am geschicktesten mache. Hab jetzt auch kapiert, dass der Timer/Counter2 bei mir immer im Non-PWM also Compare Output Mode läuft, allerdings COM20 und 21 auf "normal port operation" gestellt werden müssen, und nicht wie ich vorher auf Toggle OC2 on Compare match ... (wie du ja gesagt hattest :)
>Nur die Frage wie ich das am geschicktesten mache.
Wenn du PORTB mit 0x7F initialisierst und dann per PORTB ^= 0xFF in der
ISR betust, dann hast du doch die Funktion, die du haben willst.
Wenn du dann einzelne Segmente ausschalten willst (so wie ich LCDs bis
jetzt verstanden habe, müssen ausgeschaltete Segmente den gleichen Pegel
wie der Comon-Pin haben), dann musst du PORTB so beschreiben, dass das
ausgeschaltete Segment auch eine 0 hat.
Okay, und der Port muss dann z.b. mit 0x7F in der Main beschrieben werden? das darf ich dann nicht jeweils in der ISR machen, oder ? Könnte ja quasi für jeden wert von X das direkt in der ISR einfügen. Oder sollte man die ISR so klein wie möglich halten ?
>Könnte ja quasi für jeden wert von X das direkt in der ISR einfügen.
Das kannst du natürlich auch machen.
Quasi:
Hat sich der anzuzeigende Wert geändert?
Ja: Neuen Wert an Port schreiben
Nein: Port invertieren.
> Also wenn PB0-6 = 1 dann PB7 = 0.
Naja, logischer wäre andersrum. Wenn PB7 der Backplane-Anschluss ist,
dann müssen die Segmentausgänge in Abhängigkeit des Zustandes von PB7
gesetzt werden, allerdings eben nur dann, wenn ein neuer Wert angezeigt
werden soll. Ansonsten reicht es, den kompletten Port mit jedem
Compare-Interrupt zu invertieren, und zwar zunächst unabhängig vom
Zustand irgendwelcher Variablen. Ich denke, die sinnvollste Lösung wäre,
das Bitmuster für den neuen Wert in einer globalen Variablen abzulegen
und diese nach dem Invertieren des Ports in Abhängigkeit vom Zustand des
Backplane-Anschlusses in das Portregister zu schreiben, z.B.:
1 | volatile unsigned char bitmuster; |
2 | |
3 | ISR(TIMER2_COMP_vect) // Interruptroutine |
4 | {
|
5 | PORTB ^= 0xFF; |
6 | if(PORTB & (1 << PB7)) //PB7 == 1? |
7 | PORTB = ~bitmuster | 0x80; //inv. Wert übernehmen mit PB7 = 1 |
8 | else
|
9 | PORTB = bitmuster & 0x7F; //Wert übernehmen mit PB7 = 0 |
10 | }
|
"bitmuster" wird dann jeweils im Hauptprogramm mit dem jeweils neuen Wert beschrieben. Wichtig ist, dass das Invertieren des Ports direkt am Anfang der ISR gemacht wird, da es nur in diesem Fall gewährleistet ist, dass die Verzögerung zwischen Auftreten des Compare-Ereignisses und der Ausführung der Anweisung immer gleich ist und das Signal auch schön symmetrisch bleibt. Ich verstehe nicht ganz den Sinn der Abfrage "if(x == 0)" in der ISR...
Hmm danke für das Code-Beispiel. Zum "if(x==0)" ist folgendes zu sagen. Das spätere Programm zählt entweder eine Variable (x) hoch oder runter, je nach dem welcher Taster am eingang gedrückt ist. wenn x=2 ist, dann soll eben am LCD auch 2 angezeigt werden. Ich denke ich hätte schon vorher schreiben sollen um was es geht. Ich versuche eine Ganganzeige für ein Motorrad zu bauen. das wollte ich einfach der interesse wegen mit einem AVR machen, außerdem ist es auch nicht so einfach da ein motorrad nicht normal hoch und runter zählt. Der erste gang wird eingelegt, beim runterschalten, der 2te gang wird eingelegt durch hochschalten, der 3te auch usw. Beim Signaleingang der Neutralleuchte wird dann X auf 0 gesetzt. also Neutralgang. Das ist das eigentliche Projekt. Die auswertung ob grad hoch-bzw. runtergeschaltet wurde und die Zählung in der Variablen X ( x = eingelegter gang ) ists schon fertig. Da das programm etwas größer ist habe ich es einfach mal nun weggelassen. Wenn ich das mit der LCD ansteuerung hinbekommen habe, sollte ich es schaffen das mit einzubinden. So nun wisst ihr besser um was es geht. hoffe das macht die Sache klarer. Hätte ich vielleicht gleich sagen sollen, sorry mein Fehler.
Die Auswertung des Ganges brauchst du ja nicht in der ISR machen. Das kannst du in der Main machen und das entsprechende Bitmuster in einer Variablen speichern, die dann von der ISR ausgewertet wird, so wie JoHnny es vorgeschlagen hat. ISR sollten so kurz wie möglich sein...
Genau, hab ich ja gesagt. Ich habe das Programm der Gangauswertung ja schon fertig erstellt. @ jhonny.m : Ich habe es nun mal wie in deinem beispiel versucht, musste dann aber feststellen, das der Port länger ein als ausgeschlatet ist. ich habe die 50% nicht mehr. PB7 an, PB0-6 aus (ca.1sek) PB7aus und PB0-6 an (ca.100ms) mal grob geschätzt. die zeiten sind sowieso noch langsamer ums in der I/O View anschauen zu können. Aber ich brauche doch das 50% verhältnis AN/AUS.
Wenn Du alles korrekt konfiguriert hast, dürfte das eigentlich nicht passieren. Gerade bei den langen Zeiten, die Du momentan hast, sollte es da absolut keinen Unterschied geben. Du hast ja keine anderen Interrupt-Quellen aktiv.
Juhu, habs nun endlich hinbekommen. Werde mich dann heute abend mal dran machen, das alles in mein eingentliches Programm einzubinden. Das wird nochmal viel arbeit. Und dann hoffe ich das das alles so klappt wie ich mir das vorstelle. :) Ich werde dann berichten wie es gelaufen ist. - Kleine Hardwarefrage hier mal eingeschoben : jeden Segmentanschluss des LCDS, sollte ich noch mit einem (10k) Widerstand gegen masse (pulldown) anschließen, oder ?
>jeden Segmentanschluss des LCDS, sollte ich noch mit einem (10k) >Widerstand gegen masse (pulldown) anschließen, oder ? Warum? AVR besitzen eine push-pull-Endstufe.
Stimmt. Soooo nun hab ich da noch 1 Problem: nach dem ich nun das das Programm von meinem Laptop auf meinen PC gespielt habe, da ich dort weiterarbeiten wollte, musste ich nun feststellen, das er mir plötzlich einfach nicht mehr in die ISR laufen will. Verstehe nicht warum, da ich ja am eigentlichen Timer/Counter 2 etc. nichts verändert habe. Anbei das C File.
Folgende Warnings werden ausgegeben: "return type defaults to ´int'" und In function ´ISR': warning: control reaches end of non-void function
Du hast vermutlich auf Deinem PC eine alte Version von WINAVR bzw. der AVR-libc drauf, die ISR noch nicht kennt. Ein Update sollte das Problem lösen.
>Du hast vermutlich auf Deinem PC eine alte Version von WINAVR bzw. der >AVR-libc drauf, die ISR noch nicht kennt. Ein Update sollte das Problem >lösen. Wäre auch meine Vermutung (Voodoo-Johnny...;-)
Vielleicht solltest Du auch mal die Initialisierung der Interrupts aus der while-Schleife rausnehmen. Das gehört vor die Schleife...
allerdings. anlaufen müsste er sie dennoch. mir ist das hier
aufgefallen:
> PINC & (1<<PINC1) && (GANG>1) && (LGANGR==0)
funktioniert evtl, aber mach um deine bitweise verODERung
sicherheitshalber noch klammern. es dient auch der übersichtlichkeit.
am anfang hattest du eine umsetztabelle, die war schon wesentlich
eleganter als deine jetzige lösung. aber wenn du daran festhalten
willst, dann bediene dich einer if|else if struktur:
if (GANG==0)
bitmuster = 0b10111111;
else if (GANG==1)
bitmuster = 0b10000110;
[...]
else if (GANG==5)
bitmuster = 0b11101101;
deine datentypen können auch kleiner gemacht werden:
unsigned char GANG=0; // maximal 5
unsigned char LGANGH=0; // bool?!
unsigned char LGANGR=0; // bool?!
...
pumpkin
Hallo, @Jhonny.m : Gibt es eigentlich etwas das du nicht weißt ? Ein Update hatte mein Problem behoben. Das Programm funktioniert jetzt soweit mal am Simulator, werde es später an der hardware testen. @pumpkin, okay klar man kann bestimmt noch viel optimieren, da für lass ich mir dann Zeit wenns soweit mal läuft :) Danke schonmal an alle die mir hier bei meinen Anfängerproblemen geholfen haben. special thanks to Jhonny.m der fast nie länger als 10 min. für ne Antwort braucht ! Klasse g
> Gibt es eigentlich etwas das du nicht weißt ?
Jau, ne ganze Menge sogar. Aber es gibt eben ein paar Fehlerchen, die
man mit ein bisschen Erfahrung sofort zuordnen kann.
Und Deine Warnmeldungen oben besagen eigentlich nur, dass der Compiler
(da er das ISR-Makro aufgrund der veralteten lib nicht kennen konnte)
erstens gemerkt hat, dass Du anscheinend versuchst, eine Funktion zu
deklarieren (ISR()), ohne einen Typ für den Rückgabewert anzugeben und
zweitens diese Funktion, deren Rückgabewert er mangels Angaben zu "int"
"gedefaultet" hat, ohne "return" endet ("...reaching end of non-void
function"). Der Compiler erwartet eben, dass eine Funktionsdeklaration
bzw. -Definition der Form
<Typ_Rückgabewert> Funktionsname (<Typ_Parameter> Parametername, ...)
{
return <Rückgabewert>;
}
gehorcht. Da das auf Deine ISR eben nicht zutrifft (erwartet wird
mindestens etwas wie "int ISR()"), ist der Compiler vom Standard
ausgegangen und hat versucht, Dich durch Warnmeldungen darauf
hinzuweisen, dass da zwar Angaben fehlen, deren Abwesenheit aber nach
seiner Ansicht zunächst nicht fatal ist.
Das zeigt wieder einmal, wie wichtig es sein kann, Warnmeldungen nicht
einfach zu ignorieren, da auch sie durchaus mal auf ernstzunehmende
Fehler hinweisen, die nur eben aus Compiler-Sicht keine "echten" Fehler
sind und die der Compiler mit Standard-Annahmen erschlagen kann, was
aber eben dazu führen kann, dass das Programm nicht funktioniert.
Und die Tatsache, dass ich meist recht zügig antworten konnte, ist
zufallsbedingt... Wenn ich grad Zeit hab und ein Thread mein Interesse
erregt und ich was Fundiertes dazu zum besten geben kann, dann schreib
ich halt...
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.