Forum: Mikrocontroller und Digitale Elektronik SPI am STM32F103 funktioniert nicht wie gewünscht


von Michael L. (nightflyer88)


Lesenswert?

Hallo zusammen

Ich möchte mit einem STM32F103CBT6 einen Schrittmotor Controller TMC5160 
per SPI ansteuern. Nun habe ich ein Problem mit der konfiguration des 
SPI am STM32. Ich schaff es einfach nicht eine Verbindung zum TMC5160 
herzustellen. Ich programmiere mit der STM32cubeIDE.

Zum testen habe ich mal ein Arduino Nano genommen und den Beispielcode 
von Trinamic, und funktioniert natürlich einwandfrei, an der Hardware 
liegts also nicht.

Arduino Code (läuft):
1
#include <SPI.h>
2
#include "TMC5130_registers.h"
3
4
/* The trinamic TMC5130 motor controller and driver operates through an 
5
 *  SPI interface.  Each datagram is sent to the device as an address byte
6
 *  followed by 4 data bytes.  This is 40 bits (8 bit address and 32 bit word).
7
 *  Each register is specified by a one byte (MSB) address: 0 for read, 1 for 
8
 *  write.  The MSB is transmitted first on the rising edge of SCK.
9
 *  
10
 * Arduino Pins   Eval Board Pins
11
 * 51 MOSI        32 SPI1_SDI
12
 * 50 MISO        33 SPI1_SDO
13
 * 52 SCK         31 SPI1_SCK
14
 * 25 CS          30 SPI1_CSN
15
 * 17 DIO         8 DIO0 (DRV_ENN)
16
 * 11 DIO         23 CLK16
17
 * GND            2 GND
18
 * +5V            5 +5V
19
 */
20
21
int chipCS = 10;
22
const byte CLOCKOUT = 9;
23
int enable = 8;
24
25
void setup() {
26
  // put your setup code here, to run once:
27
  pinMode(chipCS,OUTPUT);
28
  pinMode(CLOCKOUT,OUTPUT);
29
  pinMode(enable, OUTPUT);
30
  digitalWrite(chipCS,HIGH);
31
  digitalWrite(enable,LOW);
32
  digitalWrite(CLOCKOUT,LOW);
33
34
  //set up Timer1
35
 /* TCCR1A = bit (COM1A0);                //toggle OC1A on Compare Match
36
  TCCR1B = bit (WGM12) | bit (CS10);    //CTC, no prescaling
37
  OCR1A = 0;                            //output every cycle
38
*/
39
  SPI.setBitOrder(MSBFIRST);
40
  SPI.setClockDivider(SPI_CLOCK_DIV8);
41
  SPI.setDataMode(SPI_MODE3);
42
  SPI.begin();
43
44
  Serial.begin(115200);
45
  delay(5000);
46
  Serial.println("Setup");
47
48
  sendData(0x80,0x00000000);      //GCONF
49
50
  sendData(0xEC,0x000101D5);      //CHOPCONF: TOFF=5, HSTRT=5, HEND=3, TBL=2, CHM=0 (spreadcycle)
51
  sendData(0x90,0x00070603);      //IHOLD_IRUN: IHOLD=3, IRUN=10 (max.current), IHOLDDELAY=6
52
  sendData(0x91,0x0000000A);      //TPOWERDOWN=10
53
54
  sendData(0xF0,0x00000000);      // PWMCONF
55
  //sendData(0xF0,0x000401C8);      //PWM_CONF: AUTO=1, 2/1024 Fclk, Switch amp limit=200, grad=1
56
57
  sendData(0xA4,0x000003E8);     //A1=1000
58
  sendData(0xA5,0x000186A0);     //V1=100000
59
  sendData(0xA6,0x0000C350);     //AMAX=50000
60
  sendData(0xA7,0x000186A0);     //VMAX=100000
61
  sendData(0xAA,0x00000578);     //D1=1400
62
  sendData(0xAB,0x0000000A);     //VSTOP=10
63
64
  sendData(0xA0,0x00000000);     //RAMPMODE=0
65
66
  sendData(0xA1,0x00000000);     //XACTUAL=0
67
  sendData(0xAD,0x00000000);     //XTARGET=0
68
69
  Serial.println("Run");
70
}
71
72
void loop() {
73
  // put your main code here, to run repeatedly:
74
  sendData(0xAD,0x0007D000);     //XTARGET=512000 | 10 revolutions with micro step = 256
75
  delay(20000);
76
  sendData(0x21,0x00000000);
77
  sendData(0xAD,0x00000000);     //XTARGET=0
78
  delay(20000);
79
  sendData(0x21,0x00000000);
80
}
81
82
void sendData(unsigned long address, unsigned long datagram) {
83
  //TMC5130 takes 40 bit data: 8 address and 32 data
84
85
  delay(100);
86
  unsigned long i_datagram;
87
88
  Serial.print("SPI_w: ");
89
  Serial.print(address,HEX);
90
  Serial.print(" ");
91
  Serial.println(datagram,HEX);
92
93
  digitalWrite(chipCS,LOW);
94
  delayMicroseconds(10);
95
96
  SPI.transfer(address);
97
98
  i_datagram |= SPI.transfer((datagram >> 24) & 0xff);
99
  i_datagram <<= 8;
100
  i_datagram |= SPI.transfer((datagram >> 16) & 0xff);
101
  i_datagram <<= 8;
102
  i_datagram |= SPI.transfer((datagram >> 8) & 0xff);
103
  i_datagram <<= 8;
104
  i_datagram |= SPI.transfer((datagram) & 0xff);
105
  digitalWrite(chipCS,HIGH);
106
107
  Serial.print("SPI_r: ");
108
  Serial.print(address,HEX);
109
  Serial.print(" ");
110
  Serial.println(i_datagram,HEX);
111
  Serial.println();
112
}

