Forum: Mikrocontroller und Digitale Elektronik MSP430 8 Werte von ADC über RS232 senden, geht nicht.


von Stefan S. (blacknighthawk)


Angehängte Dateien:

Lesenswert?

Hallo MSP430 Gemeinde,

ich bin gerade dabei meine eigne Ofensteuerung zu programmieren.
Platinen habe ich anfertigen lassen, die Hardware ist ok.

Die Beispiel- Codes von TI funktionieren mit ACLK=8MHz und 115200 Baud.

Das Auslesen der 8 ADC- Kanäle für Steuerfunktionen macht mir keine 
Sorgen.
Allerdings die Werte dieser 8 ADC- Kanäle über RS232 (usart) an meinen 
PC zu senden klappt hinten und vorne nicht.

Im Code ist eine Funktion, die die RS232 bedienen soll. Diese 
funktioniert wenigstens etwas, allerdings spuckt sie haufenweise Müll 
aus.

Ich habe es auch schon mit einer TX_ISR von TI versucht, damit kommt 
erst gar nichts raus.

Ich bekomme es einfach nicht hin, die 8 Arrays gescheit zu senden.
Mir ist auch noch nicht klar wie der TX- Interrupt überhaupt ausgelöst 
werden soll.

Es handelt sich um einen MSP430f167 und ich benutze Code Composer Studio
Version: 5.1.1.00031 und den MSP-FET430UIF

(Der zweite Code- Upload (rechts) ist der richtige, weiß nicht wie man 
den alten löscht)

Gruß

Stefan

von ./. (Gast)


Lesenswert?

RTF? Echt jetzt?

von Dennis (Gast)


Lesenswert?

Also einen Quelltext in RichText ist natürlich ungünstig. Hier mal 
eingefügt, damit man auch was sehen kann und nicht immer zwischen 
Browser und Word wechseln muss:
1
#include <msp430f167.h>
2
#include <intrinsics.h>
3
4
5
void Init_ADC(void); //ADC init
6
void Read_ADC(void); //Temp. T1 - T8 lesen
7
void SendUSART0 (void);
8
void init_XT(void); //Ext. Quarz 8MHz
9
10
static char Digit[8];
11
12
void main(void)
13
{
14
  init_XT();  //Ext. Quarz 8MHz und Stop WDT
15
16
//Definition des Port C für P3.4 bis P3.5 für RS232
17
18
    P3SEL |= 0x30;                    //Port C P3.4 bis P3.5 Aux Funktion
19
20
    ME1 |= UTXE0 + URXE0;           //USART0 Rx + Tx einschalten
21
    UCTL0 |= CHAR;                  //USART0 8N1
22
    UTCTL0 |= SSEL0;       //ACLK
23
    UBR00 = 0x45;                       // 115200 baud aus 8 MHz erzeugen
24
    UBR10 = 0x00;                       // 
25
    UMCTL0 = 0x00;                      // keine korrektur der division noetig
26
    UCTL0 &= ~SWRST;            // USART0 freigeben
27
    IE1 |= UTXIE0;              // TX-interrupt anschalten
28
    IFG1 &= ~UTXIFG0;            // initales interrupt-flag loeschen
29
30
__enable_interrupt();                   //Globale Intrrupts freigeben
31
32
  Init_ADC();                      //A/D- Wandler einstellen
33
34
  while (1)
35
  {
36
37
    Read_ADC();             //A/D- Wandler starten
38
39
  //  SendUSART0 ();  // Diese Funktion bringt nur Müll raus !!!
40
41
  
42
43
  }
44
}
45
46
//Ab hier Funktionen
47
48
void init_XT(void) // Ext. Quarz 8MhZ
49
{
50
  unsigned int i;
51
  WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
52
  BCSCTL1 |= XTS;                       // ACLK = LFXT1 = HF XTAL
53
  do
54
  {
55
    IFG1 &= ~OFIFG;                       // Clear OSCFault flag
56
    for (i = 0xFF; i > 0; i--);           // Time for flag to set
57
  }
58
  while ((IFG1 & OFIFG) == OFIFG);      // OSCFault flag still set?
59
  BCSCTL2 |= SELM1+SELM0;               // MCLK = LFXT1 (safe)
60
}
61
62
63
64
65
66
/*                           Diese Funktion brigt nur Müll raus!!!
67
void SendUSART0 (void)
68
{
69
  char i, j;
70
71
  for (j=0; j<8; j++)
72
  {
73
  for (i=0; i < sizeof Digit[j]; i++)
74
    U0TXBUF = Digit[j];
75
76
  }
77
}
78
*/
79
80
void Init_ADC(void)
81
{
82
  P6SEL = 0xFF;                       // Analogeingänge A0 - A7 (P6.0 - P6.7)
83
  ADC12CTL0 = ADC12ON + MSC + SHT0_12;
84
  ADC12CTL1 = ADC12DIV_7 + SHP + CONSEQ_1;
85
  ADC12MCTL0 = SREF_2 + INCH_0;       // Wert von A0 landet in ADC12MEM0
86
  ADC12MCTL1 = SREF_2 + INCH_1;       // Wert von A1 landet in ADC12MEM1
87
  ADC12MCTL2 = SREF_2 + INCH_2;       // Wert von A2 landet in ADC12MEM2
88
  ADC12MCTL3 = SREF_2 + INCH_3;       // Wert von A3 landet in ADC12MEM3
89
  ADC12MCTL4 = SREF_2 + INCH_4;       // Wert von A4 landet in ADC12MEM4
90
  ADC12MCTL5 = SREF_2 + INCH_5;       // Wert von A5 landet in ADC12MEM5
91
  ADC12MCTL6 = SREF_2 + INCH_6;       // Wert von A6 landet in ADC12MEM6
92
  ADC12MCTL7 = SREF_2 + INCH_7 + EOS; // Wert von A7 landet in ADC12MEM7
93
  ADC12IE = 0x02;                     // ADC Interrupts freigeben
94
  ADC12CTL0 |= ENC;                   // Konvertierung freigenben
95
}
96
97
void Read_ADC(void)
98
{
99
  ADC12CTL0 |= ADC12SC;               // Neue Konvertierung starten
100
}
101
102
//Ab hier ISR's
103
104
#pragma vector=ADC12_VECTOR
105
__interrupt void ADC12ISR (void)
106
{
107
108
  Digit[0] = ADC12MEM0;
109
  Digit[1] = ADC12MEM1;
110
  Digit[2] = ADC12MEM2;
111
  Digit[3] = ADC12MEM3;
112
  Digit[4] = ADC12MEM4;
113
  Digit[5] = ADC12MEM5;
114
  Digit[6] = ADC12MEM6;
115
  Digit[7] = ADC12MEM7;
116
117
}
118
119
// UART0 TX ISR                 Diese ISR bringt nichts raus!!!
120
#pragma vector=USART0TX_VECTOR
121
__interrupt void usart0_tx (void)
122
{
123
  char i, j;
124
  for (j=0; j<2; j++)
125
  {
126
  for (i=0; i < sizeof Digit[j]; i++)
127
    TXBUF0 = Digit[j];
128
129
  }
130
}

