- Labornetzteil AliExpress         
Seite 2 von 3 ErsteErste 123 LetzteLetzte
Ergebnis 11 bis 20 von 29

Thema: Senden und empfangen auf dem UART mit ISR kompatibel zur bisherigen RP6lib

  1. #11
    Erfahrener Benutzer Roboter Genie Avatar von SlyD
    Registriert seit
    27.11.2003
    Ort
    Paderborn
    Alter
    39
    Beiträge
    1.516
    Anzeige

    Praxistest und DIY Projekte
    Nett wäre es auf jeden Fall als Alternative. Wirklich vermisst habe ichs aber bislang auch nicht
    Ich hatte das damals absichtlich nicht verwendet - hast ja auch schon gesagt warum (Speicherplatz).

    Alle Beispielprogramme umändern halte ich für nicht sinnvoll.
    Einfach ein zwei neue besser dazu passende (wo man die Vorteile sieht) würde doch reichen.
    Wenn Du das tatsächlich umsetzt, mach evtl. #defines in den Code damit man zwischen beiden Varianten
    wählen kann - oder beides verwenden kann - auf der M256 hat man ja z.B. eh Platz ohne Ende da ist das egal.


    MfG,
    SlyD

  2. #12
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Hallo Dirk & Slyd
    zunächst auf Dirks Fragen bezogen:
    Der Hauptzweck...
    Also das muss jeder für sich entscheiden... was Hauptzweck ist. Ich glaube aber, das man mit dem RP6 mehr unternehmen kann als Linienfolger oder Bumpergesteuerte Rempelbots zu bauen. Je mehr Vernetzung, um so komplexer die Kommunikation.
    Durch die Aufteilung der Prozessoren...
    Richtig. Für die M256 sehe ich da auf Grund von ausreichendem Platz auch kein Problem, bei M32 und Base wirds etwas enger aber auch da nutzt das Slave Programm noch nicht 100% des Platzes, folglich ist auch da noch Platz für solche Änderungen vorhanden. Einzig mit der M128 und ihrem InterpreterC wirds kompliziert aber auch da denke ich, wäre sowas machbar. Ich hab zumindest selbst eine M128 und würde mich dann auch mal daran setzen wollen.
    Einverstanden?
    Richtig. Aber: Man braucht z.B. auf der M32 kein stdio Kanal für die Motorkontrolle aber trotzdem kann man mit Slave und Master von einer m32 aus die Motorgeschwindigkeit regeln.. woran liegt das? Abstrahiere das mal... weil ein Hardware Protokol (i2c) zur Übertragung von Informationen zwischen m32 und Base vorhanden ist! Dazu gehört weiterhin ein Treiberendstück welches auf der Base liegt.. also quasi der i2c-slave .. und der Master.. also quasi der Treiberkopf... betrachtet man nun slave, I2C und master als eine Einheit, so steuert die m32 durchaus den Motor. Ok, Jetzt noch mal zur Frage ob die m32 ein stdio kanal für die Motorsteuerung braucht... die Frage ist eher.. muss es unbedingt so kompliziert laufen wie bisher wenn man was von einem Board aufs andere kriegen will!
    Reizvoll wäre auch
    Habe ich mit dem vorherigen Punkt eigentlich schon angesprochen. Ja es wäre möglich, in einer 2.ten Ausbaustufe Datenströme als * File auch über die Boardgrenzen hinweg bereitstellen zu können weil es die Printf Funktion nicht interessiert ob da einfach nur "while (!(UCSRA & (1<<UDRE))); UDR = (uint8_t)ch;" oder ein ganzes i2c-master/slave System hinter der "putchar" steckt.
    Was meinst du mit ...
    Du hast dir ein Teil des Punktes bereits selbst beantwortet. Überleg aber mal wie eine Ausgabe von Infos als Tabelle bisher ausschaut:

    * writeString_P("Toggle Bit:");
    * writeChar(rc5data.toggle_bit + '0');
    * writeString_P(" | Device Address:");
    * writeInteger(rc5data.device, DEC);
    * writeString_P(" | Key Code:");
    * writeInteger(rc5data.key_code, DEC);
    * writeChar('\n');


    7 Writes... das lässt sich mit printf in einer einzigen Zeile ausgeben! Das meine ich mit entschlacken.
    Was schätzt du...
    Also ich baue grade für mich die UART Lib dementsprechend um. Die UART Lib ist ja zumindest für die Base und M32 gleich.
    Ich werde die Demos sicher nicht anpassen aber ich bin auch dabei, die bisherigen Writes über Kapselfunktionen an printf zu leiten.
    Das sieht dann z.B. so aus:
    Code:
    void writeNStringP(const char *pstring)
    {
        printf_P(pstring);
    }
    void writeInteger(int16_t number, uint8_t base)
    {
        char str[17];
        printf("%s", itoa(number, str, base));
    }
    Ich möchte zunächst die UART LIB fertig bekommen bevor ich mich dann an die M32 LCD Funktionen setze.
    Weitere Ein/Ausgabe Ziele habe ich für mich noch nicht geplant, ich denke aber das es mit der Zeit auch mehr wird. Großes Fernziel ist tatsächlich aber auch die angesprochene Nutzung von Geräten anderer Boards per io Stream. Dazu muss ich aber auch noch mal den I2C Treiber überarbeiten und von registerbasierter Übertragung auf Stream umstellen.
    Für mich stellt sich eigentlich nur die Frage... mach ich das im stillen Kämmerlein so wie meine freeRTOS Geschichten (die übrigends klasse laufen) oder gibts da Interesse auch von anderen die IO Funktionen bezüglich.

    Übrigends ist die SendeISR aus dem Quellcode oben noch verbesserbar.
    Sie wird nach Ende der Übertragung noch ein mal angesprungen nur um die ISR abzuschalten.
    Ich hab sie jetzt so umgebaut, das mit dem letzten Zeichen im Buffer auch direkt die ISR abgeschaltet wird.
    Code:
    ISR(USART_UDRE_vect, ISR_BLOCK) { // wir werden angesprungen, also ist ein char im buffer
    	UDR = tx_buff.ring[tx_buff.tail]; // char aus buffer auf UDR schreiben
    	tx_buff.tail = (tx_buff.tail + 1) % UART_SEND_BUFFER_SIZE; // tail um eins hochzählen
    	if (tx_buff.head == tx_buff.tail) UCSRB &= ~(1 << UDRIE); // sendebuffer leer, isr aus schalten
    }
    Ein paar Kommentare sind auch noch hinzu gekommen.

    Gruß
    Geändert von RolfD (26.04.2014 um 10:23 Uhr)
    Sind Sie auch ambivalent?

  3. #13
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.803
    Hi RolfD,

    Ich werde die Demos sicher nicht anpassen aber ich bin auch dabei, die bisherigen Writes über Kapselfunktionen an printf zu leiten.
    Das finde ich eine gute Idee. Dann könnten die Demos und bisherige eigene Programme und Libs unverändert bleiben.

    Wenn du dich da dran machst und das hier komplett öffentlich machen willst, bin ich gern dabei.

    Die (Komplett-)Aufgabe wäre dann:

    1. RP6Base:
    RP6BaseLib -> IRCOMM, int. EEPROM

    2. RP6 CONTROL M32:
    RP6ControlLib -> LCD, SPI, int. EEPROM
    RP6Control_I2CMasterLib -> unverändert (wenn übergeordnete I2C-Funktionen unveränderte Funktion bieten)

    3. RP6Base UND M32 (TWI auch für M256):
    RP6uart -> UART
    RP6I2CmasterTWI -> I2C
    RP6I2CslaveTWI -> I2C

    3. RP6 M256 WiFi:
    RP6M256Lib -> LCD, SPI, int. EEPROM
    RP6M256uart -> UART
    RP6M256_WIFIlib -> WIFI UART
    SDC-Library -> SD-In-/Output (eigene Kapsel-Lib?)
    RP6M256_I2CMasterLib -> unverändert (wenn übergeordnete I2C-Funktionen unveränderte Funktion bieten)

    4. RP6 CCPRO M128:
    RP6CClib.cc, RP6CClib.cbas -> nicht möglich (Code-Interpreter)
    Gruß
    Dirk

  4. #14
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Hallo,
    also falls jemand Lust hat da mit rein zu gucken... kommt ja nen Feiertag und Regenwetter Ich poste mal mein aktuellen stand der Lib. Es hat sich bischen was getan.

    Die uart.c incl. grober Beschreibung im Dateikopf:
    Code:
    --- entfernt da neue Version ---
    und die dazu gehörende uart.h
    Code:
    --- entfernt da neue Version ---
    Das ist keine endgültige Fassung, es ist noch nicht alles geprüft und es sind noch Baustellen/Todos und vermutlich Fehler drin.
    Zum Testen und Probieren langts aber schon.
    @Dirk vielen Dank für die Aufstellung... scheint ein größeres Vorhaben zu werden

    Verbesserungen und Anregungen gern hier ins Forum...
    Wer z.B. Lust hat, eine Ein- und Ausgabe Funktin a la uart_getchar / uart_putchar für ein anderes Device als die UART zu schreiben .. immer her damit.
    Ich hab zwar keine M256 aber ich denke, das kriegen wir auch so angepasst.
    Möglich wäre z.B. ein Soundausgabe Treiber (sound_putchar) für den Lautsprecher, welcher Ton Ascii Sequenzen wie "c4,500,g3,200" an den Speaker ausgibt.
    Oder als HEX... Im Format will ich aber nicht vorgreifen... Nur so mal als Denkanstoß... keine Ahnung ob sowas schon existiert.

    Mit der Vorhandenen Lib wäre es dann per stdio möglich, direkt Töne von einem Terminal über den UART an den Lautsprecher aus zu geben.
    Quasi sowas wie ein "Proof of Concept".

    Nachtrag: https://www.roboternetz.de/community...l=1#post453225
    Die Kombination aus der uart_2 lib von void enterString(void) und uint8_t getInputLine(void) entspricht übrigends fast dem Ansatz mit meiner Ascii line editor Eingabezeile... also dem ASCII Modus bei getchar, nur fehlt dort die Reaktion auf \b was aber nachrüstbar wäre. Also ein Beispiel wie man eine ASCII Eingabezeile auf Anwendungsebene realisiert. Und schon 4 Jahre alt... Ist mir grade so aufgefallen als ich den code durchgesehen habe
    Gruß
    Geändert von RolfD (05.05.2014 um 03:15 Uhr) Grund: neue Version der lib im Folgebeitrag
    Sind Sie auch ambivalent?

  5. #15
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Es gibt Neuigkeiten. z.B. xon/xoff im Ascii mode.. und vieles mehr. Diverse Bugs sind auch behoben.
    Ich hab versucht, das ganze im File zu beschreiben und zu kommentieren. Für erfahrene Hasen hab
    ich viel zu viel geschrieben, für Newbies viel zu wenig. Die Lib ist noch nicht fertig bis aufs letzte
    Byte getestet aber sie tut es schon mal ganz gut. Leider meckert das Forum über zu große Dateien,
    ich werde die neuen Versionen einfach als File anhängen. Es sieht erst mal nach viel Programmcode
    aus aber es ist garnicht so viel. Der eigentliche UART Code incl. der stio dürfte bei 2-3 kb sein.
    Dafür kann der Treiber aber auch was...

    Program Memory Usage : 4606 bytes 14,1 % Full
    Data Memory Usage : 308 bytes 15,0 % Full

    Das sind aktuelle Daten aus meinem Testprojekt mit xon/xoff, mit ASCII Edit mode und allem drum
    und dran sinds vielleicht noch 500 oder 1000 Byte mehr.

    Der Remotrol M32 Master (nicht auf alles stdio umgeschrieben, nur Init eingetragen) braucht mit der Lib:

    Program Memory Usage : 13104 bytes 40,0 % Full
    Data Memory Usage : 614 bytes 30,0 % Full

    Die Lib ist also "abwärtskompatibel" zur originalen RP6 UART Lib...

    RP6uart.c RP6uart.h
    Gruß
    Geändert von RolfD (05.05.2014 um 04:13 Uhr)
    Sind Sie auch ambivalent?

  6. #16
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.803
    Hi RolfD,
    Du legst ja ein irres Tempo vor ...

    Ich habe bis jetzt nur sozusagen die Abwärtskompatibilität in Stichproben gecheckt.

    Was hast du für ein "Testprogramm", um alle zusätzlichen Features zu testen?
    Würdest du das evtl. teilen?
    Wenn ja, würde ich auch da im Hintergrund mitmachen.

    Oder als prinzipielle Frage:
    Für die Kompatibilität mit den RP6-Demos und -Libs reicht es sicher, deren Funktion nach und nach durchzuspielen. Das mache ich, wenn du signalisierst, dass so in etwa ein Endstand der Entwicklung der Lib erreicht ist (und ich Zeit habe).
    Mit welchem Testprogramm sollte man darüber hinaus versuchen, die RP6uart-Lib in den Non-RP6 Modi "an ihre Grenzen zu bringen"? Nimmst du "nur" das eine Kurzprogramm, das du in RP6uart.c erwähnt hast?


    Aber erst zu meinen "Abwärtskompatibilitäts-Tests":
    Mein größter Wunsch dabei wäre diesbezüglich, dass man die RP6uart Lib ohne Änderung im RP6-Modus nutzen kann.
    Also sollte der RP6-Modus sozusagen "Default" sein und keine weiteren Initialisierungen in übergeordneten Libs oder Demos brauchen (ich bin faul und würde sonst alles gern unverändert lassen. Ich denke auch, dass die konkrete Nutzung der neuen UART-Lib bei den RP6 Usern im Sinne eines Ersatzes der Original-Lib durch deine Lib davon abhängen wird, ob sie sich komplett abwärtskompatibel integrieren läßt,- die weiteren Funktionen werden danach erst "erkundet").
    Nur wenn die Lib in einer ANDEREN (nicht RP6-Modus konformen) Betriebsart genutzt werden soll, müßte der User die Lib entsprechend initialisieren.
    Gruß
    Dirk

  7. #17
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Hallo Dirk,
    also was die testerei angeht... sobald du die lib anstelle der alten nutzt (überschreibst), werden alle Programme mit der neuen lib gebaut.
    Da alle alten Schreibfunktionen über Umleitungen geführt werden, sieht das für dich so aus als wenn du eben die alten Funktionen nutzt.. tatsächlich nutzt du aber schon neue...
    Das siehst du als Beispiel z.B. daran:
    Code:
    /**
     * compatibility implementation
     * Writes a null terminated string or buffer from SRAM to stdio.
     * Example:
     *            writeString("RP6 Robot System\n");
     */
    void writeString(char *string)
    {
        printf (string); //->stdio
    }
    Ein writeString("RP6 Robot System\n"); ruft also tatsächlich schon stdio funktionen auf... , nur merkst du bis auf den Init der stdio nichts davon. Wie man die Init anders löst überlege ich noch, ist aber für mich z.Z. eher eine marginale Frage, hab ich aber im Blick.

    Das bedeutet: Für alte Programme beim schreiben ändert sich nichts...

    Beim einlesen (readChar, readChars) benutzen die Funktionen NICHT die stdio... aber schon andere neue Programmteile der Lib.

    Das bedeutet: Für alte Programme beim lesen ändert sich nichts...

    Ergo.. man kann alle alten Programme mit der Lib compilieren ohne das man erst mal viel davon merkt. (Abgesehen von der Init)
    Hier noch mal zur Erinnerung die Init Sequenz, welche in die Main oder in die Hardwareinit der Unit.. oder ggf. noch früher geladen werden sollte:
    Code:
        static FILE uart_io = FDEV_SETUP_STREAM (uart_putchar, uart_getchar, _FDEV_SETUP_RW);
        static FILE uart_err = FDEV_SETUP_STREAM (uart_putchar, NULL, _FDEV_SETUP_WRITE);
        stdin = stdout = &uart_io;
        stderr = &uart_err;
        UART_Mode(UART_INIT);
    Im UART_RP6 Mode (in dem man sich nach der Init erst mal befindet) ändert sich auch nichts (aus Anwendersicht) gegenüber der alten Lib. Interessant wird das erst wenn man die anderen UART-Modi nutzen will.

    Da gibts zunächst die Binary Modi mit und ohne Echo, welche ALLE Zeichen von '\0x00' bis '\0xff' zuverlässig annehmen können müssen.. und diese im Echo mode (übrigends über stdio, also umlenkbar) auch wieder zurück werfen, vergleichbar mit einem Loop Back device. (kennt man aus brücken von rx/tx oder dem loopback bzw. pingen im Netzwerk) Mehr können die binary Modes auch erst mal nicht - ich hab jedenfalls z.Z. nicht vor irgendwelche Protokolle dafür zu schreiben. (was aber möglich wäre... von hexdump a la Intel bis TCP/IP ist da alles möglich)
    Um die binary Modes zu testen kann man nun einfach ein Terminal wie Realterm oder Zoc nehmen und direkt Daten senden.. bzw. sehen was zurück kommt. Realterm hat ja z.B. auch Zeichen/String Geneartoren, upload von files im Reiter "Send" usw....
    Man könnte z.B. auf dem PR6 eine Prüfsumme aus eingehenden Zeichen errechnen und diese anzeigen.. und diese Prüfsumme auch auf dem PC berechnen und dann vergleichen - es sollten gleiche Ergebnisse bei raus kommen... man kann aber auch einfach ein Binärfile zum RP6 im E-Modus schicken, das Empfangene widerum capturen und speichern und dann inhaltlich vergleichen.
    Es gibt sicher noch mehr Möglichkeiten Datenstöme beider richtungen messbar gegenüber zu stellen. Man kann aber auch einfach den Binary_Mode statt dem RP6_Mode benutzen. Technisch ist der RP6_Mode und der Binary_Mode für die Anwendung fast das Gleiche. Denn man kann mit "alten Funktionen" auch im Binary Mode arbeiten... man kann aber auch die "neuen" stdio Funktionen nutzen.

    Stdio und die UART_Modi haben nichts miteinander zu tun ausser das sie beide in einem File (uart.c) gleichzeitig bereitgestellt werden. Es geht also UART_RP6 mit stdio genau so wie UART_ASCI mit WriteString... Die UART Modi sind verwand mit der Technik wie man files öffnet und schließt... (binary mode, asci mode, schreibend, lesend, wahlfreier zugriff usw...)
    der UART_RP6 mode besagt nur, das er nicht die Sende-ISR nutzt.. und daher z.b. nicht xon/xoff fähig ist und auch weitere Dinge des ASCII Mode nicht unterstützt, dafür aber das Timing der alten Lib einhält. Der UART_Mode und die stdio haben sonst wenig miteinander zu tun.

    Kommen wir nun zum Punkt.. wie teste ich die Ascii modi. Dafür ist die uart.h wichtig zu verstehen/lesen.
    Nun zunächst mal ist der ASCII Modus wenn er per #define remakrt ist, ein Binärmodus in dem nichts passiert ausser das Zeichen gesendet und empfangen werden.
    Schaltet man ihn ein in dem man das Remark entfernt (// davor löschen) wird der Modus aktiv. Man betrachte sich.. am besten mit einem C-fähigen Editor wie notepad++ oder der Amtel Studio umgebung mal an wenn man die #ifdefs in der datei uart.c in der funktion uart_getchar() einklappt...
    Es werden also stück für stück code teile frei gegebn je nach dem wie man den ascii mode konfiguriert.

    Da xon/xoff nur mit einem ASCII Modus funktioniert (ASCII_Mode bedeutet auch, das nur druckbare Zeichen [->siehe macros isachar(), isanum(), usw..<-] verwendung finden, ggf. manchmal sogar nur 7-bit wertig sind), bauen wir uns ein Programm, welches in den UART_ASCII Mode schaltet .. etwa so.. UART_Mode(UART_ASCII); und aktivieren in der uart.h
    #define WENEEDTXXONOFF 1 // bremst die SendeISR auf Anforderung des Terminals
    #define WENEEDRXXONOFF 1 // bremst das fremde Terminal wegen Buffer voll
    falls da ein // vorstehen sollte. Es macht sinn da ein // vor zu stellen wenn man xon/xoff nicht braucht weil das code einspart. So wir haben jetzt xon/xoff in beiden richtungen aktiviert... da man es auch nur pro Richtung separat nutzen könnte...
    Das Programm sieht also bis jetzt so aus:
    Code:
        UART_Mode(UART_ASCII);
    
        uint16_t d;
    
        for (d=0;d<10000;d++)
           printf("RP6 Robot System termtest %d\n",d);
    Das führt jetzt zu einer Ausgabe von 9999 mal 'RP6 Robot System termtest ZAHL \n' untereinander... wenn man sein Terminal auf newlinemode stehen hat.. es könnte aber auch zu einem Versatz in der Ausgabe führen weil dem Terminal das \r in der Ausgabe fehlt. Schalten wir also noch das //#define WENEEDCRLF 1 // erzwingt crlf beim senden -> vorteilhaft bei ZOC/Realterm
    frei in dem wir die // entfernen. Alle anderen defines können remarkt (//) bleiben.

    So... läuft das Programm und man hat ein Terminal wie Realterm angeschlossen und - wichtig - das ebenfalls auf xon/xoff eingestellt sieht man die Zeilen durch rattern...
    drückt man nun strg-s, sendet man ein xoff cvhar an den rp6 und die Ausgabe bleibt stehen... man wartet.. und schickt ein xon mit strg-q .. läuft sie ansatzlos und ohne probleme weiter...
    Und schon hat man xon/xoff in Senderichtung getestet.

    Für den Receiver-xon/xoff muss man eine große! Datei an den RP6 schicken. Dazu geht man in realterm auf den Reiter "send" und trägt in der erste zeile vor dem button send numbers z.b.
    12345678901234567890 ein. Bei repeats etwas tiefer stellt man z.b. 1500 ein.. würde man nun auf send asci drücken, würde man dem RP6 30000 Zeichen aus der sich widerholenden Folge 123... usw. schicken. Da der RP6 nicht einfach so was annimmt, muss man ihm das bei bringen. Z.B. so:

    Code:
        int i=0,j=0,c,z=0;
        printf("RP6 Robot System termtest \n");
            while(1){
    //            delay_us(1000);
                c=getchar();
                z++;
                if (c=='r') z=0;
                if (c=='s') printf("\nTermtest %d chars\n",z);
                if (c=='q') break;
                if (i++>100) {
                    putchar('.');
                    i=0;
                    j++;
                }
                if (j>10) {
                    putchar('o');
                    j=0;
                    i=0;
                }
            }
    Nun.. was macht das Programm? Ich werde dafür kein Nobelpreis bekommen aber es holt chars, vergleicht diese gegen Buchstaben (r,s,q) und zählt ansonsten Zähler hoch bzw. setzt alle 100 Zeichen ein . und alle 1000 Zeichen ein o ins terminal. Startet man das, zeigt es auf Taste s den Zählerwert aller empfangenen Zeichen (incl. dem s), Taste r resettet den Zähler... und Taste q verlässt die Schleife. Nun was kann man damit machen? Wir drücken 5 mal hinter einander s.. und sehen \nTermtest 1 chars\n .. .\nTermtest 2 chars\n usw...
    Ok.. nun resetten wir den Zähler mit r .. verspielte Naturen drücken nochmals s um auch das zu prüfen.. usw...
    wir können aber nun die 30000 Zeichen aus dem Terminal Send-Reiter auf den RP6 loslassen. Die gelbe Lampe am RS232 Adapter leuchtet.. 30 sek später sie geht aus....
    wir drücken s und sehen \nTermtest 30001 chars\n.
    Ok.. der RP6 hat mal eben 30000 chars gelesen.. konnte er vorher mit der alten Lib auch.... aber jetzt ... produzieren wir Prozessorlast ! Viel davon... wir nehmen vor dem Delay das //weg.
    Was passiert? Nach jedem empfangenen Zeichen macht der Prozessor 1000us bzw. 1ms nichts ausser intensiv im Kreis laufen!
    Jetzt eine kleine Rechnung: 38400 Baud entsprechen fast 4000 Zeichen/pro Sek. Wenn ich bei jedem Zeichen 1 ms Pause mache kann ich im Bestfall 1000 Zeichen/sek empfangen. 4000 angebotene Zeichen ist aber deutlich mehr als max 1000 verarbeitbare Zeichen. Ergo... das Terminal überrent uns.. wäre da nicht der xon/xoff modus.. der das terminal bremst. Und mal eine Schätzung: Bei einem Verhältnis von 1:4 und einem Buffer von 32 byte dauert es wie lange bis ein normaler buffer wie in der ursprünglichen uart.c voll bzw. überlaufen ist? Genau.. ca. 40-50 Zeichen. bei 4000 Zechen/sek also ca. 100 mal die Sekunde... selbst mit einem 128er Buffer noch ca. 20 mal die Sek. Und das bei nur einer einzigen Millisekunde CPU Last pro Durchgang!
    Wir probieren das also aus.. und auch bei delay_us(2000);, delay_us(5000); oder delay_us(10000); kommen alle 30001 Zeichen immer zuverlässig an.. es dauert nur eben länger.
    Und schon haben wir den neuen xon/xoff Receiver erfolgreich getestet.
    Wer Spass daran hat kann das auch mal im RP6 oder Binary bzw. ohne xon/xoff mode testen... es kostet ja nur eine Zeile: UART_Mode(UART_RP6);
    Wenn in der aktuellen Fassung die LED3 auf dem M32 schnell blinkt, gabs einen Bufferüberlauf!

    Langer Rede, kurzer Sinn.. man könnte als Beispiel das Windows-Remotrol Programm z.B. auch mit xon/xoff Unterstützung bauen (unter windows muss man so ein Gedöns ja nicht erst komplett neu schreiben) und würde feststellen das man keine Zeichen mehr verliert und so ein quatsch wie "Hardbeats" nicht braucht.. oder man verbindet 2 Bots mittels bluetooth Radio an RS232 die miteinander reden .. ohne Zeichen zu verlieren.... ich glaub es gibt genug Ideen wie man das nutzen kann...
    Die Lib ist für Leute, die nur das (Zeilen)Terminal im Robotloader für Ausgaben nutzen eher weniger interssant. Spannend wirds da, wo ein "richtiges Terminal" wie Realterm oder ZOC zum Einsatz kommt (und man vernünftige Reaktionen vom Terminal bekommt) oder gar Bots mit Bots oder Computer mit Bots reden.
    Das eingebaute Robotloadertermial kann ja nicht mal xon/xoff ... wer aber dann sagt.. "brauch ich auch nicht" ... braucht sich meines Erachtens auch nicht über verlorene Zeichen, "bad Timings" und komische Reaktionen im eigenen Programm zu wundern.

    So.. erst mal genug... die anderen Dinge bin ich z.Z. selbst noch am testen... aber den ASCII Terminal Mode kann man z.B. auch am besten im Realterm testen...
    Dort könnte man z.B. mal ausprobieren, ob strg-d tatsächlich ein EOF sendet oder was weis ich...
    Man kann auch alle defines in der uart.h frei schalten.. nur verbraucht man dann evtl. Codesize, die man nicht braucht. Ein Terminal Mode mit edit Funktion ist für Leute die nur mit getchar arbeiten (readChar ist ja auch nichts anderes) jetzt nicht so interssant und ich glaube auch nicht, das jeder sofort ein Basic- oder noch besser ein Turtle-Interpreter schreiben würde. http://de.wikipedia.org/wiki/Program..._Python_Turtle
    (obwohl letzteres auf dem RP6 noch Sinn machen würde)

    Ich hoffe, ich konnte etwas weiter helfen.
    Es sei aber noch gesagt das die Lib vom Funktionsumfang.. also was sie können soll.. im groben schon so weit fertig ist.. es kommen also keine 1000 Änderungen um NEUES rein zu bringen... es kommen schlimmstenfalls nur noch 1000 fixes damit diese Lib das macht was als Funktionsziel abgesteckt ist.
    Und.. ich bin dankbar für jeden Hinweis auf verbesserungsfähige Dinge. Es gibt Dinge die kann ich (oder will ich) nicht umsetzen... z.B. einige ASCII Einstellunge für den UART_RP6 Modus.. da muss ich passen. Aber ich denke, das die lib genügend Möglichkeiten bietet .. auch im Zusammenarbeit mit weiteren Treibern ..., um der alten Lib nicht all zu lang nachzutrauern.

    Zu printf/scanf und den ganzen stdio Funktionen selbst gibts im Web genug Beispiele... allein die docu zur Winavr Lib wirft schon einiges aus.
    Die Lineinput Funktion ganz am Ende von uart.c ist z.B. aus einer IBM Docu zur stdio entnommen und nur geringstfügig angepasst.
    Man kann aber nun auch mal sowas angucken, umsetzen, lernen... die stdio gilt eigentlich als Grundlage für JEDEN Programmierer.
    http://www.programmingsimplified.com/c-program-examples

    Für weitere Geschichten in Zusammenhang von stdio und anderen Geräten muss ich erst weitere passende Treiber bauen.
    Ich werde wie schon gesagt als nächstes ein LCD Treiber für die M32 umsetzen, interssant wäre aber auch der schon besagte sound Treiber oder noch besser ein Treiber welcher Fileio auf dem internen eeprom (und damit auch ggf. als Prototyp für externe eeproms, sdcrads und andere Dateispeicher dienen kann) unterstützt.

    Neben der stdio über stdin, stdout und stderr gibt es z.B. durchaus Geräte, die stdio komaptibel über 2 Datenströme gleichzeitig gefahren werden.. quasi einem Datensteuerstom und einem Datendatenstrom. Oder 2 Datenströme. Die Frage ist dann nur, wie multiplext man 2 logische Verbindungen (evtl. sogar beide mit unabhängigen schreib und lese Funktion) über ggf. nur eine physische Leitung ohne großen Programmier- und Code Aufwand. Klingt sehr aus den Sternen gegriffen aber eine Kernfrage, wenn ich den TWI/I2C Treiber unter dem Aspekt der stdio noch mal angehen will.

    Gruß

    NACHTRAG_1:

    Es gibt da übrigends noch ein #define WENEEDRP6IO 1 ... irgendwann wenn alles fertig ist und man ein neues Projekt anfängt, wird man damit ALLE "alten Code Teile" auf ein Schlag abschalten KÖNNEN. Ob man.. und wer es.. dann tut, ist jedem selbst überlassen. Der Compiler optimiert zwar mit -o2 jetzt schon ungenutzte Programmteile weg aber das ist dann auch sowas wie "den inneren Schalter umlegen". Obwohl die Lib erst seid ein paar Tagen so läuft, arbeite ich inzwischen lieber mit der stdio (auch weil ich es als EDVler ... nicht ITler - das sind die mit Schlips und Studium welche c# als hardwarenahe Programmierspache lernen - gewohnt bin).

    NACHTRAG_2:

    Wer Speicher sparen muss/will, kann sich übrigends mit den Linkerflags: -Wl,-u,vfprintf -lprintf_min
    eine kleinere Version von der stdio printf laden, allerdings kann die wohl nur nur int und strings verarbeiten.
    Wer dagegen auch Support für float braucht, nimmt die Linkerflags: -Wl,-u,vfprintf -lprintf_flt -lm
    Im Atmel Studio ist per default die mittelgroße Lib ohne float aber sonst mit allem in den Linkereinstellungen
    vorgegeben, braucht man eine andere, nimmt man die bei Linkeroptions raus und trägt sich die andere bei
    Linker->Misc->Other Options von Hand ein. Default ist also: -Wl,-u,vfprintf
    Keine Ahnung ob sich noch jemand mit Makefiles rumschlägt aber das ist dort ggf. auch zu berücksichtigen.
    Die "min Version" spart etwa 1kb code ein, man nimmt sich aber doch einige wichtige konversionen damit.

    NACHTRAG_3:
    Eine wirklich gut gemachte Info Sammlung zum Thema Terminal ist auch folgendes:
    http://www-user.tu-chemnitz.de/~heha...l/terminal.htm
    Dort ist auch zu finden wie man z.B. xmodem, also ein altes(=erprobtes & weit unterstütztes) paketorientiertes Format für Binärübertragung auf Terminals einfach umsetzt.

    NACHTRAG_4:
    Falls jemand die stio selbst auch schon für eigene Zwecke erweitern möchte indem eigene Gerätetreiber dafür geschrieben werden, noch mal das Grundprinzip:
    Mit:
    Code:
        FILE *fp;
        fp = fdevopen(Streamlesefunktion,Streamschreibfunktion);
    oder mit
    Code:
        FILE *fp;
        static FILE stream = FDEV_SETUP_STREAM (Streamschreibfunktion, Streamlesefunktion, Modus); //Funktionsname ohne()
        fp=&stream;
    kann man sich ein Filehandle bauen, welches dann mit den stdio Funktionen angesprochen werden kann. 3 streams (stdin, stdout und stderr) sind dazu im System schon definiert, lassen sich aber überschreiben und man kann weitere Streams selbst anlegen.
    Code:
        fprintf(fp,"foobar");
    Der Modus ist dabei je nach Funktion _FDEV_SETUP_READ, _FDEV_SETUP_WRITE, oder _FDEV_SETUP_RW, Geräte von denen man z.b. nicht liest brauchen auch keine Streamlesefunktion, diese wird dann auf NULL gesetzt und der Modus auf _FDEV_SETUP_WRITE gestellt.
    Wichtig ist dabei eigentlich nur das die Funktionen, die den Stream verarbeiten auch alle "Spezialbefehle" können.. also als Beispiel interpretieren und Anwenden von Formatierungen wie (f)printf, im Gegensatz zu (f)puts, was einfach nur ein unformatierten String ausgibt.
    Sinn und Zweck ist eigentlich, die IO pro "Gerät" auf 2 Funktionen (in und out) zu bündeln und nicht mit 100 Spezialfuktionen irdendwas irgendwo im System zu verstellen. Statt mit einem Pseudocursor in einem LCD rumzuspringen und das LCD von Hand zu löschen kann man nämlich z.B. auch ganz einfach nach Ausgabe von \n das LCD automatisch löschen und/oder auf sonstige Steuerzeichen wie \t (tabulator) mit einem Zeilensprung reagieren. Dafür sind die Steuerzeichen immerhin mal aus gutem Grund erfunden worden - und zwar zu Zeiten als ein Kleiderschrank-großer Computer kaum mehr Speicher hatte als heute ein AMega32 Prozessor.
    Ob man nun von einem UART kilobyteweise Daten im Stream liest.. oder seine 5 Tasten mit einer Einlesefunktion versieht und die mit getchar anspricht, oder seine Sonarentfernungsmessung abfragt , ist dem Programm dann egal. getchar ist getchar...

    NACHTRAG_5:
    In der nächsten Version wird die M32 auf das empfangene BELL Zeichen hin ebenfalls piepen. Die Base kann das natürlich nicht.
    Dafür muss in der uart_getchar() der Bereich:
    Code:
                            case '\a': // BEL / strg-g
    #ifdef RINGSTRING
    #warning ASCII Modus Ersatz fuer das BELL Zeichen wurde mit compiliert
                                data='\x10'; // wenn RINGSTRING angegeben, ist das \a ungültig
                                if (tx_buff.uart_modus==UART_EASCII) printf (RINGSTRING); // stattdessen optisches Signal
    #endif
                            break;
    gegen:
    Code:
                            case '\a': // BEL / strg-g
    #ifdef RP6CONTROLLIB_H
                                sound(230,25,25); // wir laufen auf einer M32 also piepen wir auch
    #endif
    #ifdef RINGSTRING
    #warning ASCII Modus Ersatz fuer das BELL Zeichen wurde mit compiliert
                                data='\x10'; // wenn RINGSTRING angegeben, ist das \a ungültig
                                if (tx_buff.uart_modus==UART_EASCII) printf (RINGSTRING); // stattdessen optisches Signal
    #endif
                            break;
    ausgetauscht werden.

    Gruß
    Geändert von RolfD (11.05.2014 um 11:54 Uhr)
    Sind Sie auch ambivalent?

  8. #18
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.803
    Hi RolfD,

    Aber ich denke, das die lib genügend Möglichkeiten bietet .. auch im Zusammenarbeit mit weiteren Treibern ..., um der alten Lib nicht all zu lang nachzutrauern.
    Ja, das denke ich auch,- zumal man ja wirklich als Anwender keinen Unterschied merkt.
    Ich habe jetzt erstmal als "Standard" WENEEDASCII, WENEEDTXXONOFF, WENEEDRXXONOFF deaktiviert und teste so drauflos.
    Danke nochmal für die ausführliche Erklärung!
    Gruß
    Dirk

  9. #19
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    So.. wie versprochen.. ein etwas geänderter LCD Treiber für die M32, zur Verwendung mit stdio geeignet.

    Zunächst: Was ändert sich? Für Programme die auf der M32 alte Write-Funktionen nutzen - nichts! Es gibt die Schreibfunktionen über Umleitungen wie beim UART Treiber.
    Was hat sich geändert? Ich habe die eigentliche Ausgabefunktion zusammen gefasst. Sie nennt sich m32lcd_data() und kann nibbles, Commands und Daten ans Display schicken.
    Will man sich die Lib auf ein anderes Display umschreiben, braucht man nur diese Funktion an die Hardware anzupassen. Hier bin ich insbesondere von den alten Angaben zu CPU-Cycles auf echte Zeiten gegangen wie sie im Datenblatt stehen. Das macht es einfacher bei anderen Taktungen.

    Es sind neue Funktionen hinzu gekommen, unter anderem die Hauptfunktion lcd_putchar(), welche man braucht um sie im Programm mit stdio zu nutzen.
    Code:
    Eingebunden wird sie mit:
    
        FILE *M32lcd;
        static FILE lcdout = FDEV_SETUP_STREAM (lcd_putchar, NULL, _FDEV_SETUP_WRITE);
        M32lcd=&lcdout;
    
    Angesprochen z.B. so:
    
    fprintf ( M32lcd , "L\tCD Test:\v%c=%d foo\a\tbar \x80\n" , lcd_xy(1,4) , i++);
    Die Zeile ist für ein 2x16 Display zu lang aber es wird auf das LCD ausgegeben: mit printf support "L CD" durch \t Tabulatoren (derzeit 4) getrennt, dann wird mit
    \v%c der cursor umgesetzt und auf die position nach "=" Wert i geschrieben, dann "foo" ausgegeben, einmal gebimmelt, mit tabulator bar
    drangehängt und am Schluß noch das erste selbstdefinierte Zeichen aus dem GCRAM (0x80) ausgegeben - abgeschlossen von einem Linefeed,
    was dazu führt das bei der nächsten ausgabe erst das Display gelöscht wird. Macht so alles kein Zusammenhang, zeigt aber ein wenig,
    was man damit anstellen kann. Es gibt dann noch \r für nächste Zeile löschen usw. Und alles in einer Ausgabezeile...

    Das funktioniert alles nur, weil intern u.a. neuerdings ein Cursor mitgeführt wird (wo es zuletzt ein kleines, aber gelöstes Problem mit gab).
    Den kann man auch auslesen (lcd_getCursorPos) und setzen (lcd_setCursorPos), ferner wird mit \v ein kumilierter Cursor verwendet,
    welcher mit lcd_xy(line,pos) berechnet wird. Ansich ist das einfach nur die Position in einem Display welches nicht in lines unterteilt wäre sondern
    alles an einem Stück im Buffer steht. Pos 0 ist erstes Zeichen, Pos 0f das 15te, Pos10 das 16te bzw. 1 in der 2ten Line also line 1.. weil 1 line=0,
    Pos 1f daher auch 16 Zeichen in 2 Line... bzw. (1,15) ... Eigenlich müssten auch 4Zeilige Displays laufen wenn sie in der .h eingestellt sind.
    Es ist ein Line Wrapping mit Übertrag eingebaut so das man nur auf dem sichtbaren Display Ram schreibt und nicht in den Buffer. Im H-File ist das Display auch auf andere Formate (ungetestet) über Defines zu konfigurieren. Es gibt zudem eine Funktion zum einlesen von selbst definierten Grafik chars (8 Zeichen möglich)
    Und zu guter Letzt gibts noch eine Wheel Funktion die ein laufenden Cursor, Sanduhr oder Debug Zähler immitiert. (wirds so ähnlich in der UART Lib auch noch geben da es sich beim debuggen sehr bewährt hat) Ich bin dabei den Code dieser Lib noch zu testen...
    Zudem enthält die Lib wieder die 4 LIB2 Funktionen von Dirk. Für diese muss übrigends das Modul libm eingebunden sein wie ich jetzt gelesen habe.
    In der .h datei gibts noch ein Flag das man ähnlich dem ASCII/BINARY Mode aus dem UART Treiber nutzen bzw. zur compile Zeit, nicht Laufzeit... einstellen kann #define LCDMODE LCD_ASCII // Oder LCD_BINARY
    Im Binary Mode werden die Steuerzeichen nicht verarbeitet, da funktioniert nur das Linewrapping, im ASCII Mode werden die Steuerzeichen: \a \t \r \v%c DEL \b \f und Zeichen 0xFF verarbeitet. Die Funktionen stehen im Code noch mal erläutert. Hinter einem \v MUSS immer sofort danach ein Wert als char %c mit kumulierter Cursorposition folgen, dieser lässt sich mit lcd_xy wie gesagt auch berechnen.

    So.. wie nutzt man das?
    Man sucht sich in der vorher gesicherten RP6ControlLib.c den Abschnitt von // LCD bis // Keypad:
    und wirft alles dazwischen raus. Dann fügt man den Code unten genau dort wieder ein.
    Das ganze ebenso mit der .h Datei unten.

    Was hat man am Ende davon? Naja einen ziemlich flexibel nutzbaren LCD Treiber mit verbessertem Timing für die M32, der Möglichkeit bietet, das Ganze mit stdio einzubinden.

    Was kann der Treiber nicht? Nun er kann nicht mit dem Buffer Mode bzw. moving cursor arbeiten, den die Displays angeblich können. Das bekam ich bei meinen 2 Displays (Nachbauten) nicht zum laufen. Ebenfalls konnte ich den 5x10 Mode und andere Zeilen/Reihengrößen nicht testen.

    Ich überlege, ob ich nicht ein komplettes Lib Paket als ZIP mit allen Änderungen raus geben soll. Ich möchte allerdings die Sachen, die ich da nun geschrieben habe auch erst etwas genauer testen. Die Lib verbraucht übrigends nicht viel mehr Speicher als die alten Funktionen wenn man sie in den Binary Mode schaltet. Im ASCI Mode sinds ein paar Byte mehr aber dafür kann die Lib aber auch wieder vieles was bisher nicht ging oder umständlich und damit ebenfals codefressend umzusetzen war.

    Der Treiber kann auch bei geänderter Ausgabefunktion für andere LCDs verwendet werden, allerdings sollte man bei gleichzeitiger Nutzung von 2 LCDs darauf achten, das man nur die Ausgabefunktion selktierbar macht und nich den ganzen Treiber 2 mal anlegt. Ich habe es noch nicht ausprobiert aber das könnte über den bisher ungenutzten *Stream Parameter der lcd_putchar() gehen. Dann spart man wieder ein Haufen code da wieder ein Sack voll spezial-Write-Funktionen entfallen können. Wer übrigends nur mit stdio arbeiten will.. es gibt da immer noch das define WENEEDRP6IO ... auch in der LCD lib.

    Code:
    // LCD
    //Einstellung für vorgeladene ASCII Zeichen im Display
    #define PRELOADLCDCHARS 1
    
    #if PRELOADLCDCHARS==1
    const uint8_t chrdata[64] MEM = {
    // LCD_GC_CHAR0 124=|
        0b00000100,
        0b00000100,
        0b00000100,
        0b00000100,
        0b00000100,
        0b00000100,
        0b00000100,
        0b00000000,
    // LCD_GC_CHAR1 252=ü
        0b00001010,
        0b00000000,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00010011,
        0b00001101,
        0b00000000,
    // LCD_GC_CHAR2 228=ä
        0b00001010,
        0b00000000,
        0b00001110,
        0b00000001,
        0b00001111,
        0b00010001,
        0b00001111,
        0b00000000,
    // LCD_GC_CHAR3 246=ö
        0b00001010,
        0b00000000,
        0b00001110,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00001110,
        0b00000000,
    // LCD_GC_CHAR4 220=Ü
        0b00001010,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00001110,
        0b00000000,
    // LCD_GC_CHAR5 196=Ä
        0b00001010,
        0b00001110,
        0b00010001,
        0b00010001,
        0b00011111,
        0b00010001,
        0b00010001,
        0b00000000,
    // LCD_GC_CHAR6 214=Ö
        0b00001010,
        0b00001110,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00010001,
        0b00001110,
        0b00000000,
    // LCD_GC_CHAR7 223=ß
        0b00001100,
        0b00001010,
        0b00001110,
        0b00001001,
        0b00001001,
        0b00001010,
        0b00001000,
        0b00000000
    };
    #endif
    const char lcd_wheelchar[] MEM = ".,oO0*0Oo,. "; // sinnvoll wäre auch noch const char wheel[] = "0123456789ABCDEF";
    const uint8_t LCDLineAdr[] = {LCD_START_LINE1, LCD_START_LINE2, LCD_START_LINE3, LCD_START_LINE4};
    #define LCDwheelsize (sizeof(lcd_wheelchar)-1)
    static uint8_t lcd_cpos; // nächste Schreibadresse bzw. Cursorposition im Display
    static uint8_t lcd_line;
    
    /**
     * Sendet Daten an das M32LCD, für andere LCD Anschlüsse muss nur die Ausgabe per writeSPI angepasst werden
     */
    void m32lcd_data( uint8_t data, uint8_t mode )
    {
        if (mode!=LCD_SINGLE) {
            if (mode==LCD_DATA) PORTB |= LCD_RS;// RS setzen / data
            else PORTB &= ~LCD_RS; // RS löschen  / byte cmd
            externalPort.LCDD = data >> 4;
            writeSPI(externalPort.byte);
            PORTD |= STR;
            PORTB |= LCD_EN;
            PORTD &= ~STR;
            _delay_us(LCD_ENABLE_US);
            PORTB &= ~LCD_EN;
        } else PORTB &= ~LCD_RS; // RS löschen / nibble cmd
        externalPort.LCDD = data & 0x0f;
        writeSPI(externalPort.byte);
        PORTD |= STR;
        PORTB |= LCD_EN;
        PORTD &= ~STR;    
        _delay_us(LCD_ENABLE_US);
        PORTB &= ~LCD_EN;
        _delay_us(LCD_ENABLE_US);
        if (mode==LCD_DATA){
            _delay_us( LCD_WRITEDATA_US );
            lcd_cpos++; // Cursor increment
        }
        else _delay_ms( LCD_WRITECMD_MS );
    }
    
    /**
     * Initialize the LCD. Always call this before using the LCD! 
     */
    void initLCD(void)
    {
        uint8_t i;
    //    _delay_ms(LCD_BOOTUP_MS , LCD_SINGLE); No need for Power ON delay as usually the
    // Bootloader should have been executed before...
        PORTB &= ~LCD_EN;
        PORTB &= ~LCD_RS;
        _delay_ms(15);
        for(i=0;i<2;++i) {
            m32lcd_data(0x3 , LCD_SINGLE);
            _delay_ms(2);
        } //Reset
    // 4-bit Modus aktivieren
        m32lcd_data(0x2 , LCD_SINGLE);
        _delay_ms(2);
    // 4-bit Modus / 2 Zeilen / 5x7
        m32lcd_data( LCD_SET_FUNCTION | LCD_FUNCTION_4BIT | LCD_FUNCTION_2LINE | LCD_FUNCTION_5X7 , LCD_CMD);
    // Display ein / Cursor aus / Blinken aus
        m32lcd_data( LCD_SET_DISPLAY | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINKING_OFF , LCD_CMD);
    // Cursor inkrement / kein Scrollen
    #if LCD_WRAP_LINES==1
        m32lcd_data( LCD_SET_ENTRY | LCD_ENTRY_INCREASE | LCD_ENTRY_NOSHIFT , LCD_CMD);
    #else
        m32lcd_data( LCD_SET_ENTRY | LCD_ENTRY_INCREASE | LCD_ENTRY_SHIFT , LCD_CMD);
    #endif
    // vordefinieren der 8 Sonderzeichen
    #if PRELOADLCDCHARS==1
        uint8_t eeval;    
        m32lcd_data( LCD_SET_CGADR , LCD_CMD );
        for (i=0; i<64; i++ ) {
    #if MEMORY==0        
            eeval = pgm_read_byte(&chrdata[i]);
    #else
            eeval = eeprom_read_byte(&chrdata[i]);
    #endif        
            m32lcd_data( eeval , LCD_DATA);
        }
    #endif
    // Clear
        lcd_cpos=0;
        lcd_line=0;
        m32lcd_data(LCD_CLEAR_DISPLAY, LCD_CMD);
    }
    
    #ifdef WENEEDRP6IO
    /**
     * Clears the whole LCD!
     */
    void clearLCD(void)
    {
        lcd_cpos=0;
        lcd_line=0;
        m32lcd_data(LCD_CLEAR_DISPLAY , LCD_CMD);
    }
    
    /**
     * Write a single character to the LCD.
     */
    void writeCharLCD(uint8_t ch)
    {
        m32lcd_data(ch , LCD_DATA);
    }
    
    /**
     * Writes a null terminated string from flash program memory to the LCD.
     * You can use the macro writeStringLCD_P(STRING); instead, this macro
     * ensures that the String is stored in program memory only!
     * Example:
     *            writeNStringLCD_P(PSTR("RP6 Control"));
     *            // There is also a Macro that makes life easier and
     *            // you can simply write:
     *            writeStringLCD_P("RP6 Control");
     */
    void writeNStringLCD_P(const char *pstring)
    {
        uint8_t c;
        for (;(c = pgm_read_byte_near(pstring++));m32lcd_data(c , LCD_DATA));
    }
    
    /**
     * Writes a String from SRAM to the LCD.
     */
    void writeStringLCD(char *string)
    {
        while(*string)
            m32lcd_data(*string++ , LCD_DATA);
    }
    
    /**
     * Writes a string with specified length and offset from SRAM to the LCD.
     * If it is a null terminated string, output will be stopped at the
     * end. It does not need to be null terminated, but it is recommended
     * to use only null terminated strings/buffers, otherwise the function could
     * output any SRAM memory data stored after the string until it reaches a 0
     * or the specified length!
     * Example:
     *            writeStringLength("RP6 Robot Sytem",16,0);
     *            // would output: "RP6 Robot Sytem\n"
     *            writeStringLength("RP6 Robot Sytem",11,4);
     *            // would output: "Robot System"
     *             writeStringLength("RP6 Robot Sytem",40,4);
     *            // would output: "Robot System"
     *            // No matter if the specified length is 40 characters!
     */
    void writeStringLengthLCD(char *string, uint8_t length, uint8_t offset)
    {
        for(string = &string[offset]; *string && length; length--)
            m32lcd_data(*string++ , LCD_DATA);
    }
    
    /**
     * Write a number (with specified base) to the LCD.
     * Example:
     *            // Write a hexadecimal number to the LCD:
     *            writeInteger(0xAACC,16);
     *            // Instead of 16 you can also write "HEX" as this is defined in the
     *            // RP6RobotBaseLib.h :
     *            writeInteger(0xAACC, HEX);
     *            // Other Formats:
     *            writeInteger(1024,DEC);      // Decimal
     *            writeInteger(511,OCT);        // Ocal
     *            writeInteger(0b11010111,BIN); // Binary
     */
    void writeIntegerLCD(int16_t number, uint8_t base)
    {
        char buffer[17];
        itoa(number, &buffer[0], base);
        writeStringLCD(&buffer[0]);
    }
    
    /**
     * Same as writeInteger, but with defined length.
     * This means this routine will add leading zeros to the number if length is
     * larger than the actual value or cut the upper digits if length is smaller
     * than the actual value.
     * Example:
     *            // Write a hexadecimal number to the LCD:
     *            writeIntegerLength(0xAACC, 16, 8);
     *            // Instead of 16 you can also write "HEX" as this is defined in the
     *            // RP6ControlLib.h :
     *            writeIntegerLength(0xAACC, HEX, 8);
     *            // Other Formats:
     *            writeIntegerLength(1024,DEC,6);      // Decimal
     *            writeIntegerLength(511,OCT,4);        // Ocal
     *            writeIntegerLength(0b11010111,BIN,8); // Binary
     */
     
    void writeIntegerLengthLCD(int16_t number, uint8_t base, uint8_t length)
    {
        char buffer[17];
        itoa(number, &buffer[0], base);
        int8_t cnt = length - strlen(buffer);
        if(cnt > 0) {
            for(; cnt > 0; cnt--, writeCharLCD('0'));
            writeStringLCD(&buffer[0]);
        }
        else 
            writeStringLengthLCD(&buffer[0],length,-cnt);
    }
    
    /**
     * This function is useful for displaying text screens on the LCD.
     * It clears the whole LCD and writes the two Strings to line 1 and
     * line 2.
     */
    void _showScreenLCD_P(const char *line1, const char *line2)
    {
        clearLCD();
        writeNStringLCD_P(line1);
        lcd_line=1;
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD);
        writeNStringLCD_P(line2);
    }
    
    /**
     * Sets the cursor position on LCD.
     */
    void setCursorPosLCD(uint8_t line, uint8_t pos)
    {
        if (line < LCD_LINES) {
            lcd_line=line;
            lcd_cpos=pos;
            m32lcd_data(LCD_SET_DDADR + LCDLineAdr[line] + pos, LCD_CMD);
        }
    }
    
    /**
     * Clears some characters after the given position.
     */
    void clearPosLCD(uint8_t line, uint8_t pos, uint8_t length)
    {
        lcd_line=0;
        lcd_cpos=0;
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD);
        while(length--)
            m32lcd_data(' ' , LCD_DATA);
    }
    /*****************************************************************************/
    // die uart_2 lib für LCD
    /*****************************************************************************/
    void writeLongLCD(int32_t number, uint8_t base) {
        char buffer[33];
        ltoa(number, buffer, base); //->stdio
        writeStringLCD(&buffer[0]);
    }
    
    void writeLongLengthLCD(int32_t number, uint8_t base, uint8_t length) {
        char buffer[33];
        ltoa(number, &buffer[0], base);
        int8_t cnt = length - strlen(buffer);
        if(cnt > 0) {
            for(; cnt > 0; cnt--, writeCharLCD('0'));
            writeStringLCD(&buffer[0]);
        } else writeStringLengthLCD(&buffer[0], length, -cnt);
    }
    
    void writeDoubleLCD(double number, uint8_t width, uint8_t prec) {
        char buffer[width + 1];
        dtostrf(number, width, prec, buffer);
        writeStringLCD(&buffer[0]);
    }
    
    void writeDoubleExpLCD(double number, uint8_t prec, uint8_t flags) {
        char buffer[prec + 8];
        dtostre(number, buffer, prec, flags);
        writeStringLCD(&buffer[0]);
    }
    #endif
    
    // die neuen LCD Funktionen
    void lcd_CalcCursorUP(uint8_t setcursor){ //Boundary check upward
        if ( lcd_cpos >= LCD_LINE_LENGTH ) { //linewrap prüfen und nächste Zeile setzen
            lcd_cpos=0;
            if (lcd_line >= LCD_LINES) lcd_line=0; else lcd_line++;
            setcursor=true;
        }
        if (setcursor==true) m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD);
    }
    void lcd_CalcCursorDOWN(void){ //Boundary check downward
        if (lcd_cpos > 0 ) lcd_cpos--;
        else {
            if (lcd_line > 0) lcd_line--;
            else lcd_line=LCD_LINES-1;
            lcd_cpos=LCD_LINE_LENGTH-1;        
        }
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD);
    }
    
    /*****************************************************************************/
    void lcd_putxy(char c, uint8_t line, uint8_t pos) //gibt Zeichen an xy aus
    {
        lcd_line=line;
        lcd_cpos=pos;
        lcd_CalcCursorUP(true);
        m32lcd_data(c , LCD_DATA);
        lcd_CalcCursorUP(false);
    }
    
    int16_t lcd_putchar(char c, __attribute__((unused)) FILE *stream)
    {
    //*********    
    #if LCDMODE==LCD_ASCII
        static bool nl_seen;
        static bool cursor_seen;
        uint8_t i;
        
        if (cursor_seen) { // erstes Zeichen nach \v, Cursorposition also \v%c
            lcd_cpos = c % LCD_LINE_LENGTH; 
            lcd_line = (c / LCD_LINE_LENGTH) % LCD_LINES;
            lcd_CalcCursorUP(true);
            cursor_seen = false; //ok es geht weiter im String
            return 1; // darf nicht zur weiteren Auswertung gelangen.
        } else { // wenn keine Cursorposition, dann normales Zeichen
            switch (c) { // Steuerzeichen nach ISO/IEC 6429
                case '\a': // BELL Zeichen
                    sound(230,25,25);
                break;
                case '\t': // nächsten Tabulator ansteuern
                    lcd_cpos=lcd_cpos+LCD_TAB_SIZE-(lcd_cpos % LCD_TAB_SIZE);
                    lcd_CalcCursorUP(true);
                break;
                case '\r': // wagenrücklauf, nächste Zeile löschen
                    lcd_line++;
                    lcd_cpos=0;
                    lcd_CalcCursorUP(true);
                    for(i=0;i<LCD_LINE_LENGTH;i++) m32lcd_data(' ' , LCD_DATA);
                    lcd_cpos-=LCD_LINE_LENGTH;
                    lcd_CalcCursorUP(true);
                break;
                case '\v': //set cursor line, nächstes "char" ist der kumulierte cursorwert
                    cursor_seen = true;
                break;
                case '\x7f': // ist zwar ein Zeichen im Displayzeichensatz, aber laut ASCII das DEL char!
                    lcd_CalcCursorDOWN();
                    m32lcd_data(' ' , LCD_DATA);
                case '\b': // 1 Zeichen zurück gehen, nicht löschen entspricht <- [cursor].
                    lcd_CalcCursorDOWN();
                break;
                case '\xff': // steht für signed -1, EOF, ALTSPACE, hier als "jumpover" bzw. SPACE ohne löschen / [cursor] ->
                    lcd_cpos++;
                    lcd_CalcCursorUP(true);
                break;
                case '\n': // indirekter löschbefehl
                    nl_seen = true;
                break;
                case '\f': // direkter löschbefehl
                    lcd_cpos=0;
                    lcd_line=0;
                    m32lcd_data(LCD_CLEAR_DISPLAY , LCD_CMD);
                break;
                default:
                    if (nl_seen && c != '\n') { // First character after newline, clear display and home cursor.
                        lcd_cpos=0;
                        lcd_line=0;
                        m32lcd_data(LCD_CLEAR_DISPLAY , LCD_CMD);
                        nl_seen = false;
                    }
    #if PRELOADLCDCHARS==1                
                    switch (c) { // Umsetzungstabelle für Sonderzeichen laut ISO/IEC 8859-1/15
                        case 124: // LCD_GC_CHAR0 124=|
                            c=0x80+LCD_GC_CHAR0;
                        break;
                        case 252: // LCD_GC_CHAR1 252=ü
                            c=0x80+LCD_GC_CHAR1;
                        break;
                        case 228: // LCD_GC_CHAR2 228=ä
                            c=0x80+LCD_GC_CHAR2;
                        break;
                        case 246: // LCD_GC_CHAR3 246=ö
                            c=0x80+LCD_GC_CHAR3;
                        break;
                        case 220: // LCD_GC_CHAR4 220=Ü
                            c=0x80+LCD_GC_CHAR4;
                        break;
                        case 196: // LCD_GC_CHAR5 196=Ä
                            c=0x80+LCD_GC_CHAR5;
                        break;
                        case 214: // LCD_GC_CHAR6 214=Ö
                            c=0x80+LCD_GC_CHAR6;
                        break;
                        case 223: // LCD_GC_CHAR7 223=ß
                            c=0x80+LCD_GC_CHAR7;
                        break;
                    }
    #endif
                    if ((c >= ' ' && c <= '\x87')) { // ist das ein verwertbares Zeichen incl. CGCHAR?
                        m32lcd_data(c & 0x7f , LCD_DATA); // ggf. das "Steuerzeichenvehikel" für CGCHAR (0x80+CGCHAR) ausmaskieren
                        lcd_CalcCursorUP(false); //Linewrap berechen... 
                    } else printf ("%c %x. ",c,c);
                break;
            }
        }
    //*********    
    #elif LCDMODE==LCD_BINARY
        m32lcd_data(c , LCD_DATA); //nur ausgeben und cursor boundary berechnen/setzen
        lcd_CalcCursorUP(false); //cursor boundary berechnen/setzen
    #endif
        return 0;
    }
    
    void lcd_wheel(uint8_t line, uint8_t pos) //sowas wie Sanduhr/Kringel oder rotierender Cursor, gut zum debuggen
    {
        static uint8_t i=0;
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[line] + pos, LCD_CMD);
    #if MEMORY==0
        m32lcd_data(pgm_read_byte(&lcd_wheelchar[i++]), LCD_DATA);
    #else
        m32lcd_data(eeprom_read_byte(&lcd_wheelchar[i++]), LCD_DATA);
    #endif
        lcd_cpos-=1; // Cursor korrigieren, war ja kein reguläres char im Display
        if (i==LCDwheelsize) i=0;
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD);
    }
    
    uint8_t lcd_getCursorPos(void) // gibt aktuelle cursorposition als kumulierten Wert zurück
    {
        return ((lcd_line*LCD_LINE_LENGTH) + lcd_cpos);
    }
    
    void lcd_setCursorPos(uint8_t data) // setzt aktuelle cursorposition als kumulierten Wert
    {
        lcd_cpos = data % LCD_LINE_LENGTH;
        lcd_line = (data / LCD_LINE_LENGTH) % LCD_LINES;
        lcd_CalcCursorUP(true);
    }
    
    uint8_t lcd_xy(uint8_t line, uint8_t pos) // gibt x/y cursorposition als kumulierten Wert zurück
    {
        return ((line*LCD_LINE_LENGTH) + pos);
    }
    
    /*
    * Erzeugt selbst definierte Zeichen im CGRAM, Beisp.:
    *  const uint8_t chrdata0[8] EEPMEM = {
    *     0b00000000,
    *     0b00000000,
    *     0b00001010,     //   X X
    *     0b00011111,     //  XXXXX
    *     0b00001110,     //   XXX
    *     0b00000100,     //    X
    *     0b00000000,
    *     0b00000000};
    *
    * lcd_generatechar(LCD_GC_CHAR0, chrdata0); //erzeugt 5x7 oder 5x10 char (+cursorline)
    * Die Funktion erkennt selbst wie viel Byte einzulesen sind 
    * für 5x10 Zeichen muss das bitarray 11 lines haben und man kann nur jedes 2.te GCCHAR nutzen.
    * Also LCD_GC_CHAR0, LCD_GC_CHAR2, LCD_GC_CHAR4 und LCD_GC_CHAR6
    */
    
    void lcd_generatechar( uint8_t code, const uint8_t *data)
    {
        uint8_t i;
        m32lcd_data( LCD_SET_CGADR | (code<<3) , LCD_CMD ); // Startposition des Zeichens einstellen
        for (i=0; i<sizeof(data); i++ ) 
            m32lcd_data( data[i] , LCD_DATA); // Zeichendaten byteweise hoch laden
        lcd_cpos-=i; // Cursor korrigieren, war ja kein char im Display    
        m32lcd_data(LCD_SET_DDADR + LCDLineAdr[lcd_line] + lcd_cpos, LCD_CMD); // Zurück setzen auf alte cursor Position
    }
    
    /*****************************************************************************/
    // Keypad:
    Das gleiche macht man sinngemäß mit der RP6ControlLib.h und folgendem Code.

    Code:
    // LCD:
    // zur Compile Zeit einstellbar:
    #define LCD_ASCII 0 // wie ASCII Terminal, Steuerungszeichen werden verarbeitet
    #define LCD_BINARY 1 // RAW Binärübertragung ohne Beeinflussung des Datenstroms
    // Modus für putchar, wirkt sich auf die Codesize aus.
    #define LCDMODE LCD_ASCII // Oder LCD_BINARY
    // das Display selbt hat Eigenschaften:
    #define LCD_LINES                0x02     //Anzahl Zeilen
    #define LCD_DISP_LENGTH            0x10    //sichtbare Anzahl Zeichen pro Zeile IN HEX !!! 16Z=0x10,20Z=0x14,24Z=0x18
    #define LCD_WRAP_LINES            1     // 0 buffer wrap?, 1 (default) wrap auf sichtbaren Zeilengrenzen*
    #define LCD_TAB_SIZE            0x04     // Tabulator Position */
    //*Die Einstellungen für Bufferwrap konnten noch nicht getestet werden
    
    //Timings
    #define LCD_BOOTUP_MS           0x28 //40ms
    #define LCD_ENABLE_US           0x01 //1us
    #define LCD_WRITEDATA_US        0x32 //50us
    #define LCD_WRITECMD_MS            0x03 //3ms
    
    //LCD Befehle
    // Clear Display -------------- 0b00000001
    #define LCD_CLEAR_DISPLAY       0x01
    // Cursor Home ---------------- 0b0000001x
    #define LCD_CURSOR_HOME         0x02
    // Set Entry Mode ------------- 0b000001xx
    #define LCD_SET_ENTRY           0x04
    #define LCD_ENTRY_DECREASE      0x00
    #define LCD_ENTRY_INCREASE      0x02
    #define LCD_ENTRY_NOSHIFT       0x00
    #define LCD_ENTRY_SHIFT         0x01
    // Set Display ---------------- 0b00001xxx
    #define LCD_SET_DISPLAY         0x08
    #define LCD_DISPLAY_OFF         0x00
    #define LCD_DISPLAY_ON          0x04
    #define LCD_CURSOR_OFF          0x00
    #define LCD_CURSOR_ON           0x02
    #define LCD_BLINKING_OFF        0x00
    #define LCD_BLINKING_ON         0x01
    // Set Shift ------------------ 0b0001xxxx
    #define LCD_SET_SHIFT           0x10
    #define LCD_CURSOR_MOVE         0x00
    #define LCD_DISPLAY_SHIFT       0x08
    #define LCD_SHIFT_LEFT          0x00
    #define LCD_SHIFT_RIGHT         0x04
    // Set Function --------------- 0b001xxxxx
    #define LCD_SOFT_RESET          0x30
    #define LCD_SET_FUNCTION        0x20
    #define LCD_FUNCTION_4BIT       0x00
    #define LCD_FUNCTION_8BIT       0x10
    #define LCD_FUNCTION_1LINE      0x00
    #define LCD_FUNCTION_2LINE      0x08
    #define LCD_FUNCTION_5X7        0x00
    #define LCD_FUNCTION_5X10       0x04
    // Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)
    #define LCD_SET_CGADR           0x40
    #define LCD_GC_CHAR0            0
    #define LCD_GC_CHAR1            1
    #define LCD_GC_CHAR2            2
    #define LCD_GC_CHAR3            3
    #define LCD_GC_CHAR4            4
    #define LCD_GC_CHAR5            5
    #define LCD_GC_CHAR6            6
    #define LCD_GC_CHAR7            7
    // Set DD RAM Address --------- 0b1xxxxxxx  (Display Data RAM)
    #define LCD_SET_DDADR           0x80
    
    // Modus für LCD-schreibfunktion (RP6 Spezifisch)
    #define LCD_DATA                0 // 8 bit Daten mit RS1
    #define LCD_CMD                    1 // 8 bit Befehl mit RS0
    #define LCD_SINGLE                2 // 4 bit init
    
    #if LCD_WRAP_LINES==1
    #define LCD_LINE_LENGTH LCD_DISP_LENGTH // wrap auf Display Sichtgrenzen
    #else
    #define LCD_LINE_LENGTH 0x40            // wrap auf Buffer Grenzen, Wert=Buffergröße
    #endif
    
    //Dispay Daten Adressen für 2x16, 4x16
    #define LCD_START_LINE1         0x00
    #define LCD_START_LINE2         0x40
    #if LCD_DISP_LENGTH==16            // 16er Displays haben wohl andere Adressen für Line 3&4 als 20er
    #define LCD_START_LINE3         0x10
    #define LCD_START_LINE4         0x50
    #else                            // 20 zeichen/line
    #define LCD_START_LINE3         0x14
    #define LCD_START_LINE4         0x54
    #endif
    
    // neue stdio Ausgabefunktion, alles andere würd über printf und Steuerzeichen gemacht.
    int16_t lcd_putchar(char c, __attribute__((unused)) FILE *stream); // Ausgabe mit Steuerzeichen
    // neue LCD Funktion
    void lcd_wheel(uint8_t line, uint8_t ); // gibt eine Folge von chars als "Wheel" auf x/y aus
    uint8_t lcd_getCursorPos(void); // gibt aktuelle cursorposition als kumulierten Wert zurück
    void lcd_setCursorPos(uint8_t data); // setzt aktuelle cursorposition als kumulierten Wert
    uint8_t lcd_xy(uint8_t line, uint8_t pos); // gibt x/y cursorposition als kumulierten Wert zurück
    void lcd_generatechar( uint8_t code, const uint8_t *data); //schreibt Zeichen in CGRAM
    // internal use
    void m32lcd_data( uint8_t data, uint8_t mode ); // zentrale Schreibfunktion, Mode LCD_DATA/LCD_CMD/LCD_SINGLE
    void lcd_CalcCursorUP(uint8_t setcursor); //Boundary check upward
    void lcd_CalcCursorDOWN(void); //Boundary check downward
    
    /*
    Benutzung von lcd_generatechar()
        const uint8_t chrdata0[8] = {
            0b00000000,
            0b00000000,
            0b00001010,     //   X X
            0b00011111,     //  XXXXX
            0b00001110,     //   XXX
            0b00000100,     //    X
            0b00000000,
            0b00000000
        };
        lcd_generatechar(LCD_GC_CHAR0, chrdata0);
        m32lcd_data(LCD_GC_CHAR0,LCD_DATA); // als direkte Ausgabe im Display-Zeichensatz 0-7
    //oder
        fprintf(M32lcd,"LCD char: \x80"); // die 8 CGCHARS liegen also ab 0x80 bis 0x87 im ASCII Zeichensatz
    */
    
    // alte und geänderte Funktionen
    void initLCD(void);
    
    #ifdef WENEEDRP6IO
    void clearLCD(void);
    void clearPosLCD(uint8_t line, uint8_t pos, uint8_t length);
    void writeCharLCD(uint8_t ch);
    #define writeStringLCD_P(__pstr) writeNStringLCD_P((PSTR(__pstr)))
    void writeStringLengthLCD(char *string, uint8_t length, uint8_t offset);
    void writeStringLCD(char *string);
    void writeNStringLCD_P(const char *pstring);
    void _showScreenLCD_P(const char *line1, const char *line2);
    #define showScreenLCD(__line1,__line2); ({_showScreenLCD_P((PSTR(__line1)),(PSTR(__line2)));})
    void writeIntegerLCD(int16_t number, uint8_t base);
    void writeIntegerLengthLCD(int16_t number, uint8_t base, uint8_t length);
    void setCursorPosLCD(uint8_t line, uint8_t pos);
    /*****************************************************************************/
    // die uart_2 lib
    /*****************************************************************************/
    #define PRECISION 6
    #define DTOSTR_ALWAYS_SIGN 0x01
    #define DTOSTR_PLUS_SIGN 0x02
    #define DTOSTR_UPPERCASE 0x04
    void writeLongLCD(int32_t number, uint8_t base);
    void writeLongLengthLCD(int32_t number, uint8_t base, uint8_t length);
    void writeDoubleLCD(double number, uint8_t width, uint8_t prec);
    void writeDoubleExpLCD(double number, uint8_t prec, uint8_t flags);
    /*****************************************************************************/
    #endif
    #ifndef HEX
        #define HEX 16
    #endif
    #ifndef DEC 
        #define DEC 10
    #endif
    #ifndef OCT
        #define OCT 8
    #endif
    #ifndef BIN
        #define BIN 2
    #endif
    
    /*****************************************************************************/
    // Keys:
    NACHTRAG:
    Hab mal die aktuelle Version von mir eingestellt. Die Geschichte mit dem eeprom ist jetzt hier verbaut und läuft unter dem Aspekt "Deutsche Umlaute auf dem Display" mit automatischem Vorladen in der Init. An den Funktionen selbst hat sich nicht mehr viel geändert, ein paar Bugs sind gefixt. Der Treiber ist wie der UART Treiber über #defines auch wieder konfigurierbar und z.B. die Umlaute/Sonderzeichen oder Steuerzeichen abschaltbar.
    In die RP6CONTROLLIB.H muss auch noch Folgendes eingefügt werden damit es mit dem Speichermanagement klappt. Hinweise zum eeprom auch im entsprechenden Thread oder einfach #define MEMORY suchen und ggf. 0 setzen. Dann landet alles im PGMFLASH.

    Code:
    /*****************************************************************************/
    // Speicherconfig:
    
    #define MEMORY 0 /*0=PROGMEM,1=EEPMEM,2=EEMEM*/
    
    /*****************************************************************************/
    #if MEMORY==0
    #define MEM PROGMEM
    #elif MEMORY==1
    // wenn kein eep import, WEENEEDEEPIMPORT auskommentieren 
    #define WEENEEDEEPIMPORT 1
    #define EEPMEM __attribute__((section(".realeep")))
    #define MEM EEPMEM
    #elif MEMORY==2
    // wenn kein eep import, WEENEEDEEPIMPORT auskommentieren 
    #define WEENEEDEEPIMPORT 1
    #define MEM EEMEM
    #endif
    Den Code für die eep-Ladefunktion findet ihr in dem 1K EEPROM Thread. Bitte auch da den Nachtrag beachten.
    Ich setz mich dann mal an das stdio per TWI. Das kann ein paar Tage dauern. Ich hatte erst vor das über interpretierte Datenströme zu bauen aber da ihr durch Hardware, Remotrol und TWI master/slave Gefummel an die i2c-Registerverwaltung gewöhnt seid, werde ich es wohl erst mal mit virtuellen Registern für die Endgeräte versuchen obwohl die Datenstromvariante schneller, sicherer und leichter umzusetzen wäre.
    Ob Daten an den Motortreiber oder an ein UART durchgereicht werden ist ja vom Ablauf ansich egal... und erstes geht ja schon halbwechs mit der alten lib und den Master/Slave Programmen.

    Für den UART Treiber gibts z.Z. 3 Todos:
    Code:
     * TODO Lesebuffer 16bittig machen um errorcode aus UCSRA und overruns mitzuspeichern? Compatibilität zur fleury-lib?
     *  // -braucht doppelt so viel Ram für Buffer (akt. 2x32 gegen 2x64 byte)
     *  // +Hardwarefehler leichter erkennbar
     * TODO Wheelfunktion als HEX oder Dez Ausgabe
     * TODO hexfilegenerator für export_epp
     * ----
    Ich weis aber noch nicht was ich davon umsetze, mir reicht der Treiber so wie er ist und das Feedback ist mager.

    Gruß
    Geändert von RolfD (22.05.2014 um 01:45 Uhr)
    Sind Sie auch ambivalent?

  10. #20
    Erfahrener Benutzer Fleißiges Mitglied Avatar von Thorben W
    Registriert seit
    17.02.2012
    Ort
    Niedersachsen
    Beiträge
    108
    Moin,
    gibt es diese Lib auch schon für RP6M256uart -> UART ich habe gerade dort ein Problem mit Funkmodulen das mir Daten fehlen.
    Oder bedarf es dafür noch einen verregneten Feiertag

    Thorben

Seite 2 von 3 ErsteErste 123 LetzteLetzte

Ähnliche Themen

  1. IR Senden und Empfangen mit ATtiny
    Von bnitram im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 5
    Letzter Beitrag: 03.03.2012, 12:32
  2. Problem mit dem senden von Zeichen per UART
    Von KingTobi im Forum C - Programmierung (GCC u.a.)
    Antworten: 14
    Letzter Beitrag: 30.10.2008, 20:29
  3. Atmega32/STK500 -UART senden/empfangen klappt nicht
    Von Leuchtturm im Forum C - Programmierung (GCC u.a.)
    Antworten: 12
    Letzter Beitrag: 16.01.2007, 14:02
  4. Uart senden empfangen Interrups
    Von ronald im Forum AVR Hardwarethemen
    Antworten: 15
    Letzter Beitrag: 06.03.2006, 20:24
  5. UART ermöglicht Senden, aber kann nicht Empfangen
    Von batti112 im Forum C - Programmierung (GCC u.a.)
    Antworten: 3
    Letzter Beitrag: 18.09.2004, 15:05

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad