Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Bietet ein Controller nicht genügend I/O-Leitungen, dann kann man Portexpander werwenden. Dadurch hat man zusätzliche Ports zur Verfügung. Neben Portexpandern der I²C-Klasse oder SPI-Klasse bietet sich an, 8-Bit-Schieberegister zu kaskadieren und als Expander zu verwenden. Das ist oft ausreichend, weil mehr Ausgänge begraucht werden als Eingänge. Dabei verwendet man Schieberegister mit Ausgangs-Latch, d.h. die Daten werden erst dann an die Ausgänge geschaltet, wenn sie an Ort und Stelle sind.

Falls man Eingänge benötigt, können auch Schieberegister mit Input-Latches ankaskadiert werden, welche die Eingänge von parallel nach seriell umsetzen (in diesem Artikel nicht näher beschrieben).

Pro & Contra

Vorteile
  • schnell
  • preiswert
  • einfach anzusteuern (auch ohne Hardware-Unterstützung)
  • Ansteuerung ohne Interrupt-Programmierung
  • modular aufgebaut und erweiterbar
  • alle Ports können gleichzeitig geschaltet werden, auch 100 Stück
  • SPI-Interface und ISP-Pins (die oft ungenutzt bleiben) können verwendet werden
  • Bus hat nur 3 Leitungen, dadurch kein kompliziertes Layout mit vielen Leitungen
  • Hardware bzw. Code der Version ohne Hardware-Unterstützung ist auch auf anderen Controllertypen als AVR verwendbar (sofern die Ausgangspegel passen).
  • Test der Schaltung direkt am PC möglich, falls man Zugriff auf den Parallelport oder RS232 (Pegelanpassung erforderlich! z.B MAX232, Widerstände & Zenerdiode) hat. In diesem Fall verwendet man die Software PC-seitig und setzt/resettet die 3 Portleitungen, an die man die Expander angeschlossen hat.
Nachteile
  • bei dieser Version nur Ausgabe möglich
  • soll ein Ausgangs-Port geändert werden, müssen alle Daten neu gesendet werden
  • bei der Variante mit SPI-Hardware liegt MISO brach bzw ist nicht so einfach nutzbar zu machen

Resourcen

Tabelle: Resourcen-Verbrauch mit N ICs
Resource ohne SPI mit SPI
AVR-Peripherie 3 I/O-Ports SPI + 1 I/O-Port
Expander 74*595, CD4094, ... N
maximaler Datendurchsatz
in kByte pro Sekunde und MHz
10 37

Mit einer CPU-Frequenz von 16MHz hat man also mit der Hardware-Variante einen maximalen Durchsatz von ca. 590 kByte/Sekunde, und 160kByte/Sekunde mit der reinen Software-Variante.

Kleinkram:

  • evtl. Pullup/Pulldown-Widerstand: 20kΩ
  • Widerstände zum Entkoppeln vom ISP: einige kΩ
  • evtl. Kondensatoren von 100pF zum Entstören der Dateinleitungen
  • Strom für die Versorgung im Bereich von µA bis wenige mA (je nach Expander-Typ, Anzahl und Frequenz)

Schaltplan

Anschlussplan
Portexpander 74595 an AVR.png
Das umstrichelte Modul
kann einfach so oft nach rechts wiederholt werden bis man so viele Ausgänge hat wie gewünscht.
R1
ist ein Pullup-Widerstand, der während des ISP-Programmierens dafür sorgt, daß RCK nicht floatet, denn während der Programmierung sind die AVR I/O-Leitungen hochohmig. Dadurch bleiben die Ausgänge der Expander stabil (MOSI und SCK wackeln natürlich beim Programmierern).
R2, R3
entkoppeln den ISP-Adapter. Ansonsten stören sie nicht weiter, denn beim Proggen muss praktisch kein Strom fliessen – abgesehen vom minimalen Leckstrom der Ports und Füllen der Portkapazität von ein paar pF.
Eingang G (Pin13)
ist hier auf LOW verdrahtet. Falls gewünscht, kann er verwendet werden, um die Ausgänge der Expander hochohmig zu schalten (high-Z). Dann braucht man natürlich einen weiteren µC-Port, um das zu tun.

