Wie der Titel bereits aussagt, ersuche ich bei der Gemeinde wieder um
Rat ;)
Aus Platzgründen kann ich mit meinem ATmega8 keinen Multiplex Betrieb
auf gleichem Port machen, so dass ein Teil auf PortD und ein Teil auf
PortC zu laufen hat. Es ist eine 4-Stellen Anzeige.
Nun komme ich nicht drauf, wie ich das elegant mit einem Array machen
kann, wie diesem hier,
const int8_t numbers[10] =
{
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
};
weil die Bits zwischendurch besetzt sind. Kann mir jemand einen Wink
geben - vllt. eine Bitmaske o.ä?
danke schonmal,
Note
Note schrieb:> weil die Bits zwischendurch besetzt sind. Kann mir jemand einen Wink> geben - vllt. eine Bitmaske o.ä?
Erstmal brauchst du 2 Arrays, eines für die C- das andere für die
D-Daten.
Oder ein zweidimesionales. Was eigentlich dasselbe ist.
Dann brauchst du 2 Bitmasken. Auch wieder für jeden Port eine.
Beispiel:
Pins 1, 4, 5 an Port C
Bitmaske:
unsigned char MaskC = (1 << 1) | (1 << 4) | (1 << 5);
PORTC &= ~(MaskC); //Port C auf 0
PORTC |= Array[0][7] //Ausgabe des Port-C-Anteils für Ziffer 7
usw...
mfg.
Note schrieb:> Aus Platzgründen kann ich mit meinem ATmega8 keinen Multiplex Betrieb> auf gleichem Port machen, so dass ein Teil auf PortD und ein Teil auf> PortC zu laufen hat. Es ist eine 4-Stellen Anzeige.>> Nun komme ich nicht drauf, wie ich das elegant mit einem Array machen> kann,
Das hängt vor allen Dingen davon ab, wie du dann tatsächlich die Bits
auf die Ports verteilt hast. Je nachdem kann man die allgemeine
aufwändigere Lösung auch durch einfachere, codesparendere und schnellere
Versionen ersetzen.
Gut, genau - vier Zahlen (jeweils 7 Anoden + Anode für die vier
Dezimalpunkte) haben nicht genug Platz, um auf dem PortD 0..7 zu sitzen.
Es muss also ein Multiplex auf gemischte Ports sein.
Genauer gesagt: auf PortD 0-3, 5-7. Der Rest auf PortB.
Note schrieb:> Gut, genau - vier Zahlen (jeweils 7 Anoden + Anode für die vier> Dezimalpunkte) haben nicht genug Platz, um auf dem PortD 0..7 zu sitzen.> Es muss also ein Multiplex auf gemischte Ports sein.> Genauer gesagt: auf PortD 0-3, 5-7. Der Rest auf PortB.
Wie wäre es denn, wenn du ein klipp und klar sagst, welches Segment wo
angeschlossen ist und wie die Segmente gemultiplext werden.
Oder noch einfacher:
Ein Schaltplan
Note schrieb:> Gut, genau - vier Zahlen (jeweils 7 Anoden + Anode für die vier> Dezimalpunkte) haben nicht genug Platz, um auf dem PortD 0..7 zu sitzen.> Es muss also ein Multiplex auf gemischte Ports sein.> Genauer gesagt: auf PortD 0-3, 5-7. Der Rest auf PortB.
Das klingt jetzt für mich aber eigentlich nach dem Trivialfall:
Die 7 Anoden sind an jeweils einem Port
die 4 Kathoden sind an jeweils einem Port
wo also ist da jetzt das Problem?
Im Generellen:
Beschreibe nicht was du gemacht hast, sondern zeige es!
Und die beste Art eine Schaltung zu zeigen ist es, einen Schaltplan zu
zeigen. Der muss nicht computergeneriert sein. Wenn du ein wenig
handwerkliches Geschick hast und übersichtlich zeichnen kannst, dann
kann das durchaus auch eine Bleistiftzeichnung auf Papier sein, die man
dann schnell abphotographiert oder auf den Scanner legt.
Dies hier wäre die einfachste Variante - fester Dezimalpunkt und nur
eine Portaussparung für den externen Interrupt. Evtl. wird sich dieser
freie Port auch auf den Timer-Ausgang verlegen, weil ich noch nicht
genau die Sache mti den externen Interrupts durchgeblickt habe.
Tut mir Leid für die Ungenauigkeiten ;)
mfg Note
Note schrieb:> Der, für mich persönlich schwierigere, Fall wäre ein Gleitkomma.
Wo ist da jetzt das Problem?
Du hast einen Pin am Port D frei und du musst an jeder Anzeige alle DP
Anschlüsse parallel schalten. Genauso wie du es mit den Anzeigen auch
gemacht hast. Das ist ziemlich naheliegend, wie das jetzt sowoahl
hardwaretechnisch als auch softwaretechnisch zu lösen ist.
Es gibt da überhaupt kein Problem. Der Dezimalpunkt wird genau gleich
behandelt wie die einzelnen Stellen der Anzeigen auch. Lediglich in der
Zusammenstellung der einzelen, im Multiplex-Interrupt auszugebenden
Bytes gibt es einen kleine Sonderbehandlung für den Punkt. Aber die
eigentliche Multiplex-Routine tangiert das nicht weiter. Für die sind
das sowieso keine Zeichen oder Ziffern oder Dezimalpunkte, sondern aus
deren Sicht sind das einfach nur 8 LED an einem Port und sie hat 4 Bytes
im Speicher bereitgestellt, die ihr sagen welche LED einer jeden Stelle
leuchten soll und welche nicht.
Note schrieb:> weil ich noch nicht> genau die Sache mti den externen Interrupts durchgeblickt habe.
Wozu brauchst du externe Interrupts?
(Wenn du jetzt versucht bist zu sagen "für Taster", dann lautet meine
nächste Frage: müssen die Taster den µC aus dem Sleep herausholen oder
warum brauchst du für Taster externe Interrupts. Ausser für den Sleep
Fall braucht kein Mensch für Taster einen externen Interrupt)
Ein Fließkomma ist erst in Planung - geht ersteinmal um das Prinzip, wie
man nicht jeden Pin des Ports benutzt.
Beispiel:
Ich kann ja nicht (um den INT0/1 Pin zu überspringen) einfach Folgendes
in das Array schreiben:
0b011011x01
So, dass der praktisch übsprungen wird bei der Behandlung. Hoffe
bisschen verständlich rüberzukommen :D
@Karl Heinz Buchegger: den Interrupt brauche ich für ein externes
TTL-Signal. Ich taste mich aber erstmal langsam programmiertechnisch ran
- das aktuelle Problem ist eben diese Portreihenfolge und wie ich diesen
einen Pin überspringe.
Note schrieb:> Ein Fließkomma ist erst in Planung - geht ersteinmal um das Prinzip, wie> man nicht jeden Pin des Ports benutzt.> Beispiel:>> Ich kann ja nicht (um den INT0/1 Pin zu überspringen) einfach Folgendes> in das Array schreiben:>> 0b011011x01>> So, dass der praktisch übsprungen wird bei der Behandlung. Hoffe> bisschen verständlich rüberzukommen :D
Na ja.
Dann muss man eben dafür sorgen, dass sich dieser Pin eben nicht bei der
Ausgabe verändert.
Dazu liest man den Inhalt des Portregisters ein
act = PORTD;
maskiert sich mit einem & die restlichen Bits auf 0
act = act & 0b00001000;
und odert dann das auszugebende Byte, dessen Bits schon entsprechend
zurechtgeschoben wurden und welches an der Position 3 ein 0 Bit aufweist
drüber
act |= auszugebendes_Byte;
und gibt wieder alles auf den PORT
PORTD = act;
in a Nutshell: sorge einfach dafür, dass sich der Bitwert dieses Bits
während der Verarbeitung nicht ändert und alles ist gut. Gibt man ein
Byte aus, das an der bewussten Stelle einen Bitzustand hat, der sowieso
schon am Port anliegt, dann ändert sich trotz Ausgabe am externen
Zustand ja nichts.
Das ist jetzt in diesem konkreten Verdrahtungsfall die einfachste
Lösung. In anderen Fällen gibt es wieder andere 'einfachste' Lösungen.
Da Multiplexing sowieso sinnvollerweise in der ISR eines Timers
abgehandelt wird, hat man auch keine Probleme damit, dass sich
zwischendurch diese Bitwert irgend wo anders ändern könnte.
Perfekt, hat funktioniert, dankesehr Karl Heinz Buchegger :)
Zumindest bleibt der Pegel auf dem PD3 auf Null. Danke dir für die
Geduld - mir fehlte lediglich diese Bitarithmetik...
Da stolpert man über solche Kleinigkeiten :)
@Thomas Eckmann: super Einfall mit dem mehrdimensionalen Array, das kann
ich ideal für ein anderes Projekt nutzen.
Jetzt geht es Richtung Gleitkomma und Timings von externen Interrupts..
Ich bedanke mich noch einmal bei euch.
MfG Note
@ Note (Gast)
> Unbenannt.JPG> 40,3 KB, 6 Downloads
Schon mal über Netiquette nachgedacht und versucht, einen Quellcode
als .c Datei zu posten?
>Jetzt geht es Richtung Gleitkomma und Timings von externen Interrupts..
Wahrscheinlich reicht für deine Anwendung Fetkommaarithmetik.
aktuell=PORTD;// aktueller Portzustand in Variable gespeichert
4
aktuell&=bitmaske;// nur der aktuelle Zustand des Pins 3 wird erhalten
5
6
zwischenspeicher=numbers[wert];// Aktueller numbers[wert] wird gepuffert
7
zwischenspeicher|=aktuell;// numbers[wert] mit dem aktuellen Zustand des Pin 3 verglichen
8
9
PORTD=zwischenspeicher;// Gibt alles auf den Port
10
11
PORTC|=(1<<pin);// entsprechendes Digit an
12
}
Jop, von Netiquette schon mal gehört ;)
Um genau zu sein, ist die Rede von längere Codes - aber posten kann ich
ihn auch nochmal.
Ersteinmal will ich schauen, ob ich für die Anzahl der externen Impulse
den T0 Eingang brauche oder den INT0/1 - zum Komma bisschen später.
Danke für den Tipp
Nach Codezeilen bezahlt... Ein Traum für Anfänger ;)
Hast natürlich Recht - sowas sieht man als Anfänger erst nach Paar
Hundert Programmierzeilen, sprich: mit Erfahrung. Danke für den Hinweis.
Ich verstehe aktuell die Nuance nicht, die INT0/1 und T0/1
unterschiedlich macht. Ich kann eigentlich genauso gut mein externes
Signal vom T0/1 zählen lassen.
Was wäre dann der Zwek der INT0/1?
mfg Note
Note schrieb:> Platine.jpg
Eigentlich würde man das als einen Schaltplan bezeichnen. Und
Schaltpläne sind Linienzeichnungen. Und Bildkompressionsalgorithmen für
Photos tun sich i.A. schwer mit Linien. Und JPEG ist ein
verlustbehafteter Kompressionsalgorithmus für Photos. Und das ergibt
häßliche Kompressionsartefakt um Linien und insbesondere Texte. Und das
würde nicht passieren, wenn man sich an die Bildformate halten würde
und für Linienzeichnungen z.B. PNG verwendet.
@O.Hmann: Du hast formell natürlich Recht, doch wie ich finde, könntest
du dann auch gleich mit einer kurzen Erklärung des eigentlichen Problems
helfen.
Und hier ist auch meine Berichtigung ;)
Note schrieb:> Ich verstehe aktuell die Nuance nicht, die INT0/1 und T0/1> unterschiedlich macht. Ich kann eigentlich genauso gut mein externes> Signal vom T0/1 zählen lassen.
Ja, wenn deine Aufgabe im weitesten Sinne ein Zählen ist.
> Was wäre dann der Zwek der INT0/1?
Für alles was nicht in einer Zählerei mündet. :-)
Wenn du zb. ganz kurze Impulse hast, die du nicht verpassen darfst und
das Auftreten eines derartigen Impulses einen Vorgang triggert. Auf die
Schnelle fällt mir zb ein: eine Gewehrkugel, die durch eine
Lichtschranke fliegt und dabei eine Stoppuhr startet.
>aktuell=PORTD;// aktueller Portzustand in Variable gespeichert
4
>aktuell&=bitmaske;// nur der aktuelle Zustand des Pins 3 wird
5
>erhalten
6
>
7
>zwischenspeicher=numbers[wert];// Aktueller numbers[wert] wird
8
>gepuffert
9
>zwischenspeicher|=aktuell;// numbers[wert] mit dem aktuellen Zustand
10
>desPin3verglichen
11
>
12
>PORTD=zwischenspeicher;// Gibt alles auf den Port
13
>
14
>PORTC|=(1<<pin);// entsprechendes Digit an
15
>}
16
>
Hmm.
Das riecht allerdings nach einem Multiplex, so wie man ihn nicht macht.
Mach das erst mal vernünftig, ehe du dein Programm weiter ausbaust.
Um vernünftig zu multiplexen brauchst du einen Timer, der dir
regelmässige Timer Overflows auslöst.
In der zugehörigen ISR findet dann der Multiplex statt, der sich darauf
stützt, dass er 4 Bytes (sinnvollerweise als Array) global zur Verfügung
hat, in die ihm irgendwer die auszugebenden Bitmuster reingeschrieben
hat.
1
uint8_tdigitCode[]={1<<PC0,1<<PC1,1<<PC2,1<<PC3};
2
volatileuint8_tledPattern[4];
3
uint8_tnextLed;
4
5
ISR(....)
6
{
7
// erst mal alles aus, indem allen Anzeigen 'der Saft' abgedreht wird.
8
PORTC&=~((1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3));
9
10
// welches ist die nächste auszugebende Stelle?
11
nextLed++;
12
if(nextLed==4)
13
nextLed=0;
14
15
// das für diese Stelle vorgesehene Muster ausgeben
damit das läuft muss natürlich in main() noch ein Timer aktiviert werden
und ein entsprechender Interrupt angemeldet werden.
Soweit so gut. Die ISR schaufelt also die Bitmuster aus dem Array
ledPattern auf die Anzeige. Was auch immer da eingetragen ist. Und das
ständig, eine Stelle nach der anderen.
D.h. es muss noch eine Funktion geben, die genau das macht: in ledArray
die Bitmuster einzutragen, die angezeigt werden sollen. Und zwar nicht
irgendwelche Bitmuster, sondern die, die genau diejenigen LED
einschalten, die dann auf der Anzeige das Muster für die darzustellende
Zahl ergibt. Das ist aber weiter nicht schwer. Denn mit einer
fortgesetzten Division durch 10 bzw. Modulo Rechnung kann man eine Zahl
ganz leicht in die einzelnen Ziffern zerlegen:
willst du in main() eine Zahl ausgeben (zb. 1287), dann rufst du einfach
...
1
intmain()
2
{
3
....
4
5
while(1){
6
7
if(.....)
8
segNumber(1287);
9
10
....
auf und den Rest macht die FUnktion bzw. der Timer, so dass auf deiner
7-Segment Anzeige genau diese Ziffern aufscheinen. Und zwar solange, bis
du mittels segNumber eine andere Zahl anzeigen lässt.
Kannst ja zb mal probieren
1
intmain()
2
{
3
uint16_ti=0;
4
5
....
6
7
while(1){
8
segNumber(i);
9
i++;
10
delay_ms(100);
11
}
12
}
Auf deiner Anzeige sollten sich dadurch eine Zahl immer höher schrauben.
Und natürlich muss man da keine Zahlen ausgeben. Der ISR ist es ja egal,
was in den Bitmustern steht. Wenn du da von allen Anzeigen den jeweils
mittleren Balken eingeschaltet haben willst, dann brauchst du nur in
ledArray die entsprechenden Bytes reinschreiben und du kriegst
auf deiner Anzeige nur die mittleren Balken. Dieses Array IST in einem
gewissen Sinne für dein restliches Programm die 7-Segment Anzeige. Was
immer im Array eingetragen ist, diese LED werden an den Anzeigen
erleuchtet.
Karl Heinz Buchegger, ich muss mich für soviele Erklärungen bedanken,
wirklich gut erklärt. Zugegeben, ich hatte diesen Fehler auch am Anfang
gemacht und das Programm hat alle Resourcen des ATmega verschluckt.
Ich habe es aktuell ähnlich, wenn auch nicht ganz so elegant wie du
schilderst (s.Anhang)
Die Sache ist momentan - mit dem Moduloresttrick kann ich keine
Nachkommastellen erzeugen, also kommt noch eine Prozedur mit den
Dezimalpunkten hinzu. Aktuell arbeite ich auch an der Zahlenzerlegung
udn den Timermodi.
Eine Frage hätte ich auch gleich dazu: Da der Timer2 asynchrone
Operationen anbietet und "Allows Clocking from External 32kHz Watch
Crystal Independent of the I/O Clock" - könnte ich einen externen Quarz
(16MHz) für ihn verwenden?
Wie müsste ich das im programm kenntlich machen, dass der Takt nur für
den Timer gilt? (Meine Vermutung wäre einfach die FuseBits auf
ext.mid/high frequency +64ms)
mfg Note
P.S: Der Code enthätl noch paar unnötige Sachen wie die wait-Funktion,
nicht beachten.
Note schrieb:> Die Sache ist momentan - mit dem Moduloresttrick kann ich keine> Nachkommastellen erzeugen, also kommt noch eine Prozedur mit den> Dezimalpunkten hinzu.
Das kommt jetzt ganz darauf an, wie
* die Dezimalpunkte hardwaremässig angeschlossen sind.
* du Programmintern mit 'Fliesskommawerten' arbeitest.
Prinzipiell werden die Dezimalpunkte wie die anderen Segmente auch
verdrahtet. D.h. in deinem Programm hast du 4 mögliche Positionen für
den Dezimalpunkt, die du aus der ISR heraus bei Bedarf aktivierst. Genau
gleich wie bei den restlichen Segmenten.
Und ja. Klar geht die Modulo Sache nach wie vor genauso. Niemand hindert
dich daran, zb bei Zahlen kleiner als 10, die Zahl erst mal mit 1000 zu
multiplizeren, dieses Zwischen-Ergebnis als ganze Zahl auszugeben und
dir dafür für die ISR zu merken, dass der Dezimalpunkt in der 3. Stelle
einzuschalten ist. Aus Sicht der ISR ist der Punkt auch nur eine LED,
die bei der betreffenden Stelle entweder ein oder ausgeschaltet ist.
> Eine Frage hätte ich auch gleich dazu: Da der Timer2 asynchrone> Operationen anbietet und "Allows Clocking from External 32kHz Watch> Crystal Independent of the I/O Clock" - könnte ich einen externen Quarz> (16MHz) für ihn verwenden?
Was soll das für einen Sinn haben?
Nimm die 16Mhz als Taktversorgung für den ganzen µC. Dann hat auch der
Timer 2 entsprechend 16Mhz. Und alle anderen Timer ebenfalls.
Runterteilen kann man immer noch.
Note schrieb:> "Allows Clocking from External 32kHz Watch> Crystal Independent of the I/O Clock"
Das und nur das. Erspart einen separaten Uhrenchip. Und da auch nur,
wenn die Uhr im Batteriebetrieb im Sleep-Mode weiterlaufen soll.
>- könnte ich einen externen Quarz (16MHz) für ihn verwenden?
Nein. Damit läuft der Oszillator nicht.
Als Takt für Timer 2 kannst du genauso gut den CPU-Takt nehmen. Der
asynchrone Timer2-Takt bringt dir keine Vorteile.
mfg.
Also ein Denkfehler von mir.
Wenn ich einen 16MHz Quarz für den gesamten Chip an PB6/7 anschließe
samt der Keramikkondensatoren - würde es ausreichen, die FuseBits auf
externe Frequenz einzustellen oder wäre noch etwas zu beachten?
Die #define Prozedur oder Ähnliches?
Sobald der Quarz (mit 22pF Kondensatoren) an den Pins hängt UND der AVR
auf Verwenung des Quarzes umgefused wurde, läuft er auch mit 16Mhz.
Dass du dann natürlich in irgendwelchen Berechnungen mit den 16Mhz
rechnen musst, anstelle der Default 1Mhz ist auch klar.
Vielen Dank, dann werde ich das auch so anpassen...
Musste vorsichtshalber fragen, da es mit dem 16MHz Quarz auf meinem
RN-MiniControl nicht funktioniert hat.
Habe lediglich die Fusebits auf Ext.Res.High Frequency +64ms gesetzt -
und hopp! - war's schwarz ;)
Note schrieb:> würde es ausreichen, die FuseBits auf> externe Frequenz einzustellen
Nicht auf externe Frequenz, sondern auf (externen) Quarz mit internem
Quarzoszillator im Fullswing-Modus.
...
Das myAVR ProgTool bietet viele Optionen an :D
Und um nicht in Gefahr zu laufen wieder einen Chip zu vergeuden, frag'
ich lieber genau nach, welche Option die richtige wäre :)
Note schrieb:> frag' ich lieber genau nach,
Hmmm, wie wäre es denn mit logischem Denken?
Willste'n internen RC-Oszillator? Vermutlich nicht, also weiter.
Willste'n RC-Glied anschließen? - Sicher nicht, also fallen die raus.
Willste Low-Freq. oder Medium-Freq.? - Nee, Du willst Max-Freq. (16
MHz).
Also fallen wieder welche aus dem Fokus, bleibt nur noch Crystal
High-Freq..
Hast Du es beim Start besonders eilig? - Sicher nicht, somit genügt die
langsamste Startup-Sequenz, also die meisten Clocks und die meisten ms.
Wenn Du den Verfusten wieder retten willst, dann lege einen externen
Takt an seinen Clock-Eingang. Falls Du dazu Fragen hast, dann schau
bitte zuvor ins Datenblatt unter Clock-Sources, da gibt es für jede
Taktquelle eine Skizze und auch die entsprechende Fusebit-Einstellung.
Das ist zwar nicht alles mundgerecht direkt beieinander, aber doch noch
in der Nähe und auch recht verständlich formuliert.
...
Vielen Dank :)
Natürlich hat die CKOPT gefehlt, deshalb ging die Frequenz immer wieder
auf den Standartwert zurück.
Irgendwie schafft man es nicht, alles zu beachten, bei den ganzen Daten.
Ich bedanke mich recht herzlich und wünsche eine ruhige Nacht :)
mfg Note
Guten Abend die Gemeinde,
leider besteht das Problem mit dem Zurücksetzen der Frequenz auf
Standart 1MHz.
Es ist ein ATmega8 auf RN-MiniControl
Bedanke mich im Voraus
mfg Note
P.S: der gesamte Code ist nun fertig, jetzt Fehler ausbürsten und
Optmierungen vornehmen ;)