Forum: Mikrocontroller und Digitale Elektronik Probleme mit Konfiguration der SPI-Schnittstelle am MSP430


von Simon H. (simon12)


Angehängte Dateien:

Lesenswert?

Hallo,
ich versuche gerade mit dem MSP430FR5969 den Beschleunigungssensor 
ADXL362 von Analog Devices über SPI anzusprechen (Datenblätter sind im 
Anhang). Habe den Chip mit dem Arduino und der von AD bereitgestellten 
Library zum laufen bekommen d.h. die Register sollten richtig gesetzt 
sein. Es scheitert wohl an der Konfiguration der SPI-Schnittstelle auf 
dem MSP430. Um die Sache zunächst einfach zu geschalten habe ich 
versucht wie in der Arduino Library einzelne Methoden für das 
Schreiben/Lesen von Registern zu schreiben. Meine Frage wäre, ob man die 
SMCLK noch konfigurieren muss? Habe gelesen, dass die per default eig. 
schon mit 1MHz läuft, ich messe am CLK-Pin aber kein Signal. Das würde 
vlt. auch erklären warum die Ausführung beim Empfangen der Daten stoppt. 
Aber es kann natürlich auch an andere Stelle klemmen. Wäre nett, wenn 
mir da jemand auf die Sprünge helfen könnte!

Pin-Belegung der Signale ist wie folgt:
ADXL362   MSP430
4 SCLK    P2.2
6 MOSI    P1.6
7 MISO    P1.7
8 CS      P1.5

Vielen Dank und viele Grüße
Simon

Code:
1
#include <msp430.h>
2
3
/*
4
 * ADXL362
5
 * Auslesen der z-Achse
6
 *
7
 * P1.5 CS
8
 * P1.6 MOSI
9
 * P1.7 MISO
10
 * P2.2 CLOCK
11
 */
