Ich möchte gerne eine Multiplexing Ansteuerung für vier 7-Segment
Anzeigen mittels ATmega zusammenbaun.
Was nimmt man da pauschal für Bauteile? Gibt es da Standardrezept?
Also speziel jetzt um die LED's der Segmentanzeige zu treiben.
Ist der ULN2803 eine gute Wahl? Oder ULN2003? Oder ganz was anderes?
ist dieser Aufbau hier grundsätzlich in Ordnung:
http://codeandlife.com/2012/02/24/7-segment-multiplexing-with-uln2003-pnp-transistors/
danke!
Im Forum gibt es etwa 1001 Threads zu diesem Thema. Auch im AVR Tutorial
und der Codesammlung sind viele Beispiele zu lesen. Bitte benutze die
Suchfunktion.
schönen dank, ich habe mir jetzt allerdings mal ein einfaches Beispiel
aus dem Netz gesucht und für einen ATTiny 2313 angepasst.
Der AVR wird mit 8MHz betrieben.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay_basic.h>
4
5
#define SEVEN_SEGMENT_PORT PORTD
6
#define SEVEN_SEGMENT_DDR DDRD
7
8
volatile uint8_t digits[3];
9
10
void SevenSegment(uint8_t n,uint8_t dp)
11
{
12
if(n<10)
13
{
14
switch (n)
15
{
16
case 0: //.gfedcba
17
SEVEN_SEGMENT_PORT = 0b00111111;
18
break;
19
20
case 1: //.gfedcba
21
SEVEN_SEGMENT_PORT = 0b00000110;
22
break;
23
24
case 2: //.gfedcba
25
SEVEN_SEGMENT_PORT = 0b01011011;
26
break;
27
28
case 3: //.gfedcba
29
SEVEN_SEGMENT_PORT = 0b01001111;
30
break;
31
32
case 4: //.gfedcba
33
SEVEN_SEGMENT_PORT = 0b01100110;
34
break;
35
36
case 5: //.gfedcba
37
SEVEN_SEGMENT_PORT = 0b01101101;
38
break;
39
40
case 6: //.gfedcba
41
SEVEN_SEGMENT_PORT = 0b01111101;
42
break;
43
44
case 7: //.gfedcba
45
SEVEN_SEGMENT_PORT = 0b00000111;
46
break;
47
48
case 8: //.gfedcba
49
SEVEN_SEGMENT_PORT = 0b01111111;
50
break;
51
52
case 9: //.gfedcba
53
SEVEN_SEGMENT_PORT = 0b01101111;
54
break;
55
}
56
if(dp)
57
{
58
SEVEN_SEGMENT_PORT |= 0b10000000;
59
}
60
}
61
else
62
{
63
SEVEN_SEGMENT_PORT = 0b11111101;
64
}
65
}
66
67
void Wait()
68
{
69
uint8_t i;
70
for (i=0;i<10;i++)
71
{
72
_delay_loop_2(0);
73
}
74
}
75
76
void Print(uint16_t num)
77
{
78
uint8_t i=0;
79
uint8_t j;
80
81
if (num>9999) return;
82
83
while (num)
84
{
85
digits[i]=num%10;
86
i++;
87
num=num/10;
88
}
89
for (j=i;j<4;j++) digits[j]=0;
90
}
91
92
int main(void)
93
{
94
uint16_t i;
95
96
// Prescaler = FCPU/1024
97
TCCR0B |= (1<<CS02)|(1<<CS00);
98
99
//Enable Overflow Interrupt Enable
100
TIMSK |= (1<<TOIE0);
101
102
//Initialize Counter
103
TCNT0=0;
104
105
//Port B[3,2,1,0] as out put
106
DDRB|=0b00001111;
107
108
PORTB=0b00000001;
109
110
//Port D
111
SEVEN_SEGMENT_DDR=0XFF;
112
113
//Turn off all segments
114
SEVEN_SEGMENT_PORT=0X00;
115
116
//Enable Global Interrupts
117
sei();
118
119
while(1)
120
{
121
for (i=0;i<10000;i++)
122
{
123
Print(i);
124
Wait();
125
}
126
}
127
}
128
129
ISR(TIMER0_OVF_vect)
130
{
131
static uint8_t i=0;
132
if (i==3)
133
{
134
i=0;
135
}
136
else
137
{
138
i++;
139
}
140
PORTB &= (0b11110000);
141
PORTB |= (1<<i);
142
SevenSegment(digits[i],0);
143
}
Es zählt zwar, soweit ich das beurteilen kann, aber es flackert verdammt
häftig.
Sind 8MHz dafür einfach zu langsam oder woran könnte das liegen?
Und PS:
Deine Funktion 'SevenSegment' kann man auch durch ein Array ersetzen.
1
uint8_tdigitPattern[]={0b00111111,
2
0b00000110,
3
0b01011011,
4
0b01001111,
5
0b01100110,
6
0b01101101,
7
0b01111101,
8
0b00000111,
9
0b01111111,
10
0b01101111,
11
};
12
13
volatileuint8_tdigits[3];
14
....
was dann gleich auch noch den Funktionsaufruf aus der ISR entfernt und
den GCC nicht dazu zwingt, dem Tiny unötige Mehraufgabe aufzubrummen.
macht man dann auch noch die Ausgaben in der richtigen Reihenfolge ...
1
ISR(TIMER0_OVF_vect)
2
{
3
staticuint8_ti=0;
4
5
PORTB&=(0b11110000);// erst mal alles aus!
6
// das schieb ich mal an den Anfang der ISR
7
// nur für den Fall, dass irgenwo kleinere
8
// Kapazitäten ein winziges Nachleuchten
9
// der LED nach dem Abschalten zur Folge haben
10
11
if(i==3)// denn während sich das abbaut, kann der Tiny
12
{// in der Zwischenzeit bestimmen, welches die
13
i=0;// nächste Stelle ist. Quasi ein
14
}// Multitasking des Tiny mit den LED :-)
15
else
16
{
17
i++;
18
}
19
// dann können die LEDs auf das neue Muster
20
// umgeschaltet werden.
21
SEVEN_SEGMENT_PORT=digitPattern[digits[i]];
22
// das neue Muster liegt zwar jetzt an,
23
// leuchtet aber noch nicht
24
// Daher jetzt die LED endgültig mit dem
25
// neuen Muster einschalten
26
PORTB|=(1<<i);
27
}
... dann ist auch gleich noch das sog. Ghosting eliminiert.
Danke dir Karl-Heinz!
Gibt's da sowas wie einen Rechner dafür?
Ich habe jetzt mal mit den Werten
TCCR0B |= (1<<CS01) | (1<<CS00);
TCNT0=131;
ins blaue geraten.
Damit läufts perfekt.
Die Frage ist nur ob das nicht zu viel ist.
Hallo Manfred ,
da das nennt sich Datenblatt lesen.
Dort stehen die Formeln !
und Was soll das bewirken ?
TCCR0B |= (1<<CS01) | (1<<CS00);
TCNT0=131;
Programmieren hat etwas mit wissen zu tun und nicht mit raten !
Manfred W. schrieb:> Danke dir Karl-Heinz!>> Gibt's da sowas wie einen Rechner dafür?
Wozu brauchst du da einen Rechner?
Einfach ein wenig nachdenken.
Der Timer wird vom Systemtakt angesteuert.
Wenn du einen Vorteiler von 1 hättest, dann würde der in 1 Sekunde bis
8000000 zählen (ich ignorier mal, dass der nur bis 255 zählen kann)
Da du aber einen Vorteiler von 1024 hast, zählt er daher in 1 Sekunde
wie weit?
Genau. Bis 8000000 / 1024 gleich 7812
Jetzt kann der Timer aber nicht bis 7812 zählen. Der ist ja nur 8 Bit
gross kann nur bis 255 zählen (256 Zählschritte). Danach gibt es einen
Overflow und er fängt wieder bei 0 an. Genau auf diese Overflows bist du
aber aus. Denn genau bei jedem Overflow wird der Interrupt ausgelöst.
Wenn der Timer also theoretisch in 1 Sekunde bis 7812 zählen möchte, es
aber wegen der Overflow nach jeweils 256 Zählschritten nicht kann,
wieviele Oberflows sind das dann daher in 1 Sekunde?
genau. Das sind 7812 / 256 gleich 30 Overflows (und ein paar
Zerquetschte, die ich mal grosszügig ignoriere)
D.h. pro Sekunde hast du 30 Overflows. Bei jedem Overflow wird eine
andere Anzeige eingeschaltet. Du hast 4 Anzeigen. Wie oft kommt daher
jede Anzeige pro Sekunde drann?
Genau. 30 / 4 gleich 7.5 mal
Wenn dir klar ist, was da vor sich geht, dann hast du dir die Zahlen so
schneller hergeleitet, als du einen "Rechner" rausgekramt hast oder dir
die Formeln im Datenblatt gesucht hast.
Manfred W. schrieb:> ins blaue geraten.
rechne es dir einfach aus. Oder warum denkst du hab ich dir den
Rechengang aufgeschrieben?
Welcher vorteiler ist das
1
TCCR0B|=(1<<CS01)|(1<<CS00);
und mit dem wird gerechnet.
Sei nicht so faul! Das kann man alles verstehen und rechnen.
Hallo Manfred,
ich sehe 100Hz noch.
In meinem aktuellen 4-fach LED Multiplex verwende ich deshalb 250Hz LED
Anzeigefrequenz.
Hier ist noch ein Bild vom Testbetrieb.
Karl-Heinz kennt die Schaltung schon, LED Kathode Vorwiderstände 270R.
Ok, danke Karl Heinz für die ausführliche Erklärung. Das hab ich
verstanden.
Ich verwende jetzt mal einen Teiler von 64. Das ergibt dann einen Takt
von 122 Hz.
Ich belasse das mal dabei. Flackern kann ich da mit meinem Auge nicht
mehr erkennen.
jetzt hab ich noch eine Frage, ich möchte gerne das der Zähler bei den
Segmenten mit führender Null zählt also wie bei einem Bandzählwerk.
0001
0002
|
|
0010
0011
Wie stelle ich es an das die Stellen von rechts nach links rücken?
Manfred W. schrieb:> Wie stelle ich es an das die Stellen von rechts nach links rücken?
Da brauchst du überhaupt nichts rücken.
Die Print Funktion generiert doch ohnehin führende 0-en.
Also einfach einen Zähler als uint16_t Variable definieren und die
erhöhen oder erniedrigen (je nachdem was der Zähler tun soll)
hm, dann stimmt die Verkabelung nicht?
Denn der zählt von links nach rechts.
Erstes Segment zählt bis 9 dann erhöht sich das zweite Segment. Wenn das
zweite Segment über 9 kommt erhöht sich das dritte Segment. Also von
links nach rechts.
Die Kathode des ersten Segmentes hängt an PB0
Das zweite an PB1 usw.
>Die Kathode des ersten Segmentes hängt an PB0>Das zweite an PB1 usw.
Dann vertauscht die halt bis es passt.
Da musst du nicht mal die Software für ändern.
Ist das denn so schwer?
Ich habe jetzt noch ein Problem.
Ich möchte in dem Programm jetzt noch zusätzlich eine Auswertung eines
Drehgebers implementieren. Weil ich denke das die Funktionsweise eines
mechanischen Zählwerkes entspricht. Anstelle des Drehgebers selbst
möchte ich am Motor eine Scheibe montieren die dann 2 Lichtschranken
bedient.
Die Auswertung des Drehgebersbeispiel:
http://www.mikrocontroller.net/articles/Drehgeber
verwendet aber wieder ein anderes Timing wie mir scheint.
Wie implementiere ich das nun.
Ich hätte das mal so versucht:
Hallo Uwe,
TIMER0, ok. Aber welchen der beiden lasse ich dann weg?
Beim Drehgeber wird der bei jeder Drehbewegung initialisiert.
Welcher ISR außer den beiden?
Manfred W. schrieb:> Beim Drehgeber wird der bei jeder Drehbewegung initialisiert.
Wie soll ich das verstehen?
Ich kenne PaDas Drehencoder Code und kann ihn auch einsetzen, mir
scheint , dass Dir noch C Grundlagen fehlen.
Also erst den gesamten Code verstehen und dann einbinden.
Ok?
Ähm, Blödsinn. Natürlich nicht bei jeder Drehbewegung.
encode_init() wird ja nur ein mal aufgerufen.
Es ist ein ATtiny2313
Ich hab das ganze jetzt mal zusammengefasst so wie es bei meinem
Programm aufgerufen wird:
1
TCCR0A = (1<<WGM01);
2
OCR0A = (uint8_t)(F_CPU / 64.0 * 1e-3 - 0.5);
3
TCCR0B = (1<<CS01 | 1<<CS00);
4
TIMSK = (1<<OCIE0A);
5
6
TCCR0B |= (1<<CS01) | (1<<CS00);
7
TIMSK |= (1<<TOIE0);
8
TCNT0=0;
TCCR0B ... kann ich einen schon mal kübeln.
Bei TIMSK addiert man da einfach den zweiten dazu?
64
Karl Heinz, kannst du mir noch einen Tipp geben wie ich den Punkt der
Anzeige, also den dp da auch wieder in mein Programm bekomme?
Diese Auswertung ist ja beim Array flöten gegangen.
Manfred W. schrieb:> 64>> Karl Heinz, kannst du mir noch einen Tipp geben wie ich den Punkt der> Anzeige, also den dp da auch wieder in mein Programm bekomme?> Diese Auswertung ist ja beim Array flöten gegangen.
Na ja.
Indem du ihn setzt bzw. bei der entsprechenden Stelle das Digit
einschaltest, wenn du ihn brauchst.
Was soll ich dir da jetzt gross für einen Tip geben? Du wirst ja doch
wohl ein einzelnes Bit an einem Port auf 1 setzen können, wenn es 1
werden soll.
Wenn beim Multiplexen die 7Segment Anzeige mit der Nummer 0 drann ist,
dann eben den Punkt noch mit dazu einschalten
1
....
2
SEVEN_SEGMENT_PORT=digitPattern[digits[i]];
3
if(i==0)
4
SEVEN_SEGMENT_PORT|=0x80;
5
....
oder
1
#define SEVEN_SEGMENT_PORT PORTD
2
#define SEVEN_SEGMENT_DDR DDRD
3
4
// .gfedcba
5
uint8_tdigitPattern[]={
6
0b00111111,
7
0b00000110,
8
0b01011011,
9
0b01001111,
10
0b01100110,
11
0b01101101,
12
0b01111101,
13
0b00000111,
14
0b01111111,
15
0b01101111,
16
};
17
18
#define DP_DIGIT 7 // der Dezimalpunkt ist am Bit 7 verkabelt
19
20
volatileuint8_tdigits[4];
21
22
23
....
24
SEVEN_SEGMENT_PORT=digitPattern[digits[i]];
25
if(i==0)
26
SEVEN_SEGMENT_PORT|=(1<<DP_DIGIT);
27
....
dann ist es ein wenig besser organisiert und das Bit, das den
Dezimalpunkt steuert, findet sich im Code an der Stelle, in der auch der
Aufbau der anderen Ziffern aus den Segmenten der Anzeige beschrieben
ist.
(oder eben bei der Anzeige 3, je nachdem ob die Nummerierung der
Anzeigen von links nach rechts oder von rechts nach links durchläuft).
Na komm.
Denk dir was aus!
Variablen sind schon erfunden. Ein 'if' ist schon erfunden. Vergleiche
sind schon erfunden.
Ein wenig musst du auch selber was 'erfinden'. Ist ja schliesslich dein
Projekt. Ganz im Gegenteil: Das 'sich etwas ausdenken' und nachsehen ob
das funktioniert ist doch der ganze Spass an der Sache.
Guten Morgen Manfred,
Uwe S. schrieb:> Hallo,>> ich wollte Dich auf den zweiten Timer hinweisen.>> Was kommt hierbei raus?>> TCCR0B = (1<<CS01 | 1<<CS00);
Die Clock Select Bit werden nicht für einen Vorteiler von 64 gesetzt -
also FALSCH!
Der Ausdruck liefert die Zahl 2 also 0b10 und damit einen Vorteile von 8
!
Wissen ist hier gefragt nicht raten !
Siehe:
http://de.wikibooks.org/wiki/C-Programmierung:_Liste_der_Operatoren_nach_Priorit%C3%A4t
Uwe S. schrieb:>> Was kommt hierbei raus?>>>> TCCR0B = (1<<CS01 | 1<<CS00);>> Die Clock Select Bit werden nicht für einen Vorteiler von 64 gesetzt -> also FALSCH!>> Der Ausdruck liefert die Zahl 2 also 0b10
fast. Es ergibt 3
Aber warum so kompliziert. Einfach im Datenblatt nachsehen. Es werden
offensichtlich im Code die Bits CS01 und CS00 gesetzt. Im Datenblatt
gibt es beim Timer 0 eine Tabelle, in der man mit den beiden Bits CS01
bzw. CS00 nachsehen kann, welchem Vorteiler das entspricht. Den
Zahlenwert, der sich aus den Bits ergibt, braucht man dazu nicht
wirklich wissen.
Uwe S. schrieb:> Hallo Karl Heinz,>> da fehlen noch Klammern - die sehen wir gerne nicht mehr - deshalb nur> 2.
?
<< hat eine wesentlich höhere Precedence als |
Die Klammern die dir fehlen sind ein optisches Gimmick aber rein
technisch sind sie in diesem Ausdruck nicht notwendig. Der Ausdruck
parst auch ohne Klammern genau so, wie er soll.
Ob das schlau ist, die Klammern wegzulassen, steht auf einem anderen
Blatt. Notwendig sind sie hier aber konkret nicht.
>> Also eher so wird der Kompiler den Ausdruck verarbeiten:>
1
TCCR0B=(1<<(CS01|1)<<CS00);
Nope.
Das ist kompletter Quatsch.
Schau bitte mal in eine Operator Precedence Table hinein. Da gehts auch
nicht um 'eher', sondern dafür gibt es exakte Regelungen.
Edit:
Hier ist eine
http://en.cppreference.com/w/c/language/operator_precedence
Operatoren, die weiter oben stehen, binden stärker.
stimmt das schon.
Nur gewöhn dir an, dass deine Bibel das Datenblatt zu deinem Prozessor
ist und nichts anderes! Egal, was du in einem Tutorial liest, du checkst
es mit dem Datenblatt zu deinem Prozessortyp!
Uh. bei dir muss man aber wirklich ganz unten anfangen.
8 Bit
Hier sind sie
1
Bitnummer 7 6 5 4 3 2 1 0
2
+---+---+---+---+---+---+---+---+
3
0 oder 1 | | | | | | | | |
4
+---+---+---+---+---+---+---+---+
welche Kombinationen der Bits mit 0-en oder 1-en gibt es und wie werden
sie sinnvoll durchnummeriert
1
00000000 0
2
00000001 1
3
00000010 2
4
00000011 3
5
00000100 4
6
00000101 5
7
00000110 6
8
00000111 7
9
00001000 8
10
00001001 9
11
00001010 10
12
00001011 11
13
00001100 12
14
....
15
11111101 253
16
11111110 254
17
11111111 255
CS00 ist laut Datenblatt im Byte des Registers das Bit 0
CS01 ist laut Datenblatt im Byte des Registers das Bit 1
Betrachtet man also nur die beiden Bits, dann hat man 1 Byte (das mit
dem Rest des Registers verodert wird), in dem das Bit 0 und das Bit 1
gesetzt sind. Und das ist dezimal ausgedrückt welche Zahl (siehe
Tabelle)?
Binär gerechnet schon klar Karl Heinz, aber Uwe meinte ja:
> TCCR0B = (1<<CS01 | 1<<CS00);
Die Clock Select Bit werden nicht für einen Vorteiler von 64 gesetzt -
also FALSCH!
Der Ausdruck liefert die Zahl 2 also 0b10 und damit einen Vorteile von 8
Das versteh ich jetzt nicht.
Wieso entspricht 2 (oder 3) in diesem Falle dann einen Vorteiler von 8?
Manfred W. schrieb:> Binär gerechnet schon klar Karl Heinz, aber Uwe meinte ja:
Uwe hat sich geirrt.
Wie dir als C Kenner natürlich sofort aufgefallen ist :-)
Manfred W. schrieb:> Wieso entspricht 2 (oder 3) in diesem Falle dann einen Vorteiler von 8?
Weil im Rechner alles einfach nur BIts sind.
Ob du 0000 0011
als 8 Bits auffasst, in denen das Bit 0 und das Bit 1 auf 1 gesetzt ist,
oder ob du das als die Dezimalzahl 3 auffast eine reine Frage dessen
ist, welche Sichtweise du haben willst. Dem COmputer ist das egal. Der
sieht einfach nur 1 Byte in dem die beiden Bits gesetzt sind.
Und wenn du im Datenblatt nachsiehst, dann entspricht das (reduziert auf
nur die Vorteiler Bits) einem Vorteiler von 64.
Ist denn die Schreibweise nun korrekt?
Mit Klammer, ohne, oder ganz anders?
TCCR0B = (1<<CS01 | 1<<CS00);
TCCR0B = (1<<CS01) | (1<<CS00);
TCCR0B = 1<<CS01^1<<CS00;
???