MezData-Logo Creative Commons License 377 Lösungen Button :AVR: Gemultiplexte Siebensegment Anzeige

Ein Zähler wird durch einen Tastendruck um 1 erhöht. Der Zählerstand wird über eine 4 stellige 7-Segmentanzeige ausgegeben. Der Zählerstand wird in einem Feld mit 4 BCD-Stellen gespeichert.

Sieben Segmentanzeige mit 4 Stellen [FOUR DIGIT NUMERIC DISPLAY]

Die Anzeige ist gemultiplext, LED laufen auf gemeinsame Kathoden Dig1..Dig4 zusammen.
Durch Treiberbaustein werden die Stellen mit pos. Logik eingeschaltet.

Die Segmente a..f und der Dezimalpunkt DP sind mit PORTA verbunden.
Die Kathoden der Stellen sind über einen Schalttransistor mit PORTC verbunden.

PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 PC3 PC2 PC1 PC0
DP g f e d c b a Dig1 Dig2 Dig3 Dig4

Prinzip der Anzeige

Die Stellen Dig1..Dig4 werden nacheinander im Zeitmultiplexverfahren von rechts nach links eingeschaltet. Das Segmentmuster wird dazu auf PORTA ausgegeben und die jeweilige Stelle wird durch eine 1 auf dem entsprechenen Pin auf PORTC eingeschaltet. Die Werte der jeweiligen Stellen werden im SRAM in einem Feld anzeige gespeichert. Die Umwandlung der Werte in die Anzeigemuster der Ziffern sind im Programmspeicher in einem Feld bcd_7 hinterlegt. Eine Interrupt Service Routine ISR isr_timer bzw. ISR(TIMER0_OVF_vect) wird 400 mal pro Sekunde aufgerufen um die nächste Stelle an zu zeigen.

BCD / Hex nach 7 Segment umwandeln

BCD (Binary coded Dezimal) HEX
r
Zahl 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Pos. Log. 3F 06 5B 4F 66 6D 7D 07 7F 6F 77 7C 39 5E 79 71 50
Neg. Log C0 F9 A4 B0 99 92 82 F8 80 90 88 83 C6 A1 86 8E AF

Bei Druck auf Taste 1 zählt er hoch

[Multiplex7seg.asm] [Multiplex7segc.c]
;****** Multiplex7Seg 1.2 (c) Oliver Mezger 15.12.2010

.include "m16def.inc"

;*** Zuordnung zu den Ports ***
.equ SegmentIn      = PINA
.equ SegmentOut     = PORTA
.equ SegmentDdr     = DDRA
.equ TastaturIn     = PINB
.equ TastaturOut    = PORTB
.equ TastaturDdr    = DDRB
.equ StelleIn      = PINC
.equ StelleOut     = PORTC
.equ StelleDdr     = DDRC

.def null = R23     ;Register mit 0
.def tmp = R16      ;tmp-Register zum Arbeiten
.def tmp2 = R17
.def spalte = R18   ;Spalte fuer Darstellung
.def zeichen = R19  ;Index des Zeichens
.def durchlauf = R20;Zaehler fuer Zeichenwechsel
.def itmp = R21     ;tmp-Register fuer Interrupt
.def led = R22      ;RGB-LED animieren

    jmp reset       ;Einsprung nach Reset
    .org OVF0addr   ;Einsprung nach Timer0 Ueberlauf
    jmp isr_timer   ;nach ISR
    .org $2a        ;folgender Code ab Adresse $2a

isr_timer:
    in itmp,SREG    ;SREG retten
    push itmp
    ldi itmp, 150   ;Interrupttimer einstellen
    out TCNT0,itmp
    cpi spalte,0b00010000   ;wenn nicht Spalte 5
    brlo nspalte            ;dann naechste Spalte
    ldi spalte,1            ;Spalte1 initialisieren
    ldi XH, high(anzeige)   ;Datenzeiger initialisieren
    ldi XL, low(anzeige)
nspalte:                    ;naechste Spalte
    ld itmp,x+              ;lade Wert aus anzeige und erhoehe x
    ;wandle Wert in 7-Segmentmuster um
    ldi ZH, high(bcd_7*2)   ;Datenzeiger im Programmspeicher initialisieren
    ldi ZL, low(bcd_7*2)
    add ZL,itmp     ;Addiere Wert um zum Eintrag zu kommen
    adc ZH,null
    lpm itmp, z     ;lade Daten aus Flash-Programmspeicher
    out SegmentOut,itmp ;gib 7-Segmentmuster aus
    in itmp, StelleOut  ;schalte die Stelle ein
    andi itmp,0b11110000
    or itmp,spalte
    out StelleOut,itmp
    lsl spalte          ;naechste Stelle
    pop itmp            ;SREG wieder herstellen
    out SREG,itmp
    reti            ;return from Interrupt