12
13
void writeOneRegister(char value, char adress);
14
void writeTwoRegister(int value, char adress);
15
int readOneRegister(char adress);
16
int readTwoRegister(char adress);
17
void reset(void);
18
19
int value;
20
char valueH;
21
char valueL;
22
int zAxis;
23
24
25
int main(void) {
26
27
  WDTCTL = WDTPW | WDTHOLD;                 // Stop watchdog timer
28
29
  // Pin Konfiguration
30
  P1SEL1 |= BIT6 + BIT7;        // P1.6 als UCB0SIMO, P1.7 als UCB0SOMI
31
  P2SEL1 |= BIT2;               // P2.2 als UCB0CLK
32
  P1DIR |= BIT5;                // P1.5 als CS
33
  P1OUT |= BIT5;                // CS high setzen
34
  P4DIR |= BIT6;                // LED1
35
  P4OUT |= BIT6;                // LED1 an
36
  P1IE |= BIT3 + BIT4;          // P1.3 + P1.4 als INT1/2 für ADXL362
37
38
  PM5CTL0 &= ~LOCKLPM5;
39
40
  // Setup SPI (eUSCI_B)
41
  UCB0CTLW0 |= UCSWRST;      // UCB0 hold in reset state for configuration
42
  UCB0CTLW0 |= UCCKPH + UCMSB + UCMST + UCMODE_0 + UCSYNC + UCSSEL_2;    // Data captured on first edge + MSB first + Master Mode + 3-pin SPI + Synchronous Mode + SMCLK
43
  UCB0BRW = 0;              // Prescaler 1 -> CLK sollte mind. 1 MHz sein laut dem ADXL362 Datenblatt
44
45
  // Setup ADXL362
46
47
  writeOneRegister(0x15, 0x2C);      // FILTER_CTL --> HALF_BW + ODR = 400Hz
48
  writeOneRegister(0x02, 0x2D);      // POWER_CTL --> Measurement Mode
49
  reset();
50
  while(1) {
51
    P4OUT ^= BIT6;                   // Toggle LED1
52
    zAxis = readTwoRegister(0x12);   // Wert der z-Achse auselesen
53
    __delay_cycles(100000);          // 100ms Delay
54
  }
55
}
56
57
// Hilfsfunktionen
58
59
void writeOneRegister(char value, char adress) {  // 1 Byte in Register schreiben
60
  UCB0CTLW0 &= ~UCSWRST;           // reset released for operation
61
  P1OUT &= ~BIT5;                  // CS enabled
62
  UCB0TXBUF = 0x0A;                // Schreib-Befehl senden
63
  while(!(UCB0IFG & UCTXIFG));     // warten bis Shift-Register leer ist
64
  UCB0TXBUF = adress;              // Register-Adresse senden
65
  while(!(UCB0IFG & UCTXIFG));     // warten bis Shift-Register leer ist
66
  UCB0TXBUF = value;               // Register-Wert senden
67
  while(!(UCB0IFG & UCTXIFG));     // warten bis Shift-Register leer ist
68
  P1OUT |= BIT5;                   // CS disabled
69
}
70
71
int readOneRegister(char adress) { // 1-Byte Register lesen
72
  UCB0CTLW0 &= ~UCSWRST;           // reset released for operation
73
  P1OUT &= ~BIT5;                  // CS enabled
74
  UCB0TXBUF = 0x0B;                // Lese-Befehl senden
75
  while(!(UCB0IFG & UCTXIFG));     // warten bis Shift-Register leer ist
76
  UCB0TXBUF = adress;              // Register-Adresse senden
77
  while(!(UCB0IFG & UCTXIFG));     // warten bis Shift-Register leer ist
78
  value = UCB0RXBUF;               // Register-Wert lesen
79
  while(!(UCB0IFG & UCRXIFG));     // warten bis Shift-Register voll ist
80
  P1OUT |= BIT5;                   // CS disabled
81
  return value;                    // Wert zurückgeben
82
}
83
84
void writeTwoRegister(int value, char adress) {
85
  valueH = value >> 8;
86
  valueL = value;
87
  UCB0CTLW0 &= ~UCSWRST;
88
  P1OUT &= ~BIT5;
89
  UCB0TXBUF = 0x0A;
90
  while(!(UCB0IFG & UCTXIFG));
91
  UCB0TXBUF = adress;
92
  while(!(UCB0IFG & UCTXIFG));
93
  UCB0TXBUF = valueL;
94
  while(!(UCB0IFG & UCTXIFG));
95
  UCB0TXBUF = valueH;
96
  while(!(UCB0IFG & UCTXIFG));
97
  P1OUT |= BIT5;
98
}
99
100
int readTwoRegister(char adress) {
101
  UCB0CTLW0 &= ~UCSWRST;
102
  P1OUT &= ~BIT5;
103
  UCB0TXBUF = 0x0B;
104
  while(!(UCB0IFG & UCTXIFG));
105
  UCB0TXBUF = adress;
106
  while(!(UCB0IFG & UCTXIFG));
107
  valueL = UCB0RXBUF;
108
  while(!(UCB0IFG & UCRXIFG));
109
  valueH = UCB0RXBUF;
110
  while(!(UCB0IFG & UCRXIFG));
111
  P1OUT |= BIT5;
112
  value = valueL + (valueH << 8);
113
  return value;
114
}
115
116
void reset(void) {
117
  UCB0CTLW0 &= ~UCSWRST;
118
  P1OUT &= ~BIT5;
119
  UCB0TXBUF = 0x0B;
120
  while(!(UCB0IFG & UCTXIFG));
121
  UCB0TXBUF = 0x0B;
122
  while(!(UCB0IFG & UCTXIFG));
123
  value = UCB0RXBUF;
124
  while(!(UCB0IFG & UCRXIFG));
125
  P1OUT |= BIT5;
126
}

von spess53 (Gast)


Lesenswert?

Hi

>  value = UCB0RXBUF;               // Register-Wert lesen
>  while(!(UCB0IFG & UCRXIFG));     // warten bis Shift-Register voll ist

Das dürfte falsch sein. Im allg. läuft das Lesen von einem Slave bei µCs 
wie folgt ab:

- Senden eines Dummy-Bytes (zur Takterzeugung)
- Warten bis Empfangspuffer voll ist
- Auslesen des Empfangspuffers

Würde mich wundern, wenn es beim MSP anders ist.

MfG Spess

von Simon H. (simon12)


Lesenswert?