von Dennis (Gast)


Lesenswert?

Jetzt zu deinem Code:

Das hier:

Dennis schrieb:
> while (1)
>   {
>     Read_ADC();             //A/D- Wandler starten
>   }

Und das hier:

Dennis schrieb:
> void Read_ADC(void)
> {
>   ADC12CTL0 |= ADC12SC;               // Neue Konvertierung starten
> }

geht natürlich schonmal garnicht! Du kannst ja nicht immer eine neue 
Wandlung starten, ohne zu prüfen, ob die alte überhaupt fertig ist. Die 
neue Wandlung startest du wenn am Ende der ISR oder irgendwo anders, 
vorausgesetzt du weißt, dass der ADC wenigstens mal die Zeit hatte in 
Ruhe zu wandeln.

Das hier:

Dennis schrieb:
> static char Digit[8];

Und das hier:

Dennis schrieb:
> Digit[0] = ADC12MEM0;

geht natürlich auch überhaupt nicht! Erstens deklariert man Variablen, 
die in der ISR manipuliert werden als volatile, aber das ist bei dem 
folgenden Patzer noch relativ belanglos...der AD-Wandler ist doch ein 
12-Bit-Wandler. Wie willst du das Ergebnis in einen 8-Bit char 
quetschen? Der läuft dir doch über.

Dennis schrieb:
> Diese Funktion brigt nur Müll raus!!!
> void SendUSART0 (void)
> {
>   char i, j;
>
>   for (j=0; j<8; j++)
>   {
>   for (i=0; i < sizeof Digit[j]; i++)
>     U0TXBUF = Digit[j];
>
>   }
> }
> */

Das kann auch nicht funktionieren, da du deinen eigenen Puffer immer 
wieder überschreibst.

Dennis schrieb:
> // UART0 TX ISR                 Diese ISR bringt nichts raus!!!
> #pragma vector=USART0TX_VECTOR
> __interrupt void usart0_tx (void)
> {
>   char i, j;
>   for (j=0; j<2; j++)
>   {
>   for (i=0; i < sizeof Digit[j]; i++)
>     TXBUF0 = Digit[j];
>
>   }
> }

Das ist genauso falsch - hier passiert dasselbe wie in der main, dass du 
das im Interrupt ausführst, macht es nicht besser. Du musst EIN Zeichen 
senden und in der ISR, welche  nach dem "TX-Buffer-leer"-Interrupt 
kommt, das nächste. Und während eines gesendet wird, machste was 
anderes, bis der Interrupt wieder eintritt.

