avr ersterer schritt
  Eingänge (Wie kommen Signale in den µC)
 
Eingänge (Wie kommen Signale in den µC)

Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.

[Bearbeiten] Signalkopplung

Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.

Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z. B. Optokoppler, Spannungsteiler, "Levelshifter" aka Pegelwandler) anpassen.

Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 ("low") bzw. 1 ("high") erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.

Spannungstabelle
(ca. Grenzwerte)
  Low High
bei 5 V 1 V 3,5 V
bei 3,3 V 0,66 V 2,31 V
bei 1,8 V 0,36 V 1,26 V


Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.

Dabei ist wichtig, zur Abfrage der Eingänge nicht etwa Portregister PORTx zu verwenden, sondern Eingangsregister PINx. Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-"Erstkontakt".

Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:

#include <avr/io.h>
#include <stdint.h>
...
uint8_t bPortD;
...
bPortD = PIND;
...

Mit den C-Bitoperationen kann man den Status der Bits abfragen.

#include <avr/io.h>
...
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das "zweite" Bit) in PINC gesetzt (1) ist */
if ( PINC & (1<<PINC1) ) {
  /* Aktion */
}
 
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das "dritte" Bit) in PINB geloescht (0) ist */
if ( !(PINB & (1<<PINB2)) ) {
  /* Aktion */
}
...

Siehe auch Bitmanipulation#Bits_prüfen

[Bearbeiten] Interne Pull-Up Widerstände

Portpins für Ein- und Ausgänge (GPIO) eines AVR verfügen über zuschaltbare interne Pull-Up Widerstände (nominal mehrere 10kOhm, z. B. ATmega16 20-50kOhm). Diese können in vielen Fällen statt externer Widerstände genutzt werden.

Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register PORTx aktiviert bzw. deaktiviert, wenn ein Pin als Eingang geschaltet ist.

Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.

Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.

#include <avr/io.h>
...
DDRD  = 0x00; /* alle Pins von Port D als Eingang */
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */
...
DDRC  &= ~(1<<DDC7);  /* Pin PC7 als Eingang */
PORTC |= (1<<PC7);    /* internen Pull-Up an PC7 aktivieren */

[Bearbeiten] Tasten und Schalter

Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen: Active Low und Active High.

Der Widerstandswert von Pull-Up- und Pull-Down-Widerständen ist an sich nicht kritisch. Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert. Die AVRs verfügen an den meisten Pins softwaremäßig über zuschaltbare interne Pull-Up Widerstände (vgl. Abschnitt Interne Pull-Up Widerstände), welche insbesondere wie hier bei Tastern und ähnlichen Bauteilen (z. B. Drehgebern) statt externer Bauteile verwendet werden können.

[Bearbeiten] (Tasten-)Entprellung

Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.

Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.

Beim folgenden einfachen Beispiel für eine Entprellung ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z. B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).

#include <avr/io.h>
#include <inttypes.h>
#ifndef F_CPU
#warning "F_CPU war noch nicht definiert, wird nun mit 3686400 definiert"
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */
#endif
#include <util/delay.h>     /* bei alter avr-libc: #include <avr/delay.h> */      
 
/* Einfache Funktion zum Entprellen eines Tasters */
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
    if ( ! (*port & (1 << pin)) )
    {
        /* Pin wurde auf Masse gezogen, 100ms warten   */
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz
        _delay_ms(50); 
        if ( *port & (1 << pin) )
        {
            /* Anwender Zeit zum Loslassen des Tasters geben */
            _delay_ms(50);
            _delay_ms(50); 
            return 1;
        }
    }
    return 0;
}
 
int main(void)
{
    DDRB &= ~( 1 << PB0 );                 /* PIN PB0 auf Eingang (Taster)            */
    PORTB |= ( 1 << PB0 );                 /* Pullup-Widerstand aktivieren            */
    ...
    if (debounce(&PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */
        PORTD = PIND ^ ( 1 << PD7 );  /* ..LED an Port PD7 an-
                                   bzw. ausschalten */
    ...
}

Die obige Routine hat leider mehrere Nachteile:

  • sie detektiert nur das Loslassen (unergonomisch)
  • sie verzögert die Mainloop immer um 100ms bei gedrückter Taste
  • sie verliert Tastendrücke, je mehr die Mainloop zu tun hat.

Eine ähnlich einfach zu benutzende Routine, aber ohne all diese Nachteile findest Du hier: Entprellung für Anfänger

Zum Thema Entprellen siehe auch:

 
  Heute waren schon 1 Besucher (7 Hits) hier!  
 
Diese Webseite wurde kostenlos mit Homepage-Baukasten.de erstellt. Willst du auch eine eigene Webseite?
Gratis anmelden