Forum: Mikrocontroller und Digitale Elektronik SPI Kommunikation zwischen 2 MSP430 funktioniert nicht


von M430 (Gast)


Lesenswert?

Hallo zusammen,

ich bin neu in MSP430 Programmierung und tue mich grade mit SPI 
Kommunkation etwas schwer. Ich habe zwei Boards und möchte einfach mit 
char Zeichen Leds steuern. Ich habe einen MSP430 als Master und einen 
als Slave. Derzeit schaut der Code so aus:

Master:
1
#include <msp430.h>
2
3
unsigned char MST_Data, SLV_Data;
4
5
int main(void) {
6
  volatile unsigned int i;
7
8
  WDTCTL = WDTPW + WDTHOLD;                   // Stop watchdog timer
9
  FLL_CTL0 |= XCAP14PF;                     // Configure load caps
10
  P2DIR = 0xFF;
11
12
  // Wait for xtal to stabilize
13
  do {
14
    IFG1 &= ~OFIFG;                           // Clear OSCFault flag
15
    for (i = 0x47FF; i > 0; i--)
16
      ;             // Time for flag to set
17
  } while ((IFG1 & OFIFG));                   // OSCFault flag still set?
18
19
  for (i = 2100; i > 0; i--)
20
    ;                      // Now with stable ACLK, wait for
21
                 // DCO to stabilize.
22
  P5OUT = 0x04;                            // P5 setup for LED and slave reset
23
  P5DIR |= 0x06;                            //
24
  P7SEL |= 0x0E;                            // P7.3,2,1 option select
25
  UCA0CTL0 |= UCMST + UCSYNC + UCCKPL + UCMSB;    //3-pin, 8-bit SPI master
26
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
27
  UCA0BR0 = 0x02;                           // /2
28
  UCA0BR1 = 0;                              //
29
  UCA0MCTL = 0;                             // No modulation
30
  UCA0CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**
31
  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
32
33
  P5OUT &= ~0x04;                         // Now with SPI signals initialized,
34
  P5OUT |= 0x04;                            // reset slave
35
  P5OUT &= ~0x02;
36
  P2OUT = 0x00;
37
38
  while (1)
39
  {
40
    while (!(IFG2 & UCA0TXIFG))
41
      ; // USART1 TX buffer ready?
42
    UCA0TXBUF = 'a';                     // Transmit character
43
    P5OUT ^= 0x02;
44
//    P2OUT ^= 0x02;        // Toggle P2.0 using exclusive-OR
45
//
46
//    i = 10000;          // SW Delay
47
//    do
48
//      i--;
49
//    while (i != 0);
50
    __delay_cycles(1000000);
51
  }
52
53
//  __bis_SR_register(LPM0_bits + GIE);
54
  __bis_SR_register(GIE);
55
  // CPU off, enable interrupts
56
}
57
58
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
59
#pragma vector=USCIAB0RX_VECTOR
60
__interrupt void USCIA0RX_ISR(void)
61
#elif defined(__GNUC__)
62
void __attribute__ ((interrupt(USCIAB0RX_VECTOR))) USCIA0RX_ISR (void)
63
#else
64
#error Compiler not supported!
65
#endif
66
{
67
  volatile unsigned int i;
68
  while (!(IFG2 & UCA0RXIFG));          // USART1 RX buffer ready?
69
  if (UCA0RXBUF == 'A')                  // Test for correct character RX'd
70
  {
71
    P2OUT |= 0x02;                          // If correct, light LED
72
  }
73
74
//  MST_Data++;                                 // Increment data
75
//  SLV_Data++;
76
//  UCA0TXBUF = MST_Data;                       // Send next value
77
78
  for (i = 100; i > 0; i--)
79
    ;                              // Add time between transmissions to
80
}                                             // make sure slave can keep up