Danke für den Hinweis! Habe mich für die Programmierung an den Code 
Examples von TI und hier 
http://www.argenox.com/library/msp430/ch9-msp430_spi.php orientiert. Da 
kann ich jetzt nicht erkennen das ein Dummy-Byte gesendet wird. Wäre 
super wenn sich einer der Erfahrungen mit MSP430 hat dazu äußern könnte 
;-)

von spess53 (Gast)


Lesenswert?

Hi

>Da kann ich jetzt nicht erkennen das ein Dummy-Byte gesendet wird.

Und was ist das:

    UCA0TXBUF = 0xAA;              // Send 0xAA over SPI to Slave
    while (!(IFG2 & UCA0RXIFG));   // USCI_A0 RX Received?
    received_ch = UCA0RXBUF;       // Store received data

MfG Spess

von Simon H. (simon12)


Lesenswert?

Hmm...ich dachte das ist die Register-Adresse oder ähnliches. Ich sende 
wenn ich lesen will ja zunächst den Wert 0x0B damit der Chip erkennt, 
dass ich ein Register lesen will. Dann sende ich die Adresse und dann 
dachte ich bekomm ich den Wert zurück. Meinst du ich sollte quasi noch 
ein drittes Byte senden um was zurück zu bekommen?

von spess53 (Gast)


Lesenswert?

Hi

>Meinst du ich sollte quasi noch
>ein drittes Byte senden um was zurück zu bekommen?

Ja. Um ein Byte zu senden braucht der Slave einen Takt vom Master. Und 
den bekommt er nur wenn der Master sendet.

MfG Spess

von Simon H. (simon12)


Lesenswert?

Habe den Code jetzt mal angepasst! Meintest du so?
1
int readTwoRegister(char adress) {
2
  UCB0CTLW0 &= ~UCSWRST;
3
  P1OUT &= ~BIT5;
4
  while(!(UCB0IFG & UCTXIFG));
5
  UCB0TXBUF = 0x0B;
6
  while(!(UCB0IFG & UCTXIFG));
7
  UCB0TXBUF = adress;
8
  while(!(UCB0IFG & UCTXIFG));
9
  UCB0TXBUF = 0x00;
10
  while(!(UCB0IFG & UCRXIFG));
11
  valueL = UCB0RXBUF;
12
  while(!(UCB0IFG & UCTXIFG));
13
  UCB0TXBUF = 0x00;
14
  while(!(UCB0IFG & UCRXIFG));
15
  valueH = UCB0RXBUF;
16
  while(!(UCB0IFG & UCRXIFG));
17
  P1OUT |= BIT5;
18
  value = valueL + (valueH << 8);
19
  return value;
20
}

von Dirk K. (dekoepi)


Lesenswert?

Mir deucht, dir fehlen die Kenntnisse über das sehr einfache 
SPI-Protokoll. Es ist quasi ein Schieberegister - nur, wenn du Daten 
reinschiebst, bekommst du am anderen Ende welche heraus.

http://de.wikipedia.org/wiki/Serial_Peripheral_Interface

Einfach noch mal paar SPI-Beispiele (egal, für welchen Prozessor) lesen, 
vielleicht wird es dann deutlicher:

Beitrag "SPI-Routine Fehlerhaft"

Vom Prinzip her:
-> SPI setup (Geschwindigkeit, Phase, GPIOs...)
-> SPI transfer (read & write gleichzeitig!):
  char lese_ergebnis= SPI_transfer(char schreibe_daten);

-> Ansteuerung des SPI-Slaves umsetzen
zB SPI-RAM:
1
Chip_Select(); 
2
SPI_transfer(MODE_REGISTER);  // select the mode register to change it
3
SPI_transfer(continous_mode); // auto increment address
4
Chip_Deselect(); 
5
Chip_Select();
6
SPI_transfer(will_schreiben);
7
SPI_transfer(Adresse<<8); //MSB first
8
SPI_transfer(Adresse);  // LSB then
9
while (machmal) {
10
  empfangsbuffer[x] = SPI_transfer(meine_daten[x]);  // Dummy data ends in empfangsbuffer
11
  x++;
12
  if (x>100) machmal=false;
13
}
14
Chip_Deselect();
15
Chip_Select();
16
x=0;
17
SPI_transfer(will_lesen);
18
SPI_transfer(Adresse<<8); //MSB first
19
SPI_transfer(Adresse);  // LSB then
20
while (machmal) {
21
  empfangsbuffer[x] = SPI_transfer(meine_daten[x]);  // REAL! data ends in empfangsbuffer
22
  x++;
23
  if (x>100) machmal=false;
24
}
25
Chip_Deselect();


