Forum: Mikrocontroller und Digitale Elektronik MCP23S18 I/O Expander


von Nico F. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo miteinander,

Ich komme irgendiwie an meine Grenzen mit dem Verständlichen eines 
Datenblattes. Auch wen ich es noch so ganz genau anschaue sehe ich da 
nicht wirklich durch. Vieleicht hat auch schon jemand mit diesem 
Baustein (I/O Expander) MCP23S18 erfahrungen gemacht. Alles was ich 
möchtes ist lediglich An PORTA etwas ausgeben also z.B. 0x04. Schon 
alleine die Register Adressen sind nirgends erklärt da bin ich aber 
soweit das ich das Register OLATA benötige um ein Ausgangssignal zu 
generieren. Aber schon das zweite mit der BANK 1/0 ist nicht wirklich 
erleutert wie ich das setze. Würde ich dies mit I2C programmieren wäre 
es ev. einfacher da gibt es ein wunderbares Flussdiagramm aber bei SPI 
nicht. Sie sagen man sollte auch das definieren ob es jezt ein Eingang 
oder ein Ausgang (DDR) ist, aber das kann ich auch nicht mehr entnehmen 
von dem Ablauf her was zuerst kommt. Was mir auch klar ist das ich mit 
0x40 schreiben und mit 0x41 lesen kann. Und wen ich jetzt das so 
anschaue müsste ich für meinen Zweck 0x40(Schreiben), 0x14 Regeister 
Adresse PortA, 0x04 (Pin 3 output 3,3V) senden.
Ev. könnte mir jemand auf die Sprünge helfen oder sieht gleich wiso ich 
so nicht durchblicke. Leider habe ich auch noch keine Legende entdeckt 
über all diese Abkürzungen der Register.

Danke.

von Horst (Gast)


Lesenswert?

Nico F. schrieb:
> alleine die Register Adressen sind nirgends erklärt

Doch im Datenblatt das Du angehängt hast, mußt Du nur genau lesen.

Du entscheidest Dich zuerst, ob Du zwei 8Bit Ports oder einen 16Bit-Port 
haben willst. Dementsprechend setzt Du Bit 7 (Bank) in IOCON 
(0x05/0x15).
Dadurch hast Du die Adressen der anderen Register festgelegt.
Dann legst Du in IODIRA oder IODIRB (je nach Port) fest welcher Pin Ein- 
oder Ausgang sein soll.
Danach kannst Du GPIOA oder GPIOB schreiben oder lesen. Wenn Du es liest 
bekommst Du den aktuellen Zustand, wenn Du schreibst wird OLATA/B 
entsprechend gesetzt. Du kannst auch OLATA/B direkt beschreiben.

Der Rest kommt später ;-)

von Horst (Gast)


Lesenswert?

ach ja:

Nico F. schrieb:
> Leider habe ich auch noch keine Legende entdeckt
> über all diese Abkürzungen der Register.

ab 1.6.1, Seite 18 im Datenblatt. Nur die erste Seite lesen reicht 
nicht.

von Jochen S. (schoenf)


Lesenswert?

Hi, also ich nutze den 23S17, der ist ganz ähnlich.

Die Register sind doch ab Seite 18 im Datenblatt sehr genau beschrieben. 
Du musst doch bloß im IODIR das entsprechende Muster reinschreiben, 
damit die Pins Ausgang werden (Standard ist Input) und dann in GPIO das, 
was du ausgeben willst.

Da das BANK-Bit beim Starten 0 ist, sind die Register abwechselnd 
gemappt, wie du in Tabelle 1-1 sehen kannst. Also IODIR A/B auf 0x00 und 
0x01 und GPIO auf 0x12 und 0x13.

