Hallo,
kleines Problem...
Bin gerade dabei meine Anzeigen zu testen.
Möchte gerne einen Counter darstellen.
Hochzählen klappt prima.
Aktuell zeigt er mir " 0 | 0 | 0 | Einer"
Also ich möchte die "0" en ausmaskieren solange der Wert noch nicht
ereicht ist (aus lassen).
Tue mir da gerade etwas schwer mit...
Was muss ich noch Einfügen?
1
/*
2
* _7_Segment_Counter.c
3
*
4
* Created: 30.09.2015 15:27:52
5
* Author:
6
*/
7
8
#define F_CPU 8000000
9
10
#include<avr/io.h>
11
#include<util/delay.h>
12
#include<avr/interrupt.h>
13
14
#define STATE_LED_RED_ON PORTA |= (1<<PA0)
15
#define STATE_LED_RED_TOGGLE PORTA ^= (1<<PA0)
16
#define STATE_LED_RED_OFF PORTA &= ~(1<<PA0)
17
18
#define STATE_LED_GREEN_ON PORTD |= (1<<PD2)
19
#define STATE_LED_GREEN_TOGGLE PORTD ^= (1<<PD2)
20
#define STATE_LED_GREEN_OFF PORTD &= ~(1<<PD2)
21
22
constcharnumbers[11]={
23
0b00111111,// 0
24
0b00000110,// 1
25
0b01011011,// 2
26
0b01001111,// 3
27
0b01100110,// 4
28
0b01101101,// 5
29
0b01111101,// 6
30
0b00000111,// 7
31
0b01111111,// 8
32
0b01101111,// 9
33
0b00000000// all off
34
};
35
36
volatileuint16_tVRAM=0x0000;
37
38
39
voidcount_up(void);
40
41
42
intmain(void)
43
{
44
45
/* data pins */
46
DDRB|=0xFF;
47
DDRD|=((1<<PD6)|(1<<PD5)|(1<<PD4)|(1<<PD3)|(1<<PD2));// data direction register for 7- segment display & transistors
48
DDRA|=(1<<PA0);// data direction register for state led red
49
50
PORTD|=(1<<PD2);// state led green on
51
PORTA|=(1<<PA0);// state led red on
52
53
/* Legt den VRAM ZUM Überlaufes fest ( ca. jede 1 Sec. ) */
Jan schrieb:> Hallo,>>> kleines Problem...> Bin gerade dabei meine Anzeigen zu testen.> Möchte gerne einen Counter darstellen.> Hochzählen klappt prima.>> Aktuell zeigt er mir " 0 | 0 | 0 | Einer"> Also ich möchte die "0" en ausmaskieren solange der Wert noch nicht> ereicht ist (aus lassen).>> Tue mir da gerade etwas schwer mit...> Was muss ich noch Einfügen?
Wie wärs mit einem if, das abfragt ob die Zahl zum Beispiel kleiner als
0x1000 ist. Denn in diesem Fall willst du ja nicht das Muster für die
'Tausender'-Stelle anzeigen (welches ja offensichtlich 0 wäre), sondern
das Muster für 'all off'.
Allerdings.
Ehe du dich da jetzt drann machst, wartet noch eine Menge andere Arbeit
auf dich (so schlimm ist es jetzt auch wieder nicht). Denn so wie das
jetzt abläuft ist das ganze Multiplexen praktisch nicht zu gebrauchen.
Ein vernünftiger Multiplex läuft genau anders herum ab. Mit dem Timer
steuerst du die Anzeige an und nicht dadurch, dass du in der
Hauptschleife da einen Haufen delays einbaust.
Und zum anderen gibt es auch keinen Grund, warum du da Hexadezimal
hochzählen sollst und da enorme Klimmzüge für den Zehner-Übertrag machen
musst. Du hast einen COmputer. Der kann rechnen! Für den ist es kein
Problem aus der Zahl 5638 herauszurechnen, wieviele Tausender da drinn
enthalten sind. Die DIvision mit Rest ist schon lange erfunden.
Ja das mit dem Multiplexen änder ich auch noch!
Ja das mit der if klappt nicht so wie ich mir das vorstelle.
if (VRAM < 0x0009)
{
VRAM &= 0xAAAF;
}
oder sehe ich das falsch? Das ist jetzt nur für die 1 er
Jan schrieb:> Ja das mit dem Multiplexen änder ich auch noch!>> Ja das mit der if klappt nicht so wie ich mir das vorstelle.>>> if (VRAM < 0x0009)> {> VRAM &= 0xAAAF;> }
Wieso kleiner 0x0009.
Deine Tasuenderstelle ist 0x1000
Und du willst da auch nicht VRAM ändern. in VRAM zählst du ja. Du willst
etwas anderes anzeigen, wenn da eigentlich eine 0 stehen sollte.
> oder sehe ich das falsch? Das ist jetzt nur für die 1 er
Bei den Einern wird aber eine 0 nicht unterdrückt.
Es geht nicht nur um die Tausender.
Wenn ich im einer Bereich bin soll nur die eine Stelle angezeigt werden,
die anderen Segmente sollen dann aus sein.
Karl H. schrieb:> Wieso kleiner 0x0009.>> Deine Tasuenderstelle ist 0x1000>> Und du willst da auch nicht VRAM ändern. in VRAM zählst du ja. Du willst> etwas anderes anzeigen, wenn da eigentlich rein rechnerisch eine 0> stehen sollte.
Jan schrieb:> Ja will ich!
Na dann
Führende 0-en fangen nun mal in der Tausender Stelle an.
Natürlich gibt es sie auch in der Hunderter Stelle und in der Zehner
Stelle. Diese Erweiterung für diese Stellen ist als Übung für den
Lernenden gelassen. Ist genau das gleiche Prinzip
Wenn dein VRAM kleiner als 0x0100 ist, dann wird in der Hunderter STelle
nichts ausgegeben, andernfalls das Muster welches zu dieser Ziffer
gehört.
Wenn VRAM kleiner als 0x0010 ist, dann wird in der Zehner Stelle nichts
ausgegeb, andernfalls das Muster welches zu dieser Ziffer gehört.
Beachte. Wenn die Zahl kleiner als 0x0100 ist, dann ist sie damit auch
automatisch kleiner als 0x1000. Wenn also die Hunderter unterdrückt
werden, dann werden damit auch bei der Bewertung der Tausender diese
ebenfalls unterdrückt.
Jan schrieb:> Es geht nicht nur um die Tausender.> Wenn ich im einer Bereich bin soll nur die eine Stelle angezeigt werden,> die anderen Segmente sollen dann aus sein.
Vorschlag:
Wenn Du führende Nullen unterdrücke willst dann fange doch von oben an:
if (VRAM >= 1000)
"display digit"
else
"display nothing"
delay 4ms;
// next digit
if (VRAM > 100)
...
rgds
Jan schrieb:> Perfekt. Funktioniert!> Besten dank.
Gut.
Dann den Multiplex entsprechend umbauen.
Zusätzlich führst du dir noch ein Array mit 4 Elementen ein, in welchem
das Muster gespeichert wird, das an der jeweiligen Stelle auszugeben
ist.
dein main soll am Ende so aussehen
1
constuint8_tnumbers[11]={
2
0b00111111,// 0
3
0b00000110,// 1
4
0b01011011,// 2
5
0b01001111,// 3
6
0b01100110,// 4
7
0b01101101,// 5
8
0b01111101,// 6
9
0b00000111,// 7
10
0b01111111,// 8
11
0b01101111,// 9
12
0b00000000// all off
13
};
14
15
volatileuint8_tmuster[4];
16
....
17
18
voidoutput(uint16_tzahl)
19
{
20
....zahlzerlegen,muster[]entsprechendbefüllen
21
}
22
23
intmain()
24
{
25
uint16_tcnt;
26
....
27
28
29
while(1)
30
{
31
cnt++;
32
output(cnt);
33
34
_delay_ms(500);
35
}
36
}
die Funktion output (die du schreiben musst), zerlegt die Zahl in die
Tausender, Hunderter, Zehner und Einer und schreibt die jeweils
auszugebenden Muster in das globale Muster Array. Die Timergetrieben
Interrupt Funktion gibt reihum die so hinterlegten Muster aus. D.h.
während des eigentlichen Bedienens der Anzeige, interessiert sich die
Interrupt Funktion nicht mehr dafür, was die Muster bedeuten, die sie an
die LED anlegt. Die schaufelt einfach nur die Muster genau so auf die
Anzeige, wie sie im Muster Array stehen. (und macht das ganze natürlich
ohne delays)
Jan schrieb:> Das mit dem neuen Muster Array...> Spart das an Leistung ein?
erstens das.
Zweitens soll deine Anzeige ja ständig laufen, ohne dass du dir im
Hauptprogramm (also dort wo dann die eigentliche Arbeit gemacht wird)
dauernd den Kopf darüber zerbrechen willst, wie du eine Zahl auf die
Anzeige kriegst.
Du kriegst einen Zahlenwert von irgendwo her, zum Bleistift vom ADC,
rufst damit die Funktion output auf und dann steht sie auch schon auf
der Anzeige.
1
....
2
3
while(1)
4
{
5
wert=ADC_read(0);
6
output(wert);
7
}
Du willst dich ja auf deine eigentliche Berechnung bzw. Aufgabe
konzentrieren und nicht um so Nebensächlichkeiten wie eine 7-Segment
Anzeige.
und drittens.
Wer sagt denn, dass immer Zahlen auf der Ausgabe sein müssen. Man kann
ja auch zum Bleistift einen Fehler damit anzeigen, dass die Anzeige
lauter '-' anzeigt. Einfach das Muster für '-' in die 4 Stellen des
Muster Arrays schreiben und die Interrupt Routine zeigt auch das brav
an.
Jan schrieb:> [c]> void display(uint16_t value)> {> uint16_t result_tmp;>> result_tmp = value / 1000;> sample[0] = result_tmp;>> result_tmp = value / 100;> sample[1] = result_tmp;>> result_tmp = value / 10;> sample[2] = result_tmp;>> }> [\c]>> so in etwa das zerlegen?
fast. probiers halt mal mit konkreten Zahlen durch.
value sei zum Bleistift 5634. Die einzelnen Stellen müssen also 5, 6, 3,
und 4 sein.
1
result_tmp = value / 1000;
5634 / 1000 ergibt 5
1
result_tmp = value / 100;
5634 / 100 ergibt 56. Da sollte aber 6 rauskommen.
1
result_tmp = value / 10;
5634 / 10 ergibt 563. Da sollte aber 3 rauskommen.
Was dir fehlt, das ist der % Operator. Das ergibt den Rest bei einer
Division. So wie du das in der Grundschule gelernt hast. Eine Mutter hat
5 Kinder und 13 Äpfel. Wenn jedes Kind gleich viele Äpfel bekommt,
wieviele Äpfel kriegt es dann und wieviele bleiben der Mutter übrig?
13 / 5 ergibt 2. Jedes Kind kriegt 2 Äpfel
13 % 5 ergibt 3. Und der Mutter bleiben 3 Äpfel übrig.
Probe. 5*2 + 3 ergibt wieder die 13
was also ergibt
32 / 10
32 % 10
und was hat das mit den Stellen der Dezimalzahl 32 zu tun?
Jan schrieb:> Karl H. schrieb:>> was also ergibt>> 32 / 10>> 32 % 10>> 32/ 10 = 3>> 32 % 10 = 2
genau. Das hat also die Zahl 32 in 3 und 2 zerlegt.
Jetzt hast du aber nicht eine 2 stellige Zahl sondern zb 846.
Wie stellst du es jetzt an, die in die Ziffern 8 4 und 6 zu zerlegen?
846 / 100 ergibt 8
846 % 100 ergibt 46 (denn 8*100 + 46 ergibt wieder die 846)
46 / 10 ergibt 4
46 % 10 ergibt 6
da sind sie. 8, 4 und 6
Jetzt das ganze mit 4 stelligen Zahlen. Zu zerlegen ist 9625
??
Karl H. schrieb:> Willst du führende 0-en unterdrücken oder nicht?Jan schrieb:> Ja will ich!
Wir sind in Deutschland -da werden führende Nullen nicht
unterdrückt...!
MfG Paul
Jan schrieb:> 3291>> 3291 / 1000 = 3,291
vergiss bitte hier die Nachkommastellen. IN deinem Programm werden sie
nie erzeugt. Wenn du einen int durch einen anderen int dividierst, dann
ist das Ergebnis wieder ein int. Also eine ganze Zahl
> so richtig ?
Jep.
Jetzt das ganze in Codeform.
Jan schrieb:> so?>> uint16_t result_tmp[6];>> result_tmp[0] = value / 1000;> result_tmp[1] = value % 1000;>> result_tmp[2] = result_tmp[1] / 100;> result_tmp[3] = result_tmp[2] % 100;>> result_tmp[4] = result_tmp[3] / 10;> result_tmp[5] = result_tmp[4] % 10;
IM Prinzip: ja.
Aber da braucht es kein 6-er Array.
Du brauchst genau einen uint16_t um das 'zwischenergebnis' nach der
Modulo-Division (=Rest) zwischenzuspeichern und um damit weiter zu
rechnen. Es ist nicht verboten Variablen auch mehrmals zu beschreiben,
wenn man ihren Wert nicht mehr weiter benötigt.
PS: wir sind mit dem Umbau noch lange nicht fertig.
Aber mir gefällt, wie dau das Stück für Stück machst. Am Ende werden wir
trotzdem beim Ziel angekommen sein. Und gerade deswegen wirst du auch
jeden Zwischenschritt und auch das Endergebnis verstehen
Und ach ja.
Das hab ich vergessen. Deine führende 0-Unterdrückung arbeitet momentan
noch nicht richtig. Also wieder raus damit. Die kommt im übernächsten
Schritt wieder rein (aber an einer anderen Stelle)
Jan schrieb:> Das mit dem neuen Muster Array...> Spart das an Leistung ein?
Aber hallo, ganz gewaltig.
Damit es nicht flackert muß man mit 100Hz oder mehr multiplexen. Kein
Mensch kann aber 100 Werte/s ablesen und da wäre es vergeudete CPU-Zeit,
den Wert immer ständig neu zu wandeln. 2..5/s reicht völlig aus oder
einfach nur, wenn er sich auch ändert.
Karl H. schrieb:> Und ach ja.> Das hab ich vergessen. Deine führende 0-Unterdrückung arbeitet momentan> noch nicht richtig. Also wieder raus damit. Die kommt im übernächsten> Schritt wieder rein (aber an einer anderen Stelle)
Das wollte ich gerade bemerken das dass noch nicht funzt.
Wo kommt die denn rein? Ich wollte das mit dem Multiplexen (Timer)
morgen machen.
Kann ich das mit der Unterdrückung schon einbauen?
Jan schrieb:> Karl H. schrieb:>> Und ach ja.>> Das hab ich vergessen. Deine führende 0-Unterdrückung arbeitet momentan>> noch nicht richtig. Also wieder raus damit. Die kommt im übernächsten>> Schritt wieder rein (aber an einer anderen Stelle)>> Das wollte ich gerade bemerken das dass noch nicht funzt.> Wo kommt die denn rein? Ich wollte das mit dem Multiplexen (Timer)> morgen machen.> Kann ich das mit der Unterdrückung schon einbauen?
Nein.
Die nächste Beobachtung ist nämlich, dass wir hier
1
PORTB=numbers[sample[2]];
eigentlich viel zu viel Aufwand treiben.
wozu da jedesmal indirekt über samples[2] ins numbers Array gehen?
Im Grunde interessiert uns doch gar nicht, welchen Zahlenwert die Stelle
hatte. Alles was wir an dieser Stelle brauchen, das ist doch nur das
Muster, das auszugeben ist.
Dieses Muster, das kann aber auch die Funktion display gleich selbst ins
sample-Array schreiben.
1
voiddisplay(uint16_tvalue)
2
{
3
uint16_ttmp;
4
5
sample[0]=numbers[value/1000];
6
tmp=value%1000;
7
8
sample[1]=numbers[tmp/100];
9
tmp=tmp%100;
10
11
sample[2]=numbers[tmp/10];
12
sample[3]=numbers[tmp%10];
13
}
das ermöglicht dir an dieser Stelle
1
PORTD|=(1<<PD5);
2
PORTB=numbers[sample[0]&0x000F];
3
_delay_ms(4);
4
PORTD&=~(1<<PD5);
einfacher zu schreiben
1
PORTD|=(1<<PD5);
2
PORTB=sample[0];
3
_delay_ms(4);
4
PORTD&=~(1<<PD5);
denn in sample[0] steht ja schon das LED Muster für diese Stelle
fix&fertig aufbereitet drinnen.
Karl H. schrieb:> denn in sample[0] steht ja schon das LED Muster fix&fertig aufbereitet> drinnen.
Und jetzt hast du auch die Code-Position, in die die Unterdrückung der
führenden 0-en hin kommt.
Es ist die Funktion display, die entweder das jeweils korrekte Muster
für eine Stelle ins Array schreibt, oder eben das Muster für 'keine
LED'. Die Unterdrückung geschieht code-technisch dem Prinzip nach genau
gleich wie vorher (nur dass du natürlich Dezimalzahlen nimmst)
1
voiddisplay(uint16_tvalue)
2
{
3
uint16_ttmp;
4
5
if(value<1000)
6
sample[0]=numbers[10];// es gibt keine Tausender, diese Position leer lassen
Karl H. schrieb:> Muss mich entschuldigen.> Ich war kurzzeitig in einem anderen Thread.
Macht nichts.
Klappt sehr gut, besten dank für das kleine Tutorial;)
Jan schrieb:> Karl H. schrieb:>> Muss mich entschuldigen.>> Ich war kurzzeitig in einem anderen Thread.>> Macht nichts.> Klappt sehr gut, besten dank für das kleine Tutorial;)
Irgendwie musst du es ja lernen :-)
Bis jetzt soweit alles nachvollziehbar und die Gedankengänge klar?
Den Umbau auf Timer machen wir dann morgen.
Es ist jetzt nur noch ein kleiner SChritt, bis du die ANzeige insofern
vergessen kannst, dass du einfach nur noch display aufrufen musst und
deine Anzeige zeigt dir die Zahl. Egal was deine Hauptschleife in main
sonst noch so treibt :-)
Jan schrieb:> Ja, soweit alles klar. Man muss nur erstmal drauf kommen.> Welche Zeitbasis währe denn perfekt für das Multiplexen?
Peter hat es schon angesprochen.
Irgendwas so um die 100Hz. Der Wert ist nicht wirklich kritisch, solange
er nur hoch genug ist.
(Obwohl es auch ganz reizvoll ist, erst mal mit einer kleineren Frequenz
anzufangen. Wenn man das erste mal sieht, wie eine STelle nach der
anderen aufleuchtet und verlöscht und dann sukzessive die
Geschwindigkeit steigert, bis die Anzeige rock solid steht, dann erkennt
man erst, welcher Trick da dahinter steckt und wie leicht wir Menschen
uns doch täuschen lassen)
Hast du einen Timer 0?
Wenn ja, dann nehmen wir den dafür her. Der Timer 1 mit seinen
vielfältigen Möglichkeiten ist dazu zu wertvoll. Welchen µC hast du
eigentlich?
Jan schrieb:> Habe einen TINY2313.
Passt.
Der hat einen Timer 0.
da suchen wir uns einen Vorteiler, dass er pro Sekunde so zirka 100 mal
seinen Wertebereich 0..255 durchläuft. Wenns 200 mal sind, ist es auch
kein Beinbruch. Da ist genug Spielraum.
Man kann das ganze aber auch C-like machen, C hat ja schon fertige
Bibliotheken für die formatierte Zahlenausgabe.
Man muß das Fahrrad nicht nochmal erfinden.
Die Umwandlung in 7-Segment bleibt und zu beachten ist die Ausgabe
erfolgt in ASCII, d.h. man muß noch die '0' abziehen.
1
#include<stdio.h>
2
#include<string.h>
3
4
constcharnumbers[]={
5
0b00111111,// 0
6
0b00000110,// 1
7
0b01011011,// 2
8
0b01001111,// 3
9
0b01100110,// 4
10
0b01101101,// 5
11
0b01111101,// 6
12
0b00000111,// 7
13
0b01111111,// 8
14
0b01101111,// 9
15
};
16
#define CHAR_BLANK 0
17
18
volatileuint8_tsample[4];
19
20
voidvalout_4digit(uint16_tval)
21
{
22
chartemp[5];// 4 Digits + '/0'
23
if(val>=10000){
24
memset(sample,CHAR_BLANK,4);// blank on overrange
25
return;
26
}
27
sprintf(temp,"%4u",val);// 4 digits without leading zeros
Peter D. schrieb:> Man kann das ganze aber auch C-like machen, C hat ja schon fertige> Bibliotheken für die formatierte Zahlenausgabe.> Man muß das Fahrrad nicht nochmal erfinden.
Korrekt.
Ich finde es schadet aber auch nichts, wenn man sich einmal überlegt,
wie eigentlich unser Zahlensystem funktioniert. Die Leute haben sowieso
alle schon viel zu viel Angst vor ein bisschen rechnen.
Karl H. schrieb:> Peter D. schrieb:>> Man kann das ganze aber auch C-like machen, C hat ja schon fertige>> Bibliotheken für die formatierte Zahlenausgabe.>> Man muß das Fahrrad nicht nochmal erfinden.>> Korrekt.> Ich finde es schadet aber auch nichts, wenn man sich einmal überlegt,> wie eigentlich unser Zahlensystem funktioniert. Die Leute haben sowieso> alle schon viel zu viel Angst vor ein bisschen rechnen.
1
/* Legt den TIMER0 zum Überlauf fest ( ca. jede 10 ms. ) */
2
OCR0A=0x4D;
3
4
/* Compare Match Interrupt enable */
5
TIMSK|=(1<<OCIE0A);
6
7
/* Prescaler auf 1024 setzen (F_CPU/1024) */
8
TCCR0B|=((1<<CS02)|(1<<CS00));
9
10
/* CTC */
11
TCCR0A|=(1<<WGM01);
12
13
/* Erlaubt globale Interrupts */
14
sei();
Jetzt sollte TIMER0 ca. jede 10 ms. überlaufen.
Ich finde den Weg von Karz Heinz, für den Anfang besser um alles zu
verstehen.
Jan schrieb:> Habe einen TINY2313.
Der stammt ja noch aus der Zeit, wo Atmel-AVR mit Flash gegeizt hatte
(AT90S2313).
Da könnte es mit printf etwas knapp werden (86% full).
Jan schrieb:> Ich finde den Weg von Karz Heinz, für den Anfang besser um alles zu> verstehen.
Ist es auf jeden Fall.
Meine Variante sollte nur zeigen, wie es ein langjähriger
C-Programmierer machen würde. Die haben printf/scanf quasi schon mit der
Muttermilch aufgesogen.
>/* Legt den TIMER0 zum Überlauf fest ( ca. jede 10 ms. ) */
2
>OCR0A=0x4D;
3
>
4
>/* Compare Match Interrupt enable */
5
>TIMSK|=(1<<OCIE0A);
6
>
7
>/* Prescaler auf 1024 setzen (F_CPU/1024) */
8
>TCCR0B|=((1<<CS02)|(1<<CS00));
9
>
10
>/* CTC */
11
>TCCR0A|=(1<<WGM01);
12
>
13
>/* Erlaubt globale Interrupts */
14
>sei();
15
>
>> Jetzt sollte TIMER0 ca. jede 10 ms. überlaufen.
GuMo
Da hast du dir ja eine Menge Arbeit gemacht :-)
Ich hätt einfach den nächst kleineren Vorteiler genommen, Timer im ganz
normalen Modus, Interrupt auf Overflow.
Passt schon.
Die Interrupt Service Routine hast du schon erstellt?
Karl H. schrieb:> Die Interrupt Service Routine hast du schon erstellt?
Ich geh mal davon aus, dass es sie schon gibt, dass die aber noch leer
ist. Also so
1
ISR(TIMER0_COMPA_vect)
2
{
3
}
Gleich ein Wort der Warnung: Wenn du einen Interrupt frei gibst, dann
musst du immer eine ISR dafür schreiben. Auch wenn sie leer ist.
Denn sonst kommt eine Standard-ISR ins Spiel, die den Prozessor neu
startet.
OK. Genug des Vorgeplänkels. Welche Aufgabe hat die ISR?
Einfach. Sie soll die 7-Segment Anzeigen ansteuern.
Wie macht sie das?
Das macht sie nicht so, wie du das bisher gemacht hast, dass sie eine
Stelle einschaltet, wartet, nächste Stelle einschaltet etc.
Sondern eine Stelle der Anzeige leuchtet, während das Programm nicht
in der ISR ist. Die ISR hat die Aufgabe von einer gerade leuchtenden
Stelle auf die nächste Stelle umzuschalten, die leuchten soll. Danach
hat sie ihre Arbeit getan und ist fertig. Diese Stelle leuchtet dann,
während das Programm irgend etwas anderes macht. Bis dann ein paar
Millisekunden der nächste Interrupt auftritt und die ISR reihum auf die
nächste Stelle weiterschaltet.
Der Vorteil liegt auf der Hand. Rechenzeit für die Ansteuerung der
7-Segment Anzeige wird nur dazu benötigt um dieses Umschalten
durchzuführen. Das aber geht schnell und vor allen Dingen: aus Sicht des
µC mit seinen 8 Mhz ist ein Ereignis, dass nur alle 10ms eintritt
seltener als für uns Weihnachten. In Summ braucht die Ansteuerung der
Anzeige dadurch praktisch keine Rechenzeit mehr. Es sind weit weniger
als ein paar Promille, die dafür draufgehen.
Zur Sache.
Die ISR muss offensichtlich wissen, welche Stelle gerade leuchtet, denn
beim nächsten Aufruf soll ja dann 'die nächste' leuchten. 'Die nächste'
kann ich aber nur dann feststellen, wenn ich weiss was jetzt gerade
Sache ist und das muss ich mir irgendwo merken.
Wir brauchen also eine Variable, in der wir uns das merken, welche
Stelle gerade drann ist.
Was hat die ISR noch zu tun?
Am Anfang schaltet sie die gerade aktive Stelle ab. Der Einfachheit
halber schalten wir einfach ALLE Stellen ab, dann ist die jetzt gerade
leuchtende mit Sicherheit auch dabei und wir brauchen da keine
Fallunterscheidungen bezüglich der Portpins mit denen abgeschaltet wird.
Dann wird das Muster für die nächste Stelle auf den Port ausgegeben.
Und diese Stelle wird eingeschaltet.
Und das wars auch schon. Klingt doch gar nicht so übel.
In Code
1
volatileuint8_tsample[4];
2
uint8_tnextSample;
3
4
ISR(TIMER0_COMPA_vect)
5
{
6
// erst mal alles aus!
7
PORTD&=~((1<<PD6)|(1<<PD5)|(1<<PD4)|(1<<PD3);
8
9
// welches ist die nächste Stelle, die angezeigt werden soll?
10
nextSample++;
11
if(nextSample==4)
12
nextSample=0;
13
14
// das zugehörige Muster auf die Ausgänge
15
PORTB=sample[nextSample];
16
17
// und die betreffende Stelle einschalten
18
if(nextSample==0)
19
PORTD|=(1<<PD5);
20
elseif(nextSample==1)
21
PORTD|=(1<<PD4);
22
elseif(nextSample==2)
23
PORTD|=(1<<PD3);
24
elseif(nextSample==3)
25
PORTD|=(1<<PD6);
26
27
// das wars.
28
// die Stelle leuchtet jetzt, bis die ISR vom Timer das nächste mal
29
// aufgerufen wird. Dann wird die Stelle wieder abgeschaltet
30
// und die nächste Stelle kommt drann und darf leuchten
31
}
Der letzte Teil, der mit dem aktivieren der nächsten Stelle, den könnte
man noch etwas eleganter formulieren, aber so schlimm ist die Version
auch wieder nicht.
Jetzt kannst du dein main noch aufräumen. Innerhalb der Hauptschleife
bleibt im Grunde nichts übrig, als ein Aufruf der display Funktion und
ev. ein _delay_ms
1
intmain()
2
{
3
...
4
Portsfreischalten
5
Timer0aufsetzen
6
...
7
VRAM=0;
8
9
sei();
10
11
while(1){
12
display(VRAM);
13
_delay_ms(500);
14
VRAM++;
15
}
16
}
Beim ersten Test wird man vielleicht nicht gleich einen Zähler machen,
sondern erst mal eine statische Anzeige
1
intmain()
2
{
3
...
4
Portsfreischalten
5
Timer0aufsetzen
6
...
7
8
display(5678);
9
10
sei();
11
12
while(1){
13
}
14
}
aber im Grunde ist das 7-Segment Subsystem damit fertig.
Wenn es in Zukunft etwas auszugeben gibt, dann rufst du einfach display
mit dem Zahlenwert auf und der Mechanismus dahinter kümmert sich
eigenständig darum, dass die Zahl auf die Anzeige kommt.
Jetzt bleibt nur noch: Programm aufräumen. Alles was nicht mehr
gebraucht wird, rauswerfen. Noch mal über die Form drüber schauen, ob
alle Einrückungen konsistent sind und das ganze so organisieren, dass
man auch nach 2 Monaten noch nachvollziehen kann, was da abgeht. Denn
das nächste Projekt kommt bestimmt und es gibt keinen Grund, warum man
da diese Anzeige Routinen nicht weiterverwenden können sollte.
Peter D. schrieb:> Karl H. schrieb:>> if( nextSample == 4 )>> nextSample = 0;>>>> // das zugehörige Muster auf die Ausgänge>> PORTB = sample[nextSample];>>>> // und die betreffende Stelle einschalten>> if( nextSample == 0 )>> PORTD |= (1<<PD5);>> else if( nextSample == 1 )>> PORTD |= (1<<PD4);>> else if( nextSample == 2 )>> PORTD |= (1<<PD3);>> else if( nextSample == 3 )>> PORTD |= (1<<PD6);>> Solche if-Kaskaden macht man besser lesbar mit switch/case.
Kann man machen.
Ich bevorzuge bei so wenigen Fällen die Kaskade (wenn ich kein Array
benutzen kann). Ein switch/case zieht den Code so in die Länge oder ich
müsste die Formatierung aufgeben (was ich normalerweise nicht will)
Karl H. schrieb:> Ich bevorzuge bei so wenigen Fällen die Kaskade (wenn ich kein Array> benutzen kann).
Aber: Warum eigentlich nicht?
Jan ist ein wiffes Kerlchen. Ich denke, das kann ich ihm zutrauen.
Überhaupt wenn er die Herleitung kennt, was an dieser Stelle passieren
soll.
Jan schrieb:> Karl H. schrieb:>> uint8_t nextSample;>> sollte dort nicht auch "volatile" stehen?
Braucht es nicht.
Die Variable wird aussliesslich in der ISR benutzt
FAQ: Was hat es mit volatile auf sich
Das Problem kann also per Definition nicht auftreten.
Was man machen sollte, ist die Variable in die ISR hineinziehen. Aber
ich weiss nicht, wie gut dein C ist :-)
Jan schrieb:> Geisterbilder sind auch vorhanden.
welche Version?
Wie genau werden die einzelnen Stellen ein bzw. aus geschaltet.
Ich bin davon ausgegangen, dass eine 0 am Pin aussschaltet und eine 1
einschaltet
Karl H. schrieb:> Jan schrieb:>>> Geisterbilder sind auch vorhanden.>> welche Version?> Wie genau werden die einzelnen Stellen ein bzw. aus geschaltet.> Ich bin davon ausgegangen, dass eine 0 am Pin aussschaltet und eine 1> einschaltet
1
if(nextdigit>4)
2
{
3
nextdigit=0;
4
}
das war das Problem.
Wie löse ich das jetzt am besten mit der "0" Unterdrückung?
Jan schrieb:> Karl H. schrieb:>> Jan schrieb:>>>>> Geisterbilder sind auch vorhanden.>>>> welche Version?>> Wie genau werden die einzelnen Stellen ein bzw. aus geschaltet.>> Ich bin davon ausgegangen, dass eine 0 am Pin aussschaltet und eine 1>> einschaltet>>
1
>if(nextdigit>4)
2
>{
3
>nextdigit=0;
4
>}
5
>
>> das war das Problem.
wenn schon, dann
1
if(nextdigit>=4)
2
{
3
nextdigit=0;
4
}
denn ein Wert von 4 in nextdigit ist ja nicht zulässig.
Das Array hat nur 4 Stellen.
1
sample[0]
2
sample[1]
3
sample[2]
4
sample[3]
zähl nach. Sind genau 4 Stück. Der höchste zulässige Index ist aber 3
und nicht 4
beric schrieb:> Karl H. schrieb:>> wenn schon, dann>> if( nextdigit >= 4 )>> {>> nextdigit = 0;>> }>> Warum nicht einfach>
1
>nextdigit&=0x3;// nimm nextdigit modulo 4
2
>
Weil ich nicht Moby bin :-)
Ne, im Ernst. Wenn du dir sicher bist, dass du immer genau 4 Stellen
hast, dann mach das. Ich schreib mir die Dinge für mich persönlich so
um, dass ich die Anzahl der Stellen nur an einer einzigen Stelle hab und
der Compiler den Code für eine geänderte Stallenzahl anpasst. D.h. der
Code würde bei mir sowieso nicht so bleiben, die Konstante 4 taucht da
mit Sicherheit in Produktionscode nicht auf. 3 Taktzyklen mehr kratzen
mich dabei nicht wirklich.
PS: Wenn schon,dann schreib ich das so
1
nextdigit%=4;
Compiler sind gut darin, bei bekannten Zahlenwerten die wortwörtliche
Operation durch etwas, was schneller geht auszutauschen.