Forum: Mikrocontroller und Digitale Elektronik MSP430 über UART Daten senden


von Wolle G. (wolleg)


Angehängte Dateien:

Lesenswert?

Mir geht es so ähnlich, wie im
Beitrag "MSP430 UART senden"  angefragt wurde.
Am Ausgang P3.4 UTXD0 soll zunächst im Abstand eines Timertaktes ein 
Byte gesendet werden. Leider kommt an P3.4, gemessen mit einem Oszi, 
nichts an. (nur LED blinkt im Timertakt) Der Ausgang bleibt auf 3V 
stehen.
Kann jemand meinen Fehler erkennen? Vermutlich wird der Sendeprozess 
nicht angestoßen?
 Verwendet wird der MSP430F169

: Bearbeitet durch User
von Gerald K. (geku)


Lesenswert?

Ist das Port von GPIO auf UART umgestellt?

Wer generiert den ersten Tx-Interrupt? => versuche das erste Zeichen in 
main() auszugeben (oberbalb von for(;;)

: Bearbeitet durch User
von Wolle G. (wolleg)


Lesenswert?

Gerald K. schrieb:
> Ist das Port von GPIO auf UART umgestellt?
Wie ist das gemeint? Mein gesamtes Programm ist hier: 
0167-iar-UART_DS04_V01.c dargestellt

von Gerald K. (geku)


Lesenswert?

Ich sehe es ist in der Programmzeile 60 durchgeführt. Also kann man den 
ersten Punkt abhaken.

Zum zweiten Punkt: der Interrupt ist  freigegben (Code Zeile 68), aber 
wer löst den Tx Interrupt aus?

<<USART Transmit Interrupt Operation
The UTXIFGx interrupt flag is set by the transmitter to indicate that 
UxTXBUF
is ready to accept another character. An interrupt request is generated 
if
UTXIEx and GIE are also set. UTXIFGx is automatically reset if the 
interrupt
request is serviced or if a character is written to UxTXBUF.
UTXIFGx is set after a PUC or when SWRST = 1. UTXIEx is reset after a 
PUC
or when SWRST = 1. The operation is shown is Figure 13−10.>>

Quelle: https://www.ti.com/lit/pdf/slau049

13-29:  UTXIFG1 bzw. 2 muss gesetzt werden um eiinen TX Interrupt 
auszulösen. Normalerweise passiert das mit dem Ende des Stopbits des 
zuletzt gesendeten Bytes. Wurde nichts gesendet, dann muss dieses Bit 
gesetzt werden.

: Bearbeitet durch User
von Wolle G. (wolleg)


Lesenswert?

Gerald K. schrieb:
> 13-29:  UTXIFG1 bzw. 2 muss gesetzt werden um eiinen TX Interrupt
> auszulösen. Normalerweise passiert das mit dem Ende des Stopbits des
> zuletzt gesendeten Bytes. Wurde nichts gesendet, dann muss dieses Bit
> gesetzt werden.
Und wie würden die entsprechenden Zeilen und Befehle aussehen.
"txwert" ist bei mir das Byte, welches gesendet werden soll.
Dieser Wert wird bei jeden Timerdurchlauf gemäß txwert=txwert+1;
"berechnet" und soll zunächst mit dem Oszi an P3.4 nachweisbar sein, 
also gesendet werden.
Später sollen mal Messwerte über UART gesendet werden.

von Gerald K. (geku)


Lesenswert?

Wolle G. schrieb:
> Und wie würden die entsprechenden Zeilen und Befehle aussehen.
> "txwert" ist bei mir das Byte, welches gesendet werden soll.
1
int main(void) { 
2
  WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer 
3
  Timer_einstellen();
4
  UART_einstellen();
5
  P1DIR |= BIT7; //LED grün 
6
// P2DIR |= BIT0; //LED rot 
7
8
9
10
  TXBUF1 = txwert; /* erstes Zeichen in main() verschicken (damit wird der Tx-Interrupt angestoßen), nächste Zeichen werden über Interrupt geholt */
11
12
13
14
  for(;;); 
15
}

: Bearbeitet durch User
von Wolle G. (wolleg)


Angehängte Dateien:

Lesenswert?

Danke.
Jetzt erscheint das Datenbyte an P3.4 UTXD0 (mit Oszi gemessen)

Die Zeile: TXBUF0=txwert;     // alt: TXBUF1=txwert;

steht jetzt im Timer (nicht in main) und

// UART0 TX ISR
#pragma vector=UART0TX_VECTOR
__interrupt void usart1_tx (void)
{
}
bleibt leer.
siehe Anhang

Im nächsten Schritt muss jetzt noch der Datenempfang zum Laufen gebracht 
werden.

von Gerald K. (geku)


Lesenswert?

Wolle G. schrieb:
> steht jetzt im Timer (nicht in main)

Ist auch eine Möglichkeit der Realisierung. Aber Achtung, der Ticker 
darf nicht schneller als die Zeit für das Ausgeben des Bytes sein.

Also Senden ohne Tx Interrupt im Timerinterrupt, dann würde ich den Tx 
Interrupt sperren auch sperren, dann braucht es keine 
Tx-Interruptroutine.

: Bearbeitet durch User
von Wolle G. (wolleg)



Lesenswert?

Zwischenzeitlich funktioniert auch das Senden von 8 Bit breiten Zeichen.
Es sollen aber auch mehrere verschiedene 16Bit breite Messwerte 
übertragen werden. (z. B. Temperatur 1, Temperatur 2 usw.)
Man könnte vor dem Senden den Messwert in ein oberes und unteres Byte 
zerlegen und dann diese zwei Bytes senden.
Das Zerlegen und Zusammenfügen ist nicht das Problem. Aber wie müssten 
die Programme aussehen, damit die richtigen Bytes gesendet bzw. richtig 
empfangen und zusammengesetzt werden können?
Im Anhang meine aktuellen Programme.

von Gerald K. (geku)


Lesenswert?

Wenn man den Tx-Buffer in der Timerroutine befüllt, bedarf es keines 
Tx-Interrupts.
Der Tx-Interrupt (buffer empty) kann man die gesamte Laufzeit auf 
disabled belassen.

IE1 |= UTXIE0 + URXIE0;  ==>  IE1 |= URXIE0

Wenn nicht, dann wird nach jedem versendeten Zeichen die leere 
Interruptroutine usart1_tx ( ) aufgerufen. Fällt nicht auf, kostet aber 
Rechenzeit.

Beitrag #6723556 wurde vom Autor gelöscht.
Beitrag #6723572 wurde vom Autor gelöscht.
von Wolle G. (wolleg)


Angehängte Dateien:

Lesenswert?

@Gerald K.
Zwischenzeitlich hatte ich noch ein weiteres Thema zur 16Bit-übertragung 
aufgemacht.
(Beitrag "MSP430—über UART 16Bit Daten übertragen")

Den noch nicht richtig funktionierenden Programmentwurf hänge ich hier 
mal an.
Kannst Du meine Fehler entdecken?
Zur Erklärung:
Mit dem ersten Byte (0xAA) soll dem Empfänger signalisiert werden, dass 
im Anschluss ein neues „Datentelegramm“ kommt.
Danach soll der Interupt abgeschaltet werden, damit nicht laufend weiter 
gesendet wird.
Wenn im Timer wieder der Interupt aktiviert wird, dann wird ein neues 
Telegramm gesendet.
Leider sind am Oszi nur das 1.Byte (0xAA) und das letzte Byte zu sehen, 
obwohl es 3 Bytes sein sollten.
Hoffentlich habe ich mich verständlich ausgedrückt.

Es kann sich natürlich hier jeder beteiligen, der helfen möchte.

von Gerald K. (geku)


Lesenswert?

Wolle G. schrieb:
> Hoffentlich habe ich mich verständlich ausgedrückt.

Ja.

Die Tx-Interruptroutine kann so nicht funktionieren.

Was passiert in der Interruptroutine?

Es wird das erste Byte 0x50 in den Sendepuffer geschrieben und 
anschließend sofort dss zweite Byte 0xab nachgeschoben.

Der TX-Teil hat keinen Zwischenpuffer und so wird das zweite Byte durch 
das dritte überschrieben.

Der Sendevorgang eines Byte dauert wesentlich länger als zwei 
aufeinander folgende Schreibvorgänge in den Puffer.

: Bearbeitet durch User
von Gerald K. (geku)


Lesenswert?

**Lösung**  :

Ein Byte nach dem anderen in der Timerinterruptroutine ausgeben. Die 
zeitlichen Bedingungen habe ich in einem vorangegangenen Beitrag 
beschrieben.
Es ist kein Tx-Interrupt notwendig. Er sollte immer gesperrt (disabled) 
sein.

: Bearbeitet durch User
von Wolle G. (wolleg)


Lesenswert?

Gerald K. schrieb:
> **Lösung**  :
>
> Ein Byte nach dem anderen in der Timerinterruptroutine ausgeben. Die
> zeitlichen Bedingungen habe ich in einem vorangegangenen Beitrag
> beschrieben.
> Es ist kein Tx-Interrupt notwendig. Er sollte immer gesperrt (disabled)
> sein.
Meine Programmierkünste sind nur schwach ausgebildet. Deshalb suche ich 
hier Unterstützung.
Wie sollten die Programmzeilen konkret aussehen, damit alle Bytes 
gesendet werden?

von Gerald K. (geku)


Lesenswert?

Wolle G. schrieb:
> Wie sollten die Programmzeilen konkret aussehen, damit alle Bytes
> gesendet werden?

Die zu versendenden Bytes z.B. in main () in einen Sendpuffer schreiben:
1
int tx_cnt=0;
2
char tx_buffer[]={0xaa,0x50,0xab};

oder
1
char tx_buffer[3];
2
3
int tx_cnt=0;
4
tx_buffer [0]=0xaa;
5
tx_buffer [1]=0x50;
6
tx_buffer [2]=0xab;

Mit jedem Aufruf der Timer Interruptroutine wird Byte für Byte ausgeben:
1
if (tx_cnt<3)
2
{
3
   TXBUF0=tx_buffer[tx_cnt];
4
   tx_cnt++;
5
}

oder kürzer
1
if (tx_cnt<3)  TXBUF0=tx_buffer[tx_cnt++];

Bedingung Beitrag "Re: MSP430 über UART Daten senden" beachten!

: Bearbeitet durch User
von Wolle G. (wolleg)


Angehängte Dateien:

Lesenswert?

Gerald K. schrieb:
> Mit jedem Aufruf der Timer Interruptroutine wird Byte für Byte ausgeben:

Das habe ich etwas umgestrickt, da der Timertakt für andere Zwecke 
benötigt wird.
Deshalb wurden 2 neue Funktionen geschaffen ( hier „werte_feld_fuellen“ 
und „wert_senden“)
Diese Funktionen werden im Timer aufgerufen.
Mit __delay_cycles wird dafür gesorgt, dass sich  TXBUF0 nicht 
überschneidet. Das könnte man vielleicht noch besser lösen. (Abfrage, ob 
txbuffer[i]  leer ist.--> k.A. )

Für den Empfang der gesendeten Daten habe ich allerdings noch kein 
funktionierendes Programm.
Gedacht hatte ich, wie gestern schon geschrieben, an: „Mit dem ersten 
Byte (0xAA) soll dem Empfänger signalisiert werden, dass im Anschluss 
ein neues „Datentelegramm“ kommt.“
Wäre dies eine Möglichkeit? bzw. wie würde das programmiertechnisch 
aussehen.

von Gerald K. (geku)


Lesenswert?

Der Realisierungsansatz hat zwei große Schwachstellen:

1.)  Interruptroutinene sollten niemals Delayroutinen  z.B. 
__delay_cycles enthalten