So sehen meine Routinen zum Beschreiben und Auslesen aus:
1
void Write23s17(uint8_t reg, uint8_t data) {
2
    SPITXbuffer[0] = 0x40;  // 4 is fix, 0 is SPI address #0 with R/W=0
3
    SPITXbuffer[1] = reg;   // register address follows
4
    SPITXbuffer[2] = data;  // data byte follows
5
6
    // Select MCP23S17
7
    CS_IO_EXPANDEROff();
8
    
9
    Write_Buffer_Handle4 = DRV_SPI_BufferAddWriteRead(SPIHandle4,
10
                (SPI_DATA_TYPE *) &SPITXbuffer[0], 3,
11
                (SPI_DATA_TYPE *) &SPIRXbuffer[0], 3,
12
                NULL,NULL
13
                );
14
    // Wait for transmission done
15
    while (0 == (DRV_SPI_BufferStatus(Write_Buffer_Handle4) & DRV_SPI_BUFFER_EVENT_COMPLETE));
16
    
17
    CS_IO_EXPANDEROn();
18
}
19
20
uint8_t Read23s17(uint8_t reg) {
21
    SPITXbuffer[0] = 0x41;  // 4 is fix, 1 is SPI address #0 with R/W=1
22
    SPITXbuffer[1] = reg;
23
    
24
    // Select MCP23S17
25
    CS_IO_EXPANDEROff();
26
    
27
    Write_Buffer_Handle4 = DRV_SPI_BufferAddWriteRead(SPIHandle4,
28
                (SPI_DATA_TYPE *) &SPITXbuffer[0], 2,
29
                (SPI_DATA_TYPE *) &SPIRXbuffer[0], 3,
30
                NULL,NULL
31
                );
32
    // Wait for transmission done
33
    while (0 == (DRV_SPI_BufferStatus(Write_Buffer_Handle4) & DRV_SPI_BUFFER_EVENT_COMPLETE));
34
    
35
    CS_IO_EXPANDEROn();
36
    
37
    return SPIRXbuffer[2];
38
}

Viele Grüße

Jochen

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

hmmm, die Register sind eigentlich alle im Datenblatt auf den letzten 
Seiten erklärt. Klar, da muss man sich durchbeißen. Die Register sind 
ebenfalls gleich zu meinem MCP23017 (I2C). So sieht der Auszug meiner 
Klasse für Ausgänge aus. Die Methoden readOneRegister und 
writeOneRegister sind nur die I2C Transfer Funktionen. Musste nur durch 
SPI Transfer Funktionen ersetzen. i2cAdr wäre dann bei dir der SPI /CS 
Pin.

Die Pinnummern des MCP habe ich für mich einfach durchnummeriert und 
übergebe diesen Wert bei der Objektinitialisierung.
1
#pragma once
2
3
// Register Table 3.1 mit IOCON.BANK = 0
4
constexpr uint8_t IODIRA   = 0x00;
5
constexpr uint8_t IODIRB   = 0x01;
6
constexpr uint8_t OLATA    = 0x14;
7
constexpr uint8_t OLATB    = 0x15;
8
9
10
// GPA.0 ... GPA.7 => Pinnummer 0-7
11
// GPB.0 ... GPB.7 => Pinnummer 8-15  
12
  
