Forum: Mikrocontroller und Digitale Elektronik NXP LPC1754 SPI Probleme


von Igor c. (cance)


Lesenswert?

Hallo zusammen,

ich möchte mich jetzt schon entschuldigen, dass der Code nicht richtig 
formatiert ist, ich weiss nicht mit welchem TAG ich hier das machen 
kann. Hab gesucht aber nichts gefunden.

Ich bin neu hier und habe ein Problem mit meinem SPI. Ich habe zwei uC 
(2xLPC1754) die miteinander kommunizieren sollen. Wahrscheinlich reicht 
mir sogar wenn der Master dem Slave Daten schicken kann und dieser die 
Daten lesen kann. Der Slave muss also nicht unbedingt was zurück 
schicken können.

Ich habe zuerst mal im User Manual 
(http://www.nxp.com/documents/user_manual/UM10360.pdf) nachgelesen, da 
ich wenig Erfahrung damit habe. Jedoch ist es dort sehr oberflächlich 
erklärt oder so, dass ich es nicht verstehe. Ich habe dann mit einem 
Beispiel, dass ich gefunden habe probiert es zum Laufen zu bringen. Nur 
läuft es nicht gerade so wie es soll. Ich habe den Code leider nicht zu 
Hause und bin nicht mehr im Geschäft, kann den noch nachreichen aber ist 
in etwa das Folgende zur Initialisierung


  PINSEL_CFG_Type PinCfg;
  SPI_DATA_SETUP_Type xferConfig;
  uint32_t tmp;

  /*
   * Initialize SPI pin connect
   * P0.15 - SCK;
   * P0.16 - SSEL - used as GPIO
   * P0.17 - MISO
   * P0.18 - MOSI
   */
  PinCfg.Funcnum = 3;
  PinCfg.OpenDrain = 0;
  PinCfg.Pinmode = 0;
  PinCfg.Portnum = 0;
  PinCfg.Pinnum = 15;
  PINSEL_ConfigPin(&PinCfg);
  PinCfg.Pinnum = 17;
  PINSEL_ConfigPin(&PinCfg);
  PinCfg.Pinnum = 18;
  PINSEL_ConfigPin(&PinCfg);
  PinCfg.Pinnum = 16;
  PinCfg.Funcnum = 0;
  PINSEL_ConfigPin(&PinCfg);

  SPI_ConfigStruct.CPHA = SPI_CPHA_SECOND;
  SPI_ConfigStruct.CPOL = SPI_CPOL_LO;
  SPI_ConfigStruct.ClockRate = 2000000;
  SPI_ConfigStruct.DataOrder = SPI_DATA_MSB_FIRST;
  SPI_ConfigStruct.Databit = SPI_DATABIT_SIZE;
  SPI_ConfigStruct.Mode = SPI_MASTER_MODE;

  SPI_Init(LPC_SPI, &SPI_ConfigStruct);

Damit sollte ich das SPI initialisiert haben (Clock, Power und sonstige 
Einstellungen).
Ich habe nun eine Quadrature Signal, also zwei Signale die 90° 
verschoben sind. Die Idee ist nun, dass ich jedes mal, wenn eines der 
Signale ändert die Interruptroutine aufgerufen wird und der Zählerwert 
per SPI an den Slave geschickt wird. Die Interruptroutine hab ich schon 
und das Auslesen des Zählers und der Interrupt selber funktioniert ohne 
Probleme. Das Problem ist, dass ich den Wert des Counters nun schicken 
will. Dazu schreibe ich ins LPC_SPI->SPDR, wenn ich mich richtig 
erinnere und laut User Manual und anderen Kommentaren im Internet, 
sollte durch das beschreiben die Datenübertragung starten, dies passiert 
aber nicht, es passiert gar nichts. nach dem beschreiben will ich 
eigentlich auf das SPIF warten und dann SPDR auslesen also wie folgt

while(!LPC_SPI->SPIF);
tmp = LPC_SPI->SPDR;

jedoch startet das SPI gar nicht mit dem senden.

Woran könnte es liegen? Vergesse ich da was Wichtiges?

Ich habe das SPI auch schon zum laufen gebracht aber glaube nicht so wie 
es eigentlich soll ^^
Ich habe dazu einfach jedes mal das LPC_SPI->SPDR beschrieben und danach

  CS_Force(0);

  for (tmp = 10000; tmp; tmp--);

  xferConfig.tx_data = Tx_Buf;
  xferConfig.rx_data = Rx_Buf;
  xferConfig.length = BUFFER_SIZE;
  SPI_ReadWrite(LPC_SPI, &xferConfig, SPI_TRANSFER_POLLING);

  for (tmp = 10000; tmp; tmp--);

  CS_Force(1);


gemacht. Irgendwie konnte ich dann senden und mit dem Slave empfangen 
aber habe was geändert und jetzt läuft gar nichts mehr und ich weiss 
nicht was ich geändert habe. Ist es nicht so, dass ich das 
SPI_ReadWrite() gar nicht benötige sondern die Daten selber geschickt 
werden sollten wenn ich was ins SPDR lege?
Auf der Slave Seite habe ich einen Interrupt für das CS Signal und wenn 
dieses auf HIGH geht warte ich auf das SPIF und lese dann das 
LPC_SPI->SPDR aus, dies hat auch funktioniert. Gibt wahrscheinlich auch 
dafür ne einfachere Methode aber hab halt selber was versucht um 
bisschen Erfahrung zu sammeln und hat ja gut funktioniert :-)

