- LiTime Speicher und Akkus         
Seite 1 von 3 123 LetzteLetzte
Ergebnis 1 bis 10 von 29

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

  1. #1
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414

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

    Anzeige

    Powerstation Test
    Hallo...
    ich hab vor einiger Zeit mal versuchsweise die RP6uart.c umgeschrieben so das sie per ISR sendet.
    da ich den Code grade wieder vor liegen habe, poste ich ihn mal zur allgemeinen Begutachtung.
    Anlass für die Änderungen waren immer wieder Laufzeitprobleme im Zusammenhang mit UART
    Ausgaben sowie teilweise Datenmüll auf der Receiver.

    Es wurden noch ein paar Dinge korrigiert.
    U.a kann man nun das Zeichen 0x00 zuverlässig empfangen.
    Die Sendefunktion blockt nur bis im Software SendeBuffer wieder Platz ist, sonst wird normal weiter gearbeitet.
    Den Rest erledigt die SendeISR. Die ReceiverISR prüft nun auf Fehler und nimmt nur Zeichen an wenn kein Datenmüll anliegt.
    Bei erkanntem Software Bufferoverrun setzt die Lesefunktion den LeseBuffer zurück.
    Die dazu aufgerufene Funktion clearReceptionBuffer(); kann man gut für eigenes Debuging nutzen.
    Man kann alle alten Funktionen wie gehabt nutzen, es gibt aber zusätlich 2 Ersatzfunktionen
    mit Namen serial_sendc und serial_getc, serial_sendc macht das gleiche wie writeChar, serial_getc
    liest das Zeichen jedoch auf ein Pointer (eines buffers) und gibt im Returnwert 0/1 zurück ob ein Zeichen
    gelesen wurde. Wenn serial_getc eine 1 zurück gibt ist das Zeichen auf der Bufferadresse gültig!
    Auch wenn 0x00 übertragen wurde. Der Sende und Empfangsteil ist unabhängig voneinander,
    man kann auch unterschiedlich große Buffer in der .h einstellen. Die Pointer in den Ringbuffern
    nutzen jeweils nur noch head und tail. Die Codegröße hat sich kaum verändert, einige Vars
    konnte ich einsparen, dafür kommt nun ein weiterer SendeBuffer mit aktuell 32 byte Ram hinzu.

    Ich hoffe, die geänderte lib findet Anklang, vielleicht baut man sie sich ja dauerhaft ein.
    Wäre schön wenn dazu Feedback kommt.

    Dazu die alten Dateien RP6uart.c und RP6uart.h umbenennen -> z.b. so _RP6...
    Neue Dateien in den Verzeichnissen anlegen und den Code unten rein schieben.
    Oder einfach die angehängten Dateien nutzen.

    Die RP6uart.c
    Code:
    /* ****************************************************************************
     * File: RP6uart.c
     *
     * ISR(USART_RXC_vect) geändert. erkennt Frame errors
     * Umbau auf serial_getc, erkennt nun 0x00 chars
     * ISR zum senden
     */
     
    /*****************************************************************************/
    // Includes:
    
    #include "RP6uart.h"
    
    /*****************************************************************************/
    // new UART transmit functions:
    // Data for Ringbuffer
    
    static volatile t_txbuffer tx_buff;
    
    /**
     * UART send ISR.
     * Handles transmission from circular buffer.
     */
    ISR(USART_UDRE_vect, ISR_BLOCK) {
        if (tx_buff.head != tx_buff.tail) {
            UDR = tx_buff.ring[tx_buff.tail];
            tx_buff.tail = (tx_buff.tail + 1) % UART_SEND_BUFFER_SIZE;
        }
        else 
            UCSRB &= ~(1 << UDRIE);
    }
    
    /**
     * send function, nonblocking if free space in buffer
     */
    
    uint8_t serial_sendc(unsigned char data) {
        
        uint8_t next = ((tx_buff.head + 1) % UART_SEND_BUFFER_SIZE);
        while (next == tx_buff.tail);
        tx_buff.ring[tx_buff.head] = data;
        tx_buff.head = next;
        UCSRB |= (1 << UDRIE);
        return 1;
    }
    
    /**
     * compatibility implementation
     * old writeChar function, use serial_sendc instead
     */
    void writeChar(char ch)
    {
        serial_sendc(ch); 
    }
    
    /**
     * Writes a null terminated string or buffer from SRAM to UART.
     * Uses serial_sendc
     * Example:
     *            writeString("RP6 Robot System\n");
     */
    void writeString(char *string)
    {
        while(*string)
            serial_sendc(*string++);
    }
            
    /**
     * Writes a null terminated string from flash program memory to UART.
     * Uses serial_sendc
     * Example:
     *            writeNStringP(PSTR("RP6 Robot System\n"));
     *            // There is also a Macro that makes life easier and
     *            // you can simply write:
     *            writeString_P("RP6 Robot System\n");
      */
    void writeNStringP(const char *pstring)
    {
        unsigned char c;
        for (;(c = pgm_read_byte_near(pstring++));serial_sendc(c));
    }
    
    /**
     * Writes a string with specified length and offset from SRAM to UART.
     * Uses serial_sendc
     * Example:
     *            writeStringLength("RP6 Robot Sytem\n",16,0);
     *            // would output: "RP6 Robot Sytem\n"
     *            writeStringLength("RP6 Robot Sytem\n",11,4);
     *            // would output: "Robot System"
     *             writeStringLength("RP6 Robot Sytem\n",40,4);
     *            // would output: "Robot System\n"
     *            // No matter if the specified length is 40 characters!
     */
    void writeStringLength(char *string, uint8_t length, uint8_t offset)
    {
        for(string = &string[offset]; *string && length; length--)
            serial_sendc(*string++);
    }
    
    /**
     * Write a number (with specified base) to the UART.
     * Example:
     *            // Write a hexadecimal number to the UART:
     *            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(044,OCT);        // Ocal
     *            writeInteger(0b11010111,BIN); // Binary
     */
    void writeInteger(int16_t number, uint8_t base)
    {
        char buffer[17];
        itoa(number, &buffer[0], base);
        writeString(&buffer[0]);
    }
    
    /**
     * Same as writeInteger, but with defined length.
     * Example:
     *            // Write a hexadecimal number to the UART:
     *            writeIntegerLength(0xAACC, 16, 8);
     *            // Instead of 16 you can also write "HEX" as this is defined in the
     *            // RP6RobotBaseLib.h :
     *            writeIntegerLength(0xAACC, HEX, 8);
     *            // Other Formats:
     *            writeIntegerLength(1024,DEC,6);      // Decimal
     *            writeIntegerLength(044,OCT,4);        // Ocal
     *            writeIntegerLength(0b11010111,BIN,8); // Binary
     */
    void writeIntegerLength(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--, writeChar('0'));
            writeString(&buffer[0]);
        }
        else 
            writeStringLength(&buffer[0],length,-cnt);
    }
    
    /*****************************************************************************/
    // new UART receive functions:
    // Data for Ringbuffer
    
    static volatile t_rxbuffer rx_buff;
    
    /**
     * UART receive ISR.
     * Handles reception to circular buffer, handles errors.
     */
    ISR(USART_RXC_vect, ISR_BLOCK) {
        if ( ! (UCSRA & ((1<<FE)|(1<<DOR)|(1<<PE)))) {
            volatile uint8_t data = UDR;
            volatile uint8_t next = ((rx_buff.head + 1) % UART_RECEIVE_BUFFER_SIZE);
            if (next != rx_buff.tail) {
                rx_buff.ring[rx_buff.head] = data;
                rx_buff.head = next;
            } else
                rx_buff.uart_error=rx_buff.uart_error + 0x80;
        }
        else {
            volatile uint8_t data __attribute__((unused)) = UDR;
            rx_buff.uart_error++;
        }
    }
    
    /**
     * Read a char from the circular buffer to a pointer. 
     * Reset error counter on frame errors, do buffer reset on overflow
     * returns 1 if a valid char is received, 0 if not
     * Example:
     *
     * // [...]
     * if(getBufferLength())
          if (serial_getc(&receivedData[data_position++]))
             found_a_char();
     * // [...]
     *
     */
    uint8_t serial_getc(unsigned char *data) {
        if (rx_buff.uart_error < 0x80){
            if (rx_buff.head == rx_buff.tail)
                return 0;
            *data = rx_buff.ring[rx_buff.tail];
            rx_buff.tail = (rx_buff.tail + 1) % UART_RECEIVE_BUFFER_SIZE;
            rx_buff.uart_error = 0;
            return 1;
        } else {
            clearReceptionBuffer();
            return 0;
        }
    }
    
    /**
     * compatibility implementation 
     * old readChar function, use serial_getc instead
     * -> there was no way to check if a received char was NULL or the buffer was even empty
     * Example:
     * // [...]
     * if(getBufferLength())         
     *       receivedData[data_position++] = readChar();
     * // [...]
     *
     */
    char readChar(void)
    {
        unsigned char data = 0;
        serial_getc(&data);
        return data;
    }
    
    /**
     * this function copies numberOfChars chars to buf. 
     * It also returns the number of characters really copied to the buffer! 
     * Just in case that there were fewer chars in the buffer...
     */
    uint8_t readChars(unsigned char *buf, uint8_t numberOfChars)
    {
        uint8_t i = 0;
        while(serial_getc(&buf[i]) && (i != numberOfChars))
            i++;
        return i;    
    }
    
    /**
     * Returns the current number of elements in the buffer.
     * Example:
     * s. readChar function above!
     */
    uint8_t getBufferLength(void)
    {
        return ((rx_buff.head + UART_RECEIVE_BUFFER_SIZE - rx_buff.tail) % UART_RECEIVE_BUFFER_SIZE);
    }
    
    /**
     * Clears the reception buffer - it disables UART Receive interrupt for a short period of time.
     * if we execute this, something is going really wrong with the buffer (overflow)
     */
    void clearReceptionBuffer(void)
    {
        UCSRB &= ~(1 << RXCIE);
        rx_buff.head = 0;
        rx_buff.tail = 0;
        rx_buff.uart_error = 0;
        UCSRB |= (1 << RXCIE);
        
        // alert the Programmer urgently, we are in heavy trouble
    }
    
    // EOF
    und die passende RP6uart.h

    Code:
    /* ****************************************************************************
     * File: RP6uart.h
     * Version: new 1.00
     * Target: RP6 Base & Processor Expansion - ATMEGA32 @8.00 or 16.00MHz
     * ****************************************************************************
     */
    
    #ifndef RP6UART_H
    #define RP6UART_H
    
    /*****************************************************************************/
    // Includes:
    
    #include <avr/pgmspace.h>     // Program memory (=Flash ROM) access routines.
    #include <stdlib.h>            // C standard functions (e.g. itoa...)
    #include <string.h>
    #include <avr/io.h>            // I/O Port definitions
    #include <avr/interrupt.h>    // Interrupt macros (e.g. cli(), sei())
    
    /*****************************************************************************/
    // UART
    
    // TX:
    
    uint8_t serial_sendc(unsigned char);
    void writeChar(char);
    void writeStringLength(char *, uint8_t, uint8_t );
    void writeString(char *);
    void writeNStringP(const char *);
    #define writeString_P(__pstr) writeNStringP((PSTR(__pstr)))
    
    #define UART_SEND_BUFFER_SIZE 32 // Default buffer size is 32!
    
    typedef struct {
        volatile uint8_t ring[UART_SEND_BUFFER_SIZE];
        volatile uint8_t head;
        volatile uint8_t tail;
    } t_txbuffer;
    
    #define HEX 16
    #define DEC 10
    #define OCT 8
    #define BIN 2
    void writeInteger(int16_t, uint8_t);
    void writeIntegerLength(int16_t, uint8_t, uint8_t);
    
    
    // RX:
    #define UART_RECEIVE_BUFFER_SIZE 32 // Default buffer size is 32!
    
    // t_buffer.uart_error shoud be always 0, if it contains Values from 1-127,
    // we found Hardware Problems like Baud mismatch and its reseting by serial_getchar.
    // if it contains values over 127, we found buffer overruns.
    typedef struct {
        volatile uint8_t ring[UART_RECEIVE_BUFFER_SIZE];
        volatile uint8_t head;
        volatile uint8_t tail;
        volatile uint8_t uart_error;
    } t_rxbuffer;
    
    uint8_t serial_getc(unsigned char *);
    char readChar(void);
    uint8_t readChars(unsigned char *, uint8_t);
    uint8_t getBufferLength(void);
    void clearReceptionBuffer(void);
    
    #endif
    
    // EOF
    Gruß
    Angehängte Dateien Angehängte Dateien
    Geändert von RolfD (19.04.2014 um 00:45 Uhr)
    Sind Sie auch ambivalent?

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

    sieht gut aus. Werde mal in nächster Zeit testen ...

    Ich habe deine Lib im "RP6 - Programmierung" Artikel unter Projekte verlinkt.
    Gruß
    Dirk

  3. #3
    Erfahrener Benutzer Robotik Einstein Avatar von inka
    Registriert seit
    29.10.2006
    Ort
    nahe Dresden
    Alter
    76
    Beiträge
    2.180
    hi Rolf,

    so neugierig und manchmal ahnungslos ich manchmal bin habe ich nach ISR gegoogelt. Da kam dann

    von ISR - Itzehoer Schrott und Recycling GmbH & Co. KG
    überISR Ingenieurbüro Schlegel & Reußwig GmbH.
    bis zu ISR- Internationaler StudierendenRat

    bei der Suche nach AVR + ISR war das dann schon besser ...

    ich habe mich bisher mit der kommunikation über UART nur ganz wenig beschäftigt, habe mir die "RP6Base_SerialInterface_01" und "RP6Base_SerialInterface_02" angeschaut und wollte in absehbarer zeit - da mein RP6 nun dank BT "draht und kabel" unabhängig rumfahren kann - mit dem thema intensiver beschäftigen. Also nicht nur mit dem robby "übers alter plaudern", sondern auch mal wichtigeres tun und tun lassen...

    Ist die neue lib in der richtung etwas, was andere, bessere möglichkeiten bietet als die ursprügliche lib?
    gruß inka

  4. #4
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Fachlich sage ich dazu:
    ISR steht im alllgemeinen bei Programmierern für "Interupt Service Routine" weshalb in der WinAVR
    http://www.nongnu.org/avr-libc/user-...nterrupts.html
    dies auch als Macro definiert ist.
    Weitere Infos zu ISR bieten auch das Datenblatt des Prozessors oder der vorhandene RP6lib Quellcode, die boardeigene Suche erschlägt dich gar zum Thema ISR.

    In der Originaldatei steht auch drin:
    * In this new version, the reception functions have been completely rewritten
    * and are now Interrupt based with a circular buffer.
    und
    /**
    * UART receive ISR.
    * Handles reception to circular buffer.
    */
    Folglich wurde die Datei auch schon mal von Arexx bzw. Slyd umgeschrieben und man kann sich ableiten was eine ISR ist.
    Ein blick ins changelog unten in der Datei besagt ebenfalls das man da schon was dran geändert hat.

    Auf deine Frage bezogen:
    Ist die neue lib in der richtung etwas, was andere, bessere möglichkeiten bietet als die ursprügliche lib?
    Ja und nein.
    Du wirst den Unterschied zwischen den beiden Versionen vermutlich kaum merken.
    Du hattest ja anscheinend auch noch nicht die Schwierigkeiten und Beobachtungen wie ich sie einleitend beschrieb.
    Meine Version ist eine Alternative... ein Angebot ...kein verbindlicher Fix.
    Und natürlich ändert sich was... genau das was ich beschrieben habe.

    Jeder kann sich selbst entscheiden ob er die originale Version nutzt, oder die geänderte Fassung verwendet.

    Dirk hat ja auch private Änderungen in der RP6control.c angeboten.
    * - v. 1.3beta additions by Dirk Ottensmeyer
    * - NEW: ADC task, free I/O port functions, tone frequencies
    Nebenbei: Normalerweise würde ich Erweiterungen in einer separaten include Datei anbieten aber da hier nicht nur Funktionen hinzugefügt sondern ersetzt werden, geht das nur durch ersetzen der Originaldatei wenn man sein Quellcode nicht auch noch ändern will. Aber es steht auch jedem frei, die Datei anders zu benennen und z.b. über #define und #if umschaltbar zu includieren... oder komplett zu ignorieren...

    Kämst du denn auf die Idee, Dirk oder Slyd die gleiche Frage wie mir zu stellen wenn die Änderungen anbieten?
    Naja egal...
    Ich hoffe, ich konnte helfen.
    Gruß und auch frohe Ostern
    Geändert von RolfD (20.04.2014 um 23:22 Uhr)
    Sind Sie auch ambivalent?

  5. #5
    Erfahrener Benutzer Robotik Einstein Avatar von inka
    Registriert seit
    29.10.2006
    Ort
    nahe Dresden
    Alter
    76
    Beiträge
    2.180
    Zitat Zitat von RolfD Beitrag anzeigen
    In der Originaldatei steht auch drin:
    das mit den originaldateien und dem englisch wird mit zunehmendem alter und damit einem sich immer vergrößernden abstand zum berufsleben und dort verwendeten fremdsprachen immer schwieriger und anstrengender...

    Zitat Zitat von RolfD Beitrag anzeigen
    Du wirst den Unterschied zwischen den beiden Versionen vermutlich kaum merken.
    Du hattest ja anscheinend auch noch nicht die Schwierigkeiten und Beobachtungen wie ich sie einleitend beschrieb.
    nein hatte ich nicht, um im programmieren weiter zu kommen wird ISR aber sicher ein thema sein müssen...

    Zitat Zitat von RolfD Beitrag anzeigen
    oder komplett zu ignorieren...
    die schlechteste der möglichkeiten um mit verbesserungen umzugehen, man muss sie aber verstehen...

    Zitat Zitat von RolfD Beitrag anzeigen
    Kämst du denn auf die Idee, Dirk oder Slyd die gleiche Frage wie mir zu stellen wenn die Änderungen anbieten?
    klar kam ich schon öfters auf diese idee, beide können glaube ich ein lied von singen...
    gruß inka

  6. #6
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Ok, da das Thema ISR für dich Neuland zu sein scheint, versuche ich das noch etwas genauer zu erklären.

    Prozessoren arbeiten normalerweise einfach ein Programm ab und fragen dabei Zustände ab.
    Auch wenn Prozessoren dabei sehr schnell sind, bedeutet das ggf. warten auf ein Ereignis/Zustand.
    Wärend des Wartens kann es nun sein das an anderer Stelle etwas passiert, also ein Ereignis statt findet.
    Dafür hat man Interrupts erfunden. Der erste, wichtigste und selbstverständlichste "Interrupt"
    ist eigentlich das Reset Signal. Es zwingt den Prozessor die Programmstruktur von vorn abzuarbeiten, und
    unterbricht die laufende Arbeit.
    Es gibt weitere Interrupts, manche hängen mit externen Leitungen zusammen, manche passieren auf Grund
    von Änderungen im Prozessor. Der Unterschied von einem Reset zu einem echten Interruprt wie man es im
    RP6 nutzt ist, das man nach einem Interrupt an der Stelle weiter arbeiten kann, wo man vom Interrupt
    unterbrochen wurde. Wärend einer Warteschleife kann also auf Anforderung eines Interrupts spezieller Code
    abgearbeitet werden und nach Beendigung des Spezialcodes kehrt der Prozessor an seine Warteschleife zurück.
    Diesen speziellen Code nennt man ISR. Interrupts haben dabei Eigenschaften.

    Interrupts kann man per Software einzeln oder alle ab und an schalten.
    Interruptquellen gibt es massenhaft... ich meine so um ca. 20 im RP6 und werden durch bestimmte Ereigisse ausgelöst.
    Andere Prozessoren kennen bis zu mehrere hundert und sogar per Software auslösbare.
    Allerdings muss man eine ISR nur für die Interrupts haben wenn man diesen speziellen Interrupt auch braucht / nutzt.
    Interrupts bzw. die Ausführung der passenden ISR können geschachtelt passieren wenn 2 Interrupts dicht aufeinander folgen.
    ISR sollten kurz sein damit schnell wieder zum vorherigen Programmteil zurück gesprungen werden kann.
    Das Hauptprogramm merkt nicht das es durch ein Interrupt unterbrochen wurde. Da ISR aber Register und Variablen ändern
    können, sollten alle diese Variablen als volatile definiert sein.
    Der Compiler sorgt durch die Definition ISR dafür, das die Register im Prozessor vor Ausführung der ISR gesichert und bei beenden
    der ISR der alte Zustand in den Registern wieder hergestellt werden.

    Eine ISR ist also ein kleines Stück Programm, welches unabhängig vom Hauptprogramm bei Eintreten eines Ereignisses
    ausgeführt wird. Nehmen wir als Beispiel einen Radencoder aus der RP6RobotBaseLib.c.

    Code:
    ISR (INT0_vect)
    {
        mleft_dist++;
        mleft_counter++;    
    }
    Der Radencoder leitet sein Signal ENC_R oder L laut Schaltplan an PD2 bzw. PD3. Impulse an PD2 und PD3 führen
    daher zum auslösen des jeweiligen Interrupts. Eingeschaltet wurden die Interrupts in der Funktion void initRobotBase(void)
    unter // Initialize External interrupts:

    Rollt der RP6 nun, werden sehr viele Interrupts ausgelöst und wie in der ISR zu sehen 2 Variablen aufaddiert.
    Würden wir an der Stelle keinen Interrupt einsetzen, müsste der Prozessor laufend den Eingang auf Veränderungen abfragen.
    Gehen würde das auch aber der RP6 könnte kaum noch was anderes machen.
    Also nutzt man da besser Interrupts.

    Guckst du dir nun das UART file an, wirst du sehen das zum Empfangen von Zeichen schon eine ISR genutzt wird.
    Beim senden wird in der Originaldatei das Senderegister durch eine Warteschleife mit while (!(UCSRA & (1<<UDRE)));
    abgefragt. Das bedeutet, der Prozessor wartet mit dem versenden von Zeichen IM HAUPTPROGRAMM bis jeweils das Senderegister leer ist um ein weiteres Zeichen zu versenden.
    In der Zeit passieren zwar Interrupts aber er macht eben nichts anderes.
    Meine Änderung ist in der Hauptsache der Umbau der Sendefunktion auf ISR, so das das nächste Zeichen aus einem Ringbuffer (32 Byte) "automatisch" bzw. per ISR ins Senderegister kopiert wird sobald das Register frei ist. "Senderegister frei" ist dabei eine der vielen Interrupt quellen. Das Hauptprogramm füllt also den Buffer und die ISR arbeitet das ab.
    Es gibt 2 Situationen, die eine Ausnahme erfordern. Buffer voll.... dann muss eben doch gewartet werden bis wieder Platz
    im Buffer ist.. oder Buffer leer.. ist nichts zum senden da ...dann muss man den Interrupt abschalten.
    Daraus folgt, das man in der Sendefunktion den Interrupt zunächst anschalten muss damit die ISR dann die weitere Arbeit
    machen kann... Das ist auch der Unterschied zwischen Radencoder- und UART-ISR ..Die ISR für die Radencoder läuft
    immer, die SendeISR nur dann wenn Zeichen zu übertragen sind. Damit wartet aber nicht mehr das Hauptprogramm auf "Senderegister frei". Das verändert natürlich das Laufzeit Timing des Hauptprogramms so lange der Buffer nicht voll ist.
    Ein komplexes Betriebssystem könnte nun z.B. auch noch den Buffer vergrößern wenn es auf "Buffer voll" trifft. Für den RP6 ist das aber zu komplex bzw. zu viel Aufwand.

    ISR sind also kleine Progrämmchen, die auf ein Event (IRQ oder Interruptrequest) hin unabhängig vom Hauptprogramm
    vom Prozessor angesprungen werden. Das ist ein sehr vielseitiges Konzept und kann sogar dafür genutzt werden,
    eine Form von Multitasking (Timeslicing) zu nutzen indem per ISR und Timerinterrupt zwischen mehreren Hauptprogrammen
    umgeschaltet wird. Beim RP6 wird dies so nicht genutzt, dort laufen aber viele Reaktionen auf Sensoren über ISR.

    So.. das war eine kurze Einführung zum Thema ISR aus dem Ärmel geschüttelt. Vielleicht hilft dir das weiter.

    Weitere Änderungen von mir betreffen eigentlich nur einen etwas anderen (einfacherem) Ringbuffer Algorithmus, die Erkennung des Zeichen 0x00, welches vorher nicht erkannt werde konnte weil 0x00 als returnwert für "kein char empfangen" stand und ein wenig Kosmetik.
    Gruß
    Geändert von RolfD (21.04.2014 um 12:55 Uhr)
    Sind Sie auch ambivalent?

  7. #7
    Erfahrener Benutzer Robotik Einstein Avatar von inka
    Registriert seit
    29.10.2006
    Ort
    nahe Dresden
    Alter
    76
    Beiträge
    2.180
    Zitat Zitat von RolfD Beitrag anzeigen
    Ok, da das Thema ISR für dich Neuland zu sein scheint, versuche ich das noch etwas genauer zu erklären.
    danke für diese hilfreiche erklärung. Werde ich sicher noch paarmal lesen...


    Zitat Zitat von RolfD Beitrag anzeigen
    Rollt der RP6 nun, werden sehr viele Interrupts ausgelöst und wie in der ISR zu sehen 2 Variablen aufaddiert.
    Würden wir an der Stelle keinen Interrupt einsetzen, müsste der Prozessor laufend den Eingang auf Veränderungen abfragen.
    Gehen würde das auch aber der RP6 könnte kaum noch was anderes machen.
    Also nutzt man da besser Interrupts.
    was mich sehr reizen würde wäre eine kommunikation mit dem RP6, wenn er etwas weiter entfernt ist. Kabellos kann ich ja jetzt kontakt halten, ich möchte z.b dass er es mitteilt, wenn bestimmte ereignisse eintreten und auch auf anweisungen reagiert. Nicht als RC-auto oder steuerung über die fernbedienung - verstehst Du was ich meine? Ich habe jetzt aber erstmal noch keinen plan wie ich es anfange, nur völlig nebelhafte ideen...


    Zitat Zitat von RolfD Beitrag anzeigen
    Interrupts kann man per Software einzeln oder alle ab und an schalten.
    Interruptquellen gibt es massenhaft... ich meine so um ca. 20 im RP6 und werden durch bestimmte Ereigisse ausgelöst.
    Andere Prozessoren kennen bis zu mehrere hundert und sogar per Software auslösbare.
    ist z.b. der MRESET (ist ja auch ein interrupt) per software auslösbar?
    gruß inka

  8. #8
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Also so weit ich weiss, ist die Reset Leitung /RESET eine Input Schaltung am Prozessor.
    Seite 34 im PDF zur CPU unter "System Control and Reset" beschreibt aber schon allein 5 Quellen, u.a. auch ein Watchdog,
    also eine Art Timer der ein Reset auslösen kann. JTAG Adapter und auch der RS232 Adapter von AREXX können die CPU
    ebenfalls von extern aus resetten. Technisch ist resetten aber nur das zwangsweise setzen der CPU auf ein definierten Zustand...
    Ich kann mir vorstellen das es in Assembler einen Reset Befehl gibt, ich weis aber nicht ob dieser dazu führt,
    das auch das externe /Reset Signal bedient wird. Im simpelsten Fall kann man per Software ein Reset bewirken indem man den Reset Vektor anspringt.
    In Normalfall verwendet man sowas aber nicht, da man auch jedes mal die CPU initialisieren müsste. Da kann man aber statt dessen auch gleich die CPU in den State bringen, den sie haben soll.
    Es geschehen ja nicht irgend welche wunderlichen Dinge beim resetten - wenn man mal von so Dingen wie dem Bootloader absieht.
    Da würde ich dich aber thematisch gern an Slyd verweisen weil der Bootloader nach wie vor nicht open source und auch kaum dokumentiert ist.
    Man kann sich aber durchaus auch freie Bootloader angucken und lernen... allerdings ist das glaube ich zu speziell für RP6 Anwender und nicht essentiell für das allgemeine Verständnis von Interrupts relevant. Ich hatte den Reset auch nur angeführt um zu zeigen das die aktuelle Programmausführung durch ein externes Signal unterbrochen wird ohne das das Hauptprogramm das mitbekommt. Von einem Reset aus wird nicht zurück ins Programm gesprungen, von einem INT aus durch reti ja. reti = assemblerbefehl am Schluß einer ISR vergleichbar mit "return from interrupt".

    Gruß
    Sind Sie auch ambivalent?

  9. #9
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Ich versuche hier mal laut zu denken...
    Es gibt ja die stdio Funktionen, welche man evtl. aus anderen Projekten kennt. Leider führen diese Funktionen zu einer doch recht relevanten code Vergrößerung von ca. üblichen 40 byte RAM und 1400 byte Code, allerdings bieten die auch genormte, erprobte, einheitliche Schnittstellen. Wenn man um jedes Byte Platz in der CPU kämpft, sollte man sie vielleicht vermeiden. Klar! Als einfache "Hallo Welt" Write Funktionen reichen die internen aus der RP6 Lib allemal.
    Entschlackt man aber die Programme und wirft die RP6 Funktionen raus, spart man auch wieder Platz. Die reale Codegröße im Projekt ist also kleiner als die stdio Funktionen auf den ersten Blick brauchen. Ich schätze mal vielleicht 1000 Byte. Nimmt man noch mal den vorhandenen Aufwand für bisher extra schreib/lese Funktionen bei TWI, LCD und File_IO auf SD und EEPROM in der RP6 lib, verkleinert sich der Platzbedarf letztlich noch mal gegenüber der bisher genutzten Write Geschichten. Man braucht ja letztlich nur eine lese/schreib funktion per "device" und den rest erledigt printf/scanf. Allerdings ist man dann an die Möglichkeiten dieser Funktionen auch zunächst gebunden. Viele größere Projekte bauen allerdings auf die stdio auf.

    Eine Ausgabe auf RS232 und alternativ TWI sähe dann so aus:
    Code:
    // Filehandle erstellen
        static FILE uart_io = FDEV_SETUP_STREAM (uart_putchar, uart_getchar, _FDEV_SETUP_RW);
        static FILE twi_io = FDEV_SETUP_STREAM (twi_putchar, twi_getchar, _FDEV_SETUP_RW);
    
    //Zuweisung von Standardkanälen, kann alles mögliche an devices sein
        stdin = stdout = &uart_io;
    //Ausgabe auf Standardkanal
        printf     ("Hello, world!\n");
    //Ausgabe auf TWI-Kanal
        fprintf (twi_io,"Hello, world!\n");
    //jetzt wird TWI-Kanal als Standard Kanal getauscht
        stdin = stdout = &twi_io;
    //Ausgabe auf UART-Kanal
        fprintf (uart_io,"Hello, world!\n");
    //Ausgabe auf twi als Standardkanal
        printf     ("Hello, world!\n");
    Das würde natürllich auch mit scanf, also formatierter Eingabe gehen... und es ständen eben alle Funktionen aus der stdio zur Verfügung.
    Das Ganze hat neben dem Platzbedarf einen großen Nachteil für Anfänger und die Demo-Programme.
    Man müsste entweder die Write Funktionen auf das stio umschreiben was wieder Platz kostet...
    oder man müsste die Demos auf stdio umschreiben was viel Arbeit ist.
    Die stdio Funktionen bieten auch die Formatierungen der rp6 Write Funktionen und mehr... incl. WriteLong und WriteFloat und ähnlichem Krempel.

    Der Nutzen von stdio wächst mit der Komplexität von Ein/Ausgaben, ein Projekt was jedoch nur ab und zu eine debug Meldung absetzt und ansonsten nur als Statemachine Sensoren abfragt, profitiert weniger davon. Aber wenn es auf geregelte Kommunikation ankommt... also bei Funkmodulen, diverse UARTs, I2C zwischen Prozessorboards, Filesystemen usw., ist sie von Vorteil.

    Ich hab ein bischen versucht, die Vor und Nachteile abzuwägen.. nun meine Fragen an euch: Würdet ihr auf stdio umsteigen? Besteht Bedarf für eine angepasste Lib? Wie seht ihr das?

    Gruß
    Geändert von RolfD (23.04.2014 um 14:28 Uhr)
    Sind Sie auch ambivalent?

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

    prinzipiell finde ich deinen Vorschlag gut, allerdings habe ich Verständnis-Fragen dazu:

    1. Der Hauptzweck mobiler Roboter besteht ja sicher eher nicht darin, Messwerte und Daten in großer Menge ANZUZEIGEN, sondern ihr Verhalten daran auszurichten. Das heißt nicht, dass das Anzeigen von Daten nicht wichtig wäre: Im Gegenteil kann man das gut zur Kontrolle der eigenen Programmierung nutzen. Frage: Braucht man also optimierte Ausgabefunktionen unter der genannten Einschränkung?

    2. Durch die Aufteilung der Prozessoren im RP6-System (Base, M32, M128, M256 WiFi) auf jeweils eigene autonome Systeme müßte man ja im Prinzip die printf/scanf Funktionen für jede dieser Plattformen zur Verfügung stellen (das ist ja bei den jetzigen Funktionen auch so). Macht das Sinn?

    3. Die printf/scanf Funktionen haben ja ihre Hauptvorteile neben den standardisierten Formatierungen in der einfachen Wählbarkeit des Ein-/Ausgabekanals. Das finde ich natürlich auch reizvoll. Aber: Nicht alle "Kanäle" (I2C, UART, SPI, Filezugriff auf SD, int./ext. EEPROM ...) sind auf allen Plattformen verfügbar. D.h. man würde nicht den Zugriff auf alle Kanäle auf jeder Plattform brauchen. Einverstanden?

    4. Reizvoll wäre auch, wenn die verschiedenen Kanäle über das komplette RP6-System von allen Plattformen hinweg angesprochen werden könnten. Das scheitert allerdings daran, dass als alleinige Verbindung zwischen den Plattformen der I2C-Bus vorgesehen ist. Dazu kommen dann nur noch je nach Hardwareausstattung wenige Ein-Ausgabekanäle auf der jeweiligen Plattform. Und: Zur kabelgebundenen Verbindung zum PC wird ausschließlich der UART verwendet, eine Kommunikation ist aber davon abhängig, ob das Interface gerade an die jeweilige Plattform angeschlossen ist. Welche Vorteile könnten printf/scanf Funktionen bei dieser vorliegenden Hardware bringen?

    5. Was meinst du mit "Entschlackt man aber die Programme und wirft die RP6 Funktionen raus, spart man auch wieder Platz."? Die meisten Funktionen in den RP6 Libs sind ja nicht Ein-/Ausgabefunktionen. Was sollte man da rauswerfen (ok, die bisherigen Ein-/Ausgabefunktionen ja auf jeden Fall,- die würden ja durch neue Funktionen ersetzt...), ohne dass Funktionalität fehlt?

    6. Was schätzt du, wie hoch der Aufwand ist, die Ein-/Ausgabefunktionen und ggf. die Demos neu zu schreiben? Würdest du das unter best. Bedingungen angehen?

    7. Würdest du mal eine "Musterfunktion" oder Demo zum Testen hier zeigen?
    Gruß
    Dirk

Seite 1 von 3 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, 13: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, 21: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, 15:02
  4. Uart senden empfangen Interrups
    Von ronald im Forum AVR Hardwarethemen
    Antworten: 15
    Letzter Beitrag: 06.03.2006, 21: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, 16:05

Berechtigungen

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

LiFePO4 Speicher Test