------------------------------------------------------------------------------------- Wie sag ich's meinem AVR ? 30.10.2002 ------------------------------------------------------------------------------------- Gerhard Paulus gp@gnomsoft.de Copyright (c) 2002 GNOM SOFT GmbH, Dresden Dieser Text ist für alle Leute interessant, die sich in die Programmierung der AVR Mikrocontroller einarbeiten wollen. Mit einfachen Assembler-Programmen wird ausprobiert, was sich mit den Input-Output-Registern eine AVR Mikrocontrollers so alles bewirken läßt. Um mit Assembler etwas anzufangen, könnte gegebenenfalls die Einführung "Assembler ab 0 und 1" ganz praktisch sein. Die Assembler-Programme decken die folgenden Standard-Fälle ab: Digitale Eingabe und Ausgabe Externer Interrupt Timer0 Timer1 Counter0 Watch Dog Reset EEPROM read/write Serial Peripheral Interface (SPI) UART (RS232 - Schnittstelle) Analog Comparator Analog-Digital-Converter (ADC) SRAM read/write Abgeschlossen wird das ganze von einer Auflistung und kurzen Erläuterung der einzelnen Register und Bit-Felder. Als Hardware kommt der AT90S4433 bzw. AT90S2333 zum Einsatz und das Entwicklungs-Board STK500. Der Vollständigkeit halber werden die Pin-Belegung des 4433 und die Stecker des STK500 kurz dargestellt. 1. Pin-Belegung des AT90S4433 : ------------------------------- Als Übersicht hier die Pin-Belegung des AT90S4433 der PDIP-Version (plastic dual inline package) : 1 RESET 28 PC5 bzw. ADC5 2 PD0 bzw. RXD 27 PC4 bzw. ADC4 3 PD1 bzw. TXD 26 PC3 bzw. ADC3 4 PD2 bzw. INT0 25 PC2 bzw. ADC2 5 PD3 bzw. INT1 24 PC1 bzw. ADC1 6 PD4 bzw. T0 23 PC0 bzw. ADC0 7 VCC 22 AGND 8 GND 21 AREF 9 XTAL1 20 AVCC 10 XTAL2 19 PB5 bzw. SCK 11 PD5 T1 18 PB4 bzw. MISO 12 PD6 AIN0 17 PV4 bzw. MOSI 13 PD7 AIN1 16 PV4 bzw. SS 14 PB0 ICP 15 PV4 bzw. OC1 Grundfunktionen: ---------------- RESET : MC neu starten VCC : Versorgungsspannung (meist +5 Volt) GND : Grund / Masse XTAL1 : Eingang von Oszillator XTAL2 : Ausgang zu Oszillator digitale Eingabe/Ausgabe : PB0 - PB5 : Port B PC0 - PC5 : Port C PD0 - PD7 : Port D Zu diesen Grundfunktionen kommen alternativ noch folgende Nutzungsmöglichkeiten der Pins: für UART (RS232 - Schnittstelle): RXD : Eingang von RxD der seriellen Schnittstelle TXD : Ausgang zu TxD der seriellen Schnittstelle für externe Interrupts: INT0 : Eingang für externen Interrupt 0 INT1 : Eingang für externen Interrupt 1 für Counter (Zähler): T0 : Eingang für Counter 0 T1 : Eingang für Counter 1 für Analog-Vergleiche : AIN0 : positiver Input des Analog-Vergleichers AIN1 : negativer Input des Analog-Vergleichers für 16-Bit Timer/Counter : ICP : Input Capture Pin OCR1 : Output Compare Register für SPI (Serial Peripherals Interface) SS : Slave Select MOSI : Master Out Slave In MISO : Master In Slave Out SCK : Serial Clock (Taktgeber) für Analog-Digital-Umwandler : AVCC : Versorgungsspannung AREF : Referenzspannung AGND : Masse ADC0 - ADC5 : analoge Eingabe 2. STK500 : ----------- Abgesehen vom relativ hohen Preis (ca. 125 Euro, in den USA mit $79 preiswerter) ist das STK500 recht praktisch, um einfache AVR Programme zu entwickeln und zu testen. Auch Leute mit minimalen elektronischen Kenntnissen haben mit diesem Teil eine Chance, recht schnell zu ersten Ergebnissen zu kommen. Wer weniger Geld ausgeben möchte bzw. diese Sachen sowieso lieber selber baut, der findet in dem Tutorial auf http://www.mikrocontroller.net alle nötigen Informationen. In dem STK500 kann man (unter anderem) den 4433 stecken und programmieren über eine serielle Schnittstelle. Die dazu passende Software liefert das AVR Studio, das man sich gratis von http://www.atmel.com besorgen kann. Das AVR-Studio muß man aber nicht unbedingt interaktiv nutzen. So wurden die folgenden Assembler-Programme alle in einem Konsole-Fenster assembliert und im Flash programmiert über 2 Batch-Dateien: asm.bat avrasm %1.asm %1.lst %1.hex prog.bat stk500 -dAT90S4433 -ms -e -pf -vf -if%1.hex Zumindest am Anfang ist es am einfachsten, wenn alles in einem Verzeichnis liegt: Assembler (avrasm.exe), Programmierer (stk500.exe), Quellcode und auch die Include-Dateien (zB. 4433def.inc). Die Pins des AVR sind auf dem STK500 verdrahtet mit diversen Steckern bzw. Steckerleisten bzw. Erweiterungs-Steckerleisten. Diese Stecker kann man dann verbinden mit zB. 8 Tastern (switches) und 8 Lämpchen (LEDs). Und es gibt noch eine zweite serielle Schnittstelle, von der die Leitungen TxD und RxD an zwei Stecker geführt werden und die dann auch mit den AVR-Pins verbunden werden können. Mit popeligem ASCII sehen die wichtigsten Stecker des STK500 in etwa so aus: ----------------------------------------------------------------------- ....................... SW7 LED7 . EXPAND0 . .......... .......... ....................... SW6 LED6 .SWITCHES. .PORTD . .......... .......... SW5 LED5 +----------------+ SW4 LED4 RXD TXD XTAL1 XTAL2 | AT90S4433 | VTG GND +----------------+ SW3 LED3 .......... SW2 LED2 .PORTB . ......... .......... SW1 LED1 .LEDS . ......... .......... ........................ SW0 LED0 .PORTC . . EXPAND1 . .......... ........................ ----------------------------------------------------------------------- | | | | | | | | | | | | Stecker(leisten) Stecker(leisten) Erweiterungs-Steckerleisten | 8 LED's 8 Taster EXPAND0 - Buchsen EXPAND1 - Buchsen ----------------- ----------------- 1 2 1 2 3 4 3 4 5 6 5 6 7 8 7 8 9 10 9 10 11 12 11 12 13 14 REF 13 14 RST 15 16 15 16 17 18 XT1 17 18 XT2 19 20 VTG 19 20 VTG 21 22 GND 21 22 GND PC7 23 24 PC6 PD7 23 24 PB6 PC5 25 26 PC4 PD5 25 26 PB4 PC3 27 28 PC2 PD3 27 28 PB2 PC1 29 30 PC0 PD1 29 30 PB0 31 32 PD7 31 32 PD6 33 34 PD5 33 34 PD4 35 36 PD3 35 36 PD2 37 38 PD1 37 38 PD0 39 40 39 40 3. Assembler-Programme: ------------------------ 3a) Digitale Eingabe und Ausgabe : ---------------------------------- ;---------------------------- ; buttons.asm ; digitale Eingabe und Ausgabe ;---------------------------- .include "4433def.inc" ldi r16, 0xFF ; 1111 1111 out DDRB, r16 ; Port B output ldi r16, 0x00 ; 0000 0000 out DDRD, r16 ; Port D input loop: in r16, PIND ; Eingabe von Port D out PORTB, r16 ; Ausgabe zu Port D rjmp loop Bei Drücken eines Tasters, der an ein Pin von Port D angeschlossen ist, wird die dazu passende LED eingeschaltet. Wenn der Taster wieder losgelassen wird, dann geht die LED wieder aus. Der Vollständigkeit sei angemerkt, daß ein Taster nicht das gleiche ist wie ein Schalter; der Taster geht nach Loslassen automatisch wieder in seine Ausgangsstellung zurück, der Schalter bleibt in seiner neuen Stellung. Beim STK500 ist dazu mit 8-er Kabeln die Steckerleiste "PORTD" zu verbinden mit "SWITCHES" und die Steckerleiste "PORTB" zu verbinden mit "LEDS". Es werden die folgenden Register genutzt: DDRB, DDRD, PIND, PORTB. Mit den Registern DDRB und DDRD wird zuerst definiert, welche Funktion die einzelnen Pins der Ports haben. Dazu wird jeweils ein Byte, also 8 Bits, an den jeweiligen Port ausgegeben. Jedes Bit korrespondiert dabei mit einem Pin des jeweiligen Ports (Bit 0 mit Pin 0, Bit 1 mit Pin 1 usw.). Und wenn das Bit auf 1 gesetzt ist, dann wird der entsprechende Pin ab jetzt als Ausgabe (output) genutzt. Andernfalls, und das ist der Standardfall, wird das Pin zur Eingabe genutzt. Daß Port B beim 4433 nur 6 Beinchen hat spielt in diesem Zusammenhang keine Rolle, die obersten 2 Bits werden einfach ignoriert. In einer Schleife wird dann permanent der Status der Eingabe-Pins abgefragt. Das Ergebnis ist jeweils ein Byte, das in des Arbeitsregister r16 geschrieben wird. Dabei korrespondiert jedes Bit wieder mit einem Beinchen des Ports, und logisch 1 bedeutet "ausgeschaltet". Das ist nicht gerade intuitiv, aber mit der Zeit gewöhnt man sich dran. Wenn also nur die "unterste" Taste gedrückt wird (SW0 beim STK500), dann hat dieses Byte den Wert 1111 1110. Dieses Byte wird dann sofort im nächsten Befehl an Port B ausgegeben. Bei der Ausgabe gilt analog zur Eingabe, daß ein Bit mit dem Wert 0 das entsprechende Ausgabe-Pin einschaltet. Und wenn nur das unterste Bit den Wert 0 hat, dann leuchtet nur die "unterste" LED (LED0 beim STK). Obiges Programm ist in Minimalform gehalten. Es ist allerdings wirklich besser, sich von Anfang den richtigen Aufbau eines Assembler-Programms anzugewöhnen. Und dazu ist die früher oder später sowieso benötigte Interrupt-Tabelle zu berücksichtigen und vor allem der Stackzeiger anzulegen. .include "4433def.inc" .def temp = r16 rjmp reset ; Reset Handler reti ; IRQ0 Handler reti ; IRQ1 Handler reti ; Timer1 Capture Handler reti ; Timer1 compare Handler reti ; Timer1 Overflow Handler reti ; Timer0 Overflow Handler reti ; SPI Transfer Complete Handler reti ; UART RX Complete Handler : RXCIE reti ; UDR Empty Handler reti ; UART TX Complete Handler reti ; ADC Conversion Complete Interrupt Handler reti ; EEPROM Ready Handler reti ; Analog Comparator Handler reset: ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse out SPL, temp ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse out SPH, temp ; beim 4433 mit seinen 128 Bytes RAM reichen dafür auch 8 Bit : ; ldi temp, RAMEND ; out SP, temp ldi r16, 0xFF ; 1111 1111 out DDRB, r16 ; Port B output ldi r16, 0x00 ; 0000 0000 out DDRD, r16 ; Port D input loop: in r16, PIND ; Eingabe von Port D out PORTB, r16 ; Ausgabe zu Port D rjmp loop Der oben beschriebene Sachverhalt, daß eine logische 0 an einem Pin "eingeschaltet" bedeutet, hängt mit folgender Schaltungsskizze zusammen, die in ähnlicher Form auch bei den Tastern des STK500 zutreffen müßte: T +5V --- Masse 0V (+) ------ R ------(a)--- ------(-) | |+5V | Masse PD1 -----------(-) Zwischen VCC (hier +5Volt) und Masse ist ein Widerstand R geschaltet (üblicherweise 10 KiloOhm) und ein Taster T. An Punkt a wird außerdem eine Verbindung zu PD1 (Pin 1 von Port D) hergestellt. An Pin PD1 wird vom Mikrocontroller letztendlich die elektrische Spannung gemessen, die zwischen Punkt a und Masse besteht. Liegt diese Spannung im Bereich +2V bis +5V, dann wird dies vom AVR als HIGH bzw. logisch 1 interpretiert. Dies ist der Fall, wenn der Taster wie oben dargestellt nicht gedrückt ist. Wird der Taster gedrückt, dann sieht das so aus: T +5V Masse (+) ------ R ------(a)------------(-) | |0V | Masse PD1 -----------(-) Das ganze funktioniert dann wie ein Spannungsteiler mit nur einem Widerstand R, an dem die 5 Volt abfallen. Taster und Leitungen weisen zwar auch einen elektrischen Widerstand auf, aber der ist im Vergleich zu R minimal. An PD1 wird jetzt gegen Masse eine elektrische Spannung von (annähernd) 0V gemessen. Der AVR interpretiert Spannungen in einem Bereich von 0V bis +0,8V als LOW bzw. logisch 0. Exakt umgekehrt ist die Sache, wenn Widerstand und Taster vertauscht werden und der Taster somit direkt mit VCC verbunden ist. T +5V --- Masse 0V (+) ------ ------(a)--- R ------(-) | |0V | Masse PD1 -----------(-) In diesem Fall wird in Ruhestellung an Pin PD1 eine Spannung von 0 Volt und damit logisch 0 festgestellt. Und wenn der Taster gedrückt ist, dann wird an PD1 +5 Volt gemessen und damit ergibt die Abfrage im Programm dann logisch 1. Daß in obigem Fall eine LED leuchtet, wenn an dem entsprechenden Pin logisch 0 ausgegeben wird, hängt mit folgender Schaltungsskizze zusammen, die in ähnlicher Form auch beim STK500 vorliegen müßte: +5V D (+) ------ >| ---- R ----- PB1 +- Zwischen VCC und PB1 (Pin 1 von Port B) ist also eine LED (light emitting diode) geschaltet und zu deren Schutz ein Widerstand von üblicherweise 1 KiloOhm. Eine LED ist normalerweise nur für ca. 0,7 Volt ausgelegt, an dem Widerstand muß also der größte Teil der maximal möglichen +5 Volt abfallen. Das sagen zumindest die Lehrbücher; ich habe das ganze mal interessehalber ohne Vorwiderstand getestet, es ging auch und die LED leuchtete wesentlich heller. Wenn jetzt im Programm an PB1 logisch 0 ausgegeben wird, dann schaltet der AVR diesen Pin letztendlich gegen Masse. Dann fließt Strom zwischen VCC und Masse und wenn die LED richtig in (technischer) Stromrichtung geschaltet ist, dann leuchtet sie. In diesem Fall läßt der AVR (zumindest der 4433) einen vergleichsweise großen Strom von bis zu 20 milliAmpere fließen. +5V D Masse (+) ------ >| ---- R ----- PB1 ------------(-) logisch 0 Wenn jetzt im Programm an PB1 logisch 1 ausgegeben wird, dann schaltet der AVR diesen Pin letztendlich gegen VCC. Dann existiert kein elektrisches Spannungsgefälle und es fließt kein Strom. Und wenn kein Strom fließt, dann leuchtet auch keine LED. +5V D +5V (+) ------ >| ---- R ----- PB1 ------------(+) logisch 1 Anders sieht die Sache bei folgender Schaltung aus, bei der die LED direkt mit Masse verbunden ist: Masse D (-) ------ |< ---- R ----- PB1 -+ Hier leuchtet die LED bei Ausgabe von logisch 1, da dann der AVR den Pin 1 von Port B gegen VCC schaltet. Im Vergleich zu der ersten LED-Schaltung ist die Stromrichtung jetzt umgekehrt und die LED muß entsprechend in der neuen Stromrichtung geschaltet sein. Masse D +5V (-) ------ |< ---- R ----- PB1 ---------- (+) Und wenn an PB1 logisch 0 ausgegeben wird, dann leuchtet die LED garnicht, denn zwischen Masse und Masse fleißt kein Strom. Masse D Masse (-) ------ |< ---- R ----- PB1 ---------- (-) 3b) Externer Interrupt: -------------------------- Wenn in Register GIMSK das Bit INT0 gesetzt ist und sich an Pin INT0 (also PD2) etwas tut, dann wird ein externer Interrupt ausgelöst. In Register MCUCR kann auch spezifiziert werden, wann konkret dies passiert (ISC steht dabei für "Interrupt Sense Control"): 1 0 ISC01 ISC00 0 0 Auslöser LOW an INT0 0 1 Auslöser ist irgendeine Änderung an INT0 1 0 Auslöser ist fallende Flanke an INT0 1 1 Auslöser ist steigende Flanke an INT0 Wenn bei Ablauf des folgenden Programms die Taste SW2 gedrückt wird, erfolgt die Anzeige eines Bytes über LEDs. Die Taste ist verbunden mit dem INT0-Pin des 4433, also PD2. ;-------------------------------------------------- ; int0.asm ;-------------------------------------------------- .include "4433def.inc" .def temp = r16 .def leds = r17 rjmp reset ; Reset Handler rjmp interrupt0 ; IRQ0 Handler reti ; IRQ1 Handler reti ; Timer1 Capture Handler reti ; Timer1 compare Handler reti ; Timer1 Overflow Handler reti ; Timer0 Overflow Handler reti ; SPI Transfer Complete Handler reti ; UART RX Complete Handler : RXCIE reti ; UDR Empty Handler reti ; UART TX Complete Handler reti ; ADC Conversion Complete Interrupt Handler reti ; EEPROM Ready Handler reti ; Analog Comparator Handler reset: ldi temp, RAMEND out SP, temp ; set stack pointer ldi temp, 0xFF out DDRB, temp ; PORTB configured for output ldi leds, 0b11111000 out PORTB, leds ; Lämpchen einschalten ldi temp, 1< select slave ldi temp, 0 out SPDR, temp ; Übertragung starten mit ansonsten nutzlosem Byte wait1: sbis SPSR, SPIF rjmp wait1 in data, SPDR mov leds, data com leds out PORTB, leds sbi PORTB, SS ; SS high -> deselect slave cbi SPCR, MSTR ; dieser MC ist jetzt wieder Slave sbi SPCR, SPIE; ; interrupt wieder einschalten reti ;-------------------------------------------------- spi: ; wird in diesm Programm nur vom Slave ausgeführt inc data cpi data, 4 brne prepare ldi data, 1 ; start from scratch prepare: out SPDR, data ; nächste Übertragung vorbereiten reti 3i) Serielle Schnittstelle: UART ------------------------------------ Das folgende Programm empfängt Daten von der seriellen Schnittstelle und schickt sie gleich wieder zurück. Dabei wird der Vorspann "AVR:" hinzugefügt und das ganze mit Wagenrücklauf und Zeilenvorschub abgeschlossen. Beim STK500 ist dazu der Stecker RXD mit Pin 0 von Port D zu verbinden und desweiteren der Stecker TXD mit Pin 1 von Ports D. Damit ist der MC an den richtigen Stellen verbunden mit der zweiten RS232-Schnittstelle des STK500. Auf dem PC ist ein Terminal-Programm zu starten und auf die RS232-Schnittstelle einzustellen, die mit dem zweiten RS232-Anschluß des STK500 verbunden ist. Wenn dann ein Zeichen im Terminal-Programm eingegeben wird, dann kommt vom MC sofort das passende Echo. Bei Windows kann als Terminal-Software zB. Hyperterminal genutzt werden. Aber auch jede andere Software ist dazu brauchbar, die in der Lage ist, Bytes zur seriellen Schnittstelle zu schicken und von dort auch zu empfangen und am Bildschirm anzuzeigen. ;-------------------------------------------------- ; uart.asm ;-------------------------------------------------- .include "4433def.inc" .def temp = r16 .def data = r17 .def send = r18 ; .equ quartz = 3686400 ; für Standard STK500 .equ quartz = 8000000 ; für 8 MHz .equ baud = 9600 ; Baudrate rjmp reset ; Reset Handler reti ; IRQ0 Handler reti ; IRQ1 Handler reti ; Timer1 Capture Handler reti ; Timer1 compare Handler reti ; Timer1 Overflow Handler reti ; Timer0 Overflow Handler reti ; SPI Transfer Complete Handler rjmp receive ; UART RX Complete Handler : RXCIE reti ; UDR Empty Handler reti ; UART TX Complete Handler reti ; ADC Conversion Complete Interrupt Handler reti ; EEPROM Ready Handler reti ; Analog Comparator Handler reset: ldi temp, RAMEND out SP, temp ; set stack pointer sbi UCSRB, RXCIE ; enable receive completed interrupt sbi UCSRB, TXEN ; enable transmit sbi UCSRB, RXEN ; enable receive ldi temp, quartz / (baud * 16) - 1 out UBRR, temp ; BAUD Rate 9600 sei ; interrupts generell aktivieren main: loop: rjmp loop ;-------------------------------------------------- receive: in data, UDR ldi send, 13 ; CR rcall transmit ldi send, 10 ; LF rcall transmit ldi send, 'A' rcall transmit ldi send, 'V' rcall transmit ldi send, 'R' rcall transmit ldi send, ':' rcall transmit mov send, data rcall transmit reti ;-------------------------------------------------- transmit: sbis UCSRA,UDRE ; Warten, bis UDR bereit ist rjmp transmit out UDR, send reti 3j) Analog-Comparator: --------------------------- Das folgende Programm nutzt den Analog-Vergleicher, um einen Analog-Digital-Umwandler zu implementieren. Vorlage dazu war weitgehend Atmels's Application Note #400. Dabei ist an Pin 2 von Port D ein Widerstand und ein Kondensator mit Masse verbunden. PD2 ----- R --+-- C ------ GND zB. zu messende Bateriespannung | | | | PD6 PD7 Bei einer Taktfrequenz von 4 MHz hat R 30 KiloOhm und C hat 8.2 NanoFarad. Bei einer Taktfrequenz von 8 MHz hat R 27 KiloOhm und C hat 4,7 NanoFarad. An AIN0 (Pin PD6) liegt die zwischen R und C abgegriffene Spannung. An AIN1 (Pin PD7) liegt die zu messende bzw. zu digitalisierende Spannung. Meßbar ist bei den vorgenannten Werten für R und C eine Sapnnung von maximal 2/5 VCC, also 2 Volt. Mit LOW (logisch 0) an PD2 wird der Kondensator aufgeladen, wobei gleichzeitig ein Timer gestartet wird. Die jeweils am Kondensator aufgeladene Spannung wird an Pin AIN0 gemessen und mit der zu messenden Spannung an Pin AIN1 verglichen. Der Interrupt wird ausgelöst, sobald die Kondensator-Spannung größer ist als die zu messende Spannung ist. Der aktuelle Zählwert des Timers bestimmt dann den digitalen Wert für die zu messende Spannung. Je weniger Zeit benötigt wurde, um die zu messende Spannung zu erreichen, umso kleiner ist die zu messende Spannung und damit auch der Timer-Wert. Der Timer ist so eingestellt, daß nach spätestens 64 Timer-Zyklen automatisch der Timer-Interrupt ausgelöst wird. Damit ist der zu messenden Spannung eine obere Grenze gesetzt. Die Meß-Spannung kann einen von 64 Werten annehmen, die mit 6 Bits dargestellt werden können, was dann auch an PORT B über LEDs erfolgt. Es leuchten also maximal 6 Lämpchen. Vor jedem Messen wird eine Wartepause eingelegt, um den Kondensator wieder zu entladen. Dazu ist eine Pause von mindestens 200 Mikrosekunden nötig, mit ausgeschaltetem Pin PD2 (also Ausgabe von logisch "1"). ;---------------------------------------------------------- ; ac.asm ; Analog-Comparator als Möglichkeit zur AD-Umwandlung ;---------------------------------------------------------- .include "4433def.inc" .def temp = r16 .def result = r17 ; Ergebnis und auch temporärer Zähler .equ preset = 192 ; Timer0 Startwert (256-64) rjmp reset ; Reset Handler reti ; IRQ0 Handler reti ; IRQ1 Handler reti ; Timer1 Capture Handler reti ; Timer1 compare Handler reti ; Timer1 Overflow Handler rjmp endConversion ; Timer0 Overflow Handler reti ; SPI Transfer Complete Handler reti ; UART RX Complete Handler : RXCIE reti ; UDR Empty Handler reti ; UART TX Complete Handler reti ; ADC Conversion Complete Interrupt Handler reti ; EEPROM Ready Handler rjmp endConversion ; Analog Comparator Handler reset: ; main program ldi temp, RAMEND out SP, temp ; set stack pointer sbi ACSR, ACIE ; Interrupt für Analog-Vergleich starten sbi ACSR, ACIS1 ; Auslösung des Interrupts bei steigender Flanke sbi ACSR, ACIS0 ; ldi temp, 1< dezimal 170 Und wenn die zweite Taste gedrückt wird, müßten die LEDs in etwa folgenden Wert anzeigen: 0101 0100 -> dezimal 84 Das gibt auch Sinn, denn die an Port C jeweils an den Pins gemessene Spannung wird verglichen mit der Referenzspannung, die standardmäßig der Versorgungsspannung des MC selbst entspricht. Ist die zu messende Spannung gleich der Referenzspannung, dann wandelt der ADC dies um in den digitalen Wert 1023, was der höchsten Zahl entspricht, die mit 10 Bit dargestellt werden kann. Bei drei gleichen Widerständen wird die angelegte Spannung verteilt auf 3 gleich große Volt-Happen, was dann jeweils dem 10-Bit-Wert von ca. 341 entspricht. Auf Kanal 0 (ADC0 bzw. PC0) müßte also gemessen werden ca. 1023 - 341 = 682. Da dieser Wert für die 8 LEDs zu groß ist, wird in dem Programm durch zweimaliges Rechtsschieben ein 8-Bit-Wert ermittelt und ausgegeben. Und das ergibt dann 682 / 2 / 2 = 170 (eigentlich 170,5 aber die Stelle nach dem Komma fällt sozusagen untern Tisch, wodurch das Ergebnis ungenauer wird). Nach dem Einstellen eines neuen ADC-Kanals wird zweimal gewartet, bis eine Umwandlung erfolgt ist. Nach der ersten Umwandlung scheint meist ein Wert geliefert zu werden, der noch dem vorher eingestellten ADC-Kanal entspricht und deshalb wird dieser erste Wert sicherheitshalber verworfen. ;---------------------------------------------- ; adc.asm ; Analog-Digital-Umwandler ;---------------------------------------------- .include "4433def.inc" .def temp = r16 .def lowByte = r17 .def highByte = r18 .def channel = r19 ; Port D auf Ausgang schalten: ldi temp, 0xFF out DDRD, temp out PORTD, temp ; Port B auf Eingang schalten : ldi temp, 0x00 out DDRB, temp ; ADC initialisieren mit "Free Run" und Vorteiler 128: ldi temp, 0 ; Kanal 0 out ADMUX, temp ldi temp, ((1< Spannung an AIN1) ACIE : Analog Comparator Interrupt Enable ACIC : Analog Comparator Input Capture Enable (Input Capture von Timer 1 wird ausgelöst) ACIS1 : Analog Comparator Interrupt Mode Select ACIS0 : -------------------------------------------------------------------------- ADMUX ADC Multiplexer Select Register 6 2 1 0 ADCBG MUX2 MUX1 MUX0 ADCBG : ADC Bandgap Select (Eingangsspannung kommt nicht von ADC0 bis ADC6 sondern ist 1,22 V) MUX2 : Analog Channel Select (gibt an bei welchem Pin Eingangsspannung gemessen werden soll, MUX1 : also ADC0, ADC1, ADC2, ADC3, ADC4 oder ADC5) MUX0 : -------------------------------------------------------------------------- ADCSR ADC Control and Status Register 7 6 5 4 3 2 1 0 ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPS0 0 0 0 1/2 Taktfrequenz 0 0 1 1/2 Taktfrequenz 0 1 0 1/4 Taktfrequenz 0 1 1 1/8 Taktfrequenz 1 0 0 1/16 Taktfrequenz 1 0 1 1/32 Taktfrequenz 1 1 0 1/64 Taktfrequenz 1 1 1 1/128 Taktfrequenz ADEN : ADC Enable (ADC aktivieren) ADSC : ADC Start Conversion (Umwandlung starten) ADFR : ADC Free Run Select (Umwandlungen laufen permanent im Hintergrund) ADIF : ADC Interrupt Flag (gesetzt wenn jeweils eine Umwandlung erfolgt ist) ADIE : ADC Interrupt Enable (löst Interrupt aus, sobald Umwandlung erfolgt ist) ADPS2 : ADC Prescaler Select Bits (Vorteiler für den Taktgeber des ADC) ADPS1 : ADPS0 : -------------------------------------------------------------------------- ADCH ADC Data Register High (faßt die obersten 2 Bits des digitalisierten Meßwertes) 1 0 ADC9 ADC8 -------------------------------------------------------------------------- ADCL ADC Data Register Low (faßt die unteren 8 Bits des digitalisierten Meßwertes) 7 6 5 4 3 2 1 0 ADC7 ADC6 ADC5 ADC4 ADC3 ADC2 ADC1 ADC0 -------------------------------------------------------------------------- UBRRHI UART Baud Rate Register High ???