Home ==> Mikrobeginner ==> 8. Helligkeitsregelung
ATtiny13

Lektion 8: Helligkeitsregelung mit AD-Wandler


Mit dieser Lektion wird der im Prozessor integrierte AD-Wandler in Betrieb gesetzt.

8.0 Übersicht

  1. Einführung in die AD-Wandlung
  2. Einführung in die PCINT-Programmierung
  3. Hardware, Bauteile, Aufbau
  4. Helligkeitsregelung
  5. Helligkeitsregelung mit Farbwechsel
  6. Helligkeitsregelung dynamisch
  7. Rot/grün

8.1 Einführung in die AD-Wandlung

AD-Wandler machen folgendes: Der im ATtiny13 eingebaute AD-Wandler hat eine Auflösung von 10 Bits, kann also ein 1.024-stel oder ca. 1 Promille der verwendeten Referenzspannung messen. Das Ergebnis einer Messung folgt der Formel Ergebnis = (Messspannung * 1024) / Spannungsreferenz. Als Spannungsreferenz kann mit dem Bit REFS0 zwischen der Betriebsspannung (REFS0 = 0) und einer internen Spannungsreferenz von 1,1 V (REFS0 = 1) gewählt werden.

ADCH_ADCL Das Ergebnis braucht den Port ADCL (niedrigere 8 Bit) und zwei Bits des Ports ADCH (die nicht benötigten 6 Bits ergeben beim Lesen immer Null).

ADLAR Mit dem Bit ADLAR lässt sich dieses Verhalten ändern: mit ADLAR = 1 werden die 10 Bits linksbündig geschoben (LAR = Left Adjust Result). Im Port ADCH lassen sich dann die obersten 8 Bit des Ergebnisses lesen. Werden nur acht Bit benötigt kann auf das Lesen und Auswerten von ADCL verzichtet werden.

ADMUX In diesem Port befinden sich die Bits REFS0 und ADLAR. Die beiden Bits MUX1 und MUX0 wählen aus, von welchem Eingang das Messsignal kommt (ADC0 bis ADC3). Sollen mehrere Eingänge gemessen werden, muss der MUX-Port nacheinander gewählt und der Wandlungsprozess angestoßen werden.

DIDR0 Der oder die Eingangspin(s), der/die als ADC-Eingang/-Eingänge verwendet wird/werden, wird/werden nicht als normaler Eingang verwendet. Um Strom zu sparen, können diese Eingangsstufen stillgelegt werden. Das geschieht, indem die entsprechenden Bits im Port DIDR0 Eins gesetzt werden. Man beachte die eigenwillige Nummerierung der Bits.

ADCSRA Die gesamte restliche Steuerung des ADC erfolgt im Port ADCSRA. Darin bedeuten ADCPS2 bis 0 einen Vorteiler für das Taktsignal (000 = 2, 111 = 128), der die Dauer der Wandlungen steuert. Pro Wandlung werden 13 vorgeteilte Taktsignale benötigt. ADIE ermöglicht den Interrupt, wenn die Wandlung erfolgt ist. ADIF wird als Flagge gesetzt, wenn der Interrupt eintritt. ADATE ermöglicht den Autostart, wenn bestimmte Bedingungen eintreten (sonst muss der Start per Programm erfolgen). ADSC startet die Wandlung und bleibt solange Eins, bis die Wandlung abgeschlossen ist. ADEN schaltet den AD-Wandler ein.

Eine typische Sequenz zum Starten des ADC beim Kanal ADC3, rechtsbündig, mit der Betriebsspannung als Referenz, Interrupt und mit niedrigster Taktrate sieht dann so aus:

	ldi rmp,(1<<MUX1)|(1<<MUX0) ; Kanal 3
	out ADMUX,rmp ; in AD-Multiplexer
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; an ADC-Kontrollregister

Das waren die wichtigsten Verhältnisse, die zur Verwendung des ADC nötig sind.

Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.2 Einführung in die PCINT-Programmierung

In der vorherigen Lektion wurde die Taste am INT0-Eingang für Schaltaufgaben verwendet. Ist der Eingang INT0 nicht zu verwenden und blockiert oder muss eine weitere Taste überwacht werden, muss das mit dem PCINT-Interrupt erfolgen. Der PCINT funktioniert etwas anders als der INT0.

PCMSK Durch den PCINT sind alle Eingabepins überwachbar. Soll ein Kanal auf Pegelwechsel hin überwacht werden, wird sein entsprechendes Bit in der PCINT-Maske PCMSK auf Eins gesetzt. Sind zwei oder mehr dieser Bits gesetzt, muss per Software festgestellt werden, an welchem der Pins ein Wechsel erfolgt ist. Im Gegensatz zum INT0 kann die zu überwachende Flankenart nicht voreingestellt werden, alle Wechsel lösen einen Int aus.

GIMSK Der entsprechende Interrupt muss im General Interrupt Mask Register durch Setzen von PCIE ermöglicht werden.

Das ist schon alles, was es braucht, um Überwachung jedes beliebigen Pins zu realisieren.

Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.3 Hardware, Bauteile und Aufbau

8.3.1 Schaltung

Schaltbild Die Duo-Led ist wie bei der voherigen Schaltung angeschlossen. Der Taster ist an Pin 2, den PCINT3 verlegt. Das Poti ist mit dem Schleifer an ADC2 an Pin 3 angeschlossen, er teilt die Betriebsspannung auf. Die ISP-Anschlüsse sind wie bisher angeschlossen, hier aber nicht eingezeichnet.

8.3.2 Bauteile

Das Potentiometer Poti Poti Das hier ist das Poti. Der mittlere Anschluss ist der Schleifer. Alle drei Anschlüsse sind etwas zu breit für die Breadboard-Löcher, deshalb kriegen sie kurze Anschlussdrähte angelötet.

8.3.3 Der Aufbau

Aufbau Das hier ist der Aufbau. Die Platzierung der Bauteile ist unkritisch.


Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.4 Helligkeitsregelung

8.4.1 Aufgabe 1

