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

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
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

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 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.

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

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 Untersersionen, 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

Siehe auch


LiFePO4 Speicher Test