Forum: Mikrocontroller und Digitale Elektronik SPI STM8 mag nicht


von Max M. (maxmicr)


Lesenswert?

Hallo Forum,

ich bin gerade dabei, mit meinem STM8S103F3P6 als Master eine SPI 
Verbindung aufzubauen, leider klappt das nicht ganz. Ehrlich gesagt ist 
das Datenblatt auch ziemlich verwirrend.
Ich habe mir auch schon die einzelnen Pins (MOSI, CLK, CS) auf dem Logic 
Analyzer anzeigen lassen, leider ist da nichts passiert.
1
#define MISO 7 //SPI_MISO PC7
2
#define MOSI 6 //SPI_MOSI PC6
3
#define SCK 5 //SPI_SCK PC5
4
#define CS 3 //SPI_NSS PA3
Das hier ist meine SPIMasterInit-Funktion:
1
void initSPIMaster(){
2
  GPIOC->DDR = (1<<MOSI) | (1<<SCK) | (0<<MISO); //MOSI & SCK output, MISO input
3
  GPIOC->CR1 = (1<<MOSI) | (1<<SCK); //MOSI& SCK push-pull
4
  
5
  GPIOA->DDR |= (1<<CS); //set CS as output
6
  GPIOA->CR1 |= (1<<CS); //set CS as push-pull
7
  
8
  SPI->CR2 = 0b00000000; //2-line unidirectional
9
  SPI->CR1 = 0b01111111; //Enable CHPA, CPOL, MASTER; Baudrate: f/256, SPI Enable
10
}

Das hier ist meine Übertragungsfunktion:
1
void SPITransmit(uint8_t data){
2
  SPI->DR = data;
3
  while(!(SPI->SR & (1<<1))); //wait until transmit buffer is empty
4
}

So wird der Spaß dann aufgerufen:
1
initSPIMaster();
2
SPITransmit(0x80 & 0x32);
Ich messe weder am MOSI noch am SCK oder CS pin etwas (gut den CS Pin 
muss ich auch selber toggeln). Wo liegt mein Fehler?

: Bearbeitet durch User
von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Okay, nun bin ich ein Stückchen weiter, immerhin bekomme ich nun ein 
Signal an SCK und MOSI, mein Code:
1
void initSPIMaster(){
2
  GPIOC->DDR = (1<<MOSI) | (1<<SCK) | (0<<MISO); //MOSI & SCK output, MISO input
3
  GPIOC->CR1 = (1<<MOSI) | (1<<SCK); //MOSI& SCK push-pull
4
  
5
  GPIOA->DDR |= (1<<CS); //set CS as output
6
  GPIOA->CR1 |= (1<<CS); //set CS as push-pull
7
  
8
  SPI->CR2 = 0b00000011; //2-line unidirectional, SSM & SSI enabled
9
  SPI->CR1 = 0b01111111; //Enable CHPA, CPOL, MASTER; Baudrate: f/256, SPI Enable
10
}
11
12
void SPITransmit(uint8_t data){
13
  SPI->DR = data;
14
  while(!(SPI->SR & (1<<1))){
15
    ; //wait until transmit buffer is empty
16
  }
17
}

Die Main:
1
int main(){
2
  initSPIMaster();
3
  while(1){
4
    SPITransmit(0x79);
5
    delay_ms(100);
6
  }
7
}

Das Signal seht ihr im Anhang. Allerdings tut sich auf der CS-Linie 
nichts.
Nach dem STM8-Programming manual:
1
NSS: Slave select (port E5). This is a optional pin to select a slave device. This pin acts
2
as a ‘chip select’ to let the SPI master communicate with slaves

NSS ist bei meinem STM8 der Pin PA3.

Nach der Beschreibung von Slave Select pin management:
1
Software NSS management (SSM = 1): with this configuration, slave select
2
information is driven internally by the Internal slave select (SSI) bit value in the
3
SPI_CR2 register.The external NSS pin remains free for other application uses.
4
5
Hardware NSS management (SSM = 0): For devices set as master, this configuration
6
allows multimaster capability. For devices set as slave, the NSS pin works as a
7
classical NSS input. The slave is selected when the NSS line is in low level and is not
8
selected if the NSS line is in high level.

muss ich das SSM-Bit im SPI-CR2 Register auf '0' setzen, damit der 
Controller den Chip-Select automatisch auf high / low schaltet?

Das mache ich mit:
1
SPI->CR2 = 0b00000000;

allerdings ändert sich am 'CS' pin trotzdem nichts? Warum?

Edit:

Okay, ich hab jetzt versucht, den CS-Pin manuell high/low zu schalten, 
jetzt passiert etwas sehr seltsames, das ist mein Code:
1
int main(){
2
  initSPIMaster();
3
  while(1){
4
    GPIOA->ODR &= ~(1<<CS); //turn CS low
5
    SPITransmit(0x2D);
6
    SPITransmit(0x08);
7
    GPIOA->ODR |= (1<<CS); //turn CS high
8
    delay_ms(100);
9
  }
10
}

Den Signalverlauf seht ihr im 2.Bild.

Nun stellt sich die Frage: Warum geht CS mittendrinn high obwohl er es 
doch erst nach dem Übertragen der 16bit tuen soll? Wie man aber sieht, 
gibt es noch einen Flankenwechsel obwohl CS schon wieder high ist?

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Okay, ich hab das Problem des CS-Pins gefunden:
Man muss zuerst prüfen, ob der MC noch busy ist:
1
void SPITransmit(uint8_t data){
2
  while(!(SPI->SR & (1<<1))){
3
    nop(); //wait until transmit buffer is empty
4
  }
5
  SPI->DR = data;
6
  while((SPI->SR & (1<<7))){
7
    nop(); //wait until not busy
8
  }
9
}

Nun der nächste Fehler:

Ich versuche nun eine Funktion zu erstellen, die mir den übergebenen 
Wert in das SPI-Register schiebt, so sieht die Funktion aus:
1
void getValueFromRegister(uint8_t reg, uint8_t *val){
2
  uint8_t transmitValue = (0x80 & reg);
3
  uint8_t receivedValue = 0;
4
  
5
  GPIOA->ODR &= ~(1<<CS); //CS high
6
  SPITransmit(transmitValue); //write 'read' command
7
  receivedValue = SPIReceive();
8
  GPIOA->ODR |= (1<<CS); //CS low
9
  
10
  delay_ms(10);
11
  *val = receivedValue;
12
}

Auf meinem Logic-Analyzer erscheint auf MOSI leider kein Signal, auf SCK 
und CS dagegen schon. Sobald ich das hier:
1
SPITransmit(transmitValue); //write 'read' command

in das hier:
1
SPITransmit(0x3B); //write 'read' command

ändere, erscheint dagegen schon ein Signal auf dem MOSI Pin, so wie es 
eigentlich sein soll. Warum hat das SPI-Register Probleme mit Variablen?

Edit: okay, sorry. Problem gelöst, so sollte es eigentlich lauten:
1
uint8_t transmitValue = (0x80 | reg);

Jetzt seh ich auch was ^^

: Bearbeitet durch User
von Daniel (Gast)


Lesenswert?

Wollte gerade auch fragen, ob du mit reg eine 0 übergibst ☺️

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Das lesen der Daten klappt allerdings ganz und gar nicht. Jetzt heißt es 
natürlich raten, ob das am Sensor (ADXL345) oder an meiner SPI 
Konfiguration liegt. Egal was mich mache, ich bekomme für alle 3 Achsen 
2313 auf der Konsole ausgegeben.

Meine Funktion zum Lesen:
1
uint8_t SPIReceive(){
2
  while((SPI->SR & (1<<7))){
3
    nop(); //wait until not busy
4
  }
5
  while(!(SPI->SR & (1<<0))){
6
    nop(); //wait until receive buffer is not empty
7
  }
8
  return (SPI->DR);
9
}

Diese Funktion wird von getValueFromRegister aufgerufen, die ich in 
meinem letzten Beitrag schon gepostet habe.
Das hier ist die Funktion, die die Beschleunigungswerte für X,Y & Z 
liefert:
1
void getXYZValues(int16_t *x, int16_t *y, int16_t *z){
2
  uint8_t x0,x1,y0,y1,z0,z1;
3
  getValueFromRegister(0x32, &x0);
4
  getValueFromRegister(0x33, &x1);
5
  getValueFromRegister(0x34, &y0);
6
  getValueFromRegister(0x35, &y1);
7
  getValueFromRegister(0x36, &z0);
8
  getValueFromRegister(0x37, &z1);
9
  
10
  *x = (x1<<8) | x0;
11
  *y = (y1<<8) | y0;
12
  *z = (z1<<8) | z0;
13
}

Es handelt sich um vorzeichenbehaftete Zahlen und die Daten einer Achse 
bestehen aus High- und Low Byte.

Wie kann ich jetzt rausfinden, was nicht passt? Logic Analyzer geht 
schlecht, weil der kein Signal durchschleift.

Das einzige, was ich mir noch vorstellen könnte, das ich falsch gemacht 
habe, ist die Polarität mit CPOL und CPHA. Laut Datenblatt des 
ADXL345:
1
The timing scheme follows clock polarity
2
(CPOL) = 1 and clock phase (CPHA) = 1.

Aber die habe ich auch im CR1 Register gesetzt...

Edit: Okay, irgendwas liefert der ADXL345 doch, wenn ich das Kabel 
abstecke, bekomme ich -1 als Wert für X,Y&Z.
So sieht das Signal vom SDO-Pin des ICs aus:

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Hm, das DEVID Register kann ich erfolgreich auslesen, es kommt auch 
der richtige Wert zurück (229 dezimal).

Was stimmt dann mit dem auslesen der Koordinaten nicht?

Edit: Hm, es könnte sein, dass im Register einfach keine neuen Daten 
vorliegen bzw. ich sogar die Daten lese, die ich eigentlich schreiben 
wollte (da ja ein Register für Transmit und Receive verwendet wird). 
Oder wird das Register nach jedem Lesevorgang gelöscht?

Edit_2:

Wenn ich meine SPIReceive Funktion so schreibe:
1
uint8_t SPIReceive(){
2
  uint8_t registerValue;
3
  registerValue = (SPI->DR);
4
  return registerValue;
5
}
bekomme ich den Wert 0 (logisch).

Wenn ich die Funktion dagegen so schreibe:
1
uint8_t SPIReceive(){
2
  uint8_t registerValue;
3
  while(!(SPI->SR & (1<<0))){
4
    nop(); //wait until receive buffer is not empty
5
  }
6
  registerValue = (SPI->DR);
7
  return registerValue;
8
}
Bekomme ich den dezimalen Wert 2827 für alle Achsen.

Wenn ich sie wiederum so schreibe:
1
uint8_t SPIReceive(){
2
  uint8_t registerValue;
3
  while((SPI->SR & (1<<7))){
4
    nop(); //wait until not busy
5
  }
6
  while(!(SPI->SR & (1<<0))){
7
    nop(); //wait until receive buffer is not empty
8
  }
9
  registerValue = (SPI->DR);
10
  return registerValue;
11
}

Bekomme ich den Wert 2056 für alle Achsen.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Ich hab nun überprüft, ob vllt. doch CPHA oder CPOL die Ursache sind.
Dafür hab ich 4 Versuche gemacht.

1. Versuch (CPOL auf '0', CPHA auf '1'):
Die übertragenen Werte ändern sich sobald ich den Sensor bewege, der 
Wert des DEVIDs Registers ist falsch bzw. er ändert sich sogar auch, 
wenn ich den Sensor bewege?
2. Versuche (CPOL auf '1', CPHA auf '0'):
Das gleiche wie bei [1.]
3. Versuch (CPOL auf '0', CPHA auf '0'):
Das gleiche wie bei [1.]

Nur wenn ich CPOL  und CPHA aktiviere, wird die DEVID richtig mit 229 
ausgelesen. Hier ändern sich die Werte allerdings nicht.

Kann mir wirklich keiner helfen?

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Ich versuche aktuell, das ganze mit den Funktionen aus der STM Standard 
Peripheral Library zu machen, allerdings bekomme ich beim Aufrufen einer 
Funktion aus der *stm8s_spi.h* bereits diese Fehlermeldung beim 
Kompilieren:
1
#error clnk Debug\adxl345_spi.lkf:1 symbol _SPI_DeInit not defined (Debug\main.o )
2
 The command: "clnk -m Debug\adxl345_spi.map -l"C:\Program Files (x86)\COSMIC\FSE_Compilers\Lib"  -o Debug\adxl345_spi.sm8 Debug\adxl345_spi.lkf " has failed, the returned value is: 1
3
exit code=1.
1
#include <stm8s_spi.h>
1
SPI_DeInit();

Was mache ich falsch?

von Max M. (maxmicr)


Lesenswert?

Warum gibt es so unglaublich wenig Informationen über ST Visual Develop 
und STM8 Peripheral Library?

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.