In der ersten Aufgabe soll die Helligkeit der roten LED mit dem Poti einstellbar sein. Die Helligkeit soll steigen, wenn das Poti nach rechts gedreht wird (höhere Spannung).

8.4.2 Lösung

Es ist klar, dass dazu Damit wir das Teilen des 10-Bit-Wertes, der vom ADC kommt, durch vier lernen, gehen wir erst mal nicht über das ADLAR-Bit des ADC.

Eigentlich ist diese Aufgabe auch linear lösbar, da wir nur während der TC0-Timer im Fast-PWM-Mode das Ansteuern der LED übernimmt. Bei diesem Vorgehen wartet der Prozessor in einer Schleife 95% seiner Zeit auf die Fertigmeldung des ADC. Da das unintelligent, unästhetisch, Verschwendung von Strom und da bei den nachfolgenden Aufgaben sowieso der ADC-Interrupt vonnöten ist, machen wir das gleich im Interruptmodus.

8.4.3 Programm 1

Das Programm gibt es hier im Quellcode.

;
; *************************************
; * Helligkeitsregler mit ADC         *
; * (C)2016 by www.gsc-elektronic.net *
; *************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ---------- Register ---------------------
; frei: R0 .. R12
.def rAdcL = R13 ; LSB ADC-Ergebnis
.def rAdcH = R14 ; MSB dto.
.def rSreg = R15 ; Sicherungsregister
.def rmp   = R16 ; Vielzweckregister
.def rimp  = R17 ; Vielzweckregister Interrupts
;
; ---------- Ports ------------------------
.equ pOut = PORTB ; Ausgabeport
.equ pDir = DDRB  ; Richtungsport
.equ bRAO = PORTB2 ; Ausgabe Anode rote LED
.equ bRAD = DDB2   ; Richtung Anode rote LED
.equ bRKO = PORTB0 ; Ausgabe Kathode rote LED
.equ bRKD = DDB0   ; Richtung Kathode rote LED
;
; ---------- Timing -----------------------
; Prozessortakt = 1.200.000 Hz
; ADC-Vorteiler =       128
; ADC-Zyklen    =        13
; Messfrequenz  =       721 Hz
; TC0-Vorteiler =        64
; PWM-Stufen    =       256
; PWM-Frequenz  =        73 Hz
;
; ---------- Rest- und Interruptvektoren ---
.CSEG ; Assemblieren in den Flashspeicher (Code Segment)
.ORG 0 ; Adresse auf Null (Reset- und Interruptvektoren beginnen bei Null)
	rjmp Start ; Reset Vektor, Sprung zur Initiierung
	reti ; INT0-Int, nicht aktiv
	reti ; PCINT-Int, nicht aktiv
	reti ; TIM0_OVF, nicht aktiv
	reti ; EE_RDY-Int, nicht aktiv
	reti ; ANA_COMP-Int, nicht aktiv
	reti ; TIM0_COMPA-Int, nicht aktiv
	reti ; TIM0_COMPB-Int, nicht aktiv
	reti ; WDT-Int, nicht aktiv
	rjmp AdcIsr ; ADC-Int, aktiv
;
; Interrupt-Service-Routinen
;
AdcIsr: ; Interrupt Service Routine ADC
	in rSreg,SREG ; Sichern Statusregister
	; ADC-Ergebnis lesen
	in rAdcL,ADCL ; Lese ADC-Ergebnis, LSB
	in rAdcH,ADCH ; dto., MSB
	; ADC-Ergebnis durch 4 teilen
	lsr rAdcH ; unterstes Bit MSB in Carry
	ror rAdcL ; Carry in LSB schieben
	lsr rAdcH ; zweites Bit MSB in Carry
	ror rAdcL ; Carry in LSB schieben
	; neuen PWM-Wert setzen
	out OCR0A,rAdcL ; in Compare Register A
	; Neustart ADC (Teiler 128, Interrupt)
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; in ADC-Kontrollregister
	out SREG,rSreg ; Wiederherstellen Status
	reti
;
; ---------- Hauptprogramm-Init ------------
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Stapelzeiger auf RAMEND
	out SPL,rmp ; in Stapelport
	; LEDs konfigurieren und einschalten
	ldi rmp,(1<<bRAD)|(1<<bRKD) ; Portpins auf Ausgabe
	out pDir,rmp ; in Richtungsregister
	ldi rmp,(1<<bRAO)|(1<<bRKO) ; Ausgabe auf 1
	out pOut,rmp ; in Portregister
	; Timer als 8-Bit-PWM
	ldi rmp,0x80 ; halbe Helligkeit
	out OCR0A,rmp ; in Compare Register A
	ldi rmp,(1<<COM0A1)|(1<<COM0A0)|(1<<WGM01)|(1<<WGM00)
	out TCCR0A,rmp ; in Kontrollregister A
	ldi rmp,(1<<CS01)|(1<<CS00) ; Vorteiler 64
	out TCCR0B,rmp ; in Kontrollregister B
	; ADC-Wandler konfigurieren und starten
	ldi rmp,1<<MUX1 ; ADC-Signaleingang = ADC2
	out ADMUX,rmp ; in ADC-Multiplexer-Port
	ldi rmp,1<<ADC2D ; Disable Porttreiber-Hardware
	out DIDR0,rmp ; in Disable-Port
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; in ADC-Kontrollregister
	; Schlafmodus und Interrupts
	ldi rmp,1<<SE ; Schlafen ermoeglichen
	out MCUCR,rmp ; in Kontrollregister
	sei ; Interrupts zulassen
; Programmschleife
Schlafen:
	sleep ; schlafen legen
	nop ; Nach Aufwecken durch Int
	rjmp Schlafen ; wieder schlafen legen
;
; Ende Quellcode
;

Neu ist hier LSR und ROR. Das müssen wir näher ansehen, es wird bei Manipulationen von 16-Bit-Zahlen öfter gebraucht.

