Forum: Mikrocontroller und Digitale Elektronik MSP430 / I2C Timing / Rx Interupt


von Andreas V. (Firma: IGL) (andreas_va)


Lesenswert?

Hallo Forum,

als MSP430-Newbie versuche ich gerade einen Slave für den I2C-Bus zu 
programmieren. Der Slave-Code empfängt Daten von einem 
PC-Master-Test-Terminal. Realisierung erfolgt mit Interrupts.

Ich bekomme nicht das aktuelle Zeichen empfangen, wenn ich keine 
Wartefunktion einbaue. Stattdessen bekomme ich das zuvorgesendete 
Zeichen.
Momentan realisiere ich die Wartefunktion mit einer primitiven 
Warteschleife von 1 bis 10 mit __no_operation() Funktion.
Mit Wartefunktion funktioniert alles bestens. Das ist jedoch nicht 
gerade elegant und nicht mein Stil.

Was mache ich falsch bzw. was fehlt noch damit dieser Umstand elegant 
gelöst wird.

Eigentlich sollte das letzte Zeichen doch schon gesendet sein wenn der 
Rx-Interrupt ausgelöst wird. Sehe ich da etwas falsch?

Es wäre schön wenn Ihr mir helfen könntet.


Ich passte den Beispielcode von TI wie folgt an:
1
 
2
#include "msp430g2553.h"
3
unsigned char *pTXData;
4
unsigned char *pRXData;
5
unsigned char bufferTX[16] = {"Hello World!"};
6
unsigned char bufferRX[16] = {"            "};
7
8
volatile unsigned char RXData;
9
volatile unsigned char TxByteCtr;
10
volatile unsigned char RxByteCtr;
11
12
13
void main(void)
14
{
15
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
16
  P1DIR |= BIT0;                            // P1.0 output
17
  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
18
  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
19
  
20
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
21
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
22
  UCB0I2COA = 0x48;                         // Own Address is 048h
23
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
24
  UCB0I2CIE |= UCSTTIE;                     // Enable STT interrupt
25
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
26
// TXData = 0xff;                            // Used to hold TX data
27
  IE2 |= UCB0RXIE;                          // Enable RX interrupt //
28
  
29
  pTXData = (unsigned char *)bufferTX;
30
  pRXData = (unsigned char *)bufferRX;
31
  while (1)
32
  {
33
   
34
    TxByteCtr = 0;
35
    
36
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
37
   
38
    __no_operation(); //
39
  }
40
}
41
42
//#define UCSCLLOW            (0x40)    /* SCL low */
43
//#define UCGC                (0x20)    /* General Call address received Flag */
44
//#define UCBBUSY             (0x10)    /* Bus Busy Flag */
45
//#define UCNACKIFG           (0x08)    /* NAK Condition interrupt Flag */
46
//#define UCSTPIFG            (0x04)    /* STOP Condition interrupt Flag */
47
//#define UCSTTIFG            (0x02)    /* START Condition interrupt Flag */
48
//#define UCALIFG             (0x01)    /* Arbitration Lost interrupt Flag */
49
50
51
// USCI_B0 Data ISR
52
#pragma vector = USCIAB0TX_VECTOR
53
__interrupt void USCIAB0TX_ISR(void)
54
{  
55
  unsigned char lastChar = *pTXData;
56
  UCB0TXBUF = *pTXData;
57
  pTXData++;
58
  TxByteCtr++;
59
60
  if(TxByteCtr >= 0x11 || lastChar == 0)
61
  {
62
    pTXData = (unsigned char *)bufferTX;
63
    TxByteCtr = 0;
64
    __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0    
65
  }
66
}
67
68
69
70
// USCI_B0 State ISR
71
#pragma vector = USCIAB0RX_VECTOR
72
__interrupt void USCIAB0RX_ISR(void)
73
{
74
  UCB0STAT &= ~UCSTTIFG;                    // Clear start condition int flag  
75
  for (long x=0; x < 5;x++)
76
  {
77
    __no_operation();
78
  }  
79
  volatile unsigned char lastChar = 0; 
80
  lastChar = UCB0RXBUF;
81
  *pRXData = lastChar;
82
  pRXData++;
83
  RxByteCtr++;  
84
  if(RxByteCtr >=  16 || lastChar == 0)
85
  {
86
    pTXData = (unsigned char *)bufferTX;
87
    pRXData = (unsigned char *)bufferRX;   
88
    RxByteCtr = 0;
89
    __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0     
90
  }
91
}

von Name (Gast)


Lesenswert?

Das "TxByteCtr = 0;" in der TX-Funktion ist wohl überflüssig, weil das 
ja schon in der main() gemacht wird. Den beschriebenen Fehler sehe ich 
gerade auch nicht.

