PIC an SciLab, einfach UART-Scope für Regler in ms Bereich
von
am 09.02.2016 um 23:24 (5275 Hits)
Bei der Optimierung von Regelungen ist eine grafische Aufzeichnung der Ist- und Reglerwerte sehr nützlich. Die einfachste Möglichkeit, Daten aus dem µC zum PC zu schicken ist wohl das UART, über das die meisten µC verfügen.
Man kann die Reglerwerte per UART aus dem Controller an den PC zwecks Visualisierung schicken, jedoch bei Abtastraten im Bereich von Milisekunden ist das nicht ganz so einfach. Mit blockierenden UART Funktionen und Versenden der Werte in Textform, würde der µC für mehrere ms nur mit dem Senden beschäftigt sein. Ich habe mir folgendermaßen geholfen:
1. Die UART Funktionen habe ich als asynchrone Funktionen ohne blockierende Warteschleifen wie while( ! PIR1bits.TXIF) implementiert. Die Schreibroutinen schreiben die zu sendenden Daten in Datenpuffer und brauchen dafür max. ein paar µs. Man kann so Daten aus zeitkritischens Programmteilen wie Regelungstask oder Interruptroutinen versenden, ohne den Programmfluss zu verzögern. Im Vergleich dazu würde eine synchrone Routine bei 9600Baud ca. 1ms pro Byte brauchen. Mit while( ! PIR1bits.TXIF); würde der µC für jedes char 1ms in der Warteschleife verplempern.
2. Die UART Senderoutine (execUartSending) wird aus der main_loop aufgerufen und legt die Daten auf UART wenn der frei ist. Sie muss nur im Schnitt oft genug aufgerufen werden damit der Sendepuffer nicht überläuft. Der Datendurchsatz ist durch die Baudrate begrenzt und das Senden von den zeitkritischen Programmteilen entkoppelt. Alle anderen Task-Routinen, die aus der main_loop quasi parallel aufgerufen werden, sollten ebenfalls Delay-Frei implementiert werden.
3. Um nochmal Zeit zu sparen werden die Daten binär, also z.B. als int8, int16 oder float Werte gesendet. Am anderen Ende der seriellen Übertragungsstrecke (momentan noch USB Kabel und irgendwann WiFi wenn ich dazu komme, mich mit ESP8266 anzufreunden) wartet die OpenSource Software SciLab auf die Binärdaten.Code:#define UARTBUFLEN 32 char UartSendBuf[UARTBUFLEN]; char BytesToSend = 0; char IdxWriteToSendBuf = 0; char IdxReadFromSendBuf = 0; void incBufIdx(char *pIdx){ *pIdx = *pIdx + 1; *pIdx %= UARTBUFLEN; } void AsyncSendStringUart(char* sendString){ while(*sendString != 0){ if (BytesToSend < UARTBUFLEN){ UartSendBuf[IdxWriteToSendBuf] = *sendString; BytesToSend++; incBufIdx(&IdxWriteToSendBuf); } sendString++; } } void AsyncSendData(void *pVal, uint8_t bCnt){ char *p = (char *)pVal; if (BytesToSend + bCnt <= UARTBUFLEN){ BytesToSend += bCnt; while(bCnt){ UartSendBuf[IdxWriteToSendBuf] = *p; incBufIdx(&IdxWriteToSendBuf); p++; bCnt--; } } } void AsyncSendByteUart(char val){ if (BytesToSend < UARTBUFLEN){ UartSendBuf[IdxWriteToSendBuf] = val; incBufIdx(&IdxWriteToSendBuf); BytesToSend++; } } void execUartSending(void){ if (BytesToSend){ if((PIR1bits.TXIF == 1) && (TXSTAbits.TRMT == 1)){ TXREG = UartSendBuf[IdxReadFromSendBuf]; incBufIdx(&IdxReadFromSendBuf); BytesToSend--; } } }
Die asynchronen Senderoutinen in der Reglertask schreiben die Daten in den Sendepuffer
4. In Scilab habe ich die "Serial Communication Toolbox" installiert, mit der ich die Daten des Roboters am virtuellen COM Port empfangen und auswerten kann. Das Scilab Script zum Empfangen und Darstellen der Daten im Scilab Grafik-Fenster habe ich angefügt. Damit hat man ein einfaches Scope für schnellere µC Regelungen, hier z.B. eine Aufzeichnung meines 4ms Linienreglers - Sensor und Reglerwerte - über 7s mit bewusst provozierter Schwingung durch zu starken I-Anteil.Code:AsyncSendByteUart(LinesensorVal); AsyncSendByteUart(RegValue); AsyncSendData(&RegValueID.value, 2);
Die Übertragung ginge natürlich auch deutlich schneller und länger - der Datendurchsatz ist theoretisch nur durch die Baudrate und die Aufzeichnungsdauer durch den PC-Speicher begrenzt.