MezData-Logo

Modellbau-Servotester mit Pulsweiten-Anzeige

Synopsis [Servos] Keywords Modellbau-Servo, Multiplexanzeige, PWM-Signal, Analog/Digitalwandung

Ein Modellbauservotester mit 3 stelliger Anzeige

Ich wollte schon lange einen Servotester mit Anzeige der Pulsdauer bauen. Über ein Potentiometer wird der Winkel des Servos eingestellt. Eine 3 Farb-LED zeigt die Neutralstellung an. Bei der Wahl des Bausteins und dessen Pin-Belegung müssen die notwendigen Funktionen und die damit verbundenen Ausgänge bedacht werden:
  1. Pulsweitenmodulation
  2. A/D-Wandler

Ich habe mich für einen [ATmega 8] entschieden (lag auch noch rum und die Pins sollten reichen).
Gibt's für 1,35 € bei [Pollin].

Features des Testers

Ansteuerung von Modellbau Servos

Ein Modellbauservo sollte spätestens alle 20 ms einen Steuerimpuls bekommen. Die Impulslänge ist proportional zum Stellwinkel des Servos. Sie bewegt sich zwischen 0,3 und 2,7 ms. Für den Modellbauer interessant ist vor allem die Impulsdauer der Mittelstellung des Servos - hier 1,4 ms. Ich möchte eine Anzeige mit einer Auflösung von 10 µs also ein Anzeigebereich von 0,20 ms bis 3,00 ms.

Die erste Überlegung betrifft das Timing zur Erzeugung des PWM-Signals.
Der Timer wird mit einem vorgeteilten von der CPU-Frequenz abgeleiteten Signal betrieben. Mögliche Teilverhältnisse sind: 1,8,64,256,1024. Der Controller hat einen internen Oszillator mit folgenden wählbaren Frequenzen: 1,2,4,8 MHz. Für 1 MHz wird eine Genauigkeit von +- 3% angegeben (kann durch weitere Kalibration noch gesteigert werden). Siehe Seite 30ff der [Doku zu ATmega8].

Neutralstellung und Endausschläge verschiedener Hersteller

Hersteller Max µs Neutral µs Min µs
robbe Futaba 2120 1520 920
robbe alternativ 2300 1600 1000
Modelcraft Servotester 2000 1500 1000
Graupner 2000 1500 1000

Timer und Pulsweitenmodulation

Die Pulsweite soll in 10 µs Servo-Schritten verstellbar sein. Hier die naheliegenden Kombinationen:

CPU Frequenz Vorteiler µs pro Zahl Bewertung
1 MHz 1:1 1 Int. Oszillator ist vorkalibriert, Standard, Multiplikation mit 10 notwendig
4 MHz 1:8 2 Mehraufwand bei Kalibrierung, Multiplikation mit 5 notwendig
8 MHz 1:8 1 Mehraufwand bei Kalibrierung, Multiplikation mit 10 notwendig
Gewählt 1 MHz mit Vorteiler 1:1, zum Multiplizieren mit 10 gibt es ja beim Mega8 praktische Befehle.

Um die 20 ms = 20 000 µs zu zählen brauchen wir den 16 Bit Timer1 -Doku Seite 76ff. Betriebsart Fast PWM Mode siehe Seite 90ff. Der Timer zählt von 0 bis zu einem einstellbaren TOP Wert und springt dann wieder auf 0. Ein Ausgang kann beim Sprung von TOP auf 0 gesetzt und bei Erreichen eines einstellbaren Wertes OCR1A wieder rückgesetzt werden.

ICR1 als TOP weil nicht verändert mit Wert 19 999 für 20 ms Wiederholungsintervall, OCR1A = Servo-Schritt * 10. Ausgang ist PB1 (OC1A).

Einstellungen bei Initialisierung Servosignal ausgeben

Register Bit 7 6 5 4 3 2 1 0 Beschreibung
TCCR1A Bedeutung COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10 Ausgang OC1A bei 0 setzen und bei OCR1A löschen
Wert 1 0 0 0 0 0 1 0

Waveform Generation Mode: Fast PWM mit ICR1 als TOP

TCCR1B Bedeutung ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
Wert 0 0 0 1 1 0 0 1 Keine Vorteilung Timer mit CPU-CLK (No prescaling)

