Forum: Mikrocontroller und Digitale Elektronik Timing Multiplexen


von Luke S. (no3x)


Lesenswert?

Hallo, ich bin grad dabei ein paar LED zu multiplexen, leider leuchten 
sie sehr schwach. Wenn ich flash oder den reset betätige leuchten die 
LED so wie sollen. Im multiplexbetrieb habe ich gerade mal 0,65V statt 
5V pro Pin.

Ich denke das Multiplexen geht zu schnell oder hat einfach ein 
schlechtes timing.

Hier mal der Code, vielleicht kann mir jemand das Verhalten erklären. Da 
das Problem bereits direkt am uC zu erkennen ist, denke ich, dass es an 
der Software liegen muss.
1
#define F_CPU  8000000UL  //  8 MHz 
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
 
6
//Variablen für die Zeit
7
volatile unsigned int  millisekunden;
8
//volatile unsigned int sekunde;
9
volatile unsigned int minute;
10
volatile unsigned int stunde;
11
 
12
int Zeit[6];
13
int dezimalzahl,dezimalzahl_rest,dezimalzahl_new,durchlauf = 0;
14
int bin_zeile = 4;
15
uint8_t temp_bin = 0;
16
int Zeit[6];
17
int binZeit[6][4];
18
int ledMatrix[6][4];
19
uint8_t sekunde;
20
int timer2 = 0;
21
volatile unsigned char display_dim = 30;
22
23
void update(void) {
24
  for (int ausgabe_spalte=0; ausgabe_spalte<6; ausgabe_spalte++) {
25
26
    switch(ausgabe_spalte) {
27
        case 0:
28
          PORTD = (1 << PD0);
29
        break;
30
          case 1:
31
          PORTD = (1 << PD1);
32
        break;
33
        default:
34
          PORTD = 0;
35
        break;
36
    }
37
    for (int ausgabe_zeile=0; ausgabe_zeile<4; ausgabe_zeile++) {
38
    
39
      PORTB = 0;
40
      if(ausgabe_zeile == 0) {  
41
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
42
          case 0:
43
            PORTB &= (0 << PB0);
44
          break;
45
          case 1:
46
            PORTB = (1 << PB0);
47
          break;
48
          default:
49
            PORTB = 0;
50
          break;
51
        }
52
      }
53
54
      if(ausgabe_zeile == 1) {
55
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
56
          case 0:
57
            PORTB &= (0 << PB1);
58
          break;
59
          case 1:
60
            PORTB = (1 << PB1);
61
          break;
62
          default:
63
            PORTB = 0;
64
          break;
65
        }
66
      }
67
68
      if(ausgabe_zeile == 2) {
69
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
70
          case 0:
71
            PORTB &= (0 << PB2);
72
          break;
73
          case 1:
74
            PORTB = (1 << PB2);
75
          break;
76
          default:
77
            PORTB = 0;
78
          break;
79
        }
80
      }
81
82
      if(ausgabe_zeile == 3) {
83
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
84
          case 0:
85
            PORTB &= (0 << PB3);
86
          break;
87
          case 1:
88
            PORTB = (1 << PB3);
89
          break;
90
          default:
91
            PORTB = 0;
92
          break;
93
        }
94
      }
95
      for (unsigned long i = 0; i < display_dim; i++) { asm volatile("nop"::); }  //  Verzögerung  
96
      PORTB = 0;
97
    }
98
    
99
  }  PORTD = 0;PORTB = 0;
