-         

Ergebnis 1 bis 9 von 9

Thema: SRF10 an Mega16, Problem nur beim Auslesen?

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    14.02.2010
    Beiträge
    5

    SRF10 an Mega16, Problem nur beim Auslesen?

    Anzeige

    Hallo Experten,

    die bisher abgehandelten Beiträge habe ich alle interessiert gelesen, leider brachten sie mich nicht weiter...

    Ich habe Probleme mit dem Ultraschallsensor SRF10.

    Hardware: Mega16, 16 MHZ, Sensor an I2C, Pullups 10k, I2C
    Clock 111kHz. Adresse SRF10: 0xE0

    Software: WinAVR, AVR Studio 4, twimaster.c und I2Cmaster.h von P. Fleury

    Problembeschreibung:
    Die Distance D wird über UART ausgegeben. Er gibt für das Lowbyte L und das highbyte H immer 244 aus. Dies tut er auch, wenn ich ein anderes Register auslese. Sogar, wenn ich Register 0 (Softwareregister) auslesen möchte. Irgend etwas ist da prinzipiell verkehrt. Der Sensor blinkt bei jeder Messung. Jenachdem, wieviel Zeit ich ihm in der loop lasse, blinkt er mal schneller, mal langsamer. Ein echtes Knacken kann ich nicht wirklich wahrnehmen.

    Wegen meiner mittlerweile eingetretenen Betriebsblindheit bitte ich euch, mal einen Blick auf den verkürzten Code zu werfen. Einbnen aller Libs funktioniert, Compilieren usw. auch.

    Code:
    // Testprogrmm SRF10 am ATMega16
    // Crystal: 16.000Mhz
    // Software: winAVR, AVR Studio4,GCC
    
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdio.h>
    #include <util/twi.h>
    #include <......h> //UART, I2C libs usw
    
    unsigned int Firmware;
    char Puffer [10];
    double D;
    int H, L;
    
    #define SRF10_ADR 0xE0
     
    
    
    void read_Firmware(void)
    {
     i2c_start_wait(SRF10_ADR+TW_WRITE);     // set device address and write mode
     i2c_write(0x00);
     i2c_stop();                        // write address = 0
     i2c_rep_start(SRF10_ADR+TW_READ);       // set device address and read mode
     Firmware = i2c_readNak();
     i2c_stop();
    
     //Ausgabe
     sendUART(utoa(Firmware, Puffer, 10));
     sendUART(" ; ");
    }
    
    
    
    void Messung(void)
    {
    //Verstärkung setzen,sende START Condition, Verstärkungsregister wählen, beschreiben mit 7
     i2c_start(SRF10_ADR + TW_WRITE);
     i2c_write(0x01);
     i2c_write(0x07);
     i2c_stop();
     
     //Reichweite setzen, sende START Condition, Reichweitenregister wählen, beschreiben für 1m
     i2c_start_wait(SRF10_ADR + TW_WRITE);
     i2c_write(0x02);
     i2c_write(0x18);
     i2c_stop(); 
    
     //Messung starten, sende START Condition, Befehlsregister wählen, messen in cm
     i2c_start_wait(SRF10_ADR + TW_WRITE);
     i2c_write(0x00);
     i2c_write(0x51);
     i2c_stop();
    
     //warten
     Delay_ms(200);
    
     //Daten auslesen, sende START Condition, Leseregister wählen
     i2c_start(SRF10_ADR + TW_WRITE);
     i2c_write(0x02);               
    
     //wechseln in Lesemodus, high byte lesen, low byte lesen, Berechnung Distance
     i2c_start_wait(SRF10_ADR+TW_READ);
     H = i2c_readAck();
     L = i2c_readNak();
     
     D = (H << 8) + L;
     
     //Ausgabe UART
     sendUART(utoa(D, Puffer, 10));
    }
    
    void main (void)
    {
      ....
     Messung();
     Delay_ms(1000);
     ...
    }
    Ich bin für jeden Tipp unheimlich dankbar.
    Danke für Eure Zeit und Mühe

    Andreas

  2. #2
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Ich kenne die von Dir verwendete I2C Library nicht, aber spontan sind mir einige Ungereimtheiten aufgefallen:

    in read_Firmwarte sendest Du i2c_stop, und dann i2c_rep_start. Das ist nicht richtig. Nach einem stop sendet man ein start, oder man verzichtet auf stop und sendet einen repeated start.

    Ich bin allerdings unsicher, ob das einen Fehler auslöst, denn zumindest beim ATmega16 ist es so, dass er gar keine STOP Condition sendet, wenn man sofort danach den Befehl für eine START Condition absetzt, und außerdem unterscheidet er technisch gesehen nicht wirklich zwischen START und repeated START. Es kann aber sein, dass Deine I2C Library da kritischer ist.

    Ich vermisse eine Fehlerkontrolle. Wann immer Du eine Adresse oder ein Byte sendest, solltest DU prüfen, ob der Sensor mit einem ACK geantwortet hat und nur dann fortfahren.

    Eventuell musst Du dem Sensor nach dem Power-On etwas mehr Zeit geben, um sich zu initialisieren. Ich verwende einen SRF02 und warte eine Sekunde, bevor ich das erste mal drauf zugreife. Diesen Zeitwert habe ich geschätzt und er scheint zu passen.

  3. #3
    Neuer Benutzer Öfters hier
    Registriert seit
    14.02.2010
    Beiträge
    5
    Hallo frings,

    vielen Dank für Deine schnelle Antwort, komme leider erst jetzt zum Antworten.
    Die Fehlerkontrolle habe ich für das Forum rausgeschnitten, um den Quelltext nicht zu lang werden zu lassen. Prinzipiell ist diese aber integriert und bringt keine Diskepanzen. Zumindes beim Senden der START Condi bis zum Schreiben der 51 ins Befehlsregister. Beim Auslesen habe ich keine Fehlerkontrolle.

    Mit der ertsten Sekunde Wartezeit zur Ini des Sensors ist ein interessanter Hinweis. Ich werde dies mal ausprobieren.

    Was mich wundert, ist, dass im Manual kein Info steht, welche Software Version aufgespielt ist. Es ist ein Unterschied, ob ich eine Version "V3.11.05" auszulesen habe oder aber nur "5" zum Beispiel.

    Also, nochmals vielen Dank für Deine Hilfe/Info.
    Sollte jemanden noch etwas einfallen, dann bitte ich um Rückinfo.

  4. #4
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Jepp, das sehe ich auch so. Ich habe mich wegen der Versionsnummer schon beschwert. Mein SRF02 hat liefert mir beim Auslesen die Nummer 5, man hat bestätigt, dass diese Zahl richtig sei.

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    14.02.2010
    Beiträge
    5
    Hallo frings,
    das mit der Versionsnummer 5 habe ich auch schon mehrfach gelesen. Dies sollte also tatsächlich stimmen. Demnach mache ich mich als erstes ran, diese "5" mal auszulesen. Zumindest sollte es einstellige Zahl sein.
    Leider komme ich erst am Wochenende zum Basteln.

    Nochmals vielen Dank. Ich melde mich, wenn ich was erreichen konnte.
    Tschüß

  6. #6
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Wenn Du unsicher bist, ob es vielleicht gar ein Hardwarefehler sein könnte, dann probiere doch einfach mal meinen Source aus.

    Du musst aber vermutlich ein bisschen anpassen, weil er für einen SRF02 geschrieben wurde. Der folgende Source kommt ohne Interrupts aus.

    Du musst mit den folgenden Optionen compilieren:

    -DF_CPU=20000000 (oder wie auch immer)
    -DBAUD=19200 (oder wie gewünscht)

    Ich bin nicht 100% sicher, aber ich glaube, für printf sind die folgenden LDFLAGS nötig:

    -Wl,-u,vfprintf -lprintf_min -lm


    Code:
    #define US_SENSOR 0xE0
    #define US_START_CMD 81
    
        static int serial_write(char, FILE *);
        static int serial_read(FILE *);
        static FILE serialPort = FDEV_SETUP_STREAM(serial_write, serial_read, _FDEV_SETUP_RW);
        
        // Sende ein Zeichen
        static int serial_write(char c, FILE *f) {
            loop_until_bit_is_set(UCSRA, UDRE);
            UDR = c;
            return 0;
        }
    
    
        // Lese ein Zeichen
        static int serial_read(FILE *f) {
            while (readBit(UCSRA, RXC)==0);
            char c=UDR;
            return c;
        }
    
    
        // Initialisiere den seriellen Port
        void initserial(void) {
            // set baudrate
            UBRRH = UBRRH_VALUE;
            UBRRL = UBRRL_VALUE;
            #if USE_2X
                UCSRA |= (1 << U2X);
            #else
                UCSRA &= ~(1 << U2X);
            #endif
            // enable receiver and transmitter
            UCSRB = (1<<RXEN) | (1<<TXEN);
            // framing format 8N1
            #ifdef URSEL
                UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
            #else
                UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
            #endif
            // Bind stdout and stdin to the serial port
            stdout = &serialPort;
            stdin = &serialPort;
        }
    
    
    // Schreibe ein Byte in ein Register eines Slave Gerätes.
    // Der Rückgabewert ist 0, wenn die Kommunikation erfolgreich war.
    uint8_t i2c_write(uint8_t slave_id, uint8_t address, uint8_t data) {
        // Sende START
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTA);
        while (!(TWCR & (1<<TWINT)));
        uint8_t status=TWSR & 0xf8;
        if (status != 0x08 && status != 0x10) goto error;
        // Sende Adresse des Sensors
        TWDR=slave_id;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x18) goto error;
        // Sende Register Nummer
        TWDR=address;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x28) goto error;
        // Sende Befehl (Messen in Zentimeter)
        TWDR=data;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x28) goto error;
        // Sende STOP
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
        return 0;
        
        error:
        // Sende STOP
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
        return 255;
    }
    
    // Lese ein Byte aus einem Register eines Slave Gerätes
    // Der Rückgabewert ist 255, wenn die Kommunikation fehlgeschlagen ist.
    uint8_t i2c_read(uint8_t slave_id, uint8_t address) {
        uint8_t result=0;
        // Sende START
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTA);
        while (!(TWCR & (1<<TWINT)));
        uint8_t status=TWSR & 0xf8;
        if (status != 0x08 && status != 0x10) goto error;
        // Sende Adresse des Sensors
        TWDR=slave_id;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x18) goto error;
        // Sende Register Nummer
        TWDR=address;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x28) goto error;
        // Sende wiederholt START
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTA);
        while (!(TWCR & (1<<TWINT)));
        status=TWSR & 0xf8;
        if (status != 0x08 && status != 0x10) goto error;
        // Sende Adresse des Sensors (read mode)
        TWDR=slave_id+1;
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x40) goto error;
        // lese ein Byte ohne ACK
        TWCR=(1<<TWINT) | (1<<TWEN);
        while (!(TWCR & (1<<TWINT)));
        if ((TWSR & 0xf8) != 0x58) goto error;
        result=TWDR;
        // Sende STOP
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
        return result;
    
        error:
        // Sende STOP
        TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
        return 255;
    }
    
    // Führt eine Ultraschall-Messung durch.
    // Das Ergebnis ist die gemessene Distanz in Zentimeter
    uint16_t distance() {
        // Sende Ping Befehl
        i2c_write(US_SENSOR,0,US_START_CMD);
        // Warte, bis Ergebnis verfügbar ist
        do {
            _delay_ms(1000);
        } while (i2c_read(US_SENSOR,0)==255);
        // Lese das Ergebnis aus
        return (uint16_t) i2c_read(US_SENSOR,2)*255 +i2c_read(US_SENSOR,3);
    }
    
    
    
    // Hauptprogramm
    int main() {
        initserial();
        puts_P(PSTR("reset"));
        while (1) {
    
            // Führe jede Sekunde eine Ultraschall-Messung durch
            _delay_ms(1000);
            printf_P(PSTR("Firmware: %i\n"),i2c_read(US_SENSOR,0));
            printf_P(PSTR("Kalibrier-Wert: %i\n"),(uint16_t) i2c_read(US_SENSOR,4)*255 +i2c_read(US_SENSOR,5));
            printf_P(PSTR("Distanz: %i cm\n"),distance());
    
        }
    }

  7. #7
    Neuer Benutzer Öfters hier
    Registriert seit
    14.02.2010
    Beiträge
    5
    Hallo frings,

    danke für Deinen Quelltext. Ich habe diesen einmal ausprobiert. Die Software Version "5" konnte ich ebenfalls auslesen. Bei der Ausgabe der distance jedoch wird permanent 0 ausgegeben. Selbst das Experimentieren an Verstärkung und Reichweite brachten nichts.

    Ich vermute, dass das Modul im Eimer ist. Allerdings habe ich es bereits gegen ein neues umgetauscht mit selben Ergbnis.
    Mir wird nichts anderen übrig bleiben, als mal einen anderen Typ von US Modul zu probieren.

    Also, nochmals vielen Dank für Deine Hilfe. Sollte ichwas rausfinden, werde ich mich wieder melden.

  8. #8
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Bei meinen ersten Versuchen hatte ich neben der 1 Sekunde zum Initialisieren noch ein Problem mit völlig falschen Meßwerten im Nahbereich. Dafür habe ich über den Händler Support vom Hersteller angefordert und auch prompt bekommen.

    In der Antwort stand, dass die Initialisierung eine automatische Kalibrierung umfasst, welche nur dann funktioniert, wenn eine gewisser Mindestabstand zu Objekten besteht. Und dieser Mindestabstand muss deutlich größer sein, als der geringste messbare Abstand. Bei meinem Sensor SRF02 beginnt der Messbereich bei 16cm, für eine gute Kalibrierung empfahl man mit mindestens 50cm Abstand beim Einschalten der Stromversorgung. Die hatte ich gleich doppelt nicht eingehalten: erstens stand der Roboter auf meinem Schreibtisch direkt vor der Wand (etwa 30cm), und zweitens befanden sich Teile des Roboters im Erfassungsbereich. Ich hatte den Sensor daraufhin gemäß Empfehlung des Herstellers etwas weiter vorne und etwas höher befestigt, sowie eine Sekunde Zeit zur Initialisierung gelassen und den Abstand zur Wand vergrößert, woraufhin er sofort einwandfrei funktioniert hat.

    Vielleicht helfen diese Tipps auch Dir.

  9. #9
    Neuer Benutzer Öfters hier
    Registriert seit
    14.02.2010
    Beiträge
    5
    Die 1 Sekunde warten habe ich bereits "einprogrammiert". Dies änderte am Ergebnis nichts.

    Allerdings interressant ist der Mindestabstand beim Initialisieren. Dazu steht im Manual natürlich nichts. Aber ich hatte schon immer mal den Verdacht, dass die dazugehörige Halterung (Winkel mit Gummimuffe) evtl. im Erfassungsbereich liegt und so ein richtiges Ergebnis von "0" rausgibt. Er sich also selber sieht. Ich habe sie dann wieder demontiert und den Sensor Richtung Decke gehalten. Ohne Erfolg.

    Auch habe ich mal den Oszi drangehangen. Am Sender US habe ich bei jedem Senden ein 5V Impuls sehen können. Am Empfangs US ist das schon schwieriger, wenn man nicht weiß, was rauskommen sollte. Das Experiment liegt schon etwas zurück, an ein sinnvolles Messergebnis kann ich mich nicht erinnern. Ich hatte schon mal die Idee, für 3,9€ eine einzelne US Kapsel zu kaufen und diese mit einem Funktionsgenerator mit 40kHz anzusteuern und auf die Empfangskapsel des Modules zu richten. Wenn er dann nichts "sieht", müsste diese defekt sein.

    Nochmals vielen Dank für Deine wirklich sehr guten Tipps. Ich weiß das sehr zu schätzen.

Berechtigungen

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