von Clemens L. (c_l)


Lesenswert?

Abschnitt 17.3.7 des User's Guide sagt:
> There are two interrupt vectors for the USCI module in I²C mode. One
> interrupt vector is associated with the transmit and receive interrupt
> flags. The other interrupt vector is associated with the four state
> change interrupt flags.

Und 17.3.7.4:
> USCI_Ax and USCI_Bx share the same interrupt vectors. In I²C mode the
> state change interrupt flags UCSTTIFG, UCSTPIFG, UCNACKIFG, UCALIFG
> from USCI_Bx and UCAxRXIFG from USCI_Ax are routed to one interrupt
> vector. The I²C transmit and receive interrupt flags UCBxTXIFG and
> UCBxRXIFG from USCI_Bx and UCAxTXIFG from USCI_Ax share another
> interrupt vector.

Die Namen "TX_ISR" und "RX_ISR" wären im UART- oder SPI-Modus korrekt. 
Für I²C solltest du bessere Namen verwenden, z.B. die aus den Beispielen 
17-1 und 17-2, USCIA0_RX_USCIB0_I2C_STATE_ISR und 
USCIA0_TX_USCIB0_I2C_DATA_ISR.

von Andreas V. (Firma: IGL) (andreas_va)


Lesenswert?

Danke für die Antworten. Die haben mir enorm weiter geholfen.
Insbesondere die Antwort von Clemens war sehr hilfreich.
Ich bin allerdings davon ausgegangen dass dieser User Guide für 
MSP430G2553 nicht gilt:

http://www.ti.com/lit/ug/slau144j/slau144j.pdf


Alle Beispiel-Codes für den MSP430G2553 greifen entweder nur lesend oder 
nur schreibend zu. Alternativ gibt es die Variante: erst schreiben, dann 
lesen.

Die Interrupts mit meinen neuen Interrupt-Routinen werden korrekt 
aufgerufen und die Funktion ist mir jetzt auch klarer geworden. Ich fand 
das Beispiel extrem irreführend, aber wer lesen kann (RTFM) ist oft im 
Vorteil. Nur muss man halt wissen was gilt.

Jetzt versuchte ich den State wie in Example 17.1 und 17.2 beschrieben 
auszuwerten. Das funktioniert allerdings nicht so wie ich mir das 
vorstelle.

Setze ich den State manuell auf state=1 (lesen) oder
state=2 (schreiben), dann funktioniert der Code wie gewollt.

Mit
1
 state = (IFG2 & 3);
 sollte entweder Bit 1 oder Bit 2 gesetzt sein. Das IFG2 ändert jedoch 
nur das erste Mal den Wert richtig. Danach ändert sich der Wert falsch 
oder bleibt bestehen. Danach hängt sich der MSP auf. Fehlt da noch eine 
Initialisierung, muss ich irgendetwas zurücksetzen oder funktioniert das 
Auswerten von IFG2 mit dem MSP430G2553 nicht?

1
unsigned char state = 0;
2
// USCI_B0 Data ISR
3
#pragma vector = USCIAB0TX_VECTOR // I2C DataVector = UART_TX_Vector
4
__interrupt void USCIA0_TX_USCIB0_I2C_DATA_ISR(void)
5
{    
6
    if (IFG2 & UCA0TXIFG )
7
    {      
8
      UCB0TXBUF = *pTXData;
9
      lastCharTx = *pTXData;
10
      pTXData++;
11
      TxByteCtr++;  
12
      if(TxByteCtr >=  0x10 || lastCharTx == 0)
13
      {
14
        pTXData = (unsigned char *)bufferTX;
15
        pRXData = (unsigned char *)bufferRX;   
16
        TxByteCtr = 0;
17
        RxByteCtr = 0;
18
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0
19
      }
20
    }
21
    else //if (IFG2 & UCA0RXIFG)
22
    {
23
      lastCharRx = UCB0RXBUF;
24
      *pRXData = lastCharRx;
25
      pRXData++;
26
      RxByteCtr++;      
27
      if(RxByteCtr >= 0x10 || lastCharRx == 0)
28
      {
29
        pTXData = (unsigned char *)bufferTX;
30
        pRXData = (unsigned char *)bufferRX;   
31
        TxByteCtr = 0;
32
        RxByteCtr = 0;
33
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0        
34
      }
35
    }
36
}

und
1
// USCI_B0 State ISR                 
2
#pragma vector = USCIAB0RX_VECTOR // I2C StateVector = UART_RX_Vector
3
__interrupt void USCIA0_RX_USCIB0_I2C_STATE_ISR(void)
4
{
5
  while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
6
  UCB0STAT &= ~UCSTTIFG;      // Clear start condition int 
7
  state = (IFG2 & 3); // der State sollte hier 1 (lesen) oder 2(schreiben) zugewiesen bekommen      
8
  //state = 2;   
9
  //state = 1;
10
}