Slave:
1
#include <msp430.h>
2
3
typedef enum {
4
  NO_LED, LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8
5
} ledEnum;
6
7
void turnLedOn(char ledNum);
8
void turnLedOff(char ledNum);
9
10
volatile int flag = 0;
11
12
int main(void)
13
{
14
  WDTCTL = WDTPW + WDTHOLD;                   // Stop watchdog timer
15
  P3DIR = 0xFF;
16
  // Configure XT1
17
  PJSEL0 |= BIT4 + BIT5;
18
19
  CSCTL0_H = 0xA5;
20
  CSCTL1 |= DCOFSEL0 + DCOFSEL1;             // Set max. DCO setting
21
  CSCTL2 = SELA_0 + SELS_3 + SELM_3;        // set ACLK = XT1; MCLK = DCO
22
  CSCTL3 = DIVA_0 + DIVS_3 + DIVM_3;        // set all dividers
23
  CSCTL4 |= XT1DRIVE_0;
24
  CSCTL4 &= ~XT1OFF;
25
  do
26
  {
27
    CSCTL5 &= ~XT1OFFG;
28
    // Clear XT1 fault flag
29
    SFRIFG1 &= ~OFIFG;
30
  } while (SFRIFG1 & OFIFG);                   // Test oscillator fault flag
31
  // Configure SPI pins
32
  P1SEL1 |= BIT5;
33
  P2SEL1 |= BIT0 + BIT1;
34
35
  UCA0CTLW0 |= UCSWRST;                     // **Put state machine in reset**
36
  UCA0CTLW0 |= UCSYNC + UCCKPL + UCMSB;         // 3-pin, 8-bit SPI slave
37
                          // Clock polarity high, MSB
38
  UCA0CTLW0 |= UCSSEL_2;                    // ACLK
39
  UCA0BR0 = 0x02;                           // /2
40
  UCA0BR1 = 0;                              //
41
  UCA0MCTLW = 0;                            // No modulation
42
  UCA0CTLW0 &= ~UCSWRST;                  // **Initialize USCI state machine**
43
  UCA0IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
44
45
  turnLedOff(6);
46
  turnLedOff(7);
47
  turnLedOff(8);
48
49
//  __bis_SR_register(LPM0_bits + GIE);
50
  __bis_SR_register(GIE);
51
  while(1)
52
  {
53
    turnLedOn(5);
54
    __delay_cycles(1000000);
55
    turnLedOff(5);
56
    __delay_cycles(1000000);
57
    if(flag==1)
58
    {
59
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
60
      UCA0TXBUF = 'A';                  // Echo received data
61
      P3OUT ^= (BIT7);
62
    }
63
  }
64
  // Enter LPM0, enable interrupts
65
}
66
67
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
68
#pragma vector=USCI_A0_VECTOR
69
__interrupt void USCI_A0_ISR(void)
70
#elif defined(__GNUC__)
71
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void)
72
#else
73
#error Compiler not supported!
74
#endif
75
{
76
  if(UCA0RXBUF=='a')
77
  {
78
    P3OUT ^= (BIT6);
79
    flag=1;
80
  }
81
}

Ich kann Daten vom Masteraus senden. Der Slave reagiert wie programmiert 
darauf und die LEDs toggelen auch. Der Master empfängt jedoch nichts. 
Die Abfrage in der main Funktion auf 'A' wird nie erfüllt. Im Debugger 
erscheint der Receive Buffer des Masters mit einem Konstanten Wert von 
0x66...

Kann mir jemand vielleicht einen kleinen Tipp geben?

lg M430

von M430 (Gast)


Lesenswert?

Der Master ist ein MSP430FG4618, der Slave ist ein MSP430FR5739

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Hast Du bedacht, daß bei SPI das Senden und Empfangen starr gekoppelt 
sind?

Der Master empfängt genau dann, wenn er auch sendet.

von Nikolai.H. (Gast)


Lesenswert?

Beim Master hast du das Interface nicht resettet, beim Slave aber schon:
UCA0CTLW0 |= UCSWRST;                     // **Put state machine in 
reset**
Sollte beim Master in der Initialisierung auch rein.

von 16Bit (Gast)


Lesenswert?

Schau dir das einmal an:
http://mykyta.info/msp430/group___m_o_d___s_p_i.html

Und wie Rufus schon schrieb:

Rufus Τ. F. schrieb:
> bei SPI das Senden und Empfangen
genau gleichzeitig statt finden, vollduplex.

von M430 (Gast)


Lesenswert?

Vielen Dank an alle, die sich die Zeit genommen haben. Leider gehts bei 
mir immer noch nicht...
Ich habe was ihr geschrieben habt eingefügt. Sofort gesendet und mich 
nach der Funktion uint8_t spi_transfer_byte(uint8_t data) gerichtet.