2.)  Interruptroutinene sollten niemals Schleifenkonstrukte enthalten. 
Schleifen sollten außerhalb des Timerinterrupts geschlossen werden, dass 
heisst ein Iterationsschritt pro Interruptaufruf. Siehe 
Beitrag "Re: MSP430 über UART Daten senden"
Der Iterationsschritt wird durch tx_cnt gezählt und entspricht dem i  im 
Code 
https://www.mikrocontroller.net/attachment/520803/0167-iar_V05-UART_DS04_16bit.c.

von Gerald K. (geku)


Lesenswert?

1
// Timer A0 interrupt service routine 
2
#pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) 
3
{ 
4
    P1OUT ^= BIT7;
5
  //LED grün temp_1 = 0x1667; 
6
  // Messwert 1 Beisoiel werte_feld_fuellen(); wert_senden(); 
7
8
    if ((tx_cnt<3) && (U0TCTL&1))
9
   { 
10
        TXBUF0=tx_buffer[tx_cnt]; 
11
         tx_cnt++;
12
   }
13
}

1.) tx_buffer ist in main() zu befüllen

2.) tx_cnt ist eine gobale Variable und wird am Anfang auf 0 gesetzt.

3.) (U0TCTL&1) ist 1 wenn TXBUF0 leer ist

: Bearbeitet durch User
von Gerald K. (geku)


Angehängte Dateien:

Lesenswert?

Ich habe versuch den Sendevorang zu Impementieren. siehe example.c

Wichtig ist, dass Interruptroutinen möglichst schlank sind (kurze 
Ausführungszeit, wenig Code)

Die (zeitunkritische) Verarbeitung sollte sequenziell im Background 
erfolgen.

In den Interruptrountinen sollten keine Zeitverzögerungen und Schleifen 
eingebaut sein. Grund: wenn im Timerinterupt gewartet wird bis alle drei 
Bytes versendet sind, dann funktionieren Interrupts mit niedriger 
Priorität nicht.

Den Empfang würde ich wie das Versenden ausführen. RX-Interruptrounine 
einen genügend großen globalen Puffer rx_buffer[] zur Verfügung stellen. 
Ein globaler Empfangszähler rx_cnt wird in der Interruptroutine mit 
jedem empfangenen Byte hochgezählt. Ab einem besimmten Wert des 
Empfangszähler rx_cnt kann in der Interruptroutine ein Überlauf 
unterbunden werden.

Im Background können Empfangszählerstand und Epmfangspufferinhalt 
ausgewertet und weiterverwarbeitet werden.

Beispiel für Empfanginterrupt für 5 Zeichen:
1
int rx_cnt;
2
char rx_buffer[5];
3
4
// in main() while(1)
5
6
// Empfangsfreigabe zum richtigen Zeitpunk!
7
rx_cnt=0;
8
9
// Auswertung Empfang:
10
11
if (rx_cnt=4) // 5 Zeichen empfangen
12
{
13
    ... = rx_buffer[0]; // verfügen über RX-Pufferinhalte
14
}

Interruptroutine:
1
{
2
    if (rx_cnt<5)
3
    {
4
        rx_buffer[rx_cnt++]=RXBUF0;
5
    }
6
}

: Bearbeitet durch User
von Wolle G. (wolleg)



Lesenswert?

Aus dem „example.c „ Programm habe ich vorhandene Doppelfunktionen 
entfernt und danach als: "0167-iar_V07-UART_DS04_16bit.c" eingespielt.
Leider läuft der Timer, und damit das Programm, nicht. Aus meiner Sicht 
fehlt in main() die for(;;); -Schleife, o. ä.

Aus meinem ursprünglichen Programm habe ich __delay_cycles() entfernt 
und die Funktion: wert_senden() umgebaut. 
"0167-iar_V06_UART_DS04_16bit.c"
Mit dem Oszi sind jetzt alle 3 Bytes, wie gewünscht, zu sehen.
Mit dem Datenempfang bin ich noch nicht weiter gekommen.

von Gerald K. (geku)


Lesenswert?

Wolle G. schrieb:
> Aus meiner Sicht fehlt in main() die for(;;); -Schleife, o. ä.

Ich habe die for(;;) gegen while(1) Schleife ersetzt. Der Background 
sollte immer durch den Interrupt unterbrochen werden, außer der globale 
und/oder in diesem Fall der Timerinterrupt ist gesperrt.

Fehler ist definitiv in Zeile 72, wo der TX-Interrupt freigegen ist. 
Dieser sollte gesperrt sein. Daher Zeile auskommentieren. Die Ausgabe in 
den TX_buf erfolgt nicht in der TX-Interruptroutlne, sondern in der 
Timerinterruptroutine, daher TX-Interrupt in Zeie 72 nicht freigeben. 
(Sorry, habe ich bei meinem Beispiel übersehen)

Bei die früheren Implementierungen ist die leere TX-Interruptroutine 
unnötigerweise aufgrufen worden, dies hatte außer Rechenzeitvergeudung 
keine anderen negative Auswirkung.

Dadurch, dass die TX-Interruptroutine auskommentiert ist, greift der 
TX-Interrupt ins Leere und hängt sich der MC ein.

: Bearbeitet durch User
von Wolle G. (wolleg)



Lesenswert?

Gerald K. schrieb:
> Fehler ist definitiv in Zeile 72, wo der TX-Interrupt freigegen ist.
> Dieser sollte gesperrt sein. Daher Zeile auskommentieren.
Zunächst erst einmal vielen Dank für Deine Mühen.
Die Zeile 72 habe ich mal auskommentiert. Jetzt läuft der Timer 
(erkennbar an der blinkenden LED), aber am Oszi ist nach einem Neustart 
nur einmal ein Datenbyte (welches ?) zu sehen.
Im Anhang meine aktuellen Programme ( senden bzw empfangen). Sie laufen 
jetzt wunschgemäß. Ob es noch versteckte Ungereimtheiten gibt, ?
Da 0xAA (Anfangsmerkmal) auch in einem Datenbyte vorkommen könnte, 
sollte man, wie ich gelesen habe, die Daten in ASCII umwandeln 
(Beitrag "Problem: 16 Bit Variable über UART zu empfangen"
Mal sehen, ob ich das hinkriege.
Falls jemand in meinen Programmentwürfen noch schwer wiegende Mängel 
erkennt, her damit.
Ich werde es dann mal testen.

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.