So in etwa als Pseudocode.

: Bearbeitet durch User
von Simon H. (simon12)


Lesenswert?

Ich habe SPI bisher nur in Verbindung mit dem Arduino verwendet daher 
sind meine Kentnisse in der Tat etwas beschränkt. Habe jetzt den 
englischen Wiki-Artikel mal ganz gelesen. Der ist deutlich ausführlicher 
;-) Ich habe jetzt verstanden das quasi immer ein Bit vom Master zum 
Slave geschickt wird dann eins vom Slave zum Master bis das Byte durch 
ist. Heißt das dann ich muss nach jedem Byte was ich sende egal ob was 
sinnvolles drin steht das Byte im RX-Register auslesen? Das ist im 
Pseudocode aber auch nicht gemacht oder? Irgendwie verwirrt mich das 
ganze grad total! Ich glaube einfach das ich irgendwas bei Setup der 
SPI-Schnittstelle was falsch gemacht habe! Werde jetzt mal nach weiteren 
Beispielen suchen :-/

von spess53 (Gast)


Lesenswert?

Hi

>Heißt das dann ich muss nach jedem Byte was ich sende egal ob was
>sinnvolles drin steht das Byte im RX-Register auslesen?

Du kannst, musst es aber nicht. Allerdings muss vor dem Empfang eines 
gültigen Zeichens das Bit zurückgesetzt werden, das ein empfangenes Byte 
anzeigt.

>Ich habe SPI bisher nur in Verbindung mit dem Arduino verwendet daher
>sind meine Kentnisse in der Tat etwas beschränkt.

Vielleicht verstehst du jetzt, warum hier viele, mich eingeschlossen, 
die Arduinosoftware nicht für geeignet halten 'µCs zu lernen'.

MfG Spess

von foo (Gast)


Lesenswert?

Du kannst dir SPI auch als Ring vorstellen. Der Master schiebt ein 
Bit/Byte raus in den Slave - dieser schiebt zurück in den Master.
Es wird also immer etwas empfangen.

von Dirk K. (dekoepi)


Lesenswert?

spess53 schrieb:
> Vielleicht verstehst du jetzt, warum hier viele, mich eingeschlossen,
> die Arduinosoftware nicht für geeignet halten 'µCs zu lernen'.

Dem muss ich widersprechen. Habe genau mit SPI und Arduino angefangen. 
Die ersten 2-3 Stunden die fertigen Libs angeschaut und benutzt, 
gesehen, dass die Schrott sind, und SPI selber programmiert. Noch alles 
in der "Arduino-Welt", sogar mit Arduino-IDE.

Begreife Arduino aber auch eher als AVR in schön klein fertig aufgebaut. 
Und bin bald weg von Arduino-IDE, weil die mehr beschränkt und zuviel 
Overhead hat im Vergleich zu "richtigen" Programmierumgebungen.

Aber ich schweife ab. Grundsätzlich ist das für den Einstieg schon gut - 
immer kleine Schritte machen. Dass dann der Ehrgeiz vieler nicht reicht, 
selber zu suchen und zu probieren, kann man Arduino nicht anlasten. Das 
sind einfach Menschen, die so sind. :D

von Simon H. (simon12)


Lesenswert?

spess53 schrieb:
> Du kannst, musst es aber nicht. Allerdings muss vor dem Empfang eines
> gültigen Zeichens das Bit zurückgesetzt werden, das ein empfangenes Byte
> anzeigt.

Konkret gesagt die Flag zurücksetzen?
Was Arduino angeht, komme ich die letzten Wochen in denen ich angefangen 
habe in C zu programmieren auch zu der Einsicht. Wenn man wirklich µCs 
programmieren will ist Arduino nichts. Andererseits erreicht man damit 
sehr schnell Erfolge und ist als Einstieg wirklich nicht schlecht.

von masterless (Gast)


Lesenswert?