LEFT Aufgabe dabei ist es, die beiden niedrigsten Bits aus dem ADCH (MSB) von links her in das Register mit ADCL zu schieben. Das doppelte Rechtsschieben entspricht dem Teilen des Wertes in ADCH:ADCL durch vier, die beiden überschüssigen Bits aus ADCL fallen dabei rechts heraus. Da der ATtiny13-Prozessor kein 16-Bit-Schieben beherrscht, muss es selbst gestrickt werden. Dazu wird zunächst eine Null von links her in das MSB hineingeschoben, wobei gleichzeitig das rechts herausgeschobene Bit 0 in das Carry-Bit C des Statusregisters geschoben wird. Mit der Instruktion ROR (ROtate Right) wird der Inhalt des Carry-Bits in das LSB-Register von Links hereingeschoben. Macht man die Schritte 1 und 2 zweimal nacheinander, dann sind die beiden untersten Bits im durch vier geteilten unteren LSB angekommen.

Wieder sehen wir das gleiche Prinzip: jede Instruktion in Assembler löst genau einen Schritt des Prozessors aus. Die gesamte Operation hier braucht vier Schritte und vier einzelne Instruktionen.

Das Ergebnis ist eine feinfühlige Helligkeitsregelung der roten LED mit dem Potentiometer.

Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.5 Helligkeitsregelung mit Farbwechsel

8.5.1 Aufgabe 2

Jetzt soll nicht nur die rote, sondern auch die Helligkeit der grünen Lampe einstellbar sein. Die Umschaltung der Lampenfarbe soll mit dem Taster erfolgen. In beiden Fällen soll das Rechtsdrehen des Potis zu größerer Helligkeit führen.

8.5.2 Entprellen

Das Umschalten der LED-Farbe von Rot auf Grün und zurück, ist ein Leichtes: wenn das PORTB2-Bit oder PINB2 auf Eins steht, wird in PORTB2 Null geschrieben, und umgekehrt.

Wie wir das Drücken der Taste feststellen können, haben wir schon in der Einführung zum PCINT gesehen. Bei dieser Aufgabe wäre aber das Prellen des Tasters fatal. Je nach Anzahl an Schwarmimpulsen kommt beim Umschalten, immer wenn beim PCINT der Taster-Eingang auf Null steht, mal Rot, mal Grün raus. Das wäre unsauber, deshalb brauchen wir einen Mechanimus, der nach einem Tastendruck nachfolgende Impulse des Tasteneinganges unterdrückt.

Dazu brauchen wir Folgendes: Als Zeitmesser könnten wir wieder jede Menge Schleifenzeugs einbauen, aber das wäre unter dem bislang erreichten Programmierniveau. Für die Zeitmessung stehen uns schon zwei Quellen zur Verfügung: Da wir den ADC-Interrupt mit dieser zusätzlichen Aufgabe nicht behelligen wollen, nehmen wir dafür den TC0-CompareA-Interrupt. Da 13 ms für die Prellerkennung etwas zu kurz wäre, verwenden wir noch einen Abwärtszähler, um wenigstens 40 ms Ruhe am Tasteneingang festzustellen.

Damit kriegen wir die nachfolgenden Zeitbeziehungen zwischen Tastensignal, Inaktivitätsflagge bTA, Farbumschaltung und dem TC0-Interrupt.

Signale

Ist die Taste gedrückt (Tasteneingang geht auf Null), während die Flagge Null ist, wechselt die LED ihre Farbe und die Flagge wird gesetzt. Der Interruptzähler wird auf seinen Anfangswert 4 gesetzt.

Bei jedem nachfolgenden Null-Impuls am Eingangspin wird der Interruptzähler wieder auf vier gesetzt. das stellt sicher, dass die Flagge erst dann wieder gelöscht wird, wenn mindestens 40 ms oder vier Compare-Interrupts nach dem letzten Tasten-Null-Signal vergangen sind.

Bei jedem PWM-Interrupt wird geprüft, ob die bTA-Flagge gesetzt ist. Wenn nicht, gibt es nichts weiter zu tun. Wenn sie Eins ist, entscheidet der Zustand am Tasteneingang über den weiteren Ablauf. Ist dieser inaktiv (=Eins), dann wird der Zähler abwärts gezählt. Ist der Zähler daraufhin Null, wird die bTA-Flagge zurückgesetzt, der Wartezeitraum ist damit abgelaufen. Ist der Tastereingang aktiv (=Null), dann wird der Wartezeitraum wieder auf vier gesetzt.

Damit sind die Ablaufdiagramme für die beiden Interrupts klar:

PCINT TC0INT Das sind sie. Durch die Zusammenarbeit beider ist sichergestellt, dass mit der Taste kein Kuddelmuddel passiert.

8.5.3 Programm 2

Jetzt nutzen wir das ADLAR-Bit des ADC, um die Schieberei und Rotiererei des 10-Bit-Wertes aus dem ADC loszuwerden. Außerdem nutzen wir bei der Umschaltung der Farben die Umpolarisierung des OC0A-Ausgangs, um rot und grün korrekt anzusteuern.

Das hier ist das Programm. Den Quellcode gibt es hier.

;
; *************************************************
; * Helligkeitsregler rot/gruen mit ADC und Taste *
; * (C)2016 by www.gsc-elektronic.net             *
; *************************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ---------- Register ---------------------
; frei: R0 .. R14
.def rSreg = R15 ; Sicherungsregister
.def rmp   = R16 ; Vielzweckregister
.def rimp  = R17 ; Vielzweckregister Interrupts
.def rFlag = R18 ; Flaggenregister
	.equ bTA = 0 ; Taste-Aktiv-Flagge