Dennis schrieb:
> ADC12MCTL0 = SREF_2

Hast du eine externe Referenz angeschlossen?

Dennis schrieb:
> ADC12IE = 0x02;

Wieso den Interrupt bei 0x02? Du samplest doch noch bis MEM7.


So, das ist erstmal ein kleiner Ausschnitt deiner Fehler im Quelltext. 
Du solltest vielleicht auch mal die Hardware reinstellen.

Gruß, Dennis

von Stefan S. (blacknighthawk)


Lesenswert?

Vielen Dank Dennis,

da habe ich erst mal was zum knabbern.

Das mit der externen Referenz ist richtig so.
Die A/D- Wandlung läuft, jedenfalls für A0 und A1. Mehr habe ich bis 
jetzt noch nicht benutzt um zwei Relais anzusteuern.

Das mit dem 8-bit Char ist natürlich peinlich, kann ich denn alles über 
uart senden? Also auch int, unsigned int usw.?

Aber wie veranlasse ich denn in der Funktion oder in der ISR dass der 
Buffer auch mal ein Zeichen los schickt?
Also woher der Buffer weiß dass er senden soll ist mir noch ein Rätsel.

Heißt das ich muss in der Schleife bei jedem Durchgang prüfen ob der 
Buffer bereit ist und dann sollte das schon mal gehen?

In den Coder- Beispielen von TI wird meisten im Loop gearbeitet und 
daraus einen geeigneten Code für mich abzuleiten ist mit, wie du sehen 
kannst, leider noch nicht gelungen.

Ich werde mich gegen Abend mal wieder dran setzten und mir meinen Kopf 
zerbrechen.


Gruß

Stefan

von Dennis (Gast)


Lesenswert?

Stefan S. schrieb:
> Das mit dem 8-bit Char ist natürlich peinlich, kann ich denn alles über
> uart senden? Also auch int, unsigned int usw.?

Über UART sendest du nur 1 Zeichen. Bei einer Zahl von beispielsweise 
1276 sendest du '1', '2', '7' und '6' hintereinander. Das als ganze Zahl 
kannst du nicht versenden. Du musst die Zahl also vorher zerlegen:
1
zahl = 1276
2
3
char digits[4];
4
5
digits[0] = zahl / 1000
6
zahl %= 1000
7
digits[1] = zahl / 100
8
zahl %= 100
9
digits[2] = zahl / 10
10
digits[3] = zahl % 10
Deine digits kannste dann versenden.

Stefan S. schrieb:
> Aber wie veranlasse ich denn in der Funktion oder in der ISR dass der
> Buffer auch mal ein Zeichen los schickt?

Du kannst das erste Zeichen direkt in den Puffer schreiben, worauf der 
USCI anfängt, die Daten zu senden und in der Zeit das Flag löscht, 
welches anzeigt, dass eine Transaktion vorliegt. Ist der Puffer 
vollständig gesendet, dann wird das Flag gesetzt und die ISR wird 
aufgerufen. In der ISR schreibst du das nächste Zeichen in den Puffer 
usw.

Du kannst das alles auch in der main oder in irgendeiner anderen 
Funktion machen, als Loop also. Aber dann musst du halt immer das Flag 
abfragen:
1
sende_puffer
2
{
3
  for( i = 0; i < PUFFERGROESSE; i++ )
4
  {
5
    UART_TX_BUFFER = puffer[i];
6
    while( UART_BUSY ) {}
7
  }
8
}
Aber das ist natürlich die unelegante Lösung, wenn es schon extra IFGs 
gibt.
[/c]

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dennis schrieb:
> Deine digits kannste dann versenden.

Wenn sie in einem Terminalprogramm o.ä. lesbar sein sollen, sollte 
allerdings aus den numerischen Werten 0-9 auch die 
ASCII-Zeichendarstellung '0'-'9' gemacht werden, was durch Addieren von 
'0' (man beachte die Hochkommata!) geht.

von Dennis (Gast)


Lesenswert?

Stimmt, vergessen...

von Stefan S. (blacknighthawk)


Lesenswert?

Vielen Dank,

Digit_1 wird schon mal gesendet, der Rest folgt morgen, ist schon spät.
Allerdings alles zusammenhängend gesendet, also: 123412341234.
Wo und wie muss ich denn das Steuerzeichen für CR oder LF unterbringen?

Hab's jetzt mal wie folgt geändert:
1
static volatile char buffer_1[4], buffer_2[4], buffer_3[4], buffer_4[4], buffer_5[4], buffer_6[4], buffer_7[4], buffer_8[4];
2
int Digit_1, Digit_2, Digit_3, Digit_4, Digit_5, Digit_6, Digit_7, Digit_8;

und
1
while (1)
2
  {
3
4
    Read_ADC();
5
6
7
    buffer_1[0] = Digit_1 / 1000+'0';
8
    Digit_1 %= 1000;
9
    buffer_1[1] = Digit_1 / 100+'0';
10
    Digit_1 %= 100;
11
    buffer_1[2] = Digit_1 / 10+'0';
12
    buffer_1[3] = Digit_1 % 10+'0';
13
14
15
16
    SendUSART0 ();

und auch hier
1
void SendUSART0 (void)
2
{
3
  char i;
4
5
  for (i=0; i < 4; i++)
6
    U0TXBUF = buffer_1[i];
7
  while (!(IFG1 & UTXIFG0));
8
9
10
}

und natürlich hier
1
void Read_ADC(void)
2
{
3
  while (ADC12CTL1 & ADC12BUSY);  //ADC noch beschäftigt?
4
  ADC12CTL0 |= ADC12SC;   // Neue Konvertierung starten
5
}


Wie schaffe ich es denn nun in der ISR zuerst ein Zeichen zu senden um 
den Iterrupt auszulösen und dann in der ISR die restlichen Zeichen zu 
senden?
Wenn ich das so richtig verstanden habe.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Stefan S. schrieb:
> Allerdings alles zusammenhängend gesendet, also: 123412341234.
> Wo und wie muss ich denn das Steuerzeichen für CR oder LF unterbringen?

Wie wäre es zwischen den einzelnen Werten?

von Dietrich L. (dietrichl)


Lesenswert?

Stefan S. schrieb:
> Wie schaffe ich es denn nun in der ISR zuerst ein Zeichen zu senden um
> den Iterrupt auszulösen und dann in der ISR die restlichen Zeichen zu
> senden?

Das geht natürlich nicht. Das erste Zeichen muss in der main() gesendet 
werden.

Gruß Dietrich

von Stefan S. (blacknighthawk)


Lesenswert?

@Rufus T.
Dass ich das Zeichen zwischen den Werten unterbringen muss ist mir schon 
klar. Aber wo und wie hänge ich rein technisch das /n oder /r dran?
Egal wo ich es versucht habe kommt anschließend nur noch Müll raus, oder 
die Zeichen /n und /r werden mit als Text ausgegeben.

@Dietrich
Vielen Dank, das dachte ich mir schon so halber.
Allerdings macht das die ISR für mich wieder etwas unattraktiver da 
nicht die gesamte Ausgabe in einem Block steht.


Ansonsten versuche ich jetzt mal meine ADC- Digits etwas zu glätten, die 
springen so ca. 50 Digits auf und ab.

Dann suche ich noch nach einer geeigneten Funktion um meine Digits auch 
in °C umzurechnen, dafür gibt es aber wohl schon genug Beispiele.


Viele Grüße

Stefan

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Stefan S. schrieb:
> Aber wo und wie hänge ich rein technisch das /n oder /r dran?

Wenn Du es richtig schreiben würdest, käme auch was anderes heraus. Es 
heißt \r \n.

Ansonsten müsstest Du schon den Quelltext zeigen, in dem Du das versucht 
hast, sonst kann man Dir nicht mehr sagen, als daß der Fehler in Zeile 
42 liegt.

von Stefan S. (blacknighthawk)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Wenn Du es richtig schreiben würdest, käme auch was anderes heraus. Es
> heißt \r \n.

Kaum macht man es richtig und schon geht es.
Danke.

Aber mal noch was anderes, hat vielleicht noch jemand eine bessere Idee 
zum Thema ADC- Werte mitteln?
1
    for (i_avg = 0; i_avg < 50; i_avg++)
2
    {
3
      Read_ADC();
4
      avg_1[i_avg] = Digit_1;
5
      avg_2[i_avg] = Digit_2;
6
7
8
9
    }
10
11
    for (i_avg = 0; i_avg < 50; i_avg++)
12
    {
13
      Digit_1 = Digit_1 + avg_1[i_avg];
14
      Digit_2 = Digit_2 + avg_2[i_avg];
15
    }
16
17
    Digit_1 = Digit_1 / 50;
18
    Digit_2 = Digit_2 / 50;

Ich finde das etwas unelegant und es verlangsamt auch alles etwas.
Den Quelltext werde ich bei Gelegenheit mal komplett hier einstellen, 
muss nur noch etwas Ordnung machen und die ganzen Fehlversuche wieder 
entfernen.

von Helmut L. (helmi1)


Lesenswert?

Stefan S. schrieb:
> Allerdings macht das die ISR für mich wieder etwas unattraktiver da
> nicht die gesamte Ausgabe in einem Block steht.

Das geht aber trotzdem. Du must nur das Interruptflag in der main 
ausloesen.
Da mit stöst du die Interruptfunktion an und die macht den Rest.

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.