Ich hoffe es kann mir wer helfen, hab da schon mehrere Tage dran 
probiert aber irgendwie komme ich nicht wirklich vorwärts.


Gruss Cance

von Jim M. (turboj)


Lesenswert?

Igor cancarevic schrieb:
> auf das SPIF warten und dann SPDR auslesen also wie folgt
>
> while(!LPC_SPI->SPIF);
> tmp = LPC_SPI->SPDR;
>
> jedoch startet das SPI gar nicht mit dem senden.

Im Codeschnipsel wird SPDR nur gelesen..

Bei SPI wird immer gleichzeitig gelesen und geschrieben. Um eine 
Datenübertragung auf dem Master zu starten, muss also SPDR geschrieben 
werden, eventuell mit einem Dummy Wert.

Übrigens empfehle ich auf einem LPC17xx lieber die SSP Einheiten zu 
benutzen. Die können auch SPI, haben aber FIFOs und DMA Support 
zusätzlich und bis zu 16 Bit breite Datenwörter.

von Igor c. (cance)


Lesenswert?

Danke erstmal für die schnelle Antwort.

Den Teil wo ich ins SPDR schreibe habe ich weggelassen. Dabei lese ich 
einen Wert aus dem Counter aus und berechne daraus die Differenz zum 
letzten Wert und übergebe die Differenz dann an SPDR

LPC_SPI->SPDR = counter_delta;

danach warte ich auf SPIF und lese SPDR aus

while(!LPC_SPI->SPIF);
tmp = LPC_SPI->SPDR;



Damit startet bei mir aber keine Übertragung. Sollte dies nicht 
automatisch die Übertragung starten? Oder fehlt bei mir was in der 
Initialisierung?
Im Beispiel ist auch der Polling mode gewählt nicht der Interrupt mode. 
Ich weiss nicht genau worauf sich das bezieht, ist das vielleicht der 
Grund, dass es nicht automatisch startet, falls ja was muss ich da alles 
einstellen damit es mit Interrupt funktioniert?

Ich habe wahrscheinlich die SSP Einheit in Gebrauch, da ich bei der 
Grösse der Datenwörter zwischen 8-16bit wählen kann. Da ich aber nur die 
Differenz zwischen dem letzten Wert und dem neuen Wert übergebe reichen 
mit 8bit locker, da die Differenz in meinem Fall nur Plus oder Minus 1 
ist :-)
Vielleicht wird später auch mal mehr übertragen oder auch was vom Slave 
gesendet aber für den Moment brauch ich das nicht.

Im Moment sendet es nur etwas, wenn ich SPI_ReadWrite() verwende, was 
anscheinend nicht nötig sein sollte und wenn ich es verwende ist das 
Timing auch nicht immer super, hatte mal eine Version wo es funktioniert 
hat, dafür hat er aber immer weiter Chip Select gemacht in 
unregelmässigen Abständen :-(

Ich hoffe mein Problem ist deutlich geworden.

von leluno (Gast)


Lesenswert?

p0.15-Funcnum 3 ist laut Handbuch spi und nicht ssp. p0.16 - cs - sollte 
normaler gpio - also Funcnum 0 - sein. Bei mir hat es geholfen, zunächst 
einen software-spi zu programmieren. man kann beim software-spi leicht 
jeden einzelnen Takt überprüfen. wenn das geht, kann man auf spi oder 
ssp umstellen.

von Karl K. (leluno)


Lesenswert?

funktionierender spi-code:
//init hw spi
PINSEL0|=(3<<30);//0.15-4:ck
 PINSEL1|=(3<<2);//0.17-4:miso
 PINSEL1|=(3<<4);//0.18-4:mosi
 PINSEL1  = 0b111100; // P0.18 = MOSI, P0.17 = MISO, P0.16 = GPIO
 PINMODE0 = 2<<30;    // P0.15 has neither pull-up nor pull-down
 PINMODE1 = 0b101010; // P0.18..16 have neither pull-up nor pull-down
#define BitEnablehwspi 0
#define MSTRhwspi 5
#define CPOLhwspi 4
#define CPHAhwspi 3
#define SPIFhwspi 7


LPC_SPI->SPCR=(1<<MSTRhwspi);
LPC_SPI->SPCR&=~(1<<BitEnablehwspi);//
LPC_SPI->SPCR|=(1<<CPOLhwspi);// S0SPCR|=(1<<CPOL);//
LPC_SPI->SPCR|=(1<<CPHAhwspi);//
//LPC_SPI->SPCR&=~(1<<LSBF);//????msbf
LPC_SPI->SPCCR=8;



//send/receive hwspi
  LPC_SPI->SPDR=byte_s;
  while(!(LPC_SPI->SPSR & (1<<SPIFhwspi)));
  return(LPC_SPI->SPDR);

von Igor c. (cance)


Lesenswert?

Danke für den Code, ich musste für PINSEL noch LPC_SPI-> vorne hin 
setzen aber das war ja nicht ein grosses Ding ^^

Mit deinem Code reagiert das SPI auf das beschreiben des SPDR, jedoch 
sehe ich auf dem Oszilloskop, dass nur der MOSI und der Clock kommen, 
diese passen aber zueinander. Jedoch kommt kein CS Signal, bzw. CS, 
bleibt auf HIGH, dadurch passiert beim Slave natürlich nichts. Ist ja 
als GPIO definiert, sollte ja eigentlich passen.

Muss ich das CS einfach vor dem "LPC_SPI->SPDR=byte_s;" LOW ziehen und 
nach dem abwarten des SPIF wieder HIGH ziehen? oder sollte das von 
alleine funktionieren?

Was wird eigentlich gestartet beim beschreiben des SPDR? ist der Code 
irgendwo ersichtlich?

Gruss Cance

von Igor c. (cance)


Lesenswert?

leluno schrieb:
> p0.15-Funcnum 3 ist laut Handbuch spi und nicht ssp. p0.16 - cs - sollte
> normaler gpio - also Funcnum 0 - sein. Bei mir hat es geholfen, zunächst
> einen software-spi zu programmieren. man kann beim software-spi leicht
> jeden einzelnen Takt überprüfen. wenn das geht, kann man auf spi oder
> ssp umstellen.

OK, dann ist es wohl doch SPI ^^
Jedoch kann ich die Wortbreite von 8-16bit wählen mit dem Beispiel das 
ich gefunden habe, hat eigentlich auch einigermassen funktioniert aber 
nicht perfekt.
CS ist als GPIO definiert

  PinCfg.Pinnum = 16;
  PinCfg.Funcnum = 0;
  PINSEL_ConfigPin(&PinCfg);

Mit Hilfe von karl k.'s beispiel funktioniert es jetzt soweit ziemlich 
gut. Wenn man den CS selber machen muss vor und nach dem senden, dann 
passt das soweit. habs jetzt so gemacht.

GPIO_ClearValue(CS_PORT_NUM, (1<<CS_PIN_NUM));
for (tmp = 10; tmp; tmp--);
LPC_SPI->SPDR=send_delta;
while(!(LPC_SPI->SPSR & (1<<SPIFhwspi)));
GPIO_SetValue(CS_PORT_NUM, (1<<CS_PIN_NUM));
status = LPC_SPI->SPDR;

und sieht schon mal gut aus auf dem Oszilloskop. Danke euch für die 
schnellen Antworten.

Gruss Cance

von leluno (Gast)


Lesenswert?

im Abstand von einem halben Jahr tut der oben vorgestellte code richtig 
weh

>> //init hw spi
PINSEL0|=(3<<30);//0.15-4:ck
 PINSEL1|=(3<<2);//0.17-4:miso
 PINSEL1|=(3<<4);//0.18-4:mosi
 PINSEL1  = 0b111100; // P0.18 = MOSI, P0.17 = MISO, P0.16 = GPIO
 PINMODE0 = 2<<30;    // P0.15 has neither pull-up nor pull-down
 PINMODE1 = 0b101010; // P0.18..16 have neither pull-up nor pull-down

man sollte auf die nicht aus sich selbst heraus verständlichen 
Unter-Register ganz verzichten und Macros über Portnum und Pinnum 
definieren.

#define pinSEL(p,b,v)    PINSEL[(p) * 2 + (b) / 16] = (PINSEL[(p) * 2 + 
(b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
#define non_pullup 2

pinSEL(0,18,3)
pinMODE(0,18,non_pullup)

Man weiß dann sofort, um welchen pin es geht und was mit ihm geschehen 
soll.

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.