Forum: Mikrocontroller und Digitale Elektronik ATmega Regelung mit schneller Werteausgabe auf UART


von Markus (Gast)


Lesenswert?

Ok, ich versuch es mal verständlich zu erläutern, schonmal ein Danke 
fürs lesen.....


Aufbau :

Eine bewegliche Platte kann auf zwei Achsen (X,Y) über Servos gekippt 
werden. Auf der Platte ist ein IR Touchpad geschraubt, das Seriell 
(UART0 ,9600Baud) die Position einer rollenden Kugel an den ATmega644P 
übermittelt.
Das soll/wird über einen Software PID Regler geregelt. Ziel ist das 
halten der Kugel auf eine festgelegte Sollposition auf der Platte 
(Positionsregelung).

Zur Darstellung wurde eine VB.net Oberfläche entwickelt, die alle Werte 
der beiden Regler (x- & y-achse) darstellen kann, sowie die vom 
Controller Empfangenen Daten des Touchpads (Kugelposition etc.)
Die Daten werden vom Controller über den zweiten Serielle Port (UART1) 
an den PC gesendet.



Problem :

Wird die Hauptprogrammschleife des Controllers ausgeführt, ohne die 
Daten an den PC zu senden, reagiert die Regelung ( Das verstellen der 
Motoren in abhängigkeit zur Kugelposition ) annähernd Verzögerungsfrei.
Werden jedoch während der Hauptschleife zusätzlich noch die Daten an den 
PC versendet, merkt man einen deutliches "Motorstottern" , da der 
Controller wesentlich länger braucht, bis er wieder an den punkt 
gelangt, an dem er die aktuelle Kugelposition abfragen kann.Somit 
"verpennt" er Zwischenschritte die er eigentlich einstellen sollte, was 
das ganze sehr stotternd und unsauber macht.



Frage :

Wie kann dem ganzen abhilfe geschaffen werden. unsere überlegungen sind 
folgende :

- Den Prozessortakt erhöhen ( momentan auf 14,7456MHz )

- Die Baudrate erhöhen, damit die Daten schneller übertragen werden, 
brachte jedoch keine Besserung.

- Die Daten nicht alle auf einmal in der Schleife zu übertragen, sondern 
maximal 4 pro Schleife ( Das ist nicht Sinn des Programms, Datenansicht 
sollte möglichst Synchron laufen)

- Unwichtige Daten nur alle 10 Durchläufe senden ( Macht dann jeden 
10ten durchlauf Ruckend, is auch nicht gewünscht)

- Einen zweiten Controller verwenden, der sich um die PC-Kommunikation 
kümmert ( Das klingt doch schicker )

Und hier an diesem Punkt stellen sich jetzt diese Fragen :
Wie am besten die Kommunikation realisieren, so das der 
Regler-Controller in seiner Arbeit möglichst wenig gestört wird. SPI 
wird scheinbar ja auch durch den UART realisiert, fällt damit Flach.

Wie würde es mit einer parallelen Datenübertragung an zwei PORTS 
aussehen, die über Interrupts gesteuert werden würde ?
Pro zu übertragenden Signal wären das 5 bytes , Eins für die 
Funktionsnummer und 4 für einen FLOAT Wert.
Das wäre mit meinem jetzigen Wissenstand wohl die schnellste und 
unterbrechungsfreiste Methode. Was meint ihr dazu ?



Danke für eure Hilfe
gruss Maggus ;)

von Peter (Gast)


Lesenswert?

Da wir ja nicht wissen wie es bis jetzt gelöst ist (eventuell 
Quelltext?) kann man nur raten. Der µC ist bestimmt schnell genug für 
die aufgabe. Das senden Verbraucht keine nennenswerte rechenzeit. Das 
Problem wird vermutlich sein, das eine Zeichenkette auf einmal gesenden 
wird - in der Zeit regelt er vermutlich nicht.
Man könnte die Zeichen die zu senden sind, in einen art Ringbuffer 
stecken und über den Interrupt versenden lassen.

von Stefan E. (sternst)


Lesenswert?

> Wie kann dem ganzen abhilfe geschaffen werden.

Die seriellen Daten gar nicht in einer Schleife verschicken, sondern in 
einen Ringpuffer schreiben und Interrupt gesteuert verschicken.

von Karl H. (kbuchegg)


Lesenswert?

Markus wrote:
> Wie am besten die Kommunikation realisieren, so das der
> Regler-Controller in seiner Arbeit möglichst wenig gestört wird.

Nicht darauf warten, dass die UART Kommunikation abgeschlossen wird.
Sprich die UART Übertragung findet im Hintergrund interrupt gesteuert 
statt.