reset:                  ;Hauptprogramm
    ldi tmp,high(RAMEND);Oberste RAM-Adresse holen
    out SPH, tmp        ;Stack-Pointer initialisieren
    ldi tmp,low(RAMEND) 
    out SPL, tmp
    ldi tmp,$ff
    out SegmentDdr,tmp  ;SegmentPort als Ausgang
    out StelleDdr,tmp   ;StellenPort als Ausgang
    ldi tmp,0b11101111  ;Tastatur init
    out TastaturOut,tmp
    ldi tmp,0b00010000
    out TastaturDdr,tmp
    ldi tmp,3       ;Systemtakt / 64
    out TCCR0,tmp   ;Timer setzen
    in tmp,TIMSK    ;Timerinterrupt einschalten
    ori tmp, 1 << TOIE0
    out TIMSK,tmp
    ldi null,0
    ldi XH, high(anzeige)   ;Datenzeiger initialisieren
    ldi XL, low(anzeige)
    st X+,null              ;Feld mit 0 initialisieren
    st X+,null
    st X+,null
    st X+,null
    sei             ;globale Interruptfreigabe
loop:               ;Schleife fuer Tastaturabfrage
    sbis TastaturIn,0 ;wurde Taste gedrueckt?
    rjmp taste
    inc led         ;Animation der FarbLED
    andi led,7      ;nur von 0 bis 7
    mov tmp,led
    swap tmp        ;vertausche Nibble
    lsl tmp         ;und noch einmal schieben
    in tmp2,StelleOut  ;schreibe auf StelleOut
    andi tmp2,0b00011111
    or tmp2,tmp
    out StelleOut,tmp2
    rcall wait
    rjmp loop
taste:
    sbis TastaturIn,0 ;Taste immer noch gedrueckt
    rjmp taste
    rcall zaehle      ;wenn losgelassen erhoehe zaehler um 1
    rjmp loop

zaehle:     ;der Zaehler wird um 1 erhoeht
    ldi YH, high(anzeige)   ;Datenzeiger initialisieren
    ldi YL, low(anzeige)
nextstelle:
    ld tmp,y        ;lade Stelle
    cpi tmp,9       ;ist sie 9?
    breq ueberlauf  ;Ueberlauf auf naechste Stelle
    inc tmp         ;sonst erhoehe um 1
    st y,tmp
    ret
ueberlauf:          ;erhoehe naechste Stelle
    st y,null
    adiw y,1
    rjmp nextstelle

wait:
    ldi tmp,50      ;aeusseren Zaehler laden 50 ms
wl1:
    ldi tmp2,250    ;inneren Zaehler 1ms bei 1Mhz
wl2:
    nop
    dec tmp2        ;tmp2--
    brne wl2        ;Sprung wenn nicht null
    dec tmp         ;tmp--
    brne wl1        ;Sprung wenn nicht null
    ret

bcd_7:  ;Feld fuer BCD nach 7-Segment Wandlung
    .db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f

    .dseg; Datenbereich
anzeige: ;Feld fuer die 4 Stellen 
    .byte 4; Feld mit 4 Byte
// Multiplex7seg V 1.0 (c) Oliver Mezger 9.1.2010
#include <avr/io.h>     // Definitionen laden
#include <util/delay.h> // Delay-Bibliothek laden
#include <avr/interrupt.h>

// Zuordnungen zu Ports
#define SegmentIn   PINA
#define SegmentOut  PORTA
#define SegmentDdr  DDRA
#define TastaturIn  PINB
#define TastaturOut PORTB
#define TastaturDdr DDRB
#define StelleIn   PINC
#define StelleOut  PORTC
#define StelleDdr  DDRC

const unsigned char bcd_7[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
volatile unsigned char anzeige[4]={0,0,0,0};

ISR(TIMER0_OVF_vect){
  static unsigned char spalte=1,dzeiger=0;
  TCNT0=150;
  SegmentOut = bcd_7[anzeige[dzeiger++]]; // Segmentmuster ausgeben
  StelleOut = (StelleOut & 0b11100000) | spalte; // Stelle einschalten
  spalte *= 2;  // naechste Spalte
  if (dzeiger >3){
    spalte = 1;
    dzeiger = 0;
  }
}

void zaehle(){  // erhoehe Zaehler um 1
  unsigned char i;
  anzeige[0]++;
  for(i=0;i<3;i++){
    if (anzeige[i] > 9){
      anzeige[i]=0;
      anzeige[i+1]++;
    }
  }
}
int main(){
  unsigned char led =0;
  TCCR0 = 3;    // Systemtakt / 64
  TIMSK |= (1<<TOIE0); // Timerinterrupt frei geben
  SegmentDdr=0xff;  // Ausgang
  StelleDdr=0xff;  // Ausgang
  TastaturOut = 0b11101111;
  TastaturDdr = 0b00010000;
  sei();        // globale Interruptfreigabe
  while(1){     // Endlosschleife
    if ((TastaturIn & 1) ==0){
      while((TastaturIn & 1) ==0);
      zaehle();
    }
    led++;
    led &= 7;
    StelleOut = (StelleOut & 0b00011111) | (led<<5);
    _delay_ms(100); // warte 100ms
  }
  return 0;
}