Ports

Ohne Nutzung der SPI-Hardware hat man die freie Auswahl, welche Ports man verwendet. Natürlich ist auch die Verwendung der SPI/ISP-Ports in jeder beliebigen Anordnung möglich und das Signal "SCK" muss nicht an Port "SCK" angeschlossen sein. Einzig auf das Signal RCK sollte man achten, denn falls es zugleich durch den ISP verwendet wird, flattern beim Programmieren möglicherweise die Expander-Ausgänge.

Bei der Hardware-Version ist man auf die SPI-Pins MOSI (Master Out, Slave IN) und SCK (SPI Clock) festgelegt, mit Ausnahme von RCK. Das kann irgendein Port sein (ausser MISO, das bei aktiviertem SPI immer Input ist). Im Beispiel ist RCK an SS (SPI Slave Select) angeschlossen.

Falls man SS nicht in dieser Weise verwendet, muss man auf jeden Fall dafür sorgen, daß SS entweder auf OUT steht, oder nie auf LOW geht, da sonst SPI in den Slave-Modus schaltet!

Signalfolge

Signalfolge-Portexpander.png

Nachdem das auszugebende Bit an SER/MOSI ausgegeben wurde, wird es mit einem low-Strobe an SCK ins Schieberegister übernommen und alle Bits wandern um eine Position weiter. Nachdem so alle Bits ausgegeben wurden und an ihrer Position sind, werden die Bits durch ein Strobe an RCK an die Ausgänge gelegt und bleiben dort, bis sie wieder überschrieben werden.

C-Code

Der folgende C-Code ist Pseudocode, was Setzen der Ports angeht. Die entsprechenden Befehle sind durch die richtigen C-Befehle für diese Ports zu ersetzen.

MAKE_OUT (X)
Schaltet X als Ausgang (DDR-Register)
SET (X)
Setzt Ausgang X auf HIGH (PORT-Register)
CLR (X)
Setzt Ausgang X auf LOW (PORT-Register)
Tabelle: Resourcen-Verbrauch mit N ICs
Resource ohne SPI mit SPI
Interrupts keine
Flash (Bytes mit -Os) 52 44
SRAM (statisch) N
SRAM (Stack) 2
Laufzeit serpa_out() 19 + 101*N 16 + 27*N
SCK-Frequenz ~fcpu/9 fcpu/2

Makros, Datenstrukturen, Funktionen

#define SERPA_SIZE
Define für Anzahl der auszugebenden Bytes
extern unsigned char serpa[SERPA_SIZE]
Das Array, dessen Bytes ausgegeben werden. serpa[0] landet in dem Portexpander-IC, das direkt am Controller sitzt. Bit0 erscheint jeweils an Ausgang QA, Bit7 erscheint am Ausgang QH, etc.
void serpa_init()
Initialisiert die Schnittstelle bzw. die verwendeten Ports
void serpa_out()
Gibt die SERPA_SIZE Bytes aus dem Array serpa aus.

serpa.h

/* SERiell nach PArallel (serpa) im SPI-Protokoll */
#ifndef _SERPA_H_
#define _SERPA_H_

/* 4 Bytes (32 Ports) */
#define SERPA_SIZE 4

extern unsigned char serpa[];
extern void serpa_out();
extern void serpa_init();

#endif /* _SERPA_H_ */

Mit SPI-Hardware

Das Senden erfolgt ohne den SPI-Interrupt zu nutzen. Allein der ISR-Prolog/Epilog dauert schon so lange wie ein Schleifendurchlauf.

serpa.c

// SERiell nach PArallel (serpa) mit Hardware-Unterstützung
#include <avr/io.h>
#include "serpa.h"

// Array für die Daten
unsigned char serpa[SERPA_SIZE];