.def rCnt  = R19 ; Zaehler fuer Prellunterdrueckung
; frei: R20 .. R31
;
; ---------- Ports ------------------------
.equ pOut = PORTB ;  Ausgabeport
.equ pDir = DDRB  ;  Richtungsport
.equ pIn  = PINB  ;  Leseport
.equ bRAO = PORTB2 ; Ausgabe Anode rote LED
.equ bRAD = DDB2   ; Richtung Anode rote LED
.equ bRAI = PINB2  ; Lesen Anode rote LED
.equ bRKO = PORTB0 ; Ausgabe Kathode rote LED
.equ bRKD = DDB0   ; Richtung Kathode rote LED
.equ bTaO = PORTB3 ; Pullup-Bit Taste
.equ bTaI = PINB3  ; Inputport Taste
.equ bTaE = PCINT3 ; PCINT-Maskenbit
.equ bAdD = ADC2D  ; Input-Disable ADC-Pin
;
; ---------- Timing -----------------------
; Prozessortakt = 1.200.000 Hz
; ADC-Vorteiler =       128
; ADC-Zyklen    =        13
; Messfrequenz  =       721 Hz
; TC0-Vorteiler =        64
; PWM-Stufen    =       256
; PWM-Frequenz  =        73 Hz
; TC0-Int-Zeit  =        13,65 ms
;
; ----------- Konstanten ------------------
.equ cTakt  = 1200000 ; Prozessortakt
.equ cPresc = 64 ; TC0-Vorteiler
.equ cPwm   = 256 ; PWM-Taktdauer
.equ cPrell = 50 ; ms Prellzeit Taster
.equ cFPwm  = cTakt/cPresc/cPwm ; Frequenz PWM in Hz
; Berechnung mit Rundung
.equ cTPwm  = (1000+cFPwm/2)/ cFPwm ; Taktdauer PWM in ms
.equ cCnt   = (cPrell+cTPwm/2) / cTPwm ; Zaehlimpulse Prellen
;
; ---------- Rest- und Interruptvektoren ---
.CSEG ; Assemblieren in den Flashspeicher (Code Segment)
.ORG 0 ; Adresse auf Null (Reset- und Interruptvektoren beginnen bei Null)
	rjmp Start ; Reset Vektor, Sprung zur Initiierung
	reti ; INT0-Int, nicht aktiv
	rjmp PcIntIsr ; PCINT-Int, aktiv
	rjmp TC0OvfIsr ; TIM0_OVF, aktiv
	reti ; EE_RDY-Int, nicht aktiv
	reti ; ANA_COMP-Int, nicht aktiv
	reti ; TIM0_COMPA-Int, nicht aktiv
	reti ; TIM0_COMPB-Int, nicht aktiv
	reti ; WDT-Int, nicht aktiv
	rjmp AdcIsr ; ADC-Int, aktiv
;
; Interrupt-Service-Routinen, mit Anzahl Takte
;
PcIntIsr: ; Interrupt-Service-Routine PCINT
	in rSreg,SREG ; Sichern Statusregister, +1 = 1
	sbrc rFlag,bTA ; Uerspringe wenn Blockade inaktiv, +1/2 = 2/3
	rjmp PcIntIsrSet ; Setze Zaehler neu, +2 = 4
	sbic pIn,bTaI ; Ueberspringe wenn Taste = 0 ; +1/2 = 4/5
	rjmp PcIntIsrSet ; Setze Zaehler neu ; +2 = 6
	sbr rFlag,1<<bTA ; Flagge setzen, +1 = 6
	sbic pIn,bRAI ; Ueberspringe wenn Anode Rot = 0, +1/2 = 7/8
	rjmp PcIntIsrGruen ; Kathode Gruen = 1, +2 = 9
	sbi pOut,bRAO ; Setze LED Gruen, +2 = 10
	ldi rimp,(1<<COM0A1)|(1<<COM0A0)|(1<<WGM01)|(1<<WGM00) ; Fast/clr on match, +1 = 11 
	out TCCR0A,rimp ; an Timer-Kontrollport A, +1 = 12
	rjmp PcIntIsrSet ; Setze Zaehler neu, +2 = 14
PcIntIsrGruen:
	cbi pOut,bRAO ; Setze LED Rot, + 2 = 11
	ldi rimp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00) ; Fast/set on match, +1=12
	out TCCR0A,rimp ; an Timer-Kontrollport A, +1 = 13
PcIntIsrSet:
	ldi rCnt,cCnt ; starte Zaehler neu, + 1 = 5/7/15/14
	out SREG,rSreg ; Wiederherstellen SREG, +1 = 6/8/16/15
	reti ; Rueckkehr vom Interrupt, + 4 = 10/12/20/19
;
TC0OvfIsr: ; Interrupt Service Routine TC0-Overflow
	in rSreg,SREG ; Sichern Statusregister, +1 = 1
	sbrs rFlag,bTa ; Ueberspringe wenn bTA-Flagge 1, +1/2 = 2/3
	rjmp TC0OvfIsrRet ; Beenden, +2 = 4
	sbis pIn,bTaI ; Ueberspringe wenn Taste = 1, +1/2 = 4/5
	rjmp TC0OvfIsrNeu ; Neustart Zaehler, +2 = 6
	dec rCnt ; vermindere Zaehler, + 1 = 6
	brne TC0OvfIsrRet ; noch nicht Null, zurueckkehren, +1/2 = 7/8
	cbr rFlag,1<<bTa ; bTa-Flagge loeschen, +1 = 8
	rjmp TC0OvfIsrRet ; Zurueck, +2 = 10
TC0OvfIsrNeu:
	ldi rCnt,cCnt ; Neustart Abwaertszaehler, +1 = 7
TC0OvfIsrRet:
	out SREG,rSreg ; Wiederherstellen Statusregister, +1 = 5/9/8
	reti ; Rueckkehr vom Interrupt, + 4 = 9/13/12
;
AdcIsr: ; Interrupt Service Routine ADC
	; ADC-Ergebnis lesen
	in rimp,ADCH ; Lese ADC-Ergebnis MSB, +1 = 1
	; neuen PWM-Wert setzen
	out OCR0A,rimp ; in Compare Register A, +1 = 2
	; Neustart ADC (Teiler 128, Interrupt)
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); +1=3
	out ADCSRA,rimp ; in ADC-Kontrollregister, +1 = 4
	reti ; +4 = 8
