PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : SRF10 an Mega16, Problem nur beim Auslesen?



Andreas_1978
11.06.2010, 16:54
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.



// 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

s.frings
14.06.2010, 18:37
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.

Andreas_1978
15.06.2010, 15:00
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.

s.frings
16.06.2010, 08:55
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.

Andreas_1978
16.06.2010, 18:57
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üß

s.frings
17.06.2010, 09:31
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




#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());

}
}

Andreas_1978
20.06.2010, 18:48
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.

s.frings
21.06.2010, 07:45
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.

Andreas_1978
21.06.2010, 20:51
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.