void serpa_init()
{
   MAKE_OUT (PORT_MOSI); 
   MAKE_OUT (PORT_SCK);
   MAKE_OUT (PORT_RCK); SET (PORT_RCK);

   // !!! SS muss OUT sein, damit SPI nicht in Slave-Mode wechselt !!!
   // entfällt, falls PORT_RCK = PORT_SS  
   MAKE_OUT (PORT_SS);

   // SPI als Master
   // High-Bits zuerst
   // SCK ist HIGH wenn inaktiv
   SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL);
	
   // pullup an MISO vermeidet Floaten
   SET (PORT_MISO);

   // maximale Geschwindigkeit: F_CPU / 2	 
   SPSR |= (1 << SPI2X);
}

void serpa_out ()
{
   unsigned char anz = SERPA_SIZE;
   unsigned char* serp = serpa+SERPA_SIZE;

   do
   {
      unsigned char data = *--serp;

      // SPDR schreiben startet Übertragung
      SPDR = data;

      // warten auf Ende der Übertragung für dieses Byte
      while (!(SPSR & (1 << SPIF)));

      // clear SPIF	
      data = SPDR;
   }
   while (--anz > 0);

   // Strobe an RCK bringt die Daten von den Schieberegistern in die Latches
   CLR (PORT_RCK);
   SET (PORT_RCK);
}

Ohne SPI-Hardware

Dieser Code funktioniert auch für jeden anderen µC-Typ, der mindestend 3 digitale I/O-Ausgangsports hat, und mit einem Compiler für Standard-C übersetzt wird.

serpa.c

/* SERiell nach PArallel (serpa) via Software */
#include "serpa.h"

/* Array für die Daten */
unsigned char serpa[SERPA_SIZE];

void serpa_init ()
{
    /* Verwendete Ports auf OUT */
    MAKE_OUT (PORT_SER);
    MAKE_OUT (PORT_SCK);
    MAKE_OUT (PORT_RCK);

    /* SCR und RCK auf definierten Level HIGH */
    SET (PORT_SCK);
    SET (PORT_RCK);
}

void serpa_out ()
{
    unsigned char anz = SERPA_SIZE;
    unsigned char* serp = serpa+SERPA_SIZE;

    do
    {
        unsigned char bits;
        unsigned char data = *--serp;

        /* 8 Bits pro Byte rausschieben */
        for (bits = 8; bits > 0; bits--)
        {
            CLR (PORT_SER);
            if (data & 0x80)
            {
                SET (PORT_SER);
            }

            data <<= 1;
            /* Strobe an SCK schiebt Daten im Gänsemarsch      */
            /* um 1 Position weiter durch alle Schieberegister */
            CLR (PORT_SCK);
            SET (PORT_SCK);
        }
    }
    while (--anz > 0);

    /* Strobe an RCK bringt die Daten von den Schieberegistern in die Latches */
    CLR (PORT_RCK);
    SET (PORT_RCK);
}

Expander

Von den meisten Expandern gibt es auch Unterversionen, wie 74HC (Highspeed CMOS), 74HTC (Highspeed CMOS, TTL-Compatible), etc. Hier tut's z.B. 74HC. Pro Expander fallen Kosten von ca 30-40 Cent an (Reichelt). Ungelatchte Register wie 74*164 sind übrigens nicht zu empfehlen, weil bei deren Verwendung wäherend des Schiebens die Ausgänge flattern.

Die Verwendete SPI-Frequenz ist unkritisch und kann natürlich auch langsamer eingestellt werden. Taktfrequenzen von 100MHz sind für die ICs normalerweise kein Problem.

8-Bit Schieberegister mit Ausgangs-Latch
74*594
Shift Clear, Latch Clear, Tri-State
74*595
Shift Clear, Tri-State
74*596
Shift Clear, Open Collector/high-Z
CD4094, 74*4094
Tri-State
8-Bit Schieberegister mit Eingangs-Latch
74*589
Tri-State
74*597
Shift Clear

Siehe auch