;
; ---------- Hauptprogramm-Init ------------
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Stapelzeiger auf RAMEND
	out SPL,rmp ; in Stapelport
	; LEDs konfigurieren und einschalten
	ldi rmp,(1<<bRAD)|(1<<bRKD) ; Portpins auf Ausgabe
	out pDir,rmp ; in Richtungsregister
	ldi rmp,(1<<bRKO)|(1<<bTaO) ; LED auf Rot, Tasten-Pullup
	out pOut,rmp ; in Port-Outputregister
	; Timer als 8-Bit-PWM
	ldi rmp,0x80 ; halbe Helligkeit
	out OCR0A,rmp ; in Compare Register A
	ldi rmp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00)
	out TCCR0A,rmp ; in Kontrollregister A
	ldi rmp,(1<<CS01)|(1<<CS00) ; Vorteiler 64
	out TCCR0B,rmp ; in Kontrollregister B
	ldi rmp,1<<TOIE0 ; Overflow Interrupt
	out TIMSK0,rmp ; in Timer-Int-Maske
	; ADC konfigurieren: Left adjust, Signaleingang = ADC2
	ldi rmp,(1<<ADLAR)|(1<<MUX1) ; in Register
	out ADMUX,rmp ; in ADC-Multiplexer-Port
	; ADC-Eingangstreiber abschalten
	ldi rmp,1<<bAdD ; Disable Porttreiber
	out DIDR0,rmp ; in Disable-Port
	; ADC einschalten und starten
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; in ADC-Kontrollregister
	; PCINT-Enable
	ldi rmp,1<<bTaE ; Tasten-Interrupts
	out PCMSK,rmp ; in PCINT-Maskenregister
	ldi rmp,1<<PCIE ; PCINT einschalten
	out GIMSK,rmp ; in Extern Interrupt Register
	; Schlafmodus und Interrupts
	ldi rmp,1<<SE ; Schlafen ermoeglichen
	out MCUCR,rmp ; in Kontrollregister
	sei ; Interrupts zulassen
; Programmschleife
Schlafen:
	sleep ; schlafen legen
	nop ; Nach Aufwecken durch Int
	rjmp Schlafen ; wieder schlafen legen
;
; Ende Quellcode
;

Zunächst ergibt die genaue Zählung der Anzahl Takte in den Interrupt-Service-Routinen maximal 20 Takte oder 20/1,2 = 16,7 µs Dauer. Das bleibt deutlich unter den Millisekunden, die typisch für das Prellen sind. Es bleibt auch weit unter der Dauer für einen PWM-Zyklus des Timers. Die Routinen sind auch kurz genug, so dass eine Verlegung von Teilen in das Hauptprogramm unnötig ist.

Neue Instruktionen kommen darin nicht vor.

Wenn wir uns bei diesen Abläufen vorstellen, dies alles mittels genau konstruierter Schleifen zu erledigen, wird uns in jedem Fall schummrig vor Augen. Deshalb sind Interrupts so einfach wie hilfreich.

Noch ein Wort zur Programmierung der Aufgabe in Hochsprachen: Die ist ziemlich unmöglich, weil wir schon beim Timing in immense Schwierigkeiten laufen. Das Verweben zweier Interrupt-Service-Routinen miteinander dürfte oberste Programmierkunst nötig machen. Hochsprachen sind eben in Wirklichkeit nicht einfacher und für komplexe Aufgaben geeigneter als Assembler. Sie sind einfach nur ganz weit weg von der verfügbaren Hardware.
Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.6 Helligkeitsregelung dynamisch

8.6.1 Aufgabe 3

Bei dieser Aufgabe steigt bzw. fällt die Helligkeit der LED automatisch. Die Geschwindigkeit, mit der das geschieht, wird durch das Poti bestimmt. Der Taster schaltet zwischen zwischen roter und grüner LED um. Die Umschaltung von steigender auf fallende Helligkeit und umgekehrt erfolgt bei maximaler bzw. minimaler Helligkeit.

8.6.2 Lösung

Zusätzlich muss im TCO-Interrupt, der nach Ablauf eines PWM-Zyklusses eintritt, nicht nur das Timing des Tastendrucks ausgewertet werden. Es muss auch ausgewertet werden, ob der Vergleichswert erhöht/vermindert werden muss. Damit die Geschwindigkeit geregelt werden kann, muss hier ein Abwärtszähler hinzugebastelt werden. Sein Anfangswert bestimmt darüber, nach wieviel PWM-Zyklen diese Erhöhung/Erniedrigung erfolgt. Dieser Anfangswert wird vom AD-Wandler gesetzt, indem das 8-Bit-MSB des ADC (mit gesetztem ADLAR-Bit) durch 16 geteilt wird. Damit bei einem Wert von Null keine ewige Verlängerung eintritt (ein Abwärtszähler mit Null zu Beginn würde erst nach 256 Durchläufen wieder Null werden), wird zu dem Wert einfach eine Eins dazugezählt. Der Anfangswert kann daher zwischen 1 und 16 liegen.

Aus Erfahrung dauert so ein Durchlauf trotzdem noch ewig lang, weil er 256 einzelne Stufen durchläuft. Wir erhöhen daher den Zählertakt um das Achtfache (Vorteiler = 8 statt 64). Das ergibt vernünftige und merkliche Dynamik bei der LED.

Ansteigende und fallende Helligkeit lässt sich durch Verändern des OCR0A-Wertes realisieren. Die Feststellung, ob bei ansteigender Helligkeit der OCR0A-Wert von 255 auf Null und bei fallender Helligkeit der OCR0A-Wert von Null auf 255 wechseln würde und dann eine Richtungsumkehr vorgenommen werden muss, sind etwas längerwierige Entscheidungen. Dieser Teil wird daher in die Hauptprogrammschleife verlegt, Bearbeitungsbedarf wird mit einer Flagge signalisiert.

In der Hauptprogrammschleife wird auch die Farbe der LED eingestellt. Diese steht einfach als ein Bit im Flaggenregister. Der Tastendruck kehrt dieses Bit um, der Farbwechsel wird aber erst nach Ablauf der vorgewählten Anzahl PWM-Durchläufe und durch die Bearbeitung im Hauptprogramm an die LED weitergegeben.

Damit sind alle Elemente des Programmes geklärt und es kann ans Kodieren gehen.

8.6.3 Programm 3

Das hier ist das Programm. Den Quellcode gibt es hier.

