mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timing Multiplexen


Autor: Luke Skywalker (no3x)
Datum:

Bewertung
0 lesenswert
nicht 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.
#define F_CPU  8000000UL  //  8 MHz 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
//Variablen für die Zeit
volatile unsigned int  millisekunden;
//volatile unsigned int sekunde;
volatile unsigned int minute;
volatile unsigned int stunde;
 
int Zeit[6];
int dezimalzahl,dezimalzahl_rest,dezimalzahl_new,durchlauf = 0;
int bin_zeile = 4;
uint8_t temp_bin = 0;
int Zeit[6];
int binZeit[6][4];
int ledMatrix[6][4];
uint8_t sekunde;
int timer2 = 0;
volatile unsigned char display_dim = 30;

void update(void) {
  for (int ausgabe_spalte=0; ausgabe_spalte<6; ausgabe_spalte++) {

    switch(ausgabe_spalte) {
        case 0:
          PORTD = (1 << PD0);
        break;
          case 1:
          PORTD = (1 << PD1);
        break;
        default:
          PORTD = 0;
        break;
    }
    for (int ausgabe_zeile=0; ausgabe_zeile<4; ausgabe_zeile++) {
    
      PORTB = 0;
      if(ausgabe_zeile == 0) {  
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
          case 0:
            PORTB &= (0 << PB0);
          break;
          case 1:
            PORTB = (1 << PB0);
          break;
          default:
            PORTB = 0;
          break;
        }
      }

      if(ausgabe_zeile == 1) {
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
          case 0:
            PORTB &= (0 << PB1);
          break;
          case 1:
            PORTB = (1 << PB1);
          break;
          default:
            PORTB = 0;
          break;
        }
      }

      if(ausgabe_zeile == 2) {
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
          case 0:
            PORTB &= (0 << PB2);
          break;
          case 1:
            PORTB = (1 << PB2);
          break;
          default:
            PORTB = 0;
          break;
        }
      }

      if(ausgabe_zeile == 3) {
        switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
          case 0:
            PORTB &= (0 << PB3);
          break;
          case 1:
            PORTB = (1 << PB3);
          break;
          default:
            PORTB = 0;
          break;
        }
      }
      for (unsigned long i = 0; i < display_dim; i++) { asm volatile("nop"::); }  //  Verzögerung  
      PORTB = 0;
    }
    
  }  PORTD = 0;PORTB = 0;
}
int main(void)
{
  //PINS definieren
  DDRD |= (1<<(PD6)) | (1<<(PD5)) | (1<<(PD4)) | (0<<(PD3)) | (0<<(PD2)) | (1<<(PD1)) | (1<<(PD0));
  PORTD = 0;

  DDRA |= (1 << PA0) | (1 << PA1) | (1 << PA2) | (1 << PA3) | (1 << PA4) | (1 << PA5) | (1 << PA6) | (1 << PA7);
  PORTA = 0;

  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
  PORTB = 0;

  //Timer initialisieren
  TCCR0 = (1<<(WGM01));// CTC aktivieren
  TCCR0 |= (1<<(CS01)) | (1<<(CS00)); // Presaler: 64
  OCR0 = 124;  // 1ms
  TIMSK |= 1<<OCIE0;

  //Timer Prescaler auf /8
  TCCR2 |= (1 << CS02) | (0 << CS01);
  //Timer2_Compare Interrupt aktivieren
  TIMSK |= (1 << OCIE2);
  // compare wert auf 125 setzen um eine interruptfrequenz von 1ms zu bekommen 
  // ((1000000/8)/1000) = 125
  OCR2 = 1249;

  
  //INT0 initialisieren
  GICR = (1<< INT0);
  MCUCR = (1<<ISC01) | (0<<ISC00);
   
  // Global Interrupts aktivieren
  sei();
   
  while(1)
  {
    //Einzelne Stellen extrahieren
    Zeit[0] = sekunde / 10;
    Zeit[1] = sekunde % 10;
    Zeit[2] = sekunde / 10;
    Zeit[3] = sekunde % 10;
    Zeit[4] = sekunde / 10;
    Zeit[5] = sekunde % 10;
    /*
    //Einzelne Stellen extrahieren
    Zeit[0] = stunde / 10;
    Zeit[1] = stunde % 10;
    Zeit[2] = minute / 10;
    Zeit[3] = minute % 10;
    Zeit[4] = sekunde / 10;
    Zeit[5] = sekunde % 10;
    */  

    //Stellen in Binär umwandeln und in Array schreiben
    for(int ix = 0; ix<6; ix++) {
        dezimalzahl = Zeit[ix];
      for (int k=0; k<6; k++) {
        binZeit[ix][k] = dezimalzahl & 1;
        dezimalzahl>>=1;
      }
    }
    
    //Binärzahlen-Array in ledMatrix einsetzen
    for (int spalte=0; spalte<6; spalte++) {
      
      if(debug == 1) { printf("bearbeite Spalte Nr. %d\n", spalte); }
      bin_zeile = 4;
      for (int zeile=0; zeile<4; zeile++) {
        if(bin_zeile > 0)  { bin_zeile--; }
        ledMatrix[spalte][zeile] = binZeit[spalte][bin_zeile];
      }
    }
  }
}

ISR (TIMER0_COMP_vect)
{
  millisekunden++;
  if(millisekunden == 100) {  
    //PORTD ^= (1<<(PD6));
    sekunde++;
    millisekunden = 0;
    if(sekunde == 60)
    {
      minute++;
      sekunde = 0;
    }
    if(minute == 60)
    {
      stunde++;
      minute = 0;
    }
    if(stunde == 24)
    {
      stunde = 0;
    }
  }
}

ISR (TIMER2_COMP_vect)
{
  PORTD ^= (1 << (PD5));
  update();
}

ISR(INT0_vect) {
  PORTD = (1<< PD0);  
}

Autor: Rubelus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich werd aus deiner update() Funktion nicht schlau.

Wie sieht denn deine Hardware aus?

Autor: Luke Skywalker (no3x)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Luke Skywalker (no3x)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
uint8_t ledMatrix[4];
uint8_t actLine;
uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };

ISR (TIMER2_COMP_vect)
{
  PORTB = 0;             // aktuelle Zeile abschalten, welche es auch immer war

  actLine++;             // welches ist die nächste Zeile?
  if( actLine == 4 )
    actLine = 0;

  PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese Zeile ausgeben
  PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend einschalten.
}

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?)

Autor: Luke Skywalker (no3x)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Luke Skywalker (no3x)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.