Einen 2-ten Prozessor einzusetzen klingt zwar schick, öffnet aber einen 
neuen Sack voll Problemen.

von aha (Gast)


Lesenswert?

Wie schnell ist denn der Regelungsalgorithmus ? Zykluszeit in ms ? Ist 
der in Float oder in Integer ? Die Kommunikation sollte nicht ausmachen, 
den die kann im hintergrund laufen. 9600 baud, und auch 38400 ist nicht 
alle welt. Da sollte noch einiges an Leistung uebrig sein.

von Markus (Gast)


Lesenswert?

Eben hat es klickt gemacht !


Damit der Empfangspuffer auf der PC Seite nicht volläuft, hab ich eine 
kleine Pausenzeit an das Ende der Sendefunktion angehängt. Die wartet 
knapp 40ms bis die nächste gesendet wird....

Wenn ich die rausmache dann funktioniert es ohne stottern (PATSCH!!X°)


Hier mal die SendeFunktion damit ihr versteht wovon ich rede
1
*
2
*  Sendet eine PC-Frame an die Windows Bedienoberfläche
3
*
4
*  Param:    FC    =  Funktionscode für Geräteindex in Hex , siehe Protokoll Beschreibung
5
*        value  =  Float Wert, der Gesendet werden soll
6
*
7
*/
8
void KOM_sendInfo(unsigned char FC,float value)
9
{
10
11
  fToSend.fDato = value;
12
13
  // Startbyte
14
  uart_putc(0x02);
15
  // Funktionscode
16
  uart_putc(FC);
17
  // Daten
18
  uart_putc(fToSend.byte.Drei); // Höchstes Byte
19
  uart_putc(fToSend.byte.Zwei);
20
  uart_putc(fToSend.byte.Eins);
21
  uart_putc(fToSend.byte.Null); //Niedrigstes Byte
22
  // Checksumme
23
  uart_putc(0xFF);
24
  // Endbyte 1
25
  uart_putc(0x03);
26
  // Endbyte 2
27
  uart_putc(0x00);
28
  
29
  // Kleine Pause, damit Windows zeit für die Datenauswertung hat, bevor
30
  // die Nächsten Daten geschickt werden. Ansonsten droht ein Empfangspuffer
31
  // Überlauf im Windows bei zu kleiner Wartezeit!
32
  
33
  //zeit(WAITFORWINDOWS);      // <------------ DER ÜBELTÄTER
34
35
36
}


Zum senden wird die Uart Bibliothek von Peter Fleury benutzt, die aus 
dem Internet bezogen werden kann
http://homepage.hispeed.ch/peterfleury/uartlibrary.zip

Wenn ich die Daten ungehindert an den PC senden lasse, läuft mir dessen 
Empfangspuffer voll.
Aber die Zwangspause in die Interruptroutine von Peter Fleury schreiben 
, wird wahrscheinlich das selbe problem hervorrufen wie am Anfang 
beschrieben.
Das abarbeiten der Hauptschleife wird jedesmal durch die pause 
unterbrochen werden, da Interruptroutinen ja nicht parallel zur 
Hauptschleife ablaufen.....

Ich denke ich sollte das Problem auf der Windows Seite versuchen zu 
lösen, durch z.b. effektiveren Quellcode.


Da sieht man mal wieder, das man manchmal andere braucht um seinen 
eigenen Kram zu verstehen :D

von Peter (Gast)


Lesenswert?

> Ich denke ich sollte das Problem auf der Windows Seite versuchen zu
> lösen, durch z.b. effektiveren Quellcode.
ja das denke auch auch, bei 9600baud sollte auch ein 286 sich noch 
langweile, vermutlich ist die Gui und das auslesen der RS232 in dem 
gleichen Thread.

Du hast aber immer noch ein kleines Problem mit

sendInfo

in der Zeit wo die 9Byte gesendet werden, kannt du nichts anders machen 
(die meisten zeit wartet der µC nur damit der puffer leer wird!) das 
könnte man zwar mit einer höhere baud rate etwas verbessern aber der 
Tipp von oben mit dem Ringpuffer ist die bessere lösung.

von Markus (Gast)


Lesenswert?

Gibt es dafür ein Beispiel ? ich kann mir nicht wirklich etwas 
vorstellen, ausswer das die Daten über die Hauptschleife gesammelt 
werden und am Ende verschickt werden.