13
// *** interne Hilfsfunktionen *** //
14
constexpr uint8_t getBitMaske(const uint8_t pin) {
15
  return (pin<8) ? (1<<pin) : (1<<(pin-8));
16
}
17
18
constexpr uint8_t get_DIR_Port(const uint8_t pin) {
19
  return (pin<8) ? IODIRA : IODIRB;
20
}
21
22
constexpr uint8_t get_OUT_Port(const uint8_t pin) {
23
  return (pin<8) ? OLATA : OLATB;
24
}
25
26
27
// >>> Output <<< ------------------------------------------------------------------
28
template <uint8_t i2cAdr, uint8_t pin>
29
class Output
30
{   
31
  public:  
32
  // Konstruktor    
33
  Output ()
34
  { }
35
36
  void init()
37
  {   
38
    setLow();                        // verhindert Einschaltzucken
39
    uint8_t is = 0;    
40
    readOneRegister (i2cAdr, get_DIR_Port(pin), is);    // IODIR Port lesen
41
    is &= ~(getBitMaske(pin));               // Bit löschen
42
    writeOneRegister (i2cAdr, get_DIR_Port(pin), is);    // modifizierte Maske zurückschreiben
43
  } 
44
45
  void setHigh()
46
  {   
47
    uint8_t is = 0;
48
    readOneRegister (i2cAdr, get_OUT_Port(pin), is);    // OLAT Port lesen
49
    is |= (getBitMaske(pin));               // Bit setzen
50
    writeOneRegister (i2cAdr, get_OUT_Port(pin), is);    // modifizierte Maske zurückschreiben
51
  } 
52
53
  void setLow()
54
  {   
55
    uint8_t is = 0;
56
    readOneRegister (i2cAdr, get_OUT_Port(pin), is);    // OLAT Port lesen
57
    is &= ~(getBitMaske(pin));               // Bit löschen
58
    writeOneRegister (i2cAdr, get_OUT_Port(pin), is);    // modifizierte Maske zurückschreiben
59
  } 
60
61
  void set(const bool state)                            // für Zustandsentscheidung während der Laufzeit
62
  {   
63
    if (state)  setHigh();
64
    else  setLow();
65
  } 
66
};

Ist kostenlos, ich hoffe das es nicht umsonst ist.  :-)

von Nico F. (Gast)


Lesenswert?

Das hilft mir schon einwenig aber sehe ich das demfalle Richtig das ich 
für jeden einzelenes commando einen Restart machen muss und neu 
beginnen.

also CS /Write,0x40,0x14 CS/restart
     CS /write,0x40,0x00,0x04
somit müsste Pin3 PORTA = 3,3V sein

von Philipp K. (philipp_k59)


Lesenswert?

Du kannst auch im continuous Mode Daten schieben, steht auch im 
Datenblatt..

von Veit D. (devil-elec)


Lesenswert?

Hallo,

was meinst du mit "Restart"?

Du musst doch nur einmalig die Richtung festlegen in IODIRA. <== Ausgang
Danach schaltest du nur noch in OLATA ein oder aus mit dem jeweiligen 
Bit.

von Nico F. (Gast)


Lesenswert?

Ja ich meine eher du must die Richtung im Register festlegen. Also musst 
du sicher mal zu dem Register navigieren. und mit restart meine ich CS 
High und dan erneut Low und neu beginnen. Oder verstehe ich das falsch.

von Nico F. (Gast)


Lesenswert?

Mein Jetziger code ist bis jetzt so


CSlow;
SPIT(0x40);//Schreiben
SPIT(0x14);//IODIRA
SPIT(0x00);//Richtung Ausgang
CShigh;

CSlow;
SPIT(0x40);//Schreiben
SPIT(0x14);//OLATA
SPIT(0x04);//Output
CShigh;

von Jochen S. (schoenf)


Lesenswert?

Ja, sollte so klappen.

Gruß

Jochen

von Jochen S. (schoenf)


Lesenswert?

Ich hatte bei 23S17 noch das Problem, dass die drei Adressleitungen - 
auch im SPI-Modus - auf 0 gesetzt werden mussten, sonst war der nicht 
ansprechbar. Die Adresse wird beim 23S18 ja analog über 
Widerstandsteiler eingestellt. Wenn es nicht geht, vielleicht mal hier 
den Pin so beschalten, dass Adresse 0 eingestellt ist. Hab ich zwar beim 
Überfliegen des Datenblatts nicht gesehen, dass das erforderlich ist, 
aber der Teufel steckt ja manchmal im Detail.

Viele Grüße

Jochen

von Nico F. (Gast)


Lesenswert?

Muss korrigieren
Aber leider klappt es so nicht ev können noch SPI Einstellungen wie CPOL 
oder CPHA falsch sein sind jetzt CPOL 1 und CPHA 1

CSlow;
SPIT(0x40);//Schreiben
SPIT(0x00);//IODIRA
SPIT(0x00);//Richtung Ausgang
CShigh;

CSlow;
SPIT(0x40);//Schreiben
SPIT(0x14);//OLATA
SPIT(0x04);//Output
CShigh;

von Jochen S. (schoenf)


Lesenswert?

Ne, ich sehe gerade, du schreibst zweimal in die 0x14 (OLATA). Du musst 
einmal in 0x00 schreiben (IODIRA), am besten 0x00, dann sind alle Pins 
Ausgänge. Und dann in 0x12 (GPIOA), was ausgegeben werden soll. 0x14 
(OLATA) sollte auch klappen - Schreibzugriffe in die 0x12 werden an 0x14 
durchgereicht.

Viele Grüße

Jochen

von Jochen S. (schoenf)


Lesenswert?

Wenn du kein Oszi zur Hand hast, musst du die vier Modi, die möglich 
sind, ausprobieren. Sonst einfach mal schauen, ob die Signalform so ist, 
wie sie laut Datenblatt sein muss.

Viele Grüße

Jochen

von Nico F. (Gast)


Lesenswert?

Jochen S. schrieb:
> Wenn du kein Oszi zur Hand hast, musst du die vier Modi, die
> möglich
> sind, ausprobieren. Sonst einfach mal schauen, ob die Signalform so ist,
> wie sie laut Datenblatt sein muss.
>
> Viele Grüße
>
> Jochen

Ja die Signalform stimmt bei steigender Flanke wird eingelesen von dem 
Clock und die Zeitabstände stimmen auch wie sie sollten.

von Jochen S. (schoenf)


Lesenswert?

Kannst ja auch das Lesen mal probieren. Zwei Register sind beim 
Anschalten mit 0xFF, alle anderen mit 0x00 initialisiert. Wenn du die 
ganze Register-Map ab Adresse 0x00 ausliest, siehst du ja, ob diese 
Antworten dabei sind oder alles 0xFF ist, was darauf deuten lässt, dass 
sich der Chip nicht angesprochen fühlt. Dann wie gesagt mal den ADDR-Pin 
so beschalten, dass Adresse 0 eingestellt ist.

Viele Grüße

Jochen

Beitrag #6135314 wurde vom Autor gelöscht.
von Nico F. (Gast)


Lesenswert?

Jochen S. schrieb im Beitrag #6135314:
> Dumme Zusatzfrage: Quarz mit zwei Kondensatoren hast du auch dran?
>
> Viele Grüße
>
> Jochen

Ehm am Controller ja an dem MCP23S18 wäre mir neu ;)

von Jochen S. (schoenf)


Lesenswert?

Wie ist dein /RESET-Pin beschaltet? Der muss "externally biased" sein, 
also Pull-Up mit anderen Worten.

Viele Grüße

Jochen

von Jochen S. (schoenf)


Lesenswert?

Ja, hab mich im Datenblatt vertan. Habe beim MCP2518 geschaut, den ich 
auch noch auf dem Bildschirm hatte. Das ist ein CAN-Controller und der 
braucht Oszi. Hab meinen Kommentar aber gelöscht.

Viele Grüße

Jochen

von Nico F. (Gast)


Lesenswert?

Jochen S. schrieb:
> Wie ist dein /RESET-Pin beschaltet? Der muss "externally biased"
> sein,
> also Pull-Up mit anderen Worten.
>
> Viele Grüße
>
> Jochen

Ja, der ist auf 3,3V der Reset

von Jochen S. (schoenf)


Lesenswert?

Seltsam, wenn da gar nix geht. Hab gerade auch gesehen, dass nur der 
23018 den ADDR-Pin hat. Wenn der beim 23S18 fehlt, kann es damit auch 
nicht zusammenhängen ...

Viele Grüße

Jochen

von NichtWichtig (Gast)


Lesenswert?

Mit einem 23S17 bin ich auf die Nase gefallen wegen Spannungen.

µC BluePill mit 3V3
23S17 an 5V und an den GPIOs ein 5V LCD128x64 uralt.

Ging ein paar Stunden/Tage und dann war Schluß, LCD reagierte nicht 
mehr.

Ursache ist der geringe High-Level an SPI wenn der 23S13 an 5V hängt.
Der mag da Vcc*0.8 mindestens als high sehen

Simple Diode reduziert die ChipSpannung und schon paßt es.

Läuft alles an gleicher Spannung sollte das keine Thema sein.

von Nico F. (Gast)


Lesenswert?

> Ging ein paar Stunden/Tage und dann war Schluß, LCD reagierte nicht
> mehr.
>
> Ursache ist der geringe High-Level an SPI wenn der 23S13 an 5V hängt.
> Der mag da Vcc*0.8 mindestens als high sehen

Also das hab ich jetz auch noch überprüft. Die Spannung geht wirklich 
auf 0.0-0.1V runter also kaum das dies noch als high gesehen werden 
könnte.

von NichtWichtig (Gast)


Lesenswert?

Andersrum:
Der Highpegel vom BluePill langt dem MCP nicht als Eins wenn er an 
fetten 5V hängt.

von Nico F. (Gast)


Lesenswert?

NichtWichtig schrieb:
> Andersrum:
> Der Highpegel vom BluePill langt dem MCP nicht als Eins wenn er an
> fetten 5V hängt.

der high pegel liegt bei 3,2V dies sollte wohl reichen hängt ja nur an 
3,3V

von Manuel (justlikemanuel)


Lesenswert?

Hallo Horst,

ich habe eine Frage zum IOCON-Register.
Alle anderen Register gibt es einmal für A und B. Daher dachte ich, dass 
es das IOCON Register nur einmal gibt. Auf der Register-Karte taucht 
IOCON aber einmal bei A und einmal bei B auf.
Allerdings werden da ja auch Parameter eingetragen, die beide Register 
betreffen?

Ich sag' im Voraus schonmal besten Danke!

MfG
Manuel

von Jochen S. (schoenf)


Lesenswert?

Das ist dasselbe Register, was in beiden Bänken auftaucht, also 
gespiegelt.

Viele Grüße

Jochen

von Manuel (justlikemanuel)


Lesenswert?

Hallo Jochen,

alles klar, danke für die Antwort!

von Peter D. (peda)


Lesenswert?

Also wer sich das ausgedacht hat, das Register zur Bankumschaltung auf 
unterschiedliche Adressen je Bank zu setzen, der hat nicht alle Tassen 
im Schrank.

Um aus einem unbekannten BANK-Bit heraus zu kommen, muß man auf Verdacht 
2 Zugriffe machen, auf 05 und auf 0B.
Steht das Bit falschrum, schreibt man entweder auf ein nicht vorhandenes 
Register oder auf GPINTENB, was zumindest keinen Portpin falsch setzt.

von Jochen S. (schoenf)


Lesenswert?

Ja, das ist nicht durchdacht, da gebe ich dir recht. Aber im Normalfall 
entscheidet man sich ja beim Initialisieren für das eine oder das andere 
Modell und setzt das Bit entsprechend. Ein Problem ist es nur, wenn der 
Chip einmal initialisiert wurde und dann der Mikrocontroller z. B. durch 
Watchdog Reset neu startet. Dann weiß er halt nicht, dass der 23S18 noch 
im anderen Bankmodell ist und macht Blödsinn. Als Workaround kann man 
beim Initialisieren natürlich alle in Frage kommenden Register auf 0 
zurücksetzen, was dem Zustand nach Reset entspricht, und dann die 
eigentliche Initialisierung zu machen. Umständlich, aber kein unlösbares 
Problem.

Viele Grüße

Jochen

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.