Forum: Mikrocontroller und Digitale Elektronik UART verhindert I2C - MSP430


von Ulf S. (ulf2016)


Lesenswert?

Hallo zusammen,

in meinem derzeitigen Projekt möchte ich gerne von einem Sensor Daten 
via I2C empfangen und diese dann über die UART-Schnittstelle des 
MSP430G2553 weiterleiten. Beide Schnittstellen für sich stellen kein 
Problem da. Möchte man sie jedoch zusammen nutzen führt das bei mir zu 
erheblichen Komlikationen. Habe nun 2 Programme geschrieben. Bei dem 
einen werden alle Daten die der MSP via I2C zum Sensor schickt 
ignoriert, sobald Interrupts für die UART-Schnittstelle bzw mittels 
Timer ausführt werden. Komischerweise werden trotzdessen, dass laut dem 
Logicanalyser keine Adresse und kein Register zum Sensor gesendet wird, 
Daten nach dem Repeated Start vom Sensor zum MSP übertragen. Bei dem 
2ten Programm wird direkt nach dem Durchlaufen des Interrupts der 
Transmitdaten angehalten. Warum ist mir schleierhaft, da das I2C 
Programm einwandfrei funktioniert sofern die UART-Funktion NICHT 
durchlaufen wird.
Im Anhang befindet sich 2teres Programm, da dies nur minimal vom 
TI-Beispielcode abweicht und somit deutlich leichter verständlich sein 
sollte. Das Verschieben des I2C RX Interrupts in den USCIAB0RX_VECTOR 
ändert an dem Problem auch nichts.