Wirf einen Blick auf ein Software SPI.Es gibt einen Takt, eine 
Sendeleitung und eine Empfangsleitung. Um zu empfangen, muss der Master 
den Takt toggeln.

Wie last man den Takt bei einen Hardware SPI toggeln? In dem man sendet, 
natürlich!

von Simon H. (simon12)


Lesenswert?

Der Hinweis die Flag zurückzusetzen scheint was gebracht zu haben! 
Immerhin läuft das Programm jetzt durch aber den Wert den ich 
rausbekomme ist absoluter Mist! Ich bekomm irgendwas zwischen 0 und 
65535 also den gesamten Bereich eines Integers... Funktion zum Lesen 
sieht aktuell so aus:
1
int readTwoRegister(char adress) {
2
  UCB0CTLW0 &= ~UCSWRST;
3
  P1OUT &= ~BIT5;
4
  while(!(UCB0IFG & UCTXIFG));
5
  UCB0IFG &= ~UCTXIFG;
6
  UCB0TXBUF = 0x0B;
7
  while(!(UCB0IFG & UCTXIFG));
8
  UCB0IFG &= ~UCTXIFG;
9
  UCB0TXBUF = adress;
10
  while(!(UCB0IFG & UCTXIFG));
11
  UCB0IFG &= ~UCTXIFG;
12
  UCB0IFG &= ~UCRXIFG;
13
  UCB0TXBUF = 0x00;
14
  while(!(UCB0IFG & UCTXIFG));
15
  valueL = UCB0RXBUF;
16
  while(!(UCB0IFG & UCRXIFG));
17
  UCB0TXBUF = 0x00;
18
  while(!(UCB0IFG & UCTXIFG));
19
  valueH = UCB0RXBUF;
20
  while(!(UCB0IFG & UCRXIFG));
21
  P1OUT |= BIT5;
22
  value = valueL + (valueH << 8);
23
  return value;
24
}

Entdeckt da jemand noch irgend einen Schnitzer?

von Dirk K. (dekoepi)


Lesenswert?

Jetzt wären Details wie eingesetzte Hardware und der vollständige Code 
(insbesondere fehlt das SPI-Setup) hilfreich.

Gegebenfalls auch ein Foto vom Aufbau/Anschluss der Peripherie. Unter 
Umständen kann ein Pulldown-Widerstand in der MOSI-Leitung hilfreich 
sein. Logic-Analyse/Oszilloskop hast du da, um Signale zu prüfen, ob die 
einigermaßen plausibel laufen?

Edit: Und das "+" des L+H-Wertes, das willst du sicherlich durch ein | 
ersetzen. Du bekommst laut Datenblatt sicher zuerst den L und dann den 
H-Wert?

: Bearbeitet durch User
von Simon H. (simon12)


Lesenswert?

Dirk K. schrieb:
> Jetzt wären Details wie eingesetzte Hardware und der vollständige Code
> (insbesondere fehlt das SPI-Setup) hilfreich.
>
> Gegebenfalls auch ein Foto vom Aufbau/Anschluss der Peripherie.

Ich hab jetzt nur noch mal die Funktion gepostet an der sich was 
geändert hat. Der Rest sieht immer noch so aus wie in meinem ersten 
Post! Dort steht auch welche Pins wie verbunden sind. Habe das ganze wie 
gesagt mit dem Arduino schon getestet, deswegen sollte die Beschaltung 
stimmen. Das Problem ist wohl oder übel die Software...

von masterless (Gast)


Lesenswert?


von Simon H. (simon12)


Lesenswert?

masterless schrieb:
> http://www.ti.com/tool/MSP430ware

Hab mir die Beispiele schon angeschaut! Da wird durchgängig mit 
Interrupts gearbeitet! Würde das zunächst mal aber gerne mit den while() 
statements zum laufen kriegen.

masterless schrieb:
> Fürs nächste Problem
> http://dbindner.freeshell.org/msp430/

Sind zwar Beispiele zur SPI-Kommunikation aber die LCDs die angesteuert 
werden sind read-only! Hilft mir also leider auch nicht!

von masterless (Gast)


Lesenswert?

Ich schrieb "fürs nächste Problem"!

Und Angst vor Interrupts?

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.