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


Baustelle.gif An diesem Artikel arbeitet gerade Mitglied SprinterSB.

Am besten momentan noch keine gravierenden Ergänzungen / Änderungen vornehmen.

Dieser Hinweis verschwindet wenn der Autor soweit ist. Sollte dieser Hinweis länger als drei Tage auf einer Seite sein, bitte beim Autor SprinterSB per PM / Mail oder Forum nachfragen ob er vergessen wurde.

Vorteile
  • schnell
  • preiswert
  • einfach anzusteuern (auch ohne Hardware-Unterstützung)
  • modular aufgebaut und erweiterbar
  • alle Ports können gleichzeitig geschaltet werden, auch 100 Stück
  • SPI-Interface und ISP-Pins 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
Interrupts keine
Flash (Bytes mit -Os) 52 44
SRAM (statisch) N
SRAM (Stack) 2
Laufzeit serpa_out() 19 + 101*N 16 + 27*N
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

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)

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

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 = _BV(SPE) | _BV(MSTR) | _BV(CPOL);
	
   // pullup an MISO vermeidet Floaten
   SET (PORT_MISO);

   // maximale Geschwindigkeit: F_CPU / 2	 
   SPSR |= _BV(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);
}