Multiplexing

Aus Fingers Wiki
Zur Navigation springen Zur Suche springen

Ein AVR hat natürlich nicht eine unbegrenzte Anzahl an Portpins und somit lohnt sich bei mehr als einer 7-Segment-Anzeige fast immer das Multiplexing. Bevor es zur Berechnung der Komponenten geht und zur Programmierung, erstmal ein paar Grundschaltungen.

Funktionsweise

Und wie funktioniert das jetzt? Der Trick dahinter ist, dass immer nur eine Anzeige leuchtet und dann auf die nächste umgeschalten wird. Also das Programm legt am Zeilentreiber das Bitmuster für eine 1 an und schaltet den Spaltentreiber für die linke Anzeige durch. Die 1 leuchtet auf, nach einer gewissen Zeit wird der Spaltentreiber abgeschalten und z.B. das Bitmuster für eine 2 angelegt und der Spaltentreiber für das Display neben dem Linken eingeschalten. Somit leuchtet dort die 2 auf. Eine gewisse Zeit: Alle Displays leuchten nur für 1/4 der Zeit auf durch das Weiterschalten. Da immer nur ein Display leuchtet, muss dieser Vorgang schnell wiederholt werden, damit es nicht flimmert. Zu empfehlen sind ab 50 Hz aufwärts, also 200 mal weiterschalten pro Sekunde.

Es müssen nicht unbedingt 7-Segment-Anzeigen verwendet werden, es sind auch Einzel-LEDs so multiplexbar und damit ergeben sich viele Muster.

Schaltungen

Grundschaltung

Hier die Minimalbeschaltung für ein 4-fach Multiplexing. Der ULN2803 ist ein Transistorarray, dieser wird benötigt da ein AVR Portpin nur 40 mA liefern kann, bzw ein gesamter Port 200 mA. Dieser heißt ab jetzt Zeilentreiber. Die PNP-Transistoren sind die Spaltentreiber und werden direkt vom AVR angesteuert.

Multiplex Grundschaltung

Grundschaltung - Dimmen

Nun wird nicht immer die volle Dröhnung bei der Helligkeit benötigt, also sollte die gesamte Anzeige per PWM gedimmt werden können. Wenn die PWM synchron zu den Ziffern umgeschalten wird, dann lassen sich natürlich auch unterschiedlich helle Ziffern realisieren. Dafür hängen die Spaltentreiber nicht mehr direkt an der Versorgungsspannung, sondern an einem weiteren PNP-Transistor, welcher mit der PWM gespeist wird. Ein Dimmen über den GND Pin des Zeilentreibers ist hier nicht möglich. Durch die internen Schutzdioden des ULN2803 würden dann andere Segmente mit aufleuchten.

Das Dimmen ist besonders wichtig, wenn die Software für den AVR noch in Entwicklung ist. Durch die Stromüberhöhung (dazu später mehr) würde die Anzeige durchbrennen, wenn das Programm sich aufhängt. Bei einem 4-fach Multiplexing also maximal 25 % PWM oder bei 8-fach 12 %.

Multiplex Grundschaltung mit PWM

weitere Pins sparen

Beim 4-fach Multiplexing mit Transistorendirektansteuerung werden 8+4 Pins benötigt, aber es geht mit noch weniger. Dafür wird allerdings ein externes Logik-IC benötigt (74HC42). Dies ist ein BCD zu Dezimaldekoder und invertiert auch gleich noch die Ausgänge für die PNP-Spaltentreiber. Damit werden nur noch 8+2 Pins benötigt, klingt jetzt nach keiner großen Ersparnis. Bei 8-fach Multiplexing wird da aber schon eine Menge eingespart: 8+8 Pins für direkt- und 8+3 Pins für die Logik-IC-Variante. Weiterhin muss man jetzt nicht mehr mit Bitoperatoren arbeiten (Ausgang löschen und setzen), sondern die Ausgabe der Spaltenzahl genügt.

Multiplex Pins sparen mit BCD

weitere Pins sparen - Dimmen

Natürlich lässt sich auch hier mit PWM arbeiten, dazu die PWM an Eingang D anschließen.

Multiplex Pins sparen mit BCD mit PWM

Vorwiderstandsberechnung

Normalerweise wird der Vorwiderstand auf 20mA pro LED berechnet, doch das geht hier schief. Denn jede LED leuchtet nur zu 1/4 der Zeit auf, hat also auch nur noch 1/4 der Helligkeit. Wie wirds wieder heller? Strom aufdrehen! Ein Blick ins Datenblatt der LED (Anzeige) verrät, dass diese einen höheren Impulsstrom vertragen. Genau das wird für Multiplexing benötigt. Beim 4-fach Multiplex wird der 4-fache Strom benötigt, also 80 mA.