A/D Wandler der ATmega Bausteine

Die Wandlung funktioniert nach dem Sukzessive-Approximations-Prinzip mit 10 Bit Auflösung. Es stehen im 28 Pol-PDIP-Gehäuse 5 Eingänge zur Verfügung. Man lese in der [Doku zu ATmega8] ab Seite 196 für genauere Details. Es müssen Entscheidungen getroffen werden:

Einstellungen bei Initialisierung

Register Bit 7 6 5 4 3 2 1 0 Beschreibung
ADMUX Bedeutung REFS1 REFS0 ADLAR - MUX3 MUX2 MUX1 MUX0 Interne Referenzspannung, Wert ist rechtsbündig in ADCH:ADCL
Wert 1 1 0 0 0 1 0 1 Poti an PC5 (ADC5)
ADCSRA Bedeutung ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPS0 Wandler an, Start mit ADSCS, Interrupt frei
Wert 1 0 0 0 1 1 0 0 Vorteiler auf CLK/16

Sieben Segmentanzeige mit gemeinsamer Anode

In der Bastelkiste fanden sich noch 7-Segmentanzeigen mit der Aufschrift [HP 5082-7730].

Die Segmente haben eine gemeinsame Anode und der Dezimalpunkt ist links.

Die Anzeige ist gemultiplext, LED laufen auf gemeinsame Anode Dig1..Dig3 zusammen.
Durch einen Transistor werden die Stellen (Spalten) mit neg. Logik eingeschaltet. Ein Segment leuchtet wenn der Spaltentransistor und die Segmentkatode log. Null sind.

Segmente Stelle
PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0 PC2 PC1 PC0
DP g f e d c b a Dig1 Dig2 Dig3
6 11 2 7 8 10 13 1 3 3 3

Berechnung der Widerstände:

Durch die 8 Segment-LEDs (incl. Dezimalpunkt) sollen 20 mA Strom (ISegment) fließen. Bestimmendes Bauteil ist hierbei Rseg. Die Spannung an Rseg ist 5V - Ueb - Ud = 5V - 0,1V - 1,8V = 3,1V. Rseg = 3,1V / 20 mA = 155 Ohm. Zur Verfügung stehen 150 Ohm oder 180 Ohm - gewählt Rseg = 150 Ohm.

Durch den Transistor fließen maximal 8 * 20 mA = 160 mA. Ich habe noch [BC307B] Transitoren, deren minimaler Stromverstärkungsfaktor (Ic/Ib) Hfe liegt zwischen 222 und 455, sie können gerade eben den maximalen Strom aufnehmen. Da sie als Schalter fungieren sollen übersteuere ich sie 10 fach und gehe von einem Stom Ib = 160 mA / 20 = 8 mA aus. An Rb fällt eine Spannung von URb = 5V - Ube = 5V - 0,7V = 4,3V ab. Somit ist Rb = 4,3V / 8 mA = 537 Ohm - gewählt Rb = 560 Ohm.

BCD nach 7 Segment umwandeln negative Logik

Binär 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
Hex 0 1 2 3 4 5 6 7 8 9 A B C D E F
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

Multiplexen mit Timer0

Der 8 Bit Timer0 wird mit 1/8 Systemtakt betrieben und zählt damit von 0 bis 255. Mittels Interrupt wird so 488 mal pro Sekunde ein Anzeigenstellenwechsel durchgeführt.

3 Farb LED

Ansicht von Oben
Eine 3 Farb LED soll die Servostellung anzeigen Blau = Mitte, Grün < Mitte und Rot > Mitte.

Die Vorwiderstände der LEDs werden ermittelt:

LEDBlau: 5V - 3,9V / 10 mA = 110 Ohm - gewählt 100 Ohm

LEDRot: 5V - 2,0V / 10 mA = 300 Ohm - gewählt 270 Ohm

LEDGrün: 5V - 2,3V / 10 mA = 270 Ohm - gewählt 270 Ohm

ATmega 8

Belegung Bedeutung Pin             Pin Bedeutung Belegung
Reset (Reset) PC6 1 A
T
M
E
G
A