Gruß Ulf
1
#include <msp430.h>
2
3
#define NUM_BYTES_TX 1                         // How many bytes?
4
#define NUM_BYTES_RX 2
5
6
int RXByteCtr, RPT_Flag = 0;                // enables repeated start when 1
7
volatile unsigned char RxBuffer[128];       // Allocate 128 byte of RAM
8
unsigned char *PTxData;                     // Pointer to TX data
9
unsigned char *PRxData;                     // Pointer to RX data
10
unsigned char TXByteCtr, RX = 0, UX = 0;
11
unsigned char MSData = 0x00;
12
13
void setupUART(void);
14
void Setup_TX(void);
15
void Setup_RX(void);
16
void Transmit(void);
17
void Receive(void);
18
19
int main(void)
20
{
21
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
22
  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
23
  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
24
25
  while(1){
26
27
  setupUART();
28
  UX = 0;
29
  RX = 0;
30
  //Transmit process
31
  Setup_TX();
32
  RPT_Flag = 1;
33
  Transmit();
34
  while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
35
36
  //Receive process
37
  Setup_RX();
38
  Receive();
39
  while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
40
  }
41
}
42
43
//---------------------------------------------------------------------------------------
44
45
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
46
#pragma vector = USCIAB0RX_VECTOR
47
__interrupt void USCIAB0RX_ISR(void)
48
#elif defined(__GNUC__)
49
void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) USCIAB0TX_ISR (void)
50
#else
51
#error Compiler not supported!
52
#endif
53
{
54
  if((UX==1) && (RX == 1) && (UCA0RXBUF == 'u'))
55
  {
56
    UX=0;
57
    IE2 &= ~UCA0RXIE;
58
    //IFG2 &=~(UCA0TXIFG + UCA0RXIFG);
59
    IFG2 &=~(UCA0TXIFG);
60
    __bic_SR_register_on_exit(CPUOFF);
61
  }
62
}
63
64
//-------------------------------------------------------------------------------
65
// The USCI_B0 data ISR is used to move received data from the I2C slave
66
// to the MSP430 memory. It is structured such that it can be used to receive
67
// any 2+ number of bytes by pre-loading RXByteCtr with the byte count.
68
//-------------------------------------------------------------------------------
69
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
70
#pragma vector = USCIAB0TX_VECTOR
71
__interrupt void USCIAB0TX_ISR(void)
72
#elif defined(__GNUC__)
73
void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) USCIAB0TX_ISR (void)
74
#else
75
#error Compiler not supported!
76
#endif
77
{
78
  if(RX == 1){                              // Master Recieve?
79
  RXByteCtr--;                              // Decrement RX byte counter
80
  if (RXByteCtr)
81
  {
82
    *PRxData++ = UCB0RXBUF;                 // Move RX data to address PRxData
83
    UCA0TXBUF = RxBuffer[0];
84
  }
85
  else
86
  {
87
    if(RPT_Flag == 0)
88
        UCB0CTL1 |= UCTXSTP;                // No Repeated Start: stop condition
89
      if(RPT_Flag == 1){                    // if Repeated Start: do nothing
90
        RPT_Flag = 0;
91
      }
92
    *PRxData = UCB0RXBUF;                   // Move final RX data to PRxData
93
    UCA0TXBUF = RxBuffer[1];
94
    __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
95
  }}
96
97
  else{                                     // Master Transmit
98
      if (TXByteCtr)                        // Check TX byte counter
99
  {
100
    UCB0TXBUF = MSData;                   // Load TX buffer
101
    TXByteCtr--;                            // Decrement TX byte counter
102
  }
103
  else
104
  {
105
    if(RPT_Flag == 1){
106
    RPT_Flag = 0;
107
    PTxData = &MSData;                      // TX array start address
108
    TXByteCtr = NUM_BYTES_TX;                  // Load TX byte counter
109
    __bic_SR_register_on_exit(CPUOFF);
110
    }
111
    else{
112
    UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
113
    IFG2 &= ~UCB0TXIFG;                     // Clear USCI_B0 TX int flag
114
    __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
115
    }
116
  }
117
 }
118
119
}
120
121
void Setup_TX(void){
122
  __disable_interrupt();
123
  RX = 0;
124
  IE2 &= ~UCB0RXIE;
125
  while (UCB0CTL1 & UCTXSTP);               // Ensure stop condition got sent// Disable RX interrupt
126
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
127
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
128
  UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
129
  UCB0BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
130
  UCB0BR1 = 0;
131
  UCB0I2CSA = 0x20;                         // Slave Address is 048h
132
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
133
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
134
}
135
void Setup_RX(void){
136
  __disable_interrupt();
137
  RX = 1;
138
  IE2 &= ~UCB0TXIE;
139
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
140
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
141
  UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
142
  UCB0BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
143
  UCB0BR1 = 0;
144
  UCB0I2CSA = 0x20;                         // Slave Address is 048h
145
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
146
  IE2 |= UCB0RXIE;                          // Enable RX interrupt
147
}
148
void Transmit(void){
149
    PTxData = &MSData;                      // TX array start address
150
    TXByteCtr = NUM_BYTES_TX;                  // Load TX byte counter
151
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
152
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
153
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
154
}
155
void Receive(void){
156
    PRxData = (unsigned char *)RxBuffer;    // Start of RX buffer
157
    RXByteCtr = NUM_BYTES_RX-1;              // Load RX byte counter
158
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
159
    UCB0CTL1 |= UCTXSTT;                    // I2C start condition
160
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
161
}
162
163
void setupUART(){
164
  IE2 &= ~UCA0RXIE;
165
  IE2 &= ~UCB0RXIE;
166
  IE2 &= ~UCA0TXIE;
167
  IE2 &= ~UCB0TXIE;
168
  UX = 1;
169
  RX = 1;
170
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings
171
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO
172
  DCOCTL = CALDCO_1MHZ;
173
  P1SEL = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
174
  P1SEL2 = BIT1 + BIT2 ;                    // P1.1 = RXD, P1.2=TXD
175
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
176
  UCA0BR0 = 104;                            // 1MHz 9600
177
  UCA0BR1 = 0;                              // 1MHz 9600
178
  UCA0MCTL = UCBRS0;                        // Modulation UCBRSx = 1
179
  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
180
  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
181
182
  __bis_SR_register(CPUOFF + GIE);       // Enter LPM0, interrupts enabled
183
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das Problem hier dürfte in der ziemlich unglücklichen Behandlung der 
UDCI-Interrupts durch TI liegen, die von der bei anderen 
MSP430-Varianten mit USCI_A/_B abweicht und so die Nutzung derer 
Beispielcodes verunmöglicht bzw. einigen Portierungsaufwand bei den 
Interruptroutinen verursacht.

Sieh Dir die Tabelle mit den Interruptvektoren im Datenblatt genau an:

Die Interruptflags UCA0RXIFG und UCB0RXIFG teilen sich einen 
Interruptvektor, das bedeutet, daß Du in der Interruptroutine beide 
Funktionen behandeln musst (USCI_A0/USCI_B0 receive und USCI_B0 I2C 
status).

Dasselbe ist bei den Interruptflags UCA0TXIFG und UCB0TXIFG der Fall, 
auch hier musst Du beide Funktionen in Deiner Interruptroutine behandeln 
(USCI_A0/USCI_B0 transmit und USCI_B0 I2C receive/transmit).


Ich habe noch keinen Beispielcode gesehen, der diese Problemstellung 
löst; den bislang ausführlichsten Beispielcode für die I2C-Schnittstelle 
gibt es in slva626b bzw. sluc583, auch wenn es da primär um die 
Ansteuerung des bq769x0 geht.

von Clemens L. (c_l)


Lesenswert?

Abschnitt 17.3.7.4 des User's Guide sagt:
> USCI_Ax and USCI_Bx share the same interrupt vectors. In I2C 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 I2C transmit and receive interrupt flags UCBxTXIFG and
> UCBxRXIFG from USCI_Bx and UCAxTXIFG from USCI_Ax share another
> interrupt vector.

(Siehe auch Seite 11 des Datenblatts.)

Beispiele für gemeinsame UART- und I²C-Interrupt-Handler gibt es als 
17-1 und 17-2 im User's Guide, aber nur als Assembler-Code. In C sähe es 
ungefähr so aus:
1
void uscia0_rx_uscib0_i2c_state_isr(void)
2
{
3
    if (IFG2 & UCA0RXIFG) {
4
        // read UCA0RXBUF - clears UCA0RXIFG
5
    } else {
6
        // decode I²C state changes ...
7
    }
8
}
9
10
void uscia0_tx_uscib0_i2c_data_isr(void)
11
{
12
    if (IFG2 & UCA0TXIFG) {
13
        // write UCA0TXBUF - clears UCA0TXIFG
14
    } else if (IFG2 & UCB0RXIFG) {
15
        // read UCB0RXBUF - clears UCB0RXIFG
16
    } else {
17
        // write UCB0TXBUF - clears UCB0TXIFG
18
    }
19
}

von Ulf S. (ulf2016)


Lesenswert?

Danke für die Antworten.

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.