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 gebraucht 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).
Inhaltsverzeichnis
Pro & Contra
- Vorteile
- schnell
- preiswert
- einfach anzusteuern (auch ohne Hardware-Unterstützung, deutlich einfacher als I²C)
- 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.
- Indem die Enable-Leitung des Latches per PWM angesteuert wird, können die Ausgänge einfach une effizient gedimmt werden. Das ist interessant, wenn die Portexpander LEDs treiben.
- 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
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
- Das umstrichelte Modul
- kann einfach so oft nach rechts wiederholt werden, bis man so viele Ausgänge hat wie gewünscht. Die Bits wandern über Pin 14 (SER) in den Expander. Acht Takte später erscheint das Bit wieder an Pin 9 (QH*) und damit am SER-Eingang des folgenden Expanders.
- 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. Falls an den Expandern LEDs zum EInsatz kommen sollen, kann man G auch mit einem PWM-Ausgang des µC verdrahten, um die Anzeige dimmen zu können. Im Falle eines PWM-Betriebs ist darauf zu achten, daß durch das gleichzeitige Schalten vieler Ausgänge/Lasten die Betriebsspannung "sauber" bleibt, indem man etwa auf ausreichend große Abblock-Kondensatoren an den Expandern achtet oder andere Maßnahmen zur hinreichenden Stabilisierung der Versorgungsspannung ergreift.
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
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)
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(void)
- Initialisiert die Schnittstelle bzw. die verwendeten Ports
- void serpa_out(void)
- 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 (void); extern void serpa_init (void); #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-Unterstuetzung #include <avr/io.h> #include "serpa.h" // Array fuer die Daten unsigned char serpa[SERPA_SIZE]; void serpa_init (void) { 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 !!! // entfaellt, 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 (void) { unsigned char anz = SERPA_SIZE; unsigned char* serp = serpa+SERPA_SIZE; do { unsigned char data = *--serp; // SPDR schreiben startet Uebertragung SPDR = data; // warten auf Ende der Uebertragung für dieses Byte while (!(SPSR & (1 << SPIF))); // clear SPIF durch Lesen von SPDR 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. Die Pseudocodes MAKE_OUT, SET und CLR sind auch hier an die jeweilige Schaltung/Architektur/Compiler/Controller anzupassen.
serpa.c
/* SERiell nach PArallel (serpa) via Software */ #include "serpa.h" /* Array fuer die Daten */ unsigned char serpa[SERPA_SIZE]; void serpa_init (void) { /* 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 (void) { 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 Gaensemarsch */ /* 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); }
BASCOM-Code
Mit SPI-Hardware
serpa.bas
' SERiell nach PArallel (serpa) mit Hardware-Unterstuetzung ' Beispiel mit 2 Datenbytes ' SPI Bus (Hardware) als Master konfiguiren Config SPI = Hard , Master = Yes ' SPI Bus initialisieren ' setzt das DDR Register entsprechend Spiinit ' Bytes für die Daten Dim Serpa(2) As Byte Serpa(1) = &B10101010 Serpa(2) = &B00000000 ' Die Bytes auf den SPI Bus schieben Spiout Serpa(1) , 2
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
- avr-gcc
- Bezugsquellen
- I²C-Bus
- SPI-Bus
- RN-Definitionen - Empfohlener einheitlicher Stecker für SPI-Bus