;
; *************************************************
; * Helligkeitsregler rot/gruen mit ADC und Taste *
; * (C)2016 by www.gsc-elektronic.net             *
; *************************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ---------- Register ---------------------
; frei: R0 .. R12
.def rCyc  = R13 ; Zykluszaehler
.def rAdc  = R14 ; ADC-Ergebnis
.def rSreg = R15 ; Sicherungsregister
.def rmp   = R16 ; Vielzweckregister
.def rimp  = R17 ; Vielzweckregister Interrupts
.def rFlag = R18 ; Flaggenregister
	.equ bTA = 0 ; Taste-Aktiv-Flagge
	.equ bCy = 1 ; Behandlungsflagge Zyklusende
	.equ bAb = 2 ; Abwaertszaehlen
	.equ bGn = 3 ; LED gruen gewaehlt
.def rCnt  = R19 ; Zaehler fuer Prellunterdrueckung
; frei: R20 .. R31
;
; ---------- Ports ------------------------
.equ pOut = PORTB ;  Ausgabeport
.equ pDir = DDRB  ;  Richtungsport
.equ pIn  = PINB  ;  Leseport
.equ bRAO = PORTB2 ; Ausgabe Anode rote LED
.equ bRAD = DDB2   ; Richtung Anode rote LED
.equ bRAI = PINB2  ; Lesen Anode rote LED
.equ bRKO = PORTB0 ; Ausgabe Kathode rote LED
.equ bRKD = DDB0   ; Richtung Kathode rote LED
.equ bTaO = PORTB3 ; Pullup-Bit Taste
.equ bTaI = PINB3  ; Inputport Taste
.equ bTaE = PCINT3 ; PCINT-Maskenbit
.equ bAdD = ADC2D  ; Input-Disable ADC-Pin
;
; ---------- Timing -----------------------
; Prozessortakt = 1.200.000 Hz
; ADC-Vorteiler =       128
; ADC-Zyklen    =        13
; Messfrequenz  =       721 Hz
; TC0-Vorteiler =         8
; PWM-Stufen    =       256
; PWM-Frequenz  =       585 Hz
; TC0-Int-Zeit  =         1,7 ms
; Zaehlzeit min =         1,7 ms
; Zaehlzeit max =        28,9 ms
; Auf-/Ab-Zyklus (min)=   0,88 s
;                (max)=  14,8 sec
;
; ----------- Konstanten ------------------
.equ cTakt  = 1200000 ; Prozessortakt
.equ cPresc = 8 ; TC0-Vorteiler
.equ cPwm   = 256 ; PWM-Taktdauer
.equ cPrell = 50 ; ms Prellzeit Taster
.equ cFPwm  = cTakt/cPresc/cPwm ; Frequenz PWM in Hz
; Berechnung mit Runden
.equ cTPwm  = (1000+cFPwm/2)/ cFPwm ; Taktdauer PWM in ms
.equ cCnt   = (cPrell+cTPwm/2) / cTPwm ; Zaehlimpulse Prellen
;
; ---------- Rest- und Interruptvektoren ---
.CSEG ; Assemblieren in den Flashspeicher (Code Segment)
.ORG 0 ; Adresse auf Null (Reset- und Interruptvektoren beginnen bei Null)
	rjmp Start ; Reset Vektor, Sprung zur Initiierung
	reti ; INT0-Int, nicht aktiv
	rjmp PcIntIsr ; PCINT-Int, aktiv
	rjmp TC0OvfIsr ; TIM0_OVF, aktiv
	reti ; EE_RDY-Int, nicht aktiv
	reti ; ANA_COMP-Int, nicht aktiv
	reti ; TIM0_COMPA-Int, nicht aktiv
	reti ; TIM0_COMPB-Int, nicht aktiv
	reti ; WDT-Int, nicht aktiv
	rjmp AdcIsr ; ADC-Int, aktiv
;
; Interrupt-Service-Routinen, mit Anzahl Takte
;
PcIntIsr: ; Interrupt-Service-Routine PCINT
	in rSreg,SREG ; Sichern Statusregister, +1 = 1
	sbrc rFlag,bTA ; Uerspringe wenn Blockade inaktiv, +1/2 = 2/3
	rjmp PcIntIsrSet ; Setze Zaehler neu, +2 = 4
	sbic pIn,bTaI ; Ueberspringe wenn Taste = 0 ; +1/2 = 4/5
	rjmp PcIntIsrSet ; Setze Zaehler neu ; +2 = 6
	sbr rFlag,1<<bTA ; Flagge setzen, +1 = 6
	ldi rimp,1<<bGn ; Kehre Flagge Gruen um, +1 = 7
	eor rFlag,rimp ; aus rot wird gruen, aus gruen rot, +1 = 8
PcIntIsrSet:
	ldi rCnt,cCnt ; starte Zaehler neu, + 1 = 5/7/9
	out SREG,rSreg ; Wiederherstellen SREG, +1 = 6/8/10
	reti ; Rueckkehr vom Interrupt, + 4 = 10/12/14
;
TC0OvfIsr: ; Interrupt Service Routine TC0-Overflow
	in rSreg,SREG ; Sichern Statusregister, +1 = 1
	sbrs rFlag,bTa ; Ueberspringe wenn bTA-Flagge 1, +1/2 = 2/3
	rjmp TC0OvfIsrDwn ; Beenden, +2 = 4
	sbis pIn,bTaI ; Ueberspringe wenn Taste = 1, +1/2 = 4/5
	rjmp TC0OvfIsrNeu ; Neustart Zaehler, +2 = 6
	dec rCnt ; vermindere Zaehler, + 1 = 6
	brne TC0OvfIsrDwn ; noch nicht Null, weiter, +1/2 = 7/8
	cbr rFlag,1<<bTa ; bTa-Flagge loeschen, +1 = 8
	rjmp TC0OvfIsrDwn ; weiter, +2 = 10
TC0OvfIsrNeu:
	ldi rCnt,cCnt ; Neustart Abwaertszaehler, +1 = 7