Bei der Berechnung des Vorwiderstands fällt auch langsam der Spannungsabfall über die Treiber ins Gewicht. Also mal vorrechnen:

Über dem BC327 fällt laut Datenblatt bei 80mA bis 560mA (es leuchtet ja eine variable Anzahl an Segmenten) eine Spannung von ca 0,8V ab = U1, (bei der PWM-Variante natürlich das doppelte).

Der ULN2803 verbrät 0,9 V bei 80 mA = U4. Hier wieder die Betrachtung eines einzelnen Segmentes, der PNP Transistor versorgt eine gesamte 7 Segment Anzeige (Spalte) und der ULN2803 nur eine LED/Segment (Zeile).

Die LED will 2,2 V sehen = U2.

Also müssen 5 V-U1-U2-U4 = 1,1 V am Vorwiderstand abfallen.

Das macht nach dem ohmschen Gesetz: 1,1 V/80 mA = 13,75 Ohm. Solch einen Widerstand gibt es natürlich nicht, also nehmen wir 14 oder 16 Ohm.

Strompfad


Die Kollektor-Emitter Spannung (= der Spannungsabfall) der Transistoren aus dem ULN2803 und dem BC327 lässt sich aus dem Datenblatt entnehmen. Genauer gesgat bei den Diagrammen wie Collector-Emitter Saturationvoltage.

Programmierung

Der Zeilentreiber hängt an PORTB und der Spaltentreiber an PORTD, Variante ohne Logik-IC. Die Buffer lassen sich natürlich auch in ein Array packen.

Das Weiterschalten der Spalten erfolgt in einem Timerinterrupt und die Buffer werden irgndwann/irgendwie im Hauptprogramm gefüllt. Somit ist beides voneinander unabhängig.

Switchcase

<source line lang="c" > //ATmega16

//7seg Ausgabebuffer für Multiplex //wird vom Hauptprogramm beschrieben volatile uint8_t Buffer_1 = 0; volatile uint8_t Buffer_2 = 0; volatile uint8_t Buffer_3 = 0; volatile uint8_t Buffer_4 = 0;

//Timer einstellen (alle 1ms ein ISR) TIMSK = (1<<OCIE0); //Overflow ISR TCCR0 = (1<<WGM01)|(1<<CS02); //CTC Mode OCR0 = 16; //64 bei 16MHz sei();

ISR (TIMER0_COMP_vect) { //7-Segment-Anzeigen multiplexen static uint8_t stelle = 1; PORTD |= 0b01000111; //Alle Spaltentreiber aus switch(stelle){ case 1: PORTB = Buffer_1; //Buffer ausgeben stelle = 2; //neue Stelle PORTD &= ~(1<<PD6); //Spalte aktivieren break;

case 2: PORTB = Buffer_2; stelle = 3; PORTD &= ~(1<<PD2); break;

case 3: PORTB = Buffer_3; stelle = 4; PORTD &= ~(1<<PD0); break;

case 4: PORTB = Buffer_4; stelle = 1; PORTD &= ~(1<<PD1); break;

default: break; } } </source>

Array

<source line lang="c" > //ATmega16

//7seg Ausgabebuffer für Multiplex //wird vom Hauptprogramm beschrieben volatile uint8_t Buffer[4] = {0, 0, 0, 0};

//Angaben an welchem Pin der Anodentransistor hängt const uint8_t anoden[4] = {1<<PD6, 1<<PD2, 1<<PD0, 1<<PD1};


//Timer einstellen (alle 1ms ein ISR) TIMSK = (1<<OCIE0); //Overflow ISR TCCR0 = (1<<WGM01)|(1<<CS02); //CTC Mode OCR0 = 16; //64 bei 16MHz sei();