8
28 PC5 (ADC5) Poti
a (RXD) PD0 2 27 PC4 (ADC4) BattSpannung
b (TXD) PD1 3 26 PC3 (ADC3) ServoStrom
c (INT0) PD2 4 25 PC2 (ADC2) Dig1
d (INT1) PD3 5 24 PC1 (ADC1) Dig2
e (T0) PD4 6 23 PC0 (ADC0) Dig3
VCC 7 22 GND
GND 8 21 AREF
LEDRot (XTAL1) PB6 9 20 AVCC
LEDGrün (XTAL2) PB7 10 19 PB5 (SCK) Taster
f (T1) PD5 11 18 PB4 (MISO)
g (AIN0) PD6 12 17 PB3 (MOSI/OC2) Piezopieper
DP (AIN1) PD7 13 16 PB2 (SS/OC1B) LEDBlau
Eingang Empfänger (ICP1) PB0 14 15 PB1 (OC1A) Servo

Programmieradapter

Bedeutung Pin 6 Pol Pin Bedeutung
MISO 1 2 VTG (VCC)
SCK 3 4 MOSI
Reset 5 6 GND

Prototyp

Servotester2

Schaltplan

Erweiterung zur Messung von Empfängersignalen

Ich wollte auch die Signale eines Empfängers messen und habe dazu den ICP-Eingang verwendet. Der Taster S1 dient nun zur Umschaltung zwischen Servoausgabe oder Empfängermessung.

Prinzip der Messung:

Einstellungen bei Initialisierung Impuls-Messung des Empfängersignals

Register Bit 7 6 5 4 3 2 1 0 Beschreibung
TCCR1A Bedeutung COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10 Ausgang OC1A ohne Funktion
Wert 0 0 0 0 0 0 0 0 CTC Mode mit OCR1A als TOP
TCCR1B Bedeutung ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10 Input Capture pos Flanke
Wert 0 1 0 0 1 0 0 1 Keine Vorteilung Timer mit CPU-CLK (No prescaling)

Ausblick und ToDo

Arduino-Quellcode

Arduino-Einstellungen

Der Servotester kann mit Arduino-Software programmiert werden, dazu muss ggfs. noch die Board-Erweiterung "MiniCore" für den Mega8 geladen werden, Projektseite:

https://github.com/MCUdude/MiniCore

Installationsanleitung (Englisch)

Unter Arduino -> Einstellungen bei Zusätzliche Boardverwalter-URLs dies hinzufügen:

https://mcudude.github.io/MiniCore/package_MCUdude_MiniCore_index.json

Dann unter Werkzeuge -> Board -> Boardverwalter MiniCore installieren.

Den Boardverwalter wieder schließen, unter Werkzeuge -> Board -> MiniCore -> ATMega8 wählen.

Einstellungen siehe Bild links, die Taktfrequenz wird in der Software für die delay-Funktion beim Compilieren beachtet, daher sollte die Clock mit dem verwendeten Takt auf dem Chip übereinstimmen.

Programmieren über z.B. USPasp, diese Warnung ignorieren:

Warnung

Wichtig: Analog-EIngänge werden immer abgefragt, daher Spannungsteiler und Strommessung anschließen:

0,1Ohm Widerstand zwischen GND und Servo-GND ergänzen, Servo-GND ist an ADC3 (26) zwecks Strommessung angeschlossen.

Sonst piepst die Unterspannungswarnung ständig.

 

Quellcode [Servotester/Servotester.ino]
// Servotester V 2.1 (c) Oliver Mezger 29.1.2022
// CC-Lizenz: creativecommons.org/licenses/by-sa/3.0/de/
// ATMega8 Internal 1MHz
#include <avr/io.h>   // Definitionen laden
#include <util/delay.h> // Delay-Bibliothek laden
#include <avr/interrupt.h>
 
// Zuordnungen zu Ports
#define SegmenteIn    PIND
#define SegmenteOut   PORTD
#define SegmenteDdr   DDRD
#define SteuerungIn   PINB
#define SteuerungOut  PORTB
#define SteuerungDdr  DDRB
#define StelleIn      PINC
#define StelleOut     PORTC
#define StelleDdr     DDRC
#define LEDrot  (1<<PB6)
#define LEDgn   (1<<PB7)
#define LEDblau (1<<PB2)
#define LEDmask ~(LEDrot|LEDgn|LEDblau)
#define Taster  PB5 
#define Piepser PB3
const unsigned char bcd_7[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xaf};
const unsigned char SMIN = 100, mitte = 150, SMAX = 200;
const unsigned char mtoleranz = 3;
volatile unsigned char anzeige[3]={0,0,0};
 