Ich bekomme leider noch immer nichts zurück. Manchmal klappt das Senden 
auch nicht mehr. Man muss oft komplett alles resetten bis vlt was mal 
funktioniert.

Ich habe den Code nochmal raufgeladen. Was mache ich noch immer falsch?

Master
1
#include <msp430.h>
2
3
unsigned char MST_Data, SLV_Data;
4
5
char spi_transmit(volatile char data);
6
7
int main(void) {
8
  volatile unsigned int i;
9
10
  WDTCTL = WDTPW + WDTHOLD;                   // Stop watchdog timer
11
  FLL_CTL0 |= XCAP14PF;                     // Configure load caps
12
  P2DIR = 0xFF;
13
14
  // Wait for xtal to stabilize
15
  do {
16
    IFG1 &= ~OFIFG;                           // Clear OSCFault flag
17
    for (i = 0x47FF; i > 0; i--)
18
      ;             // Time for flag to set
19
  } while ((IFG1 & OFIFG));                   // OSCFault flag still set?
20
21
  for (i = 2100; i > 0; i--)
22
    ;                      // Now with stable ACLK, wait for
23
  // DCO to stabilize.
24
  P5OUT = 0x04;                            // P5 setup for LED and slave reset
25
  P5DIR |= 0x06;                            //
26
  P7SEL |= 0x0E;                            // P7.3,2,1 option select
27
  UCA0CTL0 |= UCMST + UCSYNC + UCCKPL + UCMSB;    //3-pin, 8-bit SPI master
28
  UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
29
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
30
  UCA0BR0 = 0x02;                           // /2
31
  UCA0BR1 = 0;                              //
32
  UCA0MCTL = 0;                             // No modulation
33
  UCA0CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**
34
  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
35
36
  P5OUT &= ~0x04;                         // Now with SPI signals initialized,
37
  P5OUT |= 0x04;                            // reset slave
38
  P5OUT &= ~0x02;
39
  P2OUT = 0x00;
40
41
  //  __bis_SR_register(LPM0_bits + GIE);
42
//  __bis_SR_register(GIE);
43
  // CPU off, enable interrupts
44
45
  while (1)
46
  {
47
    if (spi_transmit('a') == 'b')
48
    {
49
      P2OUT |= 0x02;                   // If correct, light LED
50
    }
51
    __delay_cycles(1000000);
52
    if (spi_transmit('c') == 'd')
53
    {
54
      P2OUT &= ~0x02;                   // If correct, light LED
55
    }
56
    __delay_cycles(1000000);
57
    if (spi_transmit('e') == 'f')
58
    {
59
      P2OUT |= 0x02;                   // If correct, light LED
60
    }
61
    P5OUT ^= 0x02;
62
    __delay_cycles(1000000);
63
  }
64
}
65
char spi_transmit(volatile char data)
66
{
67
  UCA0TXBUF = data;
68
  while (!(IFG2 & UCA0RXIFG))
69
    // wait for transfer to complete
70
    ;
71
  IFG2 &= ~UCA0RXIFG;    // clear the rx flag
72
  return (UCA0RXBUF);
73
}