TC0OvfIsrDwn:
	dec rCyc ; zaehle Zyklen abwaerts, +1 = 5/9/11/8
	brne TC0OvfIsrRet ; noch nicht Null, +1/2 = 6/7/12/13/9/10
	mov rCyc,rAdc ; Neustart Downzaehler, +1 = 7/13/10
	sbr rFlag,1<<bCy ; Setze Behandlungsflagge, +1 = 8/14/11
TC0OvfIsrRet:
	out SREG,rSreg ; Wiederherstellen Statusregister, +1 = 8/13/11/9/15/12
	reti ; Rueckkehr vom Interrupt, + 4 = 12/17/15/13/19/16
;
AdcIsr: ; Interrupt Service Routine ADC
	; ADC-Ergebnis lesen
	in rAdc,ADCH ; Lese ADC-Ergebnis MSB, +1 = 1
	lsr rAdc ; teile durch 16, +1 = 2
	lsr rAdc ; +1 = 3
	lsr rAdc ; +1 = 4
	lsr rAdc ; +1 = 5
	inc rAdc ; +1 = 6
	; Neustart ADC (Teiler 128, Interrupt)
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); +1=7
	out ADCSRA,rimp ; in ADC-Kontrollregister, +1 = 8
	reti ; +4 = 12
;
; ---------- Hauptprogramm-Init ------------
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Stapelzeiger auf RAMEND
	out SPL,rmp ; in Stapelport
	; LEDs konfigurieren und einschalten
	ldi rmp,(1<<bRAD)|(1<<bRKD) ; Portpins auf Ausgabe
	out pDir,rmp ; in Richtungsregister
	ldi rmp,(1<<bRKO)|(1<<bTaO) ; LED auf Rot, Tasten-Pullup
	out pOut,rmp ; in Port-Outputregister
	; Startbedingungen
	clr rFlag ; Flaggen auf Null
	ldi rmp,0x10 ; kurzer Zyklus
	mov rAdc,rmp ; in ADC-Register
	mov rCyc,rmp ; in Zykluszaehler
	; Timer als 8-Bit-PWM
	ldi rmp,0x80 ; halbe Helligkeit
	out OCR0A,rmp ; in Compare Register A
	ldi rmp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00)
	out TCCR0A,rmp ; in Kontrollregister A
	ldi rmp,(1<<CS01) ; Vorteiler 8
	out TCCR0B,rmp ; in Kontrollregister B
	ldi rmp,1<<TOIE0 ; Overflow Interrupt
	out TIMSK0,rmp ; in Timer-Int-Maske
	; ADC konfigurieren: Left adjust, Signaleingang = ADC2
	ldi rmp,(1<<ADLAR)|(1<<MUX1) ; ADLAR und ADC-Pin in Register
	out ADMUX,rmp ; in ADC-Multiplexer-Port
	; ADC-Eingangstreiber abschalten
	ldi rmp,1<<bAdD ; Disable Porttreiber
	out DIDR0,rmp ; in Disable-Port
	; ADC einschalten und starten
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; in ADC-Kontrollregister
	; PCINT-Enable
	ldi rmp,1<<bTaE ; Tasten-Interrupts
	out PCMSK,rmp ; in PCINT-Maskenregister
	ldi rmp,1<<PCIE ; PCINT einschalten
	out GIMSK,rmp ; in Extern Interrupt Register
	; Schlafmodus und Interrupts
	ldi rmp,1<<SE ; Schlafen ermoeglichen
	out MCUCR,rmp ; in Kontrollregister
	sei ; Interrupts zulassen
; Programmschleife
Schlafen:
	sleep ; schlafen legen
	nop ; Nach Aufwecken durch Int
	sbrc rFlag,bCy ; Behandlungsflagge Zyklusende
	rcall Zyklus ; Behandle Flagge
	rjmp Schlafen ; wieder schlafen legen
;
Zyklus: ; Flagge bearbeiten, mit Takten
	cbr rFlag,1<<bCy ; Flagge ruecksetzen, +1 = 1
	sbrc rFlag,bGn ; Gruene LED?, +1/2 = 2/3
	rjmp ZyklusGruen ; Ja schalte LED gruen, +2 = 4
	sbi pOut,bRAO ; schalte LED rot, +2 = 5
	ldi rmp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00); +1 = 6
	out TCCR0A,rmp ; +1 = 7
	rjmp ZyklusRichtung ; Richtungsumkehr, +2 = 9
ZyklusGruen:
	cbi pOut,bRAO ; schalte LED gruen, +2 = 6
	ldi rmp,(1<<COM0A1)|(1<<COM0A0)|(1<<WGM01)|(1<<WGM00); +1 = 7
	out TCCR0A,rmp ; +1 = 8
ZyklusRichtung:
	in rmp,OCR0A ; PWM-Vergleichswert lesen, +1 = 10/9
	sbrc rFlag,bAb ; ueberspringe wenn Abwaerts Null, +1/2 = 11/12/10/11
	rjmp ZyklusAbwaerts ; Nach Abwaerts ; +2 = 13/12
	; Vergleichswert aufwaerts
	inc rmp ; PWM eine Stufe aufwaerts, +1 = 13/12
	brne ZyklusSet ; setzen neuen Vergleichswert, +1/2 = 14/14
	sbr rFlag,1<<bAb ; setze Abwaertsflagge, +1 = 15
	ldi rmp,0xFF ; setze auf hoechsten Wert, +1 = 16
	rjmp ZyklusSet ; setze neuen Wert, +2 = 18
ZyklusAbwaerts:
	subi rmp,1 ; eins abziehen, +1 = 14/13
	brcc ZyklusSet ; setze Wert wenn kein Ueberlauf, +1/2 = 15/16/14/15
	cbr rFlag,1<<bAb ; aufwaerts zaehlen, +1 = 16/15 
	clr rmp ; bei Null starten, +1 = 17/16
ZyklusSet:
	out OCR0A,rmp ; neuen Vergleichswert setzen, +1 = 15/15/19/17/16/18/17
	ret ; fertig, +4 = 19/19/23/21/20/22/21
;
; Ende Quellcode
;

Die folgenden Instruktionen sind neu: Im Vergleich zum vorherigen Programm läuft der Timer jetzt acht mal schneller. Dazu wurde nur die Zeile mit dem Timer-Init geändert. Bei der Berechnung der Konstanten im Definitionskopf wurde 64 durch 8 getauscht. Die Konstante, mit der die Inaktivitätszeit am Tasteneingang festgelegt wird, hat sich dadurch erhöht, denn jetzt muss mehr gezählt werden, um die 50 ms abzuwarten. Die Konstante cCnt, die jetzt zur Anwendung kommt ist cCnt=25. Das ist der Vorteil, solche Konstanten zu Beginn im Kopf zu definieren: der Änderungsbedarf im Programmcode ist sehr viel geringer.

Um herauszufinden, wie gross cCnt denn jetzt ist, hilft das Listing des Studio-Assemblers nicht weiter. Mit gavrasm kriegt man mit dem Aufrufparameter -S am Ende des Listings eine Aufstellung aller Konstanten und ihrer Dezimal- und Hexwerte. Man kann es aber auch mit dem Taschenrechner ...

Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


8.7 Helligkeitsregelung rot/grün

8.7.1 Aufgabe 3

Das ist eine Bonusaufgabe. Was passiert mit der Farbe der LED, wenn wir schnell zwischen Rot und Grün hin- und herwechseln? Dazu ist eine PWM so zu schalten, dass der Regler den Anteil beider Farben an der Gesamtzeit der PWM variiert. Steigende Spannungen am Poti sollen den Rotanteil erhöhen.

8.7.2 Lösung

Gegentakt Zunächst kriegen wir das mit der bestehenden Hardware kaum elegant hin. Idealerweise lassen wir dazu die beiden PWMs im ATtiny13, A und B, gegenläufig laufen. Die Signalausgänge OCR0A und OCR0B liefern bei richtiger Programmierung ein gegenläufiges invertiertes Signal, das die Farben der LED umschaltet.

Schaltung Da der OC0B-Anschluss benötigt wird, muss der zweite Anschluss der Duo-LED an Pin 6 verlegt werden. Der Taster wird hier nicht gebraucht, er stört aber auch nicht.

8.7.3 Programm

Das hier ist das Programm. (Hier ist der Quellcode.). Da die gesamte PWM-Schalterei vollständig vom Timer übernommen wird, brauchen wir nur für den ADC-Wandler eine Interrupt-Service-Routine.

;
; *********************************
; * Duo-LED im Gegentakt ATtiny13 *
; * (C)2016 by gsc-elektronic.net *
; *********************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; -------- Register ---------------
; frei: R0 .. R15
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Vielzweckregister Interrupts
;
; -------- Ports ------------------
.equ pDir = DDRB ; Port-Ausgaenge
.equ bARD = DDB1 ; Rote Anode
.equ bKRD = DDB0 ; Rote Kathode
;
; -------- Timing -----------------
;  Takt         = 1200000 Hz
;  Prescaler    =      64
;  PWM-Stufen   =     256
;  PWM-Frequenz =      73 Hz
;
; -- Reset- und Interruptvektoren -
.CSEG ; Code Segment
.ORG 0 ; Bei 0 beginnen
	rjmp Start ; Reset Vektor, Sprung zur Initiierung
	reti ; INT0-Int, nicht aktiv
	reti ; PCINT-Int, nicht aktiv
	reti ; TIM0_OVF, nicht aktiv
	reti ; EE_RDY-Int, nicht aktiv
	reti ; ANA_COMP-Int, nicht aktiv
	reti ; TIM0_COMPA-Int, nicht aktiv
	reti ; TIM0_COMPB-Int, nicht aktiv
	reti ; WDT-Int, nicht aktiv
	rjmp AdcIsr ; ADC-Int, aktiv
;
; ----- Interrupt Service Routinen -----
;
AdcIsr:
	in rimp,ADCH ; Lese ADC-Ergebnis MSB
	out OCR0A,rimp ; in Vergleichsregister A
	out OCR0B,rimp ; in Vergleichsregister B
	; Neustart ADC (Teiler 128, Interrupt)
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; in ADC-Kontrollregister
	reti
;
; ----- Hauptprogramm Init -------------
Start:
	; Stapel einrichten
	ldi rmp,LOW(RAMEND) ; auf SRAM-Ende
	out SPL,rmp ; in Stackregister
	; Ausgaenge initiieren
	ldi rmp,(1<<bARD)|(1<<bKRD) ; Ausgaenge
	out pDir,rmp ; in Richtungsregister
	; Vergleicher initiieren
	ldi rmp,0x80 ; auf halbe/halbe
	out OCR0A,rmp ; in Vergleichsregister A
	out OCR0B,rmp ; in Vergleichsregister B
	; Timer als PWM mit A- und B-Outputsteuerung
	ldi rmp,(1<<COM0A1)|(1<<COM0A0)|(1<<COM0B1)|(1<<WGM01)|(1<<WGM00)
	out TCCR0A,rmp ; in Kontrollregister A
	; Timer mit 64 starten
	ldi rmp,(1<<CS01)|(1<<CS00) ; Prescaler 64
	out TCCR0B,rmp ; in Kontrollregister B
	; ADC konfigurieren: Left adjust, Signaleingang = ADC2
	ldi rmp,(1<<ADLAR)|(1<<MUX1) ; ADLAR und ADC-Pin in Register
	out ADMUX,rmp ; in ADC-Multiplexer-Port
	; ADC-Eingangstreiber abschalten
	ldi rmp,1<<ADC2D ; Disable Porttreiber
	out DIDR0,rmp ; in Disable-Port
	; ADC einschalten und starten
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; in ADC-Kontrollregister
	; Schlafen einschalten
	ldi rmp,1<<SE ; Sleep Enable
	out MCUCR,rmp ; in General Kontrollregister
	; Interrupts einschalten
	sei ; set I-Flagge
Schleife:
	sleep ; schlafen legen
	nop ; nach Aufwachen
	rjmp Schleife ; wieder schlafen legen
;
; Ende Quellcode
;

Hier gibt es keine neuen Instruktionen.

Home Top AD-Wandlung PCINT Helligkeit Farbwahl Dynamik Rot/grün


©2016 by http://www.gsc-elektronic.net