typedef enum {servostrom,vcc,ms} adcArt;
volatile unsigned int adcWert[]={0,9000,0}; // Messwerte des ADC
adcArt ausgabeArt= ms;
typedef enum {initservo,servo,spannungsmessung,strommessung,initimpulsmessung,impulsmessung,initprog,prog} betriebsmode;
betriebsmode bmode = initservo;
 
ISR(TIMER0_OVF_vect){ // Timer fuer Anzeige wird 488 mal pro Sek aufgerufen
  static unsigned char stelle=6,dzeiger=0;
  StelleOut &= 0b11111000; // Stelle ausschalten
  SegmenteOut = bcd_7[anzeige[dzeiger++]];       // Zeile laden
  if (dzeiger == 2) SegmenteOut &= 0b01111111;   // Dezimalpunkt setzen
  StelleOut = (StelleOut & 0b11111000) | stelle; // Stelle anschalten
  stelle = ((stelle << 1) + 1) & 0b111;          // naechste Stelle neg. Logik
  if (dzeiger >2){
    stelle = 6; // 110 wegen neg. Logik
    dzeiger = 0;
  }
  ADCSRA |= (1 << ADSC); // starte Messung
}
 
void ausgeben(){
  static unsigned char rgb_led_dunkel = 0;
  unsigned int n,k=adcWert[ms];
  switch (ausgabeArt){ // auszugebender Wert
    case servostrom:
      n = adcWert[servostrom] *27 /10;
      break;
    case vcc:
      n = adcWert[vcc] * 114 / 100;
      break;
    case ms:
      n = adcWert[ms];
      break;
  }
  rgb_led_dunkel = ~rgb_led_dunkel;
  if (k <= mitte-mtoleranz) // Binaerer Baum fuer 5 Faelle
    if (k < SMIN)
      SteuerungOut = (SteuerungOut & LEDmask) | LEDgn;  // unter Minimum
    else
      SteuerungOut = (SteuerungOut & LEDmask) | (LEDgn & rgb_led_dunkel); // zwischen Minimum und unterer Toleranz
  else if (k < mitte+mtoleranz)
     SteuerungOut = (SteuerungOut & LEDmask) | LEDblau; // in der Neutral-Tolleranz
  else if (k <= SMAX)
     SteuerungOut = (SteuerungOut & LEDmask) | (LEDrot & rgb_led_dunkel); // zwischen Toleranz und Maximum
  else
     SteuerungOut = (SteuerungOut & LEDmask) | LEDrot;  // ueber Maximum
  anzeige[0] = n % 10;
  n /= 10;
  anzeige[1] = n % 10;
  anzeige[2] = n / 10;
}
 
void ausgebenMeldung(unsigned char n){
  switch (n){
    case 1:  // OFF
      anzeige[2]=0;
      anzeige[1]=15;
      anzeige[0]=15;
      break;
    default:  // Err
      anzeige[2]=14;
      anzeige[1]=16;
      anzeige[0]=16;
  }
}
 
ISR(ADC_vect){ // Abfragen des ADC
  static adcArt adcKanal= servostrom;
  static unsigned int adcMess[]={0,0,0}; // Messungen mitteln
  static unsigned char i = 0;
  switch (adcKanal){
    case servostrom:
      if (adcMess[servostrom] < ADC) adcMess[servostrom] = ADC; // Maximum
      if ((i&127)==127){ // 128 Messungen
        adcWert[servostrom] = (adcMess[servostrom]);
        adcMess[servostrom] = 0;
        ausgeben();
      }
      adcKanal=vcc; // naechster Kanal ist dran
      break;
    case vcc:
      if (adcMess[vcc] > ADC) adcMess[vcc] = ADC; // Minimum
      if ((i&127)==127){ // 128 Messungen
        adcWert[vcc] = (adcMess[vcc] >> 1);
        adcMess[vcc] = 9000;
        ausgeben();
      }
      adcKanal=ms;
      break;
    case ms:
      adcMess[ms] += ADC; // Durchschnitt
      adcKanal = servostrom;
      if ((i&7)==7){ // 8 Messungen
        adcWert[ms] = (adcMess[ms] >> 5) + 30;
        adcMess[ms] = 0;
        OCR1A = adcWert[ms] * 10; // Impulsdauer einstellen
        ausgeben();
      }
      i++; // Eine Messrunde ist rum
  }
  ADMUX = (ADMUX & 0b11111000) | (adcKanal+3); // Kanal fuer nachste Messung einstellen
}
 