ISR (TIMER0_COMP_vect) { //7seg anzeigen multiplexen static uint8_t stelle = 0; PORTD |= 0b01000111; //Alle Spaltentreiber aus

PORTB = Buffer[stelle]; //Buffer ausgeben PORTD &= ~anoden[stelle]; //Spalte aktivieren stelle = (stelle+1)%4; //neue Stelle } </source>

Zeichensatz

7 Segment

<source line lang="c" >

  1. include <avr/pgmspace.h>

//Sollen nur Zahlen angezeigt werden -> 0 //Hexadezimal -> 1 //so viele Buchstaben wie möglich -> 2

  1. define OUT 1

//Pin für das entsprechende Segment angeben

  1. define pa PC0
  2. define pb PC1
  3. define pc PC2
  4. define pd PC3
  5. define pe PC4
  6. define pf PC5
  7. define pg PC6
  8. define dp PC7
  1. if (OUT == 0)
  2. define LENGTH 10
  3. endif
  4. if (OUT == 1)
  5. define LENGTH 16
  6. endif
  7. if (OUT == 2)
  8. define LENGTH 45
  9. endif

const uint8_t segm[LENGTH] PROGMEM = { (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf), //0,0 (1<<pb)|(1<<pc), //1,1 (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pg), //2,2 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pg), //3,3 (1<<pb)|(1<<pc)|(1<<pf)|(1<<pg), //4,4 (1<<pa)|(1<<pc)|(1<<pd)|(1<<pf)|(1<<pg), //5,5 (1<<pa)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //6,6 (1<<pa)|(1<<pb)|(1<<pc), //7,7 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //8,8 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pf)|(1<<pg) //9,9

  1. if (OUT)

, (1<<pa)|(1<<pb)|(1<<pc)|(1<<pe)|(1<<pf)|(1<<pg), //10,A (1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //11,b (1<<pd)|(1<<pe)|(1<<pg), //12,c (1<<pa)|(1<<pa)|(1<<pa)|(1<<pa), //13,d (1<<pa)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //14,E (1<<pa)|(1<<pe)|(1<<pf)|(1<<pg) //15,F

  1. endif /*OUT*/
  2. if (OUT == 2)

, (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pg), //16,a (1<<pa)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf), //17,G (1<<pa)|(1<<pb)|(1<<pc)|(1<<pf)|(1<<pg), //18,g (1<<pb)|(1<<pc)|(1<<pe)|(1<<pf)|(1<<pg), //19,H (1<<pc)|(1<<pe)|(1<<pf)|(1<<pg), //20,h (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe), //21,J (1<<pd)|(1<<pe)|(1<<pf), //22,L (1<<pe)|(1<<pf), //23,l (1<<pa)|(1<<pb)|(1<<pc)|(1<<pe)|(1<<pf), //24,N (1<<pc)|(1<<pe)|(1<<pg), //25,n (1<<pc)|(1<<pd)|(1<<pe)|(1<<pg), //26,o (1<<pa)|(1<<pb)|(1<<pe)|(1<<pf)|(1<<pg), //27,P (1<<pe)|(1<<pg), //28,r (1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf), //29,U (1<<pc)|(1<<pd)|(1<<pe), //30,u (1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //31,t (1<<pb)|(1<<pc)|(1<<pd)|(1<<pf)|(1<<pg), //32,y (1<<pg), //33,- (1<<pd), //43,_ (1<<pb)|(1<<pf) //44,"

  1. endif /*(OUT == 2)*/

};

</source>

16 Segment

<source line lang="c" >

  1. include <avr/pgmspace.h>

//Bit für das entsprechende Segment angeben

  1. define pa 14
  2. define pb 15
  3. define pc 11
  4. define pd 5
  5. define pe 0
  6. define pf 2
  7. define pg 6
  8. define ph 8
  9. define pk 10
  10. define pm 12
  11. define pn 13
  12. define pp 9
  13. define pr 3
  14. define ps 1
  15. define pt 4
  16. define pu 7

const uint16_t seg16[68] PROGMEM = { (1<<pc)|(1<<pd), //33 - ! (1<<ph)|(1<<pm), //34 - " 65535, //35 - # (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<ph)|(1<<pm)|(1<<pp)|(1<<ps)|(1<<pu), //36 - $ (1<<pa)|(1<<pd)|(1<<pe)|(1<<ph)|(1<<pm)|(1<<pn)|(1<<pp)|(1<<ps)|(1<<pt)|(1<<pu), //37 - % (1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pm)|(1<<pn)|(1<<ps)|(1<<pt), //38 - & (1<<pn), //39 - ´ (1<<pn)|(1<<pr), //40 - ( (1<<pt)|(1<<pk), //41 - ) (1<<pk)|(1<<pm)|(1<<pn)|(1<<pp)|(1<<pr)|(1<<ps)|(1<<pt)|(1<<pu), //42 - * (1<<pm)|(1<<pp)|(1<<ps)|(1<<pu), //43 - + (1<<pt), //44 - , (1<<pp)|(1<<pu), //45 - - (1<<pf)|(1<<pg)|(1<<ps)|(1<<pu), //46 - . (1<<pn)|(1<<pt), //47 - / (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pn)|(1<<pt), //48 - 0 (1<<pc)|(1<<pd)|(1<<pn), //49 - 1 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<pu)|(1<<pp), //50 - 2 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pp), //51 - 3 (1<<pc)|(1<<pd)|(1<<ph)|(1<<pp)|(1<<pu), //52 - 4 (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<ph)|(1<<pp)|(1<<pu), //53 - 5 (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //54 - 6 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd), //55 - 7 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //56 - 8 (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<ph)|(1<<pp)|(1<<pu), //57 - 9 (1<<pm)|(1<<ps), //58 - : (1<<pm)|(1<<pt), //59 - ; (1<<pn)|(1<<pr), //60 - < (1<<pe)|(1<<pf)|(1<<pp)|(1<<pu), //61 - = (1<<pt)|(1<<pk), //62 - > (1<<pb)|(1<<pc)|(1<<pp)|(1<<ps), //63 - ? (1<<pa)|(1<<pb)|(1<<pc)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pm)|(1<<pp), //64 - @ (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //65 - A (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pm)|(1<<pp)|(1<<ps), //66 - B (1<<pa)|(1<<pb)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph), //67 - C (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pm)|(1<<ps), //68 - D (1<<pa)|(1<<pb)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //69 - E (1<<pa)|(1<<pb)|(1<<pg)|(1<<ph)|(1<<pu), //70 - F (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pp), //71 - G (1<<pc)|(1<<pd)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //72 - H (1<<pa)|(1<<pb)|(1<<pe)|(1<<pf)|(1<<pm)|(1<<ps), //73 - I (1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg), //74 - J (1<<pg)|(1<<ph)|(1<<pn)|(1<<pr)|(1<<pu), //75 - K (1<<pe)|(1<<pf)|(1<<pg)|(1<<ph), //76 - L (1<<pc)|(1<<pd)|(1<<pg)|(1<<ph)|(1<<pk)|(1<<pn), //77 - M (1<<pc)|(1<<pd)|(1<<pg)|(1<<ph)|(1<<pk)|(1<<pr), //78 - N (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph), //79 - O (1<<pa)|(1<<pb)|(1<<pc)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pu), //80 - P (1<<pa)|(1<<pb)|(1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph)|(1<<pr), //81 - Q (1<<pa)|(1<<pb)|(1<<pc)|(1<<pg)|(1<<ph)|(1<<pp)|(1<<pr)|(1<<pu), //82 - R (1<<pa)|(1<<pb)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<ph)|(1<<pp)|(1<<pu), //83 - S (1<<pa)|(1<<pb)|(1<<pm)|(1<<ps), //84 - T (1<<pc)|(1<<pd)|(1<<pe)|(1<<pf)|(1<<pg)|(1<<ph), //85 - U (1<<pg)|(1<<ph)|(1<<pn)|(1<<pt), //86 - V (1<<pc)|(1<<pd)|(1<<ph)|(1<<pg)|(1<<pr)|(1<<pt), //87 - W (1<<pk)|(1<<pn)|(1<<pr)|(1<<pt), //88 - X (1<<pk)|(1<<pn)|(1<<ps), //89 - Y (1<<pa)|(1<<pb)|(1<<pe)|(1<<pf)|(1<<pn)|(1<<pt), //90 - Z (1<<pb)|(1<<pe)|(1<<pm)|(1<<ps), //91 - [ (1<<pk)|(1<<pr), //92 - backslash (1<<pa)|(1<<pf)|(1<<pm)|(1<<ps), //93 - ] (1<<pb)|(1<<pc)|(1<<pn)|(1<<pt), //94 - ^ (1<<pe)|(1<<pf), //95 - _ (1<<pk), //96 - ` (1<<pb)|(1<<pe)|(1<<pm)|(1<<ps)|(1<<pu), //123 - { (1<<pm)|(1<<ps), //124 - | (1<<pa)|(1<<pf)|(1<<pm)|(1<<pp)|(1<<ps), //125 - } (1<<pc)|(1<<pg)|(1<<pp)|(1<<pu) //126 - ~ };

uint16_t get_seg16(uint8_t chara){ //Werte außerhalb der ASCI Tabelle rausfiltern if ((chara < 33) || (chara > 126)){ return 0; } //Symbole und Großbuchstaben if (chara < 97){ return pgm_read_word(&seg16[chara-33]); } //Symbole nach Kleinbuchstaben if (chara > 122){ return pgm_read_word(&seg16[chara-59]); } //Kleinbuchstaben als Großbuchstaben anzeigen return pgm_read_word(&seg16[chara-65]); } </source>