ich glaube das Peter Fleury das genau so wie ihr meint auch gelöst 
hat...
1
/*************************************************************************
2
Function: uart_putc()
3
Purpose:  write byte to ringbuffer for transmitting via UART
4
Input:    byte to be transmitted
5
Returns:  none          
6
**************************************************************************/
7
void uart_putc(unsigned char data)
8
{
9
    unsigned char tmphead;
10
11
    
12
    tmphead  = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;
13
    
14
    while ( tmphead == UART_TxTail ){
15
        ;/* wait for free space in buffer */
16
    }
17
    
18
    UART_TxBuf[tmphead] = data;
19
    UART_TxHead = tmphead;
20
21
    /* enable UDRE interrupt */
22
    UART0_CONTROL    |= _BV(UART0_UDRIE);
23
24
}/* uart_putc */
25
26
27
28
SIGNAL(UART0_TRANSMIT_INTERRUPT)
29
/*************************************************************************
30
Function: UART Data Register Empty interrupt
31
Purpose:  called when the UART is ready to transmit the next byte
32
**************************************************************************/
33
{
34
    unsigned char tmptail;
35
36
    
37
    if ( UART_TxHead != UART_TxTail) {
38
        /* calculate and store new buffer index */
39
        tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
40
        UART_TxTail = tmptail;
41
        /* get one byte from buffer and write it to UART */
42
        UART0_DATA = UART_TxBuf[tmptail];  /* start transmission */
43
    }else{
44
        /* tx buffer empty, disable UDRE interrupt */
45
        UART0_CONTROL &= ~_BV(UART0_UDRIE);
46
    }
47
}


ich verstehe das jetz so :

1.In der "uart_putc" schiebe ich das zu sendende zeichen in den 
Ringpuffer sobald Platz vorhanden ist, und gibt durch das Aktivieren des 
interrupts das "SendeSignal" für den Controller.
2.Der Controller Sendet..
3.Wurde das zeichen gesendet wird "SIGNAL(UART0_TRANSMIT_INTERRUPT)" 
ausgelöst. hier wird geprüft ob noch zeichen zum sendenvorhanden sind. 
Falls ja, wird das nächste in das Senderegister geschrieben.


Meiner Meinung nach könntet ihr doch genau das gemeint haben, oder ?

von Peter (Gast)


Lesenswert?

ja stimmt, wird schon so gemacht.

Ich kannte den code von Peter Fleury nicht.

von Mike J. (Gast)


Lesenswert?

Der Code von Fleury ist schon sehr gut.
Mach den Ringbuffer doch größer (von 32byte auf 128) und erhöhe die 
Übertragungsrate.
An dem PC liegt es jedenfalls nicht.

von Peter (Gast)


Lesenswert?

@Mike J.
> Mach den Ringbuffer doch größer (von 32byte auf 128)

Das pauschal zu sagen macht überhaupt keinen sinn, es werden im Block 
9Byte gesendet diese Passen 3mal in den Puffer. Da wir nicht wissen wie 
oft diese Daten pro sekunden gesendet werden sollen kann man auch keine 
abschätzung der Puffer größe machen. Wenn mehr daten in den Puffer 
geschrieben werden als gesendet werden können, dann kann man in auch 
gleich auf 100Mbyte erhöhen und hat immer noch nichts gekonnt.

von Mike J. (Gast)


Lesenswert?

> Wenn mehr daten in den Puffer
> geschrieben werden als gesendet werden können, dann kann man in auch
> gleich auf 100Mbyte erhöhen und hat immer noch nichts gekonnt.
das ist klar, deshalb sagte ich auch:
>> und erhöhe die Übertragungsrate

Er soll das aber erst mal probieren, dann sieht er dass es daran lag und 
ist glücklich.
Wenn er weiß woran es liegt kann er an der Stelle ansetzen und seinen 
Code weiter optimieren. Also Puffer verkleinern, Übertragungsrate so 
weit senken wie möglich.

von Karl H. (kbuchegg)


Lesenswert?

Mike J. wrote:
>> Wenn mehr daten in den Puffer
>> geschrieben werden als gesendet werden können, dann kann man in auch
>> gleich auf 100Mbyte erhöhen und hat immer noch nichts gekonnt.
> das ist klar, deshalb sagte ich auch:
>>> und erhöhe die Übertragungsrate
>
> Er soll das aber erst mal probieren, dann sieht er dass es daran lag und
> ist glücklich.
> Wenn er weiß woran es liegt kann er an der Stelle ansetzen und seinen
> Code weiter optimieren. Also Puffer verkleinern, Übertragungsrate so
> weit senken wie möglich.

Und vor allen Dingen: überlegen in wie weit ein hohes Volumen sinnvoll 
ist.

Wenn die Daten letztendlich nur dazu dienen, einen menschlichen 
Beobachter zu informieren, reicht es völlig aus, wenn sich die Daten nur 
alle halbe Sekunde aktualisieren. Kein Mensch kann die Daten schneller 
mental verarbeiten.
Wenn an der anderen Seite natürlich ein weiterverarbeitendes Programm 
sitzt, kann das anderes aussehen.

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.