von Clemens L. (c_l)


Lesenswert?

Was soll diese "3" bedeuten?

Laut msp430g2553.h ist das eh' falsch:
1
sfr_b(IFG2);                                  /* Interrupt Flag 2 */
2
#define UC0IFG                 IFG2
3
#define UCA0RXIFG              (0x01)
4
#define UCA0TXIFG              (0x02)
5
#define UCB0RXIFG              (0x04)
6
#define UCB0TXIFG              (0x08)

Und im Status-Interrupt sind die RX/TX-Flags bedeutungslos. Wie man in 
den Bildern 17-9 und 17-10 sieht, kann man den Lese-/Schreib-Modus eines 
I²C-Slave am UCTR-Bit erkennen.

: Bearbeitet durch User
von Andreas V. (Firma: IGL) (andreas_va)


Lesenswert?

Ok, dann frag ich Read/Write nicht mit

1
if (IFG2 & UCA0TXIFG )

sondern mit

1
if (UCB0CTL1 & UCTR)
ab.

Die
1
state = (IFG2 & 3);
war ein temporärer Notbehelf-Filter. Da Read und write nicht gleichzeit 
gesetzt sein dürfen hätte ich immer genau das Flag gehabt das ich 
abfragen will. Ich bin noch am testen.
Da ich Read/Write über das UCB0CTL1 abfragen kann und so wie es aussieht 
auch setzen kann war ich mal wieder auf dem Holzweg.

Ich frage mich halt nur was die folgenden Zeilen (insbesondere die 
BIT.B) aus 17.1 sonst bewirken:
1
USCIA0_RX_USCIB0_I2C_STATE_ISR
2
BIT.B #UCA0RXIFG,&IFG2  ; USCI_A0 Receive Interrupt?
3
JNZ     USCIA0_RX_ISR
4
JMP     USCIA0_TX_ISR
5
.
6
.
7
USCIA0_RX_ISR ; Read UCA0RXBUF
8
...
9
;Hier sollte mein Receive Code stehen!!!!
10
11
12
USCIA0_TX_ISR ; Write UCA0RXBUF
13
;Hier sollte mein Transmit Code stehen!!!!


Laut "17.3.7.4 Interrupt Vector Assignment" sollte das funktionieren.
Dann kann ich an der Stelle nur Busy abfragen!

Sollte ich das dann noch einmal zusätzlich wie folgt synchronisieren 
bevor ich lese oder schreibe?
1
//UCB0TXIFG is set immidiately (UCB0TRIFG is set to indicate RXBUF is ready for more data)
2
while((IFG2 & UCB0RXIFG) == 0);     
3
receivedByte = UCB0RXBUF;
bzw. für Schreiben
1
// wait until TX buffer is empty and transmitted
2
while((IFG2 & UCB0TXIFG) == 0); 
3
UCB0TXBUF = data;

Danke noch mal für die freundlichen kompetenten Antworten.

von Clemens L. (c_l)


Lesenswert?

Diese Zeilen finde ich so nicht in SLAU144J.

Egal; wenn du nur das B0-Modul benutzt, interessieren dich die A0-Bits 
nicht.

von Andreas V. (Firma: IGL) (andreas_va)


Lesenswert?

Sorry es hätte wie folgt heißen müssen:
1
              
2
while((IFG2 & UCA0RXIFG) == 0);     
3
receivedByte = UCA0RXBUF;

Ich muss meinen gesamten Code noch mal auf solche Fehler untersuchen. 
Die Benamung der Register macht mich kirre.

Ich stelle alles auf A0 um!

Die Zeilen habe ich aus einem Master Code:

https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/510533

von Clemens L. (c_l)


Lesenswert?

Wenn du RXIFG/TXIFG schon abgefragt hast, brauchst du es nicht noch 
einmal zu machen.

von Andreas V. (Firma: IGL) (andreas_va)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

gestern abend habe ich den Test-Code noch einmal umgeschrieben. Kaum 
macht man es richtig und es funktioniert!

Natürlich ist der Code nicht perfekt, aber darauf kann man aufbauen.

Danke noch einmal an Clemens der mich geduldig geleitet hat.

Im Anhang seht ihr das Resultat des Terminals als Bild. Die Ergebnisse 
des Logic-Analyzers bzw. des Oszi's werde ich dann noch mal posten.