ISR(TIMER1_COMPA_vect){ // ISR fuer Timer-Ueberlauf wenn kein Impuls kommt
  ausgebenMeldung(1);
}
 
ISR(TIMER1_CAPT_vect){ // ISR fuer Signalwechsel am Eingang
  static unsigned int n = 0;
  if (SteuerungIn & 1){ // War es eine steigende Flanke
  TCCR1B &= ~(1<<6); // auf fallende Flanke reagieren
  n = ICR1;
  }
  else {
    TCCR1B |= (1<<6); // auf steigende Flanke reagieren
    n = (ICR1-n)/10;
    if (n>20 && n < 250){
      adcWert[ms] = n;
      ausgeben();
    }
    else
      ausgebenMeldung(0);
    TCNT1 = 0; // Setze den Zaehler auf 0
  }
}
 
void peepdown(){
  SteuerungDdr |= 1<<Piepser;
  for (int i = 15;i<30;i++){
    OCR2 = i;
    _delay_ms(10);
  }
  SteuerungDdr &= ~(1<<Piepser);
  OCR2 = 15;            // Piepsfrequenz 4167Hz
}
 
int main(){
  unsigned char taste = 0;
  TCCR0 = 2;            // AnzeigenTimer Systemtakt/8 / 256 = 488,3Hz
  TIMSK |= (1<<TOIE0);  // Timerinterrupt frei geben
  TCCR2 = 0b00011010;   // Piepser CTC,Toggle, Systemtakt / 8
  OCR2 = 15;            // Piepsfrequenz 4167Hz
  SegmenteDdr=0xff;    // Ausgang
  StelleDdr=0b00000111;   // Ausgang
  SteuerungDdr = 0b11000110;  // Ausgaenge LED und Servo
  SteuerungOut = 0b00101001;  // Pieperausgang auf 1
  sei();      // globale Interruptfreigabe
  while(1){   // Endlosschleife
    if ((SteuerungIn & (1<<Taster)) == 0){
      _delay_ms(10); // Prellen abwarten
      SteuerungDdr |= 1<<Piepser;
      while ((SteuerungIn & (1<<Taster)) == 0); // warten bis Taste wieder losgelassen
      SteuerungDdr &= ~(1<<Piepser);
      taste++;
      _delay_ms(30); // Prellen abwarten
    }
    switch (bmode){
      case initservo:
        cli();  // Interrups sperren
        ADMUX = 0b11000101;   // ADWandler Interne Referenz rechtsbuendig, ADC5
        ADCSRA = 0b10001100;  // ADWandler Vorteiler/16, Interrupt frei
        TCCR1A = 0b10000010;  // Timer1 Ausgang OC1A, Fast PWM
        TCCR1B = 0b00011001;  // Keine Vorteilung Timer mit CPU-CLK
        ICR1 = 19999;         // Signal alle 20 ms
        SteuerungDdr |= (1<<PB1); // Servoausgang an
        TIMSK &= ~((1<<TICIE1) | (1<<OCIE1A)); // T1 Interrupts sperren
        bmode=servo;
        sei();  // Interrups frei geben
        break;
      case servo:
        if (taste) bmode = spannungsmessung;
        break;
      case spannungsmessung:
        ausgabeArt = vcc;
        if (taste) bmode = strommessung;
        break;
      case strommessung:
        ausgabeArt = servostrom;
        if (taste) bmode = initimpulsmessung;
        break;
      case initimpulsmessung:
        cli();  // Interrups sperren
        ausgabeArt = ms;
        ADCSRA = 0;  // ADWandler Stopp
        SteuerungDdr &= ~(1<<PB1); // Servoausgang aus
        TCCR1A = 0b00000000;  // Timer1 nicht auf Ausgang, CTC
        TCCR1B = 0b01001001;  // Input Capture pos Flanke, Timer mit CPU-CLK
        OCR1A = 25000; // Nach 25ms Timer wieder auf 0
        TIMSK |= (1<<TICIE1) | (1<<OCIE1A); // Interrupts frei geben
        bmode=impulsmessung;
        sei();  // Interrups frei geben
        break;
      case impulsmessung:
        if (taste) bmode = initservo;
        break;
    }
    taste = 0;
    if (350>adcWert[vcc]) peepdown(); // Unterspannungswarnung
  }
  return 0;
}
 