100
}
101
int main(void)
102
{
103
  //PINS definieren
104
  DDRD |= (1<<(PD6)) | (1<<(PD5)) | (1<<(PD4)) | (0<<(PD3)) | (0<<(PD2)) | (1<<(PD1)) | (1<<(PD0));
105
  PORTD = 0;
106
107
  DDRA |= (1 << PA0) | (1 << PA1) | (1 << PA2) | (1 << PA3) | (1 << PA4) | (1 << PA5) | (1 << PA6) | (1 << PA7);
108
  PORTA = 0;
109
110
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
111
  PORTB = 0;
112
113
  //Timer initialisieren
114
  TCCR0 = (1<<(WGM01));// CTC aktivieren
115
  TCCR0 |= (1<<(CS01)) | (1<<(CS00)); // Presaler: 64
116
  OCR0 = 124;  // 1ms
117
  TIMSK |= 1<<OCIE0;
118
119
  //Timer Prescaler auf /8
120
  TCCR2 |= (1 << CS02) | (0 << CS01);
121
  //Timer2_Compare Interrupt aktivieren
122
  TIMSK |= (1 << OCIE2);
123
  // compare wert auf 125 setzen um eine interruptfrequenz von 1ms zu bekommen 
124
  // ((1000000/8)/1000) = 125
125
  OCR2 = 1249;
126
127
  
128
  //INT0 initialisieren
129
  GICR = (1<< INT0);
130
  MCUCR = (1<<ISC01) | (0<<ISC00);
131
   
132
  // Global Interrupts aktivieren
133
  sei();
134
   
135
  while(1)
136
  {
137
    //Einzelne Stellen extrahieren
138
    Zeit[0] = sekunde / 10;
139
    Zeit[1] = sekunde % 10;
140
    Zeit[2] = sekunde / 10;
141
    Zeit[3] = sekunde % 10;
142
    Zeit[4] = sekunde / 10;
143
    Zeit[5] = sekunde % 10;
144
    /*
145
    //Einzelne Stellen extrahieren
146
    Zeit[0] = stunde / 10;
147
    Zeit[1] = stunde % 10;
148
    Zeit[2] = minute / 10;
149
    Zeit[3] = minute % 10;
150
    Zeit[4] = sekunde / 10;
151
    Zeit[5] = sekunde % 10;
152
    */  
153
154
    //Stellen in Binär umwandeln und in Array schreiben
155
    for(int ix = 0; ix<6; ix++) {
156
        dezimalzahl = Zeit[ix];
157
      for (int k=0; k<6; k++) {
158
        binZeit[ix][k] = dezimalzahl & 1;
159
        dezimalzahl>>=1;
160
      }
161
    }
162
    
163
    //Binärzahlen-Array in ledMatrix einsetzen
164
    for (int spalte=0; spalte<6; spalte++) {
165
      
166
      if(debug == 1) { printf("bearbeite Spalte Nr. %d\n", spalte); }
167
      bin_zeile = 4;
168
      for (int zeile=0; zeile<4; zeile++) {
169
        if(bin_zeile > 0)  { bin_zeile--; }
170
        ledMatrix[spalte][zeile] = binZeit[spalte][bin_zeile];
171
      }
172
    }
173
  }
174
}
175
176
ISR (TIMER0_COMP_vect)
177
{
178
  millisekunden++;
179
  if(millisekunden == 100) {  
180
    //PORTD ^= (1<<(PD6));
181
    sekunde++;
182
    millisekunden = 0;
183
    if(sekunde == 60)
184
    {
185
      minute++;
186
      sekunde = 0;
187
    }
188
    if(minute == 60)
189
    {
190
      stunde++;
191
      minute = 0;
192
    }
193
    if(stunde == 24)
194
    {
195
      stunde = 0;
196
    }
197
  }
198
}
199
200
ISR (TIMER2_COMP_vect)
201
{
202
  PORTD ^= (1 << (PD5));
203
  update();
204
}
205
206
ISR(INT0_vect) {
207
  PORTD = (1<< PD0);  
208
}

von Rubelus (Gast)


Lesenswert?

Klar, durch das Multiplexen teilst du sie gerade durch fünf, daher deine 
0,65V - einzigste Möglichkeiten: Programm schneller machen..., geringere 
Vorwiderstände an den LED's.

Und wenn du den Reset betätigst, dann bleibt dein Programm ja kurze Zeit 
an einer Stelle stehen, deshalb wird nicht mehr geplext und die LEDs 
bekommen ihre volle Spannung.

von Karl H. (kbuchegg)


Lesenswert?

ISR (TIMER2_COMP_vect)
{
  PORTD ^= (1 << (PD5));
  update();
}
... und update macht alle 6 Spalten


Multiplexing macht man anders:

Bei jedem ISR Aufruf wird
  die aktuelle Spalte abgeschaltet.
  die nächste Spalte mit den richtigen Spaltenwerten bestückt
  diese Spalte eingeschaltet
  diese Spalte zur aktuellen Spalte erklärt.

und das wars. Die LED brennen bis zum nächsten ISR AUfruf, wo sie dann 
abgeschaltet werden und die jeweils nächste Spalte angezeigt wird.

Also nicht in einem ISR Aufruf alle 6 Spalten durchgehen, sondern 6 ISR 
Aufrufe bis alle 6 Spalten einmal abgearbeitet sind.

von Karl H. (kbuchegg)


Lesenswert?

Ich werd aus deiner update() Funktion nicht schlau.

Wie sieht denn deine Hardware aus?

von Luke S. (no3x)


Lesenswert?

Eine LED Matrix, 6 Breit 4 Hoch. Hab aber bisher nur 8 angeschlossen zum 
testen. Gesteuert werden sie mit einem CD4508B.
1 Datenbus (PB0..3)
1 Steuerbus (PD0..5)
Wie löse ich das am elegantesten @Karl heinz Buchegger?

von Karl H. (kbuchegg)


Lesenswert?

Luke Skywalker schrieb:
> Eine LED Matrix, 6 Breit 4 Hoch.

Und wie ist sie elektrisch angeschlossen?

(wozu die ganzen Abfragen da in deiner ISR. Das ist mir nicht klar)

> Hab aber bisher nur 8 angeschlossen zum
> testen. Gesteuert werden sie mit einem CD4508B.

Das ist ja nur ein Latch, verändert daher die logishe Funktion nicht.

> Wie löse ich das am elegantesten @Karl heinz Buchegger?

Bis jetzt hab ich noch nicht mal durch das Studium deiner ISR 
rausgefunden ob man besser Zeilen oder Spalten multiplex betreiben 
sollte

Also: wie ist das ganze angeschlossen.

von Luke S. (no3x)


Lesenswert?

