Hallo Zusammen!
Ich habe folgendes Problem: Ein ATMega48 (TQFP) soll als Rückwärtszähler
(300-000) dienen. Hierfür sind drei 7-Segment-Anzeigen (gemeinsame
Kathode) direkt an den ATMega angeschlossen.
Soweit, sogut, aber die drei Ports C, B und D bedienen nicht jeweils ein
Display, wie es am einfachsten ist, sonder gemischt:
Hunderter Ports
0 - - C3,C4,C5,D3,D2,D1
1 - - C4,C5
2 - - C3,C4,D3,D2,D0
3 - - C3,C4,C5,D3,D0
Zehner
- 0 - B4,B5,B7,B6,C0,C1
- 1 - B5,B7
- 2 - B4,B5,B6,C0,C2
- 3 - B4,B5,B7,B6,C2
- 4 - B5,B7,C1,C2
- 5 - B4,B7,B6,C1,C2
- 6 - B4,B7,B6,C0,C1,C2
- 7 - B4,B5,B7
- 8 - B4,B5,B7,B6,C0,C1,C2
- 9 - B4,B5,B7,B6,C1,C2
Einer
- - 0 D7,D6,D5,B0,B1,B2
- - 1 D6,D5
- - 2 D7,D6,B0,B1,B3
- - 3 D7,D6,D5,B0,B3
- - 4 D6,D5,B2,B3
- - 5 D7,D5,B0,B2,B3
- - 6 D7,D5,B0,B1,B2,B3
- - 7 D7,D6,D5
- - 8 D7,D6,D5,B0,B1,B2,B3
- - 9 D7,D6,D5,B0,B2,B3
Am Port D4 befindet sich ein Taster, dessen Betätigungen rückwärts
gezählt werden sollen und am Port C6 ist der Reset-Taster angeschlossen.
Wie lässt sich ein Array (für Bascom) für die drei Anzeigen aufstellen?
Beim Datenblatt des ATMega blicke ich nicht ganz durch.
>Wie lässt sich ein Array (für Bascom) für die drei Anzeigen aufstellen?
Für jede Anzeige ein Array mit drei Spalten für die Ports.
Darin werden werden dann die Bits entsprechend des anzuzeigenden Musters
gesetzt.
Am Ende der Zählroutine schreibt man die drei Bytes für jeden Port
verodert an den Port.
Das sind bereits die Ports des ATMega48, um die LEDs der Segmente
richtig anzusteuern.
In der a-bis-g-Notation der Segmente wäre dies:
C3,C4,C5,D3,D2,D1,D0 = a3,b3,c3,d3,e3,f3,g3
B4,B5,B7,B6,C0,C1,C2 = a2,b2,c2,d2,e2,f2,g2
D7,D6,D5,B0,B1,B2,B3 = a1,b1,c1,d1,e1,f1,g1
Alle Segmente werden direkt angesteuert, kein Multiplexbetrieb (was
durchaus einfacher wäre).
>Alle Segmente werden direkt angesteuert, kein Multiplexbetrieb (was>durchaus einfacher wäre).
Da wirst du dann trotzdem noch ein paar Treiber brauchen.
STK500-Besitzer schrieb:>>Wie lässt sich ein Array (für Bascom) für die drei Anzeigen aufstellen?>> Für jede Anzeige ein Array mit drei Spalten für die Ports.> Darin werden werden dann die Bits entsprechend des anzuzeigenden Musters> gesetzt.> Am Ende der Zählroutine schreibt man die drei Bytes für jeden Port> verodert an den Port.
Und nicht vergessen: vorher mit einem UND alle Portbits für eine Stelle
gezielt auf 0 setzen.
Axel L. schrieb:> Wie lässt sich ein Array (für Bascom) für die drei Anzeigen aufstellen?> Beim Datenblatt des ATMega blicke ich nicht ganz durch.
Das ist einer der wenigen Fälle, wo dir auch das Datenblatt tatsächlich
nicht weiter hilft. In deinem Fall geht es einzig und alleine um eine
geschickte Methode wie man aus der Kentnis einer Zahl ableitet, welche
Bits an welchem Port durch eine geschickte Systematik gezielt mit einem
ODER auf 1 zu setzen sind.
Das ist reines Programmierhandwerk und hat mit dem Mega erst mal gar
nichts zu tun.
Sieh mal hier:
Beitrag "Standard LCD mit Controller steuern"
da hab ich LCDs an einen Controller angeschlossen. Die Segmente sind
kunterbunt an den Ports verteilt. Der Code ist in C. Denke das ist so
wie gefordert.
Ob es eleganter geht weiß ich nicht, es funktioniert aber so...
Gruß
Thomas
Thomas schrieb:> Sieh mal hier:> Beitrag "Standard LCD mit Controller steuern">> da hab ich LCDs an einen Controller angeschlossen. Die Segmente sind> kunterbunt an den Ports verteilt. Der Code ist in C. Denke das ist so> wie gefordert.
Mist, in C kenne ich mich nicht aus. Programmiere halt sehr selten.
Die einzelnen Ports kann ich ohne Probleme ansprechen. Zumindest läuft
der µC.
Mir scheint, eine gemischte Portverteilung kommt selten vor. ;)
Axel L. schrieb:> Mir scheint, eine gemischte Portverteilung kommt selten vor. ;)
Nein, gerade beim ATmega48/88/168 passiert mir das auch häufig.
Ich kenne mich nicht mit Bascom aus - die Aufgabe ist aber so trivial,
daß dieses Problem auch für Anfänger recht fix in Assembler zu lösen
ist.
1. Du hast einen Zähler von 300 auf 0
In Bascom musst Du Dich vermutlich nicht darum kümmern, aber ich würde
hier jeder Dezimalstelle ein Byte geben und gesondert abziehen, sobald
ein Übertrag der vorherigen Stelle kommt. Die Stelle, die den Übertrag
erzeugt hat, wird auf 9 gesetzt. Ansonsten musst Du die 3 Dezimalstellen
aus der fertigen Zahl erzeugen.
Damit hast Du 3 Bytes: Hunderter, Zehner und Einser
2. Du benötigst eine Tabelle mit 3*10 Einträgen a 3 Byte
Die drei Byte repräsentieren dabei die 3 Ports (die kompletten).
Für Hunderter, Zehner und Einser werden nun 3 Umsetzungen mit dieser
Tabelle gemacht und die 3 3-Byte-Ergebnisse verODERt.
Das Ergebnis wird auf die 3 Ports geschrieben.
Wenn Du den vollen Strom pro Port nutzen möchtest, kannst Du die
einzelnen Dezimalstellen auch einschalten, einen Moment warten und dann
wieder ausschalten. (Dann die nächste Stelle usw.)
Im Prinzip ein Multiplexing.
Der mittlere Strom wird sich aber dadurch auch verringern.
Benötigst Du mehr Strom, benötigst Du Treiber.
Gruß
Jobst
>Und nicht vergessen: vorher mit einem UND alle Portbits für eine Stelle>gezielt auf 0 setzen.
Nö:
PORTA = A0 OR A1 OR A2
(Falls das in etwa BASCOM ist...)
STK500-Besitzer schrieb:>>Und nicht vergessen: vorher mit einem UND alle Portbits für eine Stelle>>gezielt auf 0 setzen.>> Nö:>> PORTA = A0 OR A1 OR A2>> (Falls das in etwa BASCOM ist...)
Das geht aber nur dann, wenn an Port A nur die Segmente für zb die
Zehnerstellen wären. Sobald an einem Port die Segmente für 2 Stellen
liegen (und bei ihm sind sie das) hast du damit ein Problem, wenn du
jeweils nur 1 Stelle verändern willst.
Ausserdem wird man das sinnvollerweise nicht so machen, sonder für jede
Stelle ein Array mit allen Mustern für alle Ziffern haben
In C
>Das geht aber nur dann, wenn an Port A nur die Segmente für zb die>Zehnerstellen wären. Sobald an einem Port die Segmente für 2 Stellen>liegen (und bei ihm sind sie das) hast du damit ein Problem.
Nee, erst sortiert man nach Digits und dann nach Ports:
z.B. Anzeige von 304
PortA = A0[3] OR A1[0] OR A2[4]
PortB = B0[3] OR B1[0] OR B2[4]
PortC = C0[3] OR C1[0] OR C2[4]
Oder sehe ich da was falsch?
In Bascom kann man das wahrscheinlich sowieso nicht so machen, das
Bascom ja nur paarweise Operationen zulässt, oder?
STK500-Besitzer schrieb:> Nee, erst sortiert man nach Digits und dann nach Ports:>> z.B. Anzeige von 304>> PortA = A0[3] OR A1[0] OR A2[4]> PortB = B0[3] OR B1[0] OR B2[4]> PortC = C0[3] OR C1[0] OR C2[4]
Ah, so meinst du das. Alle 3 Stellen in einem Rutsch zusammengeodert
ausgeben.
OK. Auf den einen Port Pin mit dem Taster müsste man aufpassen, aber
auch das ist natürlich machbar.
> In Bascom kann man das wahrscheinlich sowieso nicht so machen, das> Bascom ja nur paarweise Operationen zulässt, oder?
Meines Wissens: ja.
Aber da kenn ich mich in BASCOM auch zu wenig aus
Aber wahrscheinlich wird es wieder mal an der Erkentnis scheitern, dass
es auch in BASCOM möglich ist, einen Port (wie jedes andere
Prozessorregister auch) als ganzes mit einem Bitmuster zu beschreiben
und man sich eben das Bitmuster im Vorfeld zurecht legen muss.
Ich habe mal den C-Code genommen und ein paar Änderungen vorgenommen, da
wohl einige Fehler vorlagen. Eine Ausgabe findet statt, nur mit dem
Runterzählen von 300 bis 000 klappt es nicht so. Der Zähler startet bei
300, geht auf 807 und dann werden sinnlose Zeichen auf dem letzten
Segment ausgegeben.
Ja, Bascom kann immer nur eine Operation in einem Schritt ... leider.
Gibt dann zum Teil ziemliche Monster an Formeln bei komplexeren
Berechnungen ... was solls.
Also, ich machs bei ner 16-Segment Anzeige so, dass ich nen Zeichensatz
als Bytes liegen habe, wo die einzelnen Segmente schon fertig
dekodiert sind.
Hab da mit ner Excel-Tabelle gearbeitet um die Bitwerte zu ermitteln
für jedes Zeichen. In meinem Fall waren es 2 Ports, daher 2 Bytes,
wenn er 3 Ports verwendet muss er also bei der Methode 3 Bytes
schreiben.
Dann wirds schon kompliziert ... wenn auf den Ports noch andere Aktionen
drauf sind, die nicht über ein Specieal-Functions-Register vor Zugriff
durch den Befehl PORTx=y_byte geschützt sind muss er bevor er die
Segmente setzt den jeweils benötigten Status des jeweiligen Ports per
AND und OR
in das Byte erst setzen.
Bit sicher löschen: Byte_x=Byte_x AND &B11101111
Bit unabhängig setzen: Byte_x=Byte_x OR &B00010000
Oder der Zustand wird halt statisch in sein Zeichenarray geschrieben, so
er
sich nie ändert ...
Beim Multiplexing wirds dann nochmal schön.
Hab das so gelöst, dass ich praktisch nen Framebuffer angelegt habe, n
Array mit genau sovielen Bytes wie eben dargestellt werden müssen.
dim anzeigearray_1(5) as byte
dim anzeigearray_2(5) as byte
in der Timer ISR geh ich dann nur noch hin und schreibe zyklisch die
enthaltenen Bytes auf die Ports und setze den Select auf das nächste
Zeichen bzw. aktiviere die jeweilige Anzeigeeinheit. Bei Überlauf zurück
zum Anfang versteht sich.
>uint8_t e, z, h;> for( h = 3; h > -1; --h ) {
Wie soll eine unsigned int einen Wert von -1 erreichen?
Der andere Kram ist aus meiner Sicht auf den ersten Blick völlig
umständlich "programmiert".
Sollte so funktionieren. Gewähr für einwandfreie Funktion übernehme ich
aber nicht.
Und die Delay-Funktion solltest du dir noch mal angucken.
Die kann man "fertig kaufen".
Wenn ich nur die Einer durchlaufen lassen, erhalte ich auf der
Einer-Anzeige erstmal die Folge: 9-8-7-6-5-4-3-2-1-0
Nach einer kleinen Gedankenpause leuchten alle Anzeige mit kryptischen
Symbolen auf und wieder läuft der Einer ebenfalls mit kryptischen
Symbolen durch.
Ich habe so das Gefühl, dass die for-Schleife nicht endet. Wieso??
Lebt mein µC???
Axel L. schrieb:> uint8_t e, z, h;>> for( e = 9; e >= 0; e-- ) {
e ist ein unsigned.
Für jede Zahl, die in e überhaupt speicherbar ist, gilt: sie ist
größer oder gleich 0.
Per Definition!
wenn du daher den Schleifenabbruch daran knüpfst, dass e nicht mehr
größer oder gleich 0 sein darf, dann hast du mit Zitronen gehandelt. Der
Fall wird in der Lebenszeit dieses Universums nicht mehr eintreten.
Ok, Counter läuft nun von 300 runter auf 000.
Am Port D4 befindet sich ein Optokoppler (MOC3020) mit einem externen
10k Pull-Up-Widerstand. Der Counter soll bei jedem Schaltvorgang über
den Optokoppler sich nur um eins ändern.
Für den Test habe ich folgendes Programm:
1
#include<avr/io.h>
2
3
voiddelay_ms(intms)
4
{
5
TCCR0B|=(1<<CS01)|(1<<CS00);
6
inti;
7
for(i=0;i<ms;i++){
8
9
while(TCNT0<16);
10
TCNT0=0;
11
}
12
TCCR0B=0;
13
}
14
15
intmain()
16
17
{
18
19
DDRD=0b11101111;
20
DDRC=0b00111111;
21
DDRB=0b11111111;
22
23
while(1){
24
25
delay_ms(250);
26
27
if(PIND&(1<<PIND4)){
28
29
// Anzeigewert zum Beginn: 300
30
PORTD=0b11101001;
31
PORTC=0b00111011;
32
PORTB=0b11110111;
33
34
}
35
36
if(!(PIND&(1<<PIND4))){
37
38
// Anzeigewert zum Ende: 003
39
PORTD=0b11101110;
40
PORTC=0b00111011;
41
PORTB=0b11111001;
42
43
}
44
}
45
}
Wenn Strom auf dem µC liegt wird 300 angezeigt. Sobald der Optokoppler
ein Impuls bekommt springt die Anzeige auf 003, aber nicht nach 250ms
wieder zurück auf 300. Auch bei einem Reset an PD6 wird weiterhin 003
angezeigt.
Was läuft da schief? Irgendwie wird PIND4 nicht mehr 0 zurückgesetzt.
... schrieb:> Da ist ein Triac im Optokoppler!> Dem wirst Du wohl erstmal den Saft klauen müssen, damit der wieder> sperrt.
Na toll. Wenn man nicht immer aufpasst.
Und wie geht so etwas elegant, oder soll ich direkt auf einen 4N35
ausweichen?
So, habe den Triac gegen den 4N35 getauscht und der Counter mit drei
7-Segmentanzeigen lüppt. :)
Vielen Dank an alle Tipp-Geber und Helfer.
Hier der C-Code: