SPI Bus Problem - mehrere Devices - andere Settings

Hallo,

ich habe meine fertige MCP3550 Funktion in mein eigentlichen Projektcode geschoben. Und habe seltsame Effekte. Die SD-Karte, der MAX6675 und das Display funktionieren weiterhin. Alles am SPI Bus. Nur mein MCP3550 funktioniert nicht. Die Funktion erkennt das Readysignal auf der MISO Leitung nicht.

Trenne ich die MISO Leitung der SD-Karte vom Bus und resete den µC, werden sofort richtige Spannungswerte vom MCP3550 geliefert. Nehme ich die MISO Leitung der SD-Karte einfach so wieder dazu, funktioniert die MCP3550 Funktion weiterhin und die SD-Karte funktioniert auch. :astonished: Gespeicherte Daten werden geschrieben und gelesen. Als wenn nichts gewesen wäre. Aber nur solange bis zum nächsten µC Reset.

Die Trennung der MISO Leitung vom MAX6675 hat keine Wirkung.

Nun arbeitet die SD-Karte im Half-Speed Modus, dann sieht man im Initialisierungscode. Das ist dem MCP3550 wohl noch egal. Der MCP3550 benötigt jedoch den **SPI.setDataMode(SPI_MODE3); **
sonst kommt Datenmüll raus. Seltsamerweise juckt das aber die SD-Karte scheinbar nicht. :astonished:

Was muß ich wie machen damit jedes SPI Device mit den richtigen Einstellungen arbeitet? Ich könnte am Anfang der MCP3550 Reading Funktion alles passend für diesen einstellen und am Ende wieder alles umstellen. Nur was?

Im Anhang der komplette Projektcode und die MCP3550 Funktion einzeln.

SDcard_Write_Taster_LcdSPI_DS1820_027.ino (35.3 KB)

MCP3550_50_011.ino (8.69 KB)

Wenn man sich mal ansieht was setDataMode() eigentlich macht:

void SPIClass::setDataMode(uint8_t mode)
{
  SPCR = (SPCR & ~SPI_MODE_MASK) | mode;
}

Und da:

#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define SPI_MODE2 0x08
#define SPI_MODE3 0x0C

#define SPI_MODE_MASK 0x0C  // CPOL = bit 3, CPHA = bit 2 on SPCR

geht es hierbeir nur um zwei Bits im SPCR Register (ich nehme mal SPI Control Register)

Da könnte man das SPCR Register sichern, den Modus umschalten was machen und dann wieder den alten Zustand herstellen.

Sofern das dein einziges Problem ist. Das ist mir nicht ganz klar.

Hallo,

ich hoffe das dies mein einziges Problem ist. Dein Vorschlag klingt logisch. Ich möchte jedoch erstmal mit SPI.setDataMode(xxx); einfach den Modi setzen der sonst auch verwendet wird. Auch ohne meine Funktion. Kennst Du den Modi den die SD-Karte Library (sd.h) verwendet? Ohne das ich vorher irgendwelche Register auslesen und sichern muß. Scheinbar schaltet die SD Lib nur den Takt auf 2MHz herunter. Mehr nicht. Das dürfte aber meinen MCP3550 nicht außer Tritt bringen. Das auslesen dürfte nur doppelt so lange dauern.

Beim SPI Takt sind 4MHz Standard beim Arduino, wenn man nichts ändert.
Beim DataMode lese ich nichts von einer Voreinstellung in der IDE. Gibt es dafür keinen Standardwert?
MSB_first ist eigentlich quasi Standard, daran sollte ich nichts ändern müssen.

Siehe Sd2Card Klasse in Sd2Card::init():

SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);

SPE = SPI Enable
MSTR = Master
SPR0/1 = Clock auf µC Takt / 128

Danach stehen die CPOL (Clock Polarity) und CPHA (Clock Phase) Bits auf 0 und es ist MODE0 aktiv. Das DORD Bit (Data Order) ist auch 0 und damit gilt MSB First. Siehe Atmega328 Datenblatt Seite 173

Hallo,

Danke. Guck ich mir an. Kurz vorm Ziel scheitern gibt's nicht. :astonished:

Hallo,

ich habe das SPCR Register in meinem Sketch ohne MCP3550 ausgelesen und dann im alleinigen MCP Sketch.

ohne MCP Code, SPCR: 0x50 bzw. binär 0101 0000
MCP alleine, SPCR: 0x5C bzw. binär 0101 1100

Das wäre ohne MCP Mode0 und im MCP Sketch Mode3.

Jetzt habe ich im gesamten großen Sketch am Anfang der Reading MCP Funktion

SPI.end();
SPI.setClockDivider(32);
SPI.setDataMode(SPI_MODE3);
SPI.begin();

eingefügt und am Ende (vor return) wieder

SPI.end();
SPI.setClockDivider(128);
SPI.setDataMode(SPI_MODE0);
SPI.begin();

Das löst das Problem leider nicht korrekt. Die MCP Funktion funktioniert nicht stabil. Ich kann nach dem Sketchupload den Resettaster öfters drücken und merke das es manchmal geht und manchmal nicht. Auf dem Steckbrett ist ein 100nF an jedem IC. Genauer gesagt, bei den Reset wechselt es immer, geht, geht nicht, geht, geht nicht usw. ...
Bin echt ratlos.

Ich kann auch den ClockDivider auf 128 lassen, ändert leider nicht. Mit jeden Reset wechselt der Zustand.

Hallo,

im Sketch wird bei der SD-Karten Initialisierung von Half-Speed geredet und eingestellt. Der Teiler ist jedoch 128. Das sind doch nur 125kHz. ??? Sollte Halfspeed nicht 2MHz bedeuteten wenn man von normalen Full-Speed SPI 4MHz ausgeht.

Ich bin nochmal zurück zu den Basics. Habe den Sketch von meiner Einzelfunktion Stück für Stück mit einfachen Sachen ausgebaut. MAX6675 und LCD. Funktioniert. Dann habe ich die SD-Karte wieder mit eingebaut und nur mit initialisieren lassen. Zack, funktioniert der MCP nur nach jeden 2. Reset. Irgendwas muss hier unkontrolliert passieren.

Warum wechselt der Inhalt vom SPSR Register? Das Bit 7 SPIE (Interrupt Enable) ändert sich etwas sehr spät im Code und dann nie wieder. Allerdings scheint das keine Wirkung zu haben? Was macht das Bit genau? Darf der SPI Bus von einem anderen Interrupt unterbrochen werden oder nicht?

Ich habe dann statt der SD.h die SdFat.h verwendet. Habe darin den MCP Code eingebaut und scheint zu funktionieren. Mir graut es jedoch meinen gesamten Sketch auf die SdFat umzubauen. Weis jemand was die SD.h noch so treibt außer den SPI Takt zu ändern?

mit SD.h - Registerausgaben einmal binär und hex

Setup SPCR:	0	0
SPI begin SPCR: 101000050
LCD Init     SPCR:	1010000	50
MCP set      SPCR:	1010010	52
Initializing SD card...

SD SPCR:         	1010000	50
Initialization done.
SD Init     SPCR:	1010000	50
MCP     Request SPCR:	1010000	50
MCP set Request SPCR:	1011100	5C
MCP read      SPCR:	1011100	5C
MCP read      SPSR:	10000000	80
MCP set read SPCR:	1011100	5C
MCP set read SPSR:	10000000	80
MCP3550 Ready Error!
MAX read       SPCR:	1010000	50
LCD SPCR:        	1010000	50
MCP     Request SPCR:	1010000	50
MCP set Request SPCR:	1011100	5C
MCP read      SPCR:	1011100	5C
MCP read      SPSR:	0	0
MCP set read SPCR:	1011100	5C
MCP set read SPSR:	0	0
MCP3550 Ready Error!
Setup SPCR:	0	0
SPI begin SPCR: 101000050
LCD Init     SPCR:	1010000	50
MCP set      SPCR:	1010010	52
Initializing SD card...

SD SPCR:         	1010000	50
Initialization done.
SD Init     SPCR:	1010000	50
MCP     Request SPCR:	1010000	50
MCP set Request SPCR:	1011100	5C
MCP read      SPCR:	1011100	5C
MCP read      SPSR:	10000000	80
MCP set read SPCR:	1011100	5C
MCP set read SPSR:	10000000	80
Measurement: -2150.037mV
MAX read       SPCR:	1010000	50
LCD SPCR:        	1010000	50
MCP     Request SPCR:	1010000	50
MCP set Request SPCR:	1011100	5C
MCP read      SPCR:	1011100	5C
MCP read      SPSR:	0	0
MCP set read SPCR:	1011100	5C
MCP set read SPSR:	0	0
Measurement: -2150.049mV

Hallo,

hat wirklich niemand einen Hinweis was die Standard SD.h vielleicht noch anders macht?

Was ich auch noch nicht recht verstehe ist, warum die Beispiele mit SD.h oder SdFat.h ohne
#inlclude <spi.h>
funktionieren obwohl sie spi nutzen. Und sobald man ein zusätzliches spi device verwendet muß man spi.h extra einfügen. Da komme ich noch nicht ganz mit. Der Aufruf von spi.h sollte doch in den sd.h oder SdFat.h Library schon enthalten sein? Irgendwie fehlt mir hierfür eine klare Linie.

Man kann SPI auch ohne die SPI Lib verwenden. Und genau das macht die SdFat Lib. Und die Arduino SD Klasse ist eine sehr abgespeckte Version einer alten Version von SdFat. Deshalb sind da auch Bugs drin, die in SdFat inzwischen ausgebessert wurden.

Siehe auch hier wieder ...\Arduino\libraries\SD\utility\Sd2Card.cpp
z.B.:

/** Send a byte to the card */
static void spiSend(uint8_t b) {
  SPDR = b;
  while (!(SPSR & (1 << SPIF)));
}
/** Receive a byte from the card */
static  uint8_t spiRec(void) {
  spiSend(0XFF);
  return SPDR;
}

Code für SoftSPI ist auch vorhanden

Die SdFat Lib hat das in einer eigenen Klasse mit inline Funktionen (daher stehen die im Header): SdSPI.h

Was genau hier das Problem ist kann ich nicht sagen. So genau kenne ich mich da auch nicht aus.

Hallo,

da ich den Code der SD.h und SdFat.h eh nicht verstehe, aber auch etwas von SoftSpi gelesen hatte muß ich nochmal fragen. Die SdFat.h erkennt wohl automatisch ob man Software-SPI oder Hardware-SPI verwendet?

Gerade mal genauer hingesehen. SoftSPI geht so wohl nur auf dem Mega. In der normalen SD Lib gibt es dafür in Sd2Card.h ein Makro das man auf 1 ändern kann:

#define MEGA_SOFT_SPI 0

Aber scheinbar nicht auf dem UNO. Nur aus der .cpp Datei war das nicht ersichtlich. Den Header hatte ich vorher nicht angeschaut.

EDIT:
Suche gerade wo das in der SdFat Lib definiert ist. Die hat eine kompliziertere Klassen-Hierarchie...

EDIT 2:
Es gibt einen Config Header. Duh! :slight_smile: SdFatConfig.h

Da kann man auch die normale SPI Lib verwenden wenn man will:

#define USE_ARDUINO_SPI_LIBRARY 0

Und hier es gibt mehrere SoftSPI Makros:

#define MEGA_SOFT_SPI 0
#define LEONARDO_SOFT_SPI 0
#define USE_SOFTWARE_SPI 0

Hallo,

Danke du Fleißiger! :slight_smile:

Schon wenn ich mir damals die Bsp. der SdFat angeschaut hatte, hatte ich fast nichts verstanden. Die SD war übersichtlicher, deshalb habe ich um die SdFat einen großen Bogen gemacht. :wink: Nur werde ich nun wohl nicht mehr drumherum kommen.

Die SdFat scheint ja so auch zu funktionieren. Ich bin mir nicht sicher ob man direkt in der Config Einstellungen vornehmen darf. Ich denke das man alles im Code definiert um flexibler zu sein?

Da ich da etwas ängstlich bin, weil ich da wenig bis nichts verstehe, kann man in der Config
#define USE_ARDUINO_SPI_LIBRARY 1
die EINS setzen und dann klappt das zu 100% mit Hardware SPI ohne weitere include <spi.h> ?

Die Beispiele in der SdFat sind wesentlich komplizierter als sie sein müssten. Die Syntax geht da seht stark in Richtung reinem C++, was ein paar nette Features ermöglicht, aber das auch teilweise schwer lesbar macht. Das muss man aber auch nicht immer so schreiben. Und so Dinge wie Fehler-Behandlung mit "error" braucht man nicht unbedingt.

Aber die Standard Funktionen sind nicht so schwer. z.B. eine Datei erstellen:

void createFile(const char* filename)
{
	if(sd.begin())
	{
		if(file.exists(filename) == false)
		{
			file.open(filename, O_CREAT | O_RDWR);
			file.close();
		}
	}
}

Etwas zu schreiben geht auch ganz normal mit file.println(). Die Stream-Syntax (mit dem << Operator) ist optional.
Wobei man da super Dinge mit machen kann. Siehe z.B. das readCSV Beispiel. Da wird ein Stream verwendet um automatisch Zahlen in der Datei zu parsen. Aber wie gesagt, nicht nötig.

Was auch hilft ist die SdFat.html Datei anzusehen. Das ist eine doxygen Doku der gesamten Lib. Da steht für jede Methode genau dabei was gemacht wird. Da ist vor allem die SdFile Klasse relevant.

Da ich da etwas ängstlich bin, weil ich da wenig bis nichts verstehe, kann man in der Config
#define USE_ARDUINO_SPI_LIBRARY 1
die EINS setzen und dann klappt das zu 100% mit Hardware SPI ohne weitere include <spi.h> ?

Theoretisch ja. Verwendet habe ich das noch nicht.

Ob es dann geht weiß ich nicht, aber die Lib sollte dann die Arduino SPI Klasse verwenden und nicht die Implementierung der Lib.

Hallo,

vielen Dank. :slight_smile: Dann werde ich mich mal rantasten ...

Hallo,

mit Logik-Analyzer nochmal hingeschaut. Letzter Versuch mit der SD.h
ChipSelect vom MCP3550 wird nach µC Reset viel zu zeitig kurz auf Low gezogen. Und das Readysignal kann schon antworten. Was so gar nicht sein darf. Das erstemal wird CS bei 326ms kurz auf Low gezogen. Wenn Ready jetzt schon antworten kann, bedeutet das, dass das CS schon mindestens 80ms vorher für mich unsichtbar auf Low war. Das kann aber laut meinem Code nicht sein. Das erstemal kann erst ab 1000ms µC Laufzeit CS überhaupt auf Low gehen.

unsigned long last_MCP3550_Reading ist am Sketchanfang auf 1000 gesetzt
und
boolean allow_MCP3550_Request auf true
Das heißt doch das folgende Verrieglung erst ab µC Laufzeit ab 1sec. überhaupt erfolgen kann. Oder nicht?

  // request a new MCP3550 AD-Conversion every [ms], useful value > required Conversion Time
  if ( (allow_MCP3550_Request == true) && (millis() - last_MCP3550_Reading >= 1000) )  // Reading interval 1000ms

Nur irgendwas läßt den CS Pin schon bei 326ms kurz auf Low legen. Verstehe ich echt nicht. Gleich nachdem die SD-Karte mit Init und Inhalt auslesen fertig ist. Display läuft sofort los.

Gibt es vielleicht etwas zubeachten in welcher Reihenfolge man die Busse in setup aktiviert? Oder diese vor oder nach der Pin Definition was Ausgang/Eingang und Low/High ist?

SDcard_Write_Taster_LcdSPI_DS1820_027.ino (35.9 KB)

Hallo,

eine ganz grundlegende Frage zu setup, loop und die Definitionen die so üblicherweise gemacht werden.

in setup geht es bei mir los mit:

void setup() {
  Serial.begin(57600);
  SPI.begin();
  Serial.print("SPI SPCR: "); Serial.print("\t"); Serial.print(SPCR,BIN); Serial.print("\t"); Serial.println(SPCR,HEX);  // SPI Control-Register auslesen
   // alle Chip-Select's erstmal Output und auf HIGH
  pinMode(27, OUTPUT);     // EA DOGM
  pinMode(28, OUTPUT);     // SD-Karte
  pinMode(29, OUTPUT);     // MAX6675
  pinMode(38, OUTPUT);     // MCP3550-50
  
  digitalWrite(27, HIGH);  // EA DOGM
  digitalWrite(28, HIGH);  // SD-Karte
  digitalWrite(29, HIGH);  // MAX6675
  digitalWrite(38, HIGH);  // MCP3550-50

Das sollte doch bedeuten, dass nichts auf Low geht sondern immer auf High ist. Ich sehe jedoch im Logikanalyzer das die CS Pegel genau in der "Init Reihenfolgen" jeweils kurz auf Low gezogen werden nach µC Reset. Ist das Verhalten normal oder bin ich blöd? Schaltet der Befehle pinMode die Ausgänge kurz um?

Dreht man die Reihenfolge des setzens auf High um, ändert sich das zeitlich auch sichtbar.
Kann man das irgendwie verhindern? Also es muß alles auf High sein und bleiben nach µC Reset, außer CS soll laut Code auf Low gehen.

siehe Screenshots

Ich glaube der Reset schaltet die Pins auf Low. Das Problem gibt es auch bei low aktiven Relais.
Mach einfach die Reihenfolge anders. Du kannst den Ausgang High setzen bevor du ihn auf Output schaltest.

EDIT:
Mann kann auch gezielt Speicher Sektionen definieren:
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

Code in .initN wird noch vor main() aufgerufen und damit glaube ich auf dem Arduino auch vor setup(). Da steht auch:

.init0: Weakly bound to __init(). If user defines __init(), it will be jumped into immediately after a reset.

Ausprobiert habe ich das nicht, aber laut dem hier sollte es gehen:
http://winavr.scienceprog.com/avr-gcc-tutorial/example-of-using-sections-in-winavr.html

Hallo,

Danke Serenifly. Das war genau die Lösung des Problems wo ich/wir dachten die SD.h ist das Problem. Das war aber scheinbar Zufall und lag an meinen Fehlersuchen-Sketchen wie ich dort was in welcher Reihenfolge zufällig anders programmiert hatte. Ohne Logikanalyzer wäre ich nie darauf gekommen. Bin froh das ich das Ding habe. Werde dennoch den Sketch auf die SdFat.h umbauen. Die ist einfach schneller und besser und die erzeugten Files habe das richtige Erstelldatum. Bin aber erstmal heilfroh das Hauptproblem gelöst zu haben. Daran kann ich mich verbeißen wie ein Terrier. :smiley: Und man braucht Leute wie Dich die sich meine Gedankengänge auch durchlesen ... :smiley: