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

K
(Link zu "595 Shift Register Simulator" hinzugefügt)
 
(44 dazwischenliegende Versionen von 7 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Bietet ein Controller nicht genügend I/O-Leitungen, dann kann man Portexpander werwenden. Dadurch hat man zusätzliche Ports zur Verfügung. Neben [[I2C Chip-Übersicht|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.  
+
Bietet ein Controller nicht genügend I/O-Leitungen, dann kann ein Portexpander verwendet werden. Dadurch stehen zusätzliche Ports zur Verfügung. Neben [[I2C Chip-Übersicht|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 werden Schieberegister mit Ausgangs-Latch verwendet, 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).
+
Falls Eingänge benötigt werden, 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 =
 
= Pro & Contra =
Zeile 8: Zeile 8:
 
* schnell
 
* schnell
 
* preiswert
 
* preiswert
* einfach anzusteuern (auch ohne Hardware-Unterstützung)
+
* einfach anzusteuern (auch ohne Hardware-Unterstützung, deutlich einfacher als I²C)
 
* Ansteuerung ohne Interrupt-Programmierung
 
* Ansteuerung ohne Interrupt-Programmierung
 
* modular aufgebaut und erweiterbar
 
* modular aufgebaut und erweiterbar
Zeile 14: Zeile 14:
 
* SPI-Interface und [[In System Programming|ISP-Pins]] (die oft ungenutzt bleiben) können verwendet werden
 
* SPI-Interface und [[In System Programming|ISP-Pins]] (die oft ungenutzt bleiben) können verwendet werden
 
* Bus hat nur 3 Leitungen, dadurch kein kompliziertes Layout mit vielen Leitungen
 
* 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 & [[Diode#Zenerdiode|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 und effizient gedimmt werden. Das ist interessant, wenn die Portexpander [[LED]]s treiben.
  
 
;Nachteile
 
;Nachteile
 
* bei dieser Version nur Ausgabe möglich
 
* bei dieser Version nur Ausgabe möglich
* soll ein Ausgangs-Port geändert werden müssen ''alle'' Daten neu gesendet werden  
+
* 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
 
* bei der Variante mit SPI-Hardware liegt MISO brach bzw ist nicht so einfach nutzbar zu machen
 +
* Lange Leitungen sind problematisch, weil die CLK Leitung empfindlich auf Überschwinger reagiert.
  
 
=Resourcen=
 
=Resourcen=
Zeile 42: Zeile 46:
 
* evtl. Pullup/Pulldown-Widerstand: 20kΩ
 
* evtl. Pullup/Pulldown-Widerstand: 20kΩ
 
* Widerstände zum Entkoppeln vom ISP: einige kΩ
 
* Widerstände zum Entkoppeln vom ISP: einige kΩ
* evtl. Kondensatoren von 100pF zum Entstören der Dateinleitungen
+
* evtl. Kondensatoren von 100pF zum Entstören der Datenleitungen
 
* Strom für die Versorgung im Bereich von µA bis wenige mA (je nach Expander-Typ, Anzahl und Frequenz)
 
* Strom für die Versorgung im Bereich von µA bis wenige mA (je nach Expander-Typ, Anzahl und Frequenz)
  
Zeile 53: Zeile 57:
 
|}
 
|}
 
</center>
 
</center>
;Das umstrichelte Modul: kann einfach so oft nach rechts wiederholt werden bis man so viele Ausgänge hat wie gewünscht.
+
;Das umstrichelte Modul: kann einfach so oft nach rechts wiederholt werden, bis es soviele Ausgänge hat, wie gewünscht. Die Bits wandern über Pin&nbsp;14 (SER) in den Expander. Acht Takte später erscheint das Bit wieder an Pin&nbsp;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).
 
;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 &ndash; abgesehen vom minimalen Leckstrom der Ports und Füllen der Portkapazität von ein paar pF.
+
;R2, R3: entkoppeln den ISP-Adapter. Ansonsten stören sie nicht weiter, denn beim Programmieren muss praktisch kein Strom fliessen &ndash; 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.
+
; 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 wird natürlich einen weiteren µC-Port benötigt, um das zu tun. Falls an den Expandern [[LED]]s zum Einsatz kommen sollen, kann G auch mit einem [[PWM]]-Ausgang des µC verdrahtet werden, 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 gibt es die freie Auswahl, welche Ports verwendet werden sollen. 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.
 +
{{FarbigerRahmen |
 +
Falls '''SS''' nicht in dieser Weise verwendet wird, muss auf jeden Fall dafür gesorgt werden, daß '''SS''' ''entweder'' auf OUT steht, oder ''nie'' auf LOW geht, da sonst SPI in den Slave-Modus schaltet!
 +
}}
 +
 
 +
=Signalfolge=
 +
[[Bild:Signalfolge-Portexpander.png|center]]
 +
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=
 
=C-Code=
Zeile 87: Zeile 106:
 
;<tt>#define SERPA_SIZE</tt>: Define für Anzahl der auszugebenden Bytes
 
;<tt>#define SERPA_SIZE</tt>: Define für Anzahl der auszugebenden Bytes
 
;<tt>extern unsigned char serpa[SERPA_SIZE]</tt>: Das Array, dessen Bytes ausgegeben werden. <tt>serpa[0]</tt> landet in dem Portexpander-IC, das direkt am Controller sitzt. Bit0 erscheint jeweils an Ausgang QA, Bit7 erscheint am Ausgang QH, etc.
 
;<tt>extern unsigned char serpa[SERPA_SIZE]</tt>: Das Array, dessen Bytes ausgegeben werden. <tt>serpa[0]</tt> landet in dem Portexpander-IC, das direkt am Controller sitzt. Bit0 erscheint jeweils an Ausgang QA, Bit7 erscheint am Ausgang QH, etc.
;<tt>void serpa_init()</tt>: Initialisiert die Schnittstelle bzw. die verwendeten Ports
+
;<tt>void serpa_init(void)</tt>: Initialisiert die Schnittstelle bzw. die verwendeten Ports
;<tt>void serpa_out()</tt>: Gibt die <tt>SERPA_SIZE</tt> Bytes aus dem Array <tt>serpa</tt> aus.
+
;<tt>void serpa_out(void)</tt>: Gibt die <tt>SERPA_SIZE</tt> Bytes aus dem Array <tt>serpa</tt> aus.
  
 
'''serpa.h'''
 
'''serpa.h'''
<pre>
 
/* SERiell nach PArallel (serpa) im SPI-Protokoll */
 
#ifndef _SERPA_H_
 
#define _SERPA_H_
 
  
/* 4 Bytes (32 Ports) */
+
{{comment|SERiell nach PArallel (serpa) im SPI-Protokoll}}
#define SERPA_SIZE 4
+
#ifndef SERPA_H
 
+
#define SERPA_H
extern unsigned char serpa[];
+
extern void serpa_out();
+
{{comment|4 Bytes (32 Ports)}}
extern void serpa_init();
+
#define SERPA_SIZE 4
 
+
#endif /* _SERPA_H_ */
+
extern unsigned char serpa[];
</pre>
+
extern void serpa_out (void);
 +
extern void serpa_init (void);
 +
 +
#endif {{comment|SERPA_H}}
  
 
== Mit SPI-Hardware ==  
 
== Mit SPI-Hardware ==  
  
Das Senden erfolgt ohne den SPI-Interrupt zu nutzen. Allein der ISR-Prolog/Epilog dauert schon so lange wie ein Schleifendurchlauf.
+
Das Senden erfolgt, ohne den SPI-Interrupt zu nutzen. Allein der ISR-Prolog/Epilog dauert schon so lange, wie ein Schleifendurchlauf.
  
 
'''serpa.c'''
 
'''serpa.c'''
<pre>
 
// SERiell nach PArallel (serpa) mit Hardware-Unterstützung
 
#include <avr/io.h>
 
#include "serpa.h"
 
  
// Array für die Daten
+
{{lcomment|SERiell nach PArallel (serpa) mit Hardware-Unterstuetzung}}
unsigned char serpa[SERPA_SIZE];
+
#include <avr/io.h>
 
+
#include "serpa.h"
void serpa_init()
+
{
+
{{lcomment|Array fuer die Daten}}
  MAKE_OUT (PORT_MOSI);  
+
unsigned char serpa[SERPA_SIZE];
  MAKE_OUT (PORT_SCK);
+
  MAKE_OUT (PORT_RCK); SET (PORT_RCK);
+
void serpa_init (void)
 
+
{
  // !!! SS muss OUT sein, damit SPI nicht in Slave-Mode wechselt !!!
+
    MAKE_OUT (PORT_MOSI);  
  // entfällt, falls PORT_RCK = PORT_SS
+
    MAKE_OUT (PORT_SCK);
  MAKE_OUT (PORT_SS);
+
    MAKE_OUT (PORT_RCK); SET (PORT_RCK);
 
+
  // SPI als Master
+
    {{lcomment|!!! SS muss OUT sein, damit SPI nicht in Slave-Mode wechselt !!!}}
  // High-Bits zuerst
+
    {{lcomment|entfaellt, falls PORT_RCK &#61; PORT_SS}}
  // SCK ist HIGH wenn inaktiv
+
    MAKE_OUT (PORT_SS);
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL);
+
+
    {{lcomment|SPI als Master}}
  // pullup an MISO vermeidet Floaten
+
    {{lcomment|High-Bits zuerst}}
  SET (PORT_MISO);
+
    {{lcomment|SCK ist HIGH wenn inaktiv}}
 
+
    SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL);
  // maximale Geschwindigkeit: F_CPU / 2
+
  SPSR |= (1 << SPI2X);
+
    {{lcomment|pullup an MISO vermeidet Floaten}}
}
+
    SET (PORT_MISO);
 
+
void serpa_out ()
+
    {{lcomment|maximale Geschwindigkeit: F_CPU / 2}}
{
+
    SPSR |= (1 << SPI2X);
  unsigned char anz = SERPA_SIZE;
+
}
  unsigned char* serp = serpa+SERPA_SIZE;
+
 
+
void serpa_out (void)
  do
+
{
  {
+
    unsigned char anz = SERPA_SIZE;
      unsigned char data = *--serp;
+
    unsigned char* serp = serpa+SERPA_SIZE;
 
+
      // SPDR schreiben startet Übertragung
+
    do
      SPDR = data;
+
    {
 
+
      unsigned char data = *--serp;
      // warten auf Ende der Übertragung für dieses Byte
+
      while (!(SPSR & (1 << SPIF)));
+
      {{lcomment|SPDR schreiben startet Uebertragung}}
 
+
      SPDR = data;
      // clear SPIF
+
      data = SPDR;
+
      {{lcomment|warten auf Ende der Uebertragung für dieses Byte}}
  }
+
      while (!(SPSR & (1 << SPIF)));
  while (--anz > 0);
+
 
+
      {{lcomment|clear SPIF durch Lesen von SPDR}}
  // Strobe an RCK bringt die Daten von den Schieberegistern in die Latches
+
      (void) SPDR;
  CLR (PORT_RCK);
+
    }
  SET (PORT_RCK);
+
    while (--anz > 0);
}
+
</pre>
+
    {{lcomment|Strobe an RCK bringt die Daten von den Schieberegistern in die Latches}}
 +
    CLR (PORT_RCK);
 +
    SET (PORT_RCK);
 +
}
  
 
== Ohne SPI-Hardware ==  
 
== 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.
+
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 <tt>MAKE_OUT</tt>,  <tt>SET</tt> und <tt>CLR</tt> sind auch hier an die jeweilige Schaltung/Architektur/Compiler/Controller anzupassen.
 
+
 
'''serpa.c'''
 
'''serpa.c'''
<pre>
+
{{comment|SERiell nach PArallel (serpa) via Software}}
/* SERiell nach PArallel (serpa) via Software */
+
#include "serpa.h"
#include "serpa.h"
+
 +
{{comment|Array fuer die Daten}}
 +
unsigned char serpa[SERPA_SIZE];
 +
 +
void serpa_init (void)
 +
{
 +
    {{comment|Verwendete Ports auf OUT}}
 +
    MAKE_OUT (PORT_SER);
 +
    MAKE_OUT (PORT_SCK);
 +
    MAKE_OUT (PORT_RCK);
 +
 +
    {{comment|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;
 +
 +
        {{comment|8 Bits pro Byte rausschieben}}
 +
        for (bits = 8; bits > 0; bits--)
 +
        {
 +
            CLR (PORT_SER);
 +
            if (data & 0x80)
 +
            {
 +
                SET (PORT_SER);
 +
            }
 +
 +
            data <<= 1;
 +
            {{comment|Strobe an SCK schiebt Daten im Gaensemarsch}}
 +
            {{comment|um 1 Position weiter durch alle Schieberegister}}
 +
            CLR (PORT_SCK);
 +
            SET (PORT_SCK);
 +
        }
 +
    }
 +
    while (--anz > 0);
 +
 +
    {{comment|Strobe an RCK bringt die Daten von den Schieberegistern in die Latches}}
 +
    CLR (PORT_RCK);
 +
    SET (PORT_RCK);
 +
}
  
/* Array für die Daten */
+
= BASCOM-Code=
unsigned char serpa[SERPA_SIZE];
+
  
void serpa_init ()
+
== Mit SPI-Hardware ==
{
+
    /* Verwendete Ports auf OUT */
+
    MAKE_OUT (PORT_SER);
+
    MAKE_OUT (PORT_SCK);
+
    MAKE_OUT (PORT_RCK);
+
  
    /* SCR und RCK auf definierten Level HIGH */
+
'''serpa.bas'''
    SET (PORT_SCK);
+
<pre>
    SET (PORT_RCK);
+
' SERiell nach PArallel (serpa) mit Hardware-Unterstuetzung
}
+
' Beispiel mit 2 Datenbytes
  
void serpa_out ()
+
' SPI Bus (Hardware) als Master konfiguiren
{
+
Config SPI = Hard , Master = Yes
    unsigned char anz = SERPA_SIZE;
+
    unsigned char* serp = serpa+SERPA_SIZE;
+
  
    do
+
' SPI Bus initialisieren
    {
+
' setzt das DDR Register entsprechend
        unsigned char bits;
+
Spiinit
        unsigned char data = *--serp;
+
  
        /* 8 Bits pro Byte rausschieben */
+
' Bytes für die Daten
        for (bits = 8; bits > 0; bits--)
+
Dim Serpa(2) As Byte
        {
+
Serpa(1) = &B10101010
            CLR (PORT_SER);
+
Serpa(2) = &B00000000
            if (data & 0x80)
+
            {
+
                SET (PORT_SER);
+
            }
+
  
            data <<= 1;
+
' Die Bytes auf den SPI Bus schieben
            /* Strobe an SCK schiebt Daten im Gänsemarsch      */
+
Spiout Serpa(1) , 2
            /* 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);
+
}
+
 
</pre>
 
</pre>
  
 
=Expander=
 
=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 ([[Bezugsquellen#Elektronikbauteile|Reichelt]]). Ungelatchte Register wie 74*164 sind übrigens nicht zu empfehlen, weil bei deren Verwendung wäherend des Schiebens die Ausgänge flattern.
+
Von den meisten Expandern gibt es auch Unterversionen, wie 74HC (Highspeed CMOS), 74HCT (Highspeed CMOS, TTL-Compatible), etc. Hier tut's z.B. 74HC. Pro Expander fallen Kosten von ca 30-40 Cent an ([[Bezugsquellen#Elektronikbauteile|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.
+
Die Verwendete SPI-Frequenz ist unkritisch und kann natürlich auch langsamer eingestellt werden. Taktfrequenzen von 50MHz sind für die ICs normalerweise kein Problem. Die steilen Flanken können aber ein Problem bei längeren Kabeln werden.
  
 
;8-Bit Schieberegister mit Ausgangs-Latch:
 
;8-Bit Schieberegister mit Ausgangs-Latch:
;74*594: Shift Clear, Latch Clear, Tri-State
+
:;74*594: Shift Clear, Latch Clear, Tri-State
;74*595: Shift Clear, Tri-State
+
:;74*595: Shift Clear, Tri-State
;74*596: Shift Clear, [[Open Collector]]/high-Z  
+
:;74*596: Shift Clear, [[Open Collector]]/high-Z  
;CD4094, 74*4094: Tri-State
+
:;CD4094, 74*4094: Tri-State
 +
 
 +
;8-Bit Schieberegister mit Eingangs-Latch:
 +
:;74*589: Tri-State
 +
:;74*597: Shift Clear
  
 
=Siehe auch=
 
=Siehe auch=
Zeile 241: Zeile 285:
 
* [[Bezugsquellen]]
 
* [[Bezugsquellen]]
 
* [[I2C|I&sup2;C-Bus]]
 
* [[I2C|I&sup2;C-Bus]]
 +
* [[SPI|SPI-Bus]]
 +
* [[RN-Definitionen]] - Empfohlener einheitlicher Stecker für SPI-Bus
 +
 +
=Weblinks=
 +
* [http://www.gjlay.de/pub/ebook/index.html Dokumentiertes Projekt, das drei 74*595 als Portexpander verwendet]
 +
* [http://conductiveresistance.com/interactive-595-shift-register-simulator/ "Interactive 595 Shift Register Simulator" - zum Online-Ausprobieren]
  
 +
[[Kategorie:Elektronik]]
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Kommunikation]]
 
[[Kategorie:Kommunikation]]
 
[[Kategorie:Praxis]]
 
[[Kategorie:Praxis]]
 +
[[Kategorie:Software]]
 
[[Kategorie:Quellcode C]]
 
[[Kategorie:Quellcode C]]
 +
[[Kategorie:Projekte]]

Aktuelle Version vom 28. Oktober 2012, 12:07 Uhr

Bietet ein Controller nicht genügend I/O-Leitungen, dann kann ein Portexpander verwendet werden. Dadurch stehen 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 werden Schieberegister mit Ausgangs-Latch verwendet, d.h. die Daten werden erst dann an die Ausgänge geschaltet, wenn sie an Ort und Stelle sind.

Falls Eingänge benötigt werden, 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, 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 und 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
  • Lange Leitungen sind problematisch, weil die CLK Leitung empfindlich auf Überschwinger reagiert.

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 Datenleitungen
  • 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 es soviele 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 Programmieren 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 wird natürlich einen weiteren µC-Port benötigt, um das zu tun. Falls an den Expandern LEDs zum Einsatz kommen sollen, kann G auch mit einem PWM-Ausgang des µC verdrahtet werden, 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 gibt es die freie Auswahl, welche Ports verwendet werden sollen. 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 SS nicht in dieser Weise verwendet wird, muss auf jeden Fall dafür gesorgt werden, 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(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 
      (void) 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), 74HCT (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 50MHz sind für die ICs normalerweise kein Problem. Die steilen Flanken können aber ein Problem bei längeren Kabeln werden.

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

Weblinks


LiFePO4 Speicher Test