Ich lade nachher ein Foto hoch, bin derzeit noch auf Arbeit.

Die Abfragen in der ISR sind für eine Uhr. Es soll letzendlich eine 
Binäruhr werden.

So wie ich jetzt multiplexe möchte ich es auch lassen, da die latch(es?) 
4 bit groß sind.

von Karl H. (kbuchegg)


Lesenswert?

OK.
Ich geh mal auf Zeilenmultiplexen.
D.h. 6 LED nebeneinander, die sind am Port D angeschlossen, richtig?

Und mit Port B wählt man aus, von welcher Zeile es jeweils die 6 
nebeneinander liegenden LED sein sollen

Anfangen möchte ich damit

int ledMatrix[6][4];

das ist Unsinn.

Du hast 6 nebeneinander liegende LED. Die können nur entweder ein oder 
aus sein.
D.h. von einem uint8_t kann jedes Bit wunderbar eine LED abdecken.
Daher definier ich:

uint8_t ledMatrix[4];

4 Bytes. In jedem Byte sind die unteren 6 Bit für jeweils 1 LED 
zuständig.
1
uint8_t ledMatrix[4];
2
uint8_t actLine;
3
uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };
4
5
ISR (TIMER2_COMP_vect)
6
{
7
  PORTB = 0;             // aktuelle Zeile abschalten, welche es auch immer war
8
9
  actLine++;             // welches ist die nächste Zeile?
10
  if( actLine == 4 )
11
    actLine = 0;
12
13
  PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese Zeile ausgeben
14
  PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend einschalten.
15
}

fertig. Thats it.
Die Bytes in ledMatrix werden reihum an die jeweils richtige Zeile 
ausgegeben.

Du brauchst jetzt noch eine Funktion, die dir in ledMatrix das jeweils 
richtige Bit setzt.
zb wenn die LED in der 3. Spalte / 2. Zeile gesetzt werden soll, dann 
muss
   ledMatrix[1] |= 1<<3;
ausgeführt werden.
ledMatrix[1], weil es sich um die 2.te Zeile handelt
1<<3, weil die 3. Spalte gemeint ist.

von Karl H. (kbuchegg)


Lesenswert?

Luke Skywalker schrieb:

> So wie ich jetzt multiplexe möchte ich es auch lassen, da die latch(es?)
> 4 bit groß sind.

Wie hast du die verteilt?
Ich werd aus deiner ISR nicht schlau, wie du die 6 LED einer Zeile 
angeschlossen hast.

Mit einem 4508 gehts ja wohl nicht.
Du hast 10 Signalleitungen, 1 4508 hat aber nur 8 Latches.
Ausserdem will der ja auch noch angesteuert werden. Da gibt es eine 
Strobe Leitung und ein Output Disable.
Was hast du damit gemacht?

(Der 4508 war eine Schnapsidee, was ist an einem ULN2803 falsch?)

von Luke S. (no3x)


Lesenswert?

D:4 Pins Eingänge (Zeilen)
Q:4 Pins deren Ausgänge
Strobe:1 Pin pro Latch (Spalte)
Reset: GND
Dis(EN): GND
und GND bei Latch A bzw. Vss bei Latch B.
Foto folgt dann und somit hoffentlich mehr Klarheit. Geduld.

Habe die 4508 hier liegen, deswegen verwende ich sie. Vielen Dank für 
die Info mit dem ULN.

von Karl H. (kbuchegg)


Lesenswert?

Luke Skywalker schrieb:

> Habe die 4508 hier liegen, deswegen verwende ich sie.

Wie auch immer.
Ist dir das Prinzip des Multiplexens jetzt klarer und wie man das 
konkret in einer ISR macht?

Eigentlich sehr einfach.

Deine Hardware ist im Code da oben jetzt noch nicht vollständig 
berücksichtigt, aber für dein 2 bisher bestückten Spalten sollte es auf 
jeden Fall erst mal reichen. Sofern ich den Anschluss richtig 
rausgelesen habe.

von Luke S. (no3x)


Angehängte Dateien:

Lesenswert?

So, da ist die Zeichnug.

Karl heinz Buchegger schrieb:
> int ledMatrix[6][4];
>
> das ist Unsinn.

Das ist das direkte Abbild meiner Matrix.

Karl heinz Buchegger schrieb:
> Ist dir das Prinzip des Multiplexens jetzt klarer und wie man das
> konkret in einer ISR macht?

Naja, also vom Prinzip her klappt das Multiplexen mit meinem Code ja 
bereits, deswegen will ich jetzt nicht alles umwerfen nur weil es 
schöner aussieht, wenn man Bitmanipulation benutzt.

Ich glaube wenn ich spaltenweise multiplexe macht es mehr Sinn, wegen 
dem 4-Bit Latch.

Also könnte ich die jeweils nächste Spalte die bearbeitet werden soll 
der Funktion update() mitgeben, den ganzen Port bestücken und mit einem 
mal anschalten. So bleiben die LED so lange an, bis die ISR neu 
aufgerufen wird und die nächste Reihe dran ist.

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
Noch kein Account? Hier anmelden.