1
#include "msp430g2553.h"
2
3
unsigned char *pRXData;
4
unsigned char *pTXData;
5
unsigned char bufferTX[16] = {"Hello World!\0"};
6
unsigned char bufferRX[16] = {"            "};
7
unsigned char TxByteCtr;
8
unsigned char RxByteCtr;
9
10
11
void main(void)
12
{
13
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
14
  P1DIR |= BIT0;                            // P1.0 output
15
  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
16
  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
17
  
18
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
19
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
20
  UCB0I2COA = 0x48;                         // Own Address is 048h
21
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
22
  UCB0I2CIE |= UCSTTIE;                     // Enable STT interrupt
23
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
24
// TXData = 0xff;                            // Used to hold TX data
25
  IE2 |= UCB0RXIE;                          // Enable RX interrupt //
26
    
27
  while (1)
28
  {
29
    pTXData = (unsigned char *)bufferTX;
30
    pRXData = (unsigned char *)bufferRX;
31
    TxByteCtr = 0;
32
    RxByteCtr = 0;
33
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts   
34
    __no_operation(); ///////
35
  }
36
}
37
38
//#define UCSCLLOW            (0x40)    /* SCL low */
39
//#define UCGC                (0x20)    /* General Call address received Flag */
40
//#define UCBBUSY             (0x10)    /* Bus Busy Flag */
41
//#define UCNACKIFG           (0x08)    /* NAK Condition interrupt Flag */
42
//#define UCSTPIFG            (0x04)    /* STOP Condition interrupt Flag */
43
//#define UCSTTIFG            (0x02)    /* START Condition interrupt Flag */
44
//#define UCALIFG             (0x01)    /* Arbitration Lost interrupt Flag */
45
46
volatile unsigned char lastCharTx = 0;
47
volatile unsigned char lastCharRx = 0;
48
49
50
51
int testWhileRx = 0;
52
int testWhileTx = 0;
53
unsigned char state = 0;
54
int endReached = 0;
55
// USCI_B0 Data ISR
56
#pragma vector = USCIAB0TX_VECTOR // I2C DataVector = UART_TX_Vector
57
__interrupt void USCIA0_TX_USCIB0_I2C_DATA_ISR(void)
58
{   
59
    if (state &  UCTR)
60
    { 
61
      while((IFG2 & UCB0TXIFG) == 0){testWhileTx++;}
62
      if (endReached == 0)
63
      {
64
          lastCharTx = *pTXData;
65
          if (lastCharTx == 0)
66
          { 
67
            endReached = 1;
68
          }
69
      }
70
      else
71
      {
72
          lastCharTx = 0;
73
      }                  
74
      UCB0TXBUF = lastCharTx;      
75
      pTXData++;
76
      TxByteCtr++;  
77
      if (TxByteCtr >=  0x10)
78
      {
79
        pTXData = (unsigned char *)bufferTX;
80
        pRXData = (unsigned char *)bufferRX;   
81
        TxByteCtr = 0;
82
        RxByteCtr = 0;
83
        endReached = 0;
84
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0
85
      }
86
    }
87
    else 
88
    {      
89
      while((IFG2 & UCB0RXIFG) == 0){testWhileRx++;}     
90
      lastCharRx = UCB0RXBUF;
91
      *pRXData = lastCharRx;
92
      pRXData++;
93
      RxByteCtr++;
94
      
95
      if(RxByteCtr >= 0x10 || (lastCharRx == 0 && RxByteCtr > 1))
96
      {
97
        pTXData = (unsigned char *)bufferTX;
98
        pRXData = (unsigned char *)bufferRX;   
99
        TxByteCtr = 0;
100
        RxByteCtr = 0;
101
        endReached = 0; 
102
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0        
103
      }
104
    }
105
}
106
107
// USCI_B0 State ISR                 
108
#pragma vector = USCIAB0RX_VECTOR // I2C StateVector = UART_RX_Vector
109
__interrupt void USCIA0_RX_USCIB0_I2C_STATE_ISR(void)
110
{
111
  while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
112
  UCB0STAT &= ~UCSTTIFG;      // Clear start condition int flag     
113
  state = UCB0CTL1 & UCTR; 
114
}

von Andreas V. (Firma: IGL) (andreas_va)


Lesenswert?

P.S.: Clemens sagte es ja voraus.
1
while((IFG2 & UCB0RXIFG) == 0){testWhileRx++;}

und
1
while((IFG2 & UCB0TXIFG) == 0){testWhileTx++;}

kann man auch weglassen. Die wurden bislang noch nicht durchlaufen.

von Andreas V. (Firma: IGL) (andreas_va)


Angehängte Dateien:

Lesenswert?

Für alle die es interssiert habe ich meine ersten Daten vom Logic 
Analyzer als Datei angehängt.

Ich sendete 0xFF, 0x55, 0xAA, 0xCC und 0x33 jeweils in einer separaten 
Write-Anweisung an die Adresse 0x48.

Danke noch einmal!

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.