Assembler: Nur zur Anschauung für meine Schüler, kann wesentlich weniger

Quellcode [Servotester.asm]
; *** Servotester V 0.5 (c) Oliver Mezger 23.2.2010
; CC-Lizenz: creativecommons.org/licenses/by-sa/3.0/de/
; Assemblercode ist nicht vollständig und nur beispielhaft
.include "m8def.inc"
 
;*** Zuordnungen zu den Ports ***
 
.equ SegmenteIn = PIND
.equ SegmenteOut = PORTD
.equ SegmenteDdr = DDRD
.equ SteuerungIn = PINB
.equ SteuerungOut = PORTB
.equ SteuerungDdr = 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     ;als lokale, temporaere Variablen
.def stelle = R18   ;Stelle die dargestellt wird (neg. Logik 110,101,011)
.def itmp = R21     ;tmp-Register fuer Interrupt Service Routine (ISR)
 
;*** Interrupttabelle bei Mega8 sind die Adressen im Wort-Abstand daher rjmp
 
    rjmp reset      ;Einsprung nach Reset
    .org OVF0addr   ;Einsprung nach Timer 0 Ueberlauf
    rjmp isrAnzeige ;fuer Anzeigen ISR
    .org ADCCaddr   ;Einsprung AD-Wandler
    rjmp isrAdc     ;fuer ADC ISR
    .org $2a        ;Ende der Interruptvektoren nun Code
 
isrAnzeige: ;ISR wird mit Systemtakt/8/256 mal aufgerufen (488 Hz)
    in itmp,SREG            ;SREG retten
    push itmp
    cpi stelle,0b111        ;wenn nicht Stelle 4
    brne nstelle            ;dann naechste Stelle
    ldi stelle,0b110        ;Stelle1 initialisieren
    ldi XH, high(anzeige)   ;Datenzeiger initialisieren
    ldi XL, low(anzeige)
nstelle:
    ld itmp,x+
    ldi ZH, high(bcd_7*2)   ;Datenzeiger initialisieren
    ldi ZL, low(bcd_7*2)
    add ZL,itmp             ;Addiere Position
    adc ZH,null
    lpm itmp, z             ;lade Daten aus Flash
    cpi stelle,0b101        ;wenn nicht stelle 1
    brne keindp             ;dann kein Dezimalpunkt
    andi itmp,0b01111111    ;sonst setze Dezimalpunkt
keindp:
    out SegmenteOut,itmp    ;ausgeben Spalte
    in itmp, StelleOut      ;lade StellenPort
    andi itmp,0b11111000    ;maskiere Stellenbits
    or itmp,stelle          ;fuege Stellenmuster hinzu
    out StelleOut,itmp      ;Stelle einschalten
    lsl stelle              ;naechste Stelle kein rol verwenden!
    inc stelle
    andi stelle,0b111       ;die rausgeschobene 1 wegmaskieren
    pop itmp                ;SREG wiederherstellen
    out SREG,itmp
    reti                    ;return from Interrupt 
 
isrAdc: ;ISR fuer AnalogDigital-Wandler
    reti
 
reset: ; Initialisierung und Hauptprogramm
    ldi tmp, high(RAMEND)   ;Stackpointer initialisieren
    out SPH, tmp
    ldi tmp, low(RAMEND)
    out SPL, tmp
    ldi tmp,2               ;Timer 0 mit Systemtakt/8 betreiben
    out TCCR0,tmp
    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
    ldi tmp,0xff
    out SegmenteDdr,tmp
    ldi tmp,7
    out StelleDdr,tmp
    ldi stelle,6
    sei                     ;globale Interruptfreigabe
loop: 
    rjmp loop
 
bcd_7:
    .db 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90
 
    .dseg; Datenbereich
anzeige:
    .byte 3; Feld mit 3 Byte