Hier meine SPI Konfiguration am STM32, die nicht funktioniert:
1
static void MX_SPI1_Init(void)
2
{
3
4
  /* USER CODE BEGIN SPI1_Init 0 */
5
6
  /* USER CODE END SPI1_Init 0 */
7
8
  /* USER CODE BEGIN SPI1_Init 1 */
9
10
  /* USER CODE END SPI1_Init 1 */
11
  /* SPI1 parameter configuration*/
12
  hspi1.Instance = SPI1;
13
  hspi1.Init.Mode = SPI_MODE_MASTER;
14
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
15
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
16
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
17
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
18
  hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
19
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
20
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
21
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
22
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
23
  hspi1.Init.CRCPolynomial = 10;
24
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
25
  {
26
    Error_Handler();
27
  }
28
  /* USER CODE BEGIN SPI1_Init 2 */
29
30
  /* USER CODE END SPI1_Init 2 */
31
32
}

Hier die SPI Kommunikation mit dem Slave:
1
void tmc5160_readWriteArray(uint8_t channel, uint8_t *data, size_t length)
2
{
3
  uint8_t uartTXbuffer[50];
4
  char buffer [50];
5
  int n;
6
7
  n = sprintf (buffer, "SPI_w: %x %x %x %x %x ", data[0], data[1], data[2], data[3], data[4]);
8
  memcpy(uartTXbuffer,buffer,sizeof(buffer));
9
  CDC_Transmit_FS(uartTXbuffer,n+1);
10
11
  HAL_SPI_TransmitReceive(&hspi1, data, data, 5, 100); // <---------------------------------------------------
12
13
  n = sprintf (buffer, "SPI_r: %x %x %x %x %x\n\n", data[0], data[1], data[2], data[3], data[4]);
14
  memcpy(uartTXbuffer,buffer,sizeof(buffer));
15
  CDC_Transmit_FS(uartTXbuffer,n+1);
16
}


Der Master (STM32) senden 5 bytes zum Slave (TMC5160), gleichzeitig 
sendet der Slave 5 bytes zurück (Status). Das erste Byte ist die Adresse 
des Registers. Aussehen sollte das so :
1
SPI_w: 80 0      (Befehl)
2
SPI_r: 80 FA000  (Antwort)

Aber bei mir kommt nur ein Datenmüll zurück. Der Befehl wird schon gar 
nicht richtig zum Slave übertragen, sonst würde mindesten der Motor am 
Controller laufen.

Ich bin mir nicht sicher ob HAL_SPI_TransmitReceive richtig ist ? Im 
Arduino Code wird SPI im Mode 3 konfiguriert, bin aber der Meinung, dass 
ich das so richtig konfiguriert habe ?


Ich hoffe jemand kann mir weiter helfen, oder hat sogar ein 
Beispielcode.

Danke und Gruss
Michael

von Guest (Gast)


Lesenswert?

Du solltest vielleicht noch deinen Chip Select schalten sonst wird dein 
TMC garnichts machen.

von Guest (Gast)


Lesenswert?

Und in den gleichen Buffet zu schreiben aus dem man etwas sendet macht 
wenig Sinn. Und wo data definiert ist sehe ich auch nicht.

von Guest (Gast)


Lesenswert?

*buffer

von Michael L. (nightflyer88)


Lesenswert?

Ok danke. Ich bin davon ausgegangen, wenn NSS auf Hardware Output 
eingestellt ist, dass die SPI Schnittstelle das selber steuert. Nun habe 
ich das NSS manuel per Software gesteuert, aber es ändert sich nichts. 
Auch den rxbuffer habe ich separiert, leider ohne erfolg.

Bei HAL_SPI_TransmitReceive verstehe ich nicht ganz wie lang die 
Datenlänge eingestellt werden muss, man findet im www unterschiedliche 
angaben. Bei 5 byte senden & empangen = 10 ? Oder doch nur 5 ?

von Guest (Gast)


Lesenswert?

Die Peripherie kann soweit ich weiß nicht wirklich den NSS ansprechen 
das ist etwas verwirrend.

Wenn du bei DataSize 8_Bit eingestellt hast dann sendet er bei 1 1 Byte 
bei 2 2 Byte und so weiter. Wenn du DataSize auf z.B. 16 stellst schickt 
er mit 1 2 Byte mit 2 4 Byte usw.

In deinem Fall wenn du 5 Byte verarbeiten willst musst du bei den 
aktuellen Einstellungen mit 8 Bit also 5 einstellen. Bei TransmitRecive 
sendet und empfängt er gleichzeitig. Wie gesagt müssen die Buffer 
unterschiedlich sein.

Ist die Taktrate und der SPI Moder richtig eingestellt?

von Guest (Gast)


Lesenswert?

Wenn ich das richtig sehe will der TMC SPI Mode 3 also CPOL High und und 
Seconds Edge. Damit wären deine CLKPhase falsch. Außerdem kann der TMC 
ohne externen Oszillator nur 8MHz, wenn du den F1 mit 72MHz taktest und 
einen Prescaler von 8 hast sind das aber 9MHz. Wenn du am TMC einen 
Oszillator hast sollte das aber gehen dann kann er 16MHz. Du solltest 
den NSS auch auf disable stellen und ihn komplett in der Software 
steuern.

Ansonsten kann es noch sein das die Konfiguration der SPI Schnittstelle 
nicht vollständig ist. Der Code den du gepostet hast reicht nicht. Es 
muss noch Code geben der die GPIOs initialisiert und die Clock für die 
Peripherie aktiviert. Sollte das mit der IDE erstellt worden sein müsste 
er das aber angelegt haben.

von Michael L. (nightflyer88)


Lesenswert?

Ok danke ! super jetzt läufts ! CLKPhase war falsch, im Arduino Code war 
der auf 1 gesetzt, und ich habe gedacht das wäre das gleiche wie 1 Edge.

Aber wie gesagt, das 2. Problem war auch das NSS Signal, geht nur per 
Software. Was meinst Du, muss zwischen NSS und der ersten SPI 
Komunikation eine kleine Verzögerung sein, gemäss Timingdiagramm des TMC 
sollte es nicht nötig sein ? Es funktioniert bis jetzt auch einwandfrei.

1
HAL_GPIO_WritePin(GPIOA, CS_Pin, 0);
2
// delay ?
3
HAL_SPI_TransmitReceive(&hspi1, data, rxData, 5, 100
4
// delay ?
5
HAL_GPIO_WritePin(GPIOA, CS_Pin, 1);


Mit 16 prescaler ergibt sich eine Baudrate von 3 mbit/s gemäss IDE.

von Guest (Gast)


Lesenswert?

Michael L. schrieb:
> Was meinst Du, muss zwischen NSS und der ersten SPI Komunikation eine
> kleine Verzögerung sein

In der Regel nicht. Die Verzögerung zwischen GPIO Write und bis SPI 
anfängt zu senden reicht meistens massig aus.

Michael L. schrieb:
> Mit 16 prescaler ergibt sich eine Baudrate von 3 mbit/s gemäss IDE.

Das kommt auf deinen Systemtakt an. In dem Fall nutzt du wohl 48MHz 
statt den möglichen 72MHz

von Michael L. (nightflyer88)


Lesenswert?

Guest schrieb:
> Das kommt auf deinen Systemtakt an. In dem Fall nutzt du wohl 48MHz
> statt den möglichen 72MHz

Ja genau, läuft mit 48Mhz.

Vielen dank für deine Hilfe, hat mir sehr weitergeholfen.

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.