Slave
1
#include <msp430.h>
2
3
typedef enum {
4
  NO_LED, LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8
5
} ledEnum;
6
7
void turnLedOn(char ledNum);
8
void turnLedOff(char ledNum);
9
10
volatile int flag = 0;
11
volatile char wert, test;
12
13
int main(void) {
14
  WDTCTL = WDTPW + WDTHOLD;                   // Stop watchdog timer
15
  P3DIR = 0xFF;
16
  // Configure XT1
17
  PJSEL0 |= BIT4 + BIT5;
18
19
  CSCTL0_H = 0xA5;
20
  CSCTL1 |= DCOFSEL0 + DCOFSEL1;             // Set max. DCO setting
21
  CSCTL2 = SELA_0 + SELS_3 + SELM_3;        // set ACLK = XT1; MCLK = DCO
22
  CSCTL3 = DIVA_0 + DIVS_3 + DIVM_3;        // set all dividers
23
  CSCTL4 |= XT1DRIVE_0;
24
  CSCTL4 &= ~XT1OFF;
25
  do {
26
    CSCTL5 &= ~XT1OFFG;
27
    // Clear XT1 fault flag
28
    SFRIFG1 &= ~OFIFG;
29
  } while (SFRIFG1 & OFIFG);                   // Test oscillator fault flag
30
  // Configure SPI pins
31
  P1SEL1 |= BIT5;
32
  P2SEL1 |= BIT0 + BIT1;
33
34
  UCA0CTLW0 |= UCSWRST;                     // **Put state machine in reset**
35
  UCA0CTLW0 |= UCSYNC + UCCKPL + UCMSB;         // 3-pin, 8-bit SPI slave
36
  // Clock polarity high, MSB
37
  UCA0CTLW0 |= UCSSEL_2;                    // ACLK
38
  UCA0BR0 = 0x02;                           // /2
39
  UCA0BR1 = 0;                              //
40
  UCA0MCTLW = 0;                            // No modulation
41
  UCA0CTLW0 &= ~UCSWRST;                  // **Initialize USCI state machine**
42
  UCA0IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
43
44
  turnLedOff(6);
45
  turnLedOff(7);
46
  turnLedOff(8);
47
48
//  __bis_SR_register(LPM0_bits + GIE);
49
  __bis_SR_register(GIE);
50
  while (1)
51
  {
52
    wert = test;
53
    turnLedOn(5);
54
    __delay_cycles(1000000);
55
    turnLedOff(5);
56
    __delay_cycles(1000000);
57
    if(flag==1)
58
    {
59
      turnLedOn(6);
60
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
61
      UCA0TXBUF = 'b';
62
    }
63
    else if(flag==2)
64
    {
65
      turnLedOn(7);
66
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
67
      UCA0TXBUF = 'd';
68
    }
69
    else if(flag==3)
70
    {
71
      turnLedOn(8);
72
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
73
      UCA0TXBUF = 'f';
74
    }
75
  }
76
}
77
78
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
79
#pragma vector=USCI_A0_VECTOR
80
__interrupt void USCI_A0_ISR(void)
81
#elif defined(__GNUC__)
82
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void)
83
#else
84
#error Compiler not supported!
85
#endif
86
{
87
  if (UCA0RXBUF == 'a')
88
  {
89
    flag = 1;
90
  }
91
  else if (UCA0RXBUF == 'c')
92
  {
93
    flag = 2;
94
  }
95
  else if (UCA0RXBUF == 'e')
96
  {
97
    flag = 3;
98
  }
99
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

M430 schrieb:
> Ich bekomme leider noch immer nichts zurück.

Du bekommst was zurück, nur nicht das, was Du erwartest.

Das, was Dein Master zurückbekommt, kann nicht das Resultat dessen sein, 
was der Slave aufgrund der empfangenen Daten zurücksendet, denn der 
Slave sendet --wie auch der Master-- genau dann, wenn er auch empfängt.

Das bedeutet, daß er das Empfangene erst dann auswerten kann, wenn der 
Sendevorgang bereits abgeschlossen ist.

Obendrein darf Deine Interruptroutine das Empfangsregister nur genau 
einmal auslesen, Du aber liest es bis zu dreimal aus.

Ändere das von
1
  if (UCA0RXBUF == 'a')
2
  {
3
    flag = 1;
4
  }
5
  else if (UCA0RXBUF == 'c')
6
  {
7
    flag = 2;
8
  }
9
  else if (UCA0RXBUF == 'e')
10
  {
11
    flag = 3;
12
  }

zu
1
  char c;
2
3
  c = UCA0RXBUF;
4
5
  if (c == 'a')
6
  {
7
    flag = 1;
8
  }
9
  else if (c == 'c')
10
  {
11
    flag = 2;
12
  }
13
  else if (c == 'e')
14
  {
15
    flag = 3;
16
  }

oder

1
  char c;
2
3
  c = UCA0RXBUF;
4
5
  switch (c)
6
  {
7
    case 'a' :
8
      flag = 1;
9
      break;
10
    
11
    case 'c' :
12
      flag = 2;
13
      break;
14
15
    case 'e' :
16
      flag = 3;
17
      break;
18
  }

(Und was geschieht, wenn was anderes als a, c oder e empfangen wird?)


Aber das ist nicht das ganze Problem, nehmen wir für die folgende 
Betrachtung mal an, Deine Interruptroutine würde das tun, was Du 
beabsichtigst.


Was passiert?

1. Master sendet 'a' und empfängt X

2. Slave empfängt 'a' und setzt danach "flag" auf 1.

Da das Senderegister des Slaves zum Zeitpunkt des Empfangens nicht 
initialisiert war, hat der Slave also irgendwas gesendet, und dieses 
irgendwas ist X.

Der Master empfängt also ziemlich sicher kein 'b', und schaltet 
deswegen seine LED nicht ein.

In der Hauptschleife des Slaves wird, weil "flag" auf 1 gesetzt wurde, 
'b' in das Senderegister geschrieben.

3. Master sendet 'b' ... und empfängt 'b'

4. Der Slave empfängt 'b' und hat derweil 'b' gesendet, "flag" verändert 
er nicht.

Also wird in der Hauptschleife des Slaves wieder ein 'b' in das 
Senderegister geschrieben.

5. Master sendet 'c' ... und empfängt 'b'

6. Der Slave empfängt 'c' und hat derweil (wieder) 'b' gesendet; danach 
setzt er "flag" auf 2.

In der Hauptschleife des Slaves wird daraufhin 'd' in das Senderegister 
geschrieben.

7. Master sendet 'd' ... und empfängt 'd'

Der Slave empfängt 'd' und hat 'd' gesendet, "flag" ändert er nicht.



Wird's allählich klar?

SPI ist eine synchrone Kommunikation, jeder Sendevorgang liefert 
zwangsweise auch empfangene Daten, die aber den Zustand vor dem Senden 
wiedergeben.

von M430 (Gast)


Lesenswert?

Hallo rufus,

danke für Deinen ausführlichen Beitrag! :) ich habe dasselbe nochmal 
hier beschrieben gefunden:

https://books.google.at/books?id=sVmQQsImi7AC&pg=PA150&lpg=PA150&dq=spi+schickt+was+zuerst+im+sendepuffer&source=bl&ots=hxRVL9t59i&sig=gwWtUxwTLOY9pSM9gl6FtkPe9Xw&hl=de&sa=X&ved=0CCMQ6AEwAGoVChMIoI2V-aiyxwIVyFwUCh36zQUS#v=onepage&q=spi%20schickt%20was%20zuerst%20im%20sendepuffer&f=false

Er erklärt das auch so, dass das was gesendet wird im Sendepuffer des 
Slaves verbleibt bis es beim nächsten Byte vom Master auch tatsächlich 
geschickt wird.

Jedoch verstehe ich noch immer etwas nicht. Ich habe meinen Code in der 
ISR umgeschrieben und habe nun vom Master aus in der Endlosschleife nur 
'a' schicken lassen, Mir ist jetzt klar, dass das erste Byte was ich 
empfange irgendwas ist. Aber da ich es ja immer wieder aufrufe, müsste 
es doch spätestens im zweiten Zyklus empfangen werden, oder?

Master (Veränderung):
1
while (1)
2
{
3
  if (spi_transmit('a') == 'b')
4
  {
5
    P2OUT |= 0x02;            // If correct, light LED
6
  }
7
8
P5OUT ^= 0x02;
9
  __delay_cycles(1000000);
10
}

Slave (Veränderung):
1
while (1)
2
  {
3
    wert = test;
4
    turnLedOn(5);
5
    __delay_cycles(1000000);
6
    turnLedOff(5);
7
    __delay_cycles(1000000);
8
    if(flag==1)
9
    {
10
//      turnLedOn(6);
11
      P3OUT ^= (BIT5);
12
//      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
13
      UCA0TXBUF = 'b';
14
    }
15
    else if(flag==2)
16
    {
17
      turnLedOn(7);
18
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
19
      UCA0TXBUF = 'd';
20
    }
21
    else if(flag==3)
22
    {
23
      turnLedOn(8);
24
      while (!(UCA0IFG & UCTXIFG));  // USCI_A0 TX buffer ready?
25
      UCA0TXBUF = 'f';
26
    }
27
  }
28
}
29
30
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
31
#pragma vector=USCI_A0_VECTOR
32
__interrupt void USCI_A0_ISR(void)
33
#elif defined(__GNUC__)
34
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void)
35
#else
36
#error Compiler not supported!
37
#endif
38
{
39
  receive=UCA0RXBUF;
40
  if (receive == 'a')
41
  {
42
    flag = 1;
43
  }
44
  if (receive == 'c')
45
  {
46
    flag = 2;
47
  }
48
  if (receive == 'e')
49
  {
50
    flag = 3;
51
  }
52
}

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.