Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Ansteuerung eines SparkFun AS7265x Triad Sensors
Ich habe Probleme das oben genannte Sensor Board über I2C anzusprechen.
Anscheinend gibt es da 4 Register die man ansprechen kann.
Der Rest ist in sogenannten virtuellen Registern.
Über I2C kann man doch eigentlich nur die Adresse des Bausteins angeben und eines oder mehrere Bytes senden bzw. empfangen.
Wie kann man z.B. das Status Register auslesen? Gibt man die Adresse des Bausteins mit einem Lesebefehl an, kann man die Register doch nicht mehr addressieren.
Das Datenblatt gibt sich da kryptisch.
Im Pseudocode werden da direkt die Registeradressen mit 0x00 bis 0x03 angesprochen.
Die Arduino library hab Ich mir auch angesehen, aber auch daraus werd Ich nicht schlau, zudem werden da auch noch weitere Bibliotheken der ARDUINO Plattform verwendet.
Hat sich da schon mal wer damit beschäftigt?
Holomino
13.09.2024, 11:46
Bei I2C musst Du ja das "adressierte Lesen" (Zugriff auf eine adressierte Speicherzelle oder ein "Register") normalerweise durch zwei Sequenzen ausführen.
1. Schreibbefehl, um die abzufragende Adresse zu setzen
2. Lesebefehl, bei dem der Slave die adressierten Daten zurückgibt.
Zwischen den beiden Sequenzen wird wirklich ein I2C-Stop-Status (die richtige Konstellation der Pegelwechsel auf SDA und SCL) erwartet und der Lesebefehl enthält am Anfang auch noch einmal die Slave-Adresse (dieses mal mit gesetztem Leseflag).
Also wird wohl die Übertragung zum Lesen sein
Sequenz 1:
- I2C-Slave-Adresse mit Schreib-Flag
- Adresse Statusregister
- Stop
Sequenz 2:
- I2C-Slave-Adresse mit Lese-Flag
- Rückgabe des adressierten (Status-)Registerwertes
- Stop
Das bitteschön zusammen mit Sequenz 1 so lange wiederholt, bis das Tx-Flag das Schreiben oder lesen zulässt
Sequenz 3:
- I2C-Slave-Adresse mit Schreib-Flag
- Adresse Write-Register
- Registeradresse mit Read-Flag
- Stop
Sequenzen 5 und 6:
- Wie 1 und 2, nur auf das Rx-Flag im Statusregister angewendet
Sequenz 7:
- I2C-Slave-Adresse mit Schreib-Flag
- Adresse Read-Register
- Stop
Sequenz 8:
- I2C-Slave-Adresse mit Lese-Flag
- Holla, schon kommt der Wert
- Stop
Klingt kompliziert, ist aber ein probates Mittel, das Busy-Stretchen des Ack-Flags aus der I2C-Spec zu vermeiden.
Was Du eigentlich brauchst, ist eine Bibliothek, die sich an den Parametern der Funktionen im Pseudocode orientiert, also...
status = i2cm_read(I2C_AS72XX_SLAVE_STATUS_REG)
...würde dann für Dich die Sequenzen 1 und 2 in einem Rutsch ausführen.
Wenn Du da nix findest UND es sich bei Deinem Master um den zuletzt durchgekauten AVR32DB32 (UPDI) handelt:
Die TWI-Registerbeschreibung ähnelt sehr der Tiny-1er-Serie und da hab ich auch mal was gemacht.
Hast Du das mit so einem Sensor schon mal ausprobiert?
Wenn das so funktioniert wäre Ich schon weiter.
Das Datenblatt und der Pseudocode lassen sich im Endeffekt darüber nicht aus, es wird immer nur auf die Arduino Library verwiesen.
I2C Lese und Schreiboperationen hab Ich schon geproggt, die AS7265x programmteile müssen jetzt nur noch darauf zugreifen.
Mir war einfach nicht klar, das man die Adressierung in 2 Schritten, mit einer Stop Condition dazwischen, durchführen muss.
Übrigens geht die I2C Steuerung der AVR32DBxx Chips wesentlich einfacher, als die bei den ATMEGA's.
Die Geschwindigkeitsberechnung ist allerdings etwas schwierig.
Ich werd das mal mit dem Auslesen eines Wertes testen und geb dann wieder Bescheid.
Holomino
13.09.2024, 13:55
Scheint aber wirklich so zu funktionieren.
(https://github.com/sparkfun/SparkFun_AS7265x_Arduino_Library/blob/main/src/SparkFun_AS7265X.cpp)
uint8_t AS7265X::readRegister(uint8_t addr)
{
_i2cPort->beginTransmission(AS7265X_ADDR);
_i2cPort->write(addr);
if (_i2cPort->endTransmission() != 0)
{
//Serial.println("No ack!");
return (0); //Device failed to ack
}
_i2cPort->requestFrom((uint8_t)AS7265X_ADDR, (uint8_t)1);
if (_i2cPort->available())
{
return (_i2cPort->read());
}
//Serial.println("No ack!");
return (0); //Device failed to respond
}
wobei...
i2cPort->beginTransmission(AS7265X_ADDR);
_i2cPort->write(addr);
...die I2C-SlaveAdresse und die Registeradresse auf den Sensor schreiben und...
if (_i2cPort->endTransmission() != 0)
{
//Serial.println("No ack!");
return (0); //Device failed to ack
}
...das abschließende Ack der Sequenz auf der SDA-Leitung abfragt...
_i2cPort->requestFrom((uint8_t)AS7265X_ADDR, (uint8_t)1);
if (_i2cPort->available())
{
return (_i2cPort->read());
}
Bei der anschließenden Lesesequenz geht Arduino dann mit einer Abfrage dran, deren Erfolg über das 'available', und deren Daten über Read aus einem Puffer zurückgegeben werden.
Details findest Du, wenn Du nach "Arduino Wire.h" suchst.
Und die Funktion
uint8_t AS7265X::virtualReadRegister(uint8_t virtualAddr)
ist vielleicht auch ganz lesenswert. Da steht z.B. am Anfang ein Dummy-Lesen auf das Read-Register. Kann also sein, dass man ggf. anstehende Werte erst lesen muss, damit das AS7265X_RX_VALID-Flag im Status zurückgesetzt wird?!?
Das was Du jetzt schreibst ist ja genau mein Problem.
Wie wird dem Sensor mitgeteilt auf welches der 3 Hardware Register Ich lesen oder schreiben will?
In der Sequenz wird ja nur die Adresse eines virtuellen Registers angegeben - Muss da in einer Sequenz vorher die Adresse eines Hardwareregisters angegeben werden ( Wie Du in deinem ersten Post schreibst )?
Es muss ja dann mehrmals das Status Register wieder ausgelesen werden, bis die Transaktion intern wieder beendet ist, also muss die Adresse vom Write Register wieder auf das Status Register umgeschaltet werden.
Beim lesen ist ja sonst unklar, ob Ich aus dem Status Register, oder aus einem anderen Register gelesen werden soll.
So ganz verstanden hab Ichs leider immer noch nicht.
Meine Idee war, ob nicht bei einem Schreib bzw. Lesebefehl gleich 3 Bytes geschrieben, bzw gelesen werden, das die 3 Hardware Register symbolisiert?
Darauf findet sich in der library allerdings kein Hinweis.
Oder es werden tatsächlich die Adressen 0x00, 0x01, 0x02 per I2C angesprochen, dann wärs im Prinzip ganz einfach, allerdings bräuchte man dann keine zusätzliche I2C Adresse für die Platine ( 0x49 ).
Ich werd da mal mit meinem Debugger etwas rum spielen.
Ich denke Ich hab jetzt die Routinen soweit im Griff, das alles funktionieren sollte, allerdings reagiert der Sensor nicht wie erwartet.
36015
Im ersten Step wurde Adresse 0x92 ( Schreiben ) ausgewählt und die Adresse des Hardware Status Registers 0x00 angesprochen.
Beides wurde vom AS7265 mit ACK quittiert.
Der Master sendete dann Stop mit anschließenden Start.
Nun wurde die Adresse 0x93 ( Lesen ) ausgewählt der Slave quittiert und das Byte wurde ausgelesen.
Dieses enthielt aber leider den Wert 0x80, der aber nirgends dokumentiert ist.
Der Wert 0x01, 0x02 oder 0x03 wurde erwartet.
Der Master sendete NACK weil ja kein weiteres Byte mehr erwartet wurde.
Das Ganze hab Ich mit dem Debugger gemacht.
Lässt man den Controller frei Laufen hängt sich irgendwann die Bus Arbritation auf.
Manchmal vom Master Manchmal vom Slave und eine der Leitungen wird nach GND gezogen.
//Write to virtual Register
void i2m_AS72xx_topwrite(uint8_t virtualReg, uint8_t d)
{
uint8_t status;
TWI_timeout=TWI_timeout_time;
while (1)
{
//Read slave I²C status to see if the write buffer is ready.
TWI_Buffer[0]=I2C_AS72XX_SLAVE_STATUS_REG;
FEHLER = twi0_send(AS7265_adr,TWI_Buffer,0x01);
FEHLER = twi0_receive(AS7265_adr,TWI_Buffer,0x01);
status = TWI_Buffer[0];
if ((status & I2C_AS72XX_SLAVE_TX_VALID) == 0)
// No inbound TX pending at slave. Okay to write now.
break ;
if(TWI_timeout==0)
{
break;
FEHLER=20;
}
_delay_ms(AS7265X_POLLING_DELAY);
}
//Send Data to virtual Register
TWI_Buffer[0]=(virtualReg | 0x80);
//Send 1 Bytes
FEHLER = twi0_send(AS7265_adr,TWI_Buffer,0x01);
TWI_timeout=TWI_timeout_time;
while (1)
{
//Read slave I²C status to see if the write buffer is ready.
TWI_Buffer[0]=I2C_AS72XX_SLAVE_STATUS_REG;//****** Die Sequenz wurde hier gestoppt ******
FEHLER = twi0_send(AS7265_adr,TWI_Buffer,0x01);
FEHLER = twi0_receive(AS7265_adr,TWI_Buffer,0x01);
status = TWI_Buffer[0];
if ((status & I2C_AS72XX_SLAVE_TX_VALID) == 0)
// No inbound TX pending at slave. Okay to write now.
break ;
if(TWI_timeout==0)
{
break;
FEHLER=21;
}
_delay_ms(AS7265X_POLLING_DELAY);
}
//Send data
TWI_Buffer[0]=I2C_AS72XX_SLAVE_WRITE_REG;
TWI_Buffer[1]=d;
FEHLER=twi0_send(AS7265_adr,TWI_Buffer,0x2);
}
Hat da von Euch jemand eine Idee was da falsch läuft?
Holomino
18.09.2024, 12:50
Dieses enthielt aber leider den Wert 0x80, der aber nirgends dokumentiert ist.
Der Wert 0x01, 0x02 oder 0x03 wurde erwartet.
Meine bescheidene Ansicht nach einem Blick in den Pseudocode im DB des Sensors,...
while (1)
{
// Read slave I²C status to see if the write buffer is ready.
status = i2cm_read(I2C_AS72XX_SLAVE_STATUS_REG);
if ((status & I2C_AS72XX_SLAVE_TX_VALID) == 0)
// No inbound TX pending at slave. Okay to write now.
break ;
}
...dass das oberste Bit in Status offensichtlich nicht verwendet wird. Stattdessen werden die beiden Flags im Statusregister einzeln als Bits ausmaskiert. Eine 0 gibt dabei an, dass Du auf das Read-, bzw. Write-Register zugreifen darfst (weil der Sensor da nicht gerade busy ist).
Vereinfacht:
0x81, 0x82, oder 0x83 sind also böse, da musst Du auf irgendwas warten.
0x80 ist gut, dann darfst Du weitermachen.
;)
P.S.: Code als Zitat sieht grauselig aus. Über die "Erweitert"-Schaltfläche unten kannst Du ein Code-Tag einfügen.
0x81, 0x82, oder 0x83 sind also böse, da musst Du auf irgendwas warten.
0x80 ist gut, dann darfst Du weitermachen.
Das die beiden Bits ausmaskiert werden ist mir schon klar.
Was seltsam ist: Auch nach mehreren Versuchen taucht da niemals ein Wert in den unteren 2 Bytes auf.
Obwohl der Chip eigentlich was zu tun haben müsste.
Ich hab auch schon versucht die "Firmware Version Bytes" abzurufen, aber da kommt immer nur der Wert 0x00 zurück.
Dann hab Ich versucht die LED's zu aktivieren, aber auch das klappt nicht.
Irgendwie bin Ich ratlos!
P.S.
Das mit dem Code TAG hab Ich auch noch mal geändert!
Holomino
18.09.2024, 13:44
Aaaahahaha, dann kann man's auch lesen!
Da gibst Du dem Sensor z.B. nicht an, wohin er die Adresse des virtuellen Registers schreiben soll
//Send Data to virtual Register
TWI_Buffer[0]=(virtualReg | 0x80);
//Send 1 Bytes
FEHLER = twi0_send(AS7265_adr,TWI_Buffer,0x01);
Besser wäre wahrscheinlich:
//Send virtual address to write register
TWI_Buffer[0]=0x01; // Address write register
TWI_Buffer[1]=(virtualReg | 0x80); // data written to Write register
FEHLER = twi0_send(AS7265_adr,TWI_Buffer,0x02);
Mit dem fehlenden Schreib Befehl hattest Du eindeutig Recht.
Das Auslesen der Sensorwerte klappt nun auch, alledings erscheinen mir diese nicht plausibel.
Aber da werd Ich auch noch drauf kommen.
Allerdings verstehe Ich nicht, warum Sparkfun die Ansteuerung so verkompliziert hat.
Es können ja maximal nur 2 Werte geschrieben, bzw. 2 Werte gelesen werden.
Hätte man das über eine Adresse und jeweils 2 Bytes gelöst, wäre die Ansteuerung wesentlich einfacher und die Schreib bzw. Lesebefehle könnten nicht aus dem Takt kommen.
Holomino
21.09.2024, 08:42
Aber da werd Ich auch noch drauf kommen.
Dieser Sensor scheint auch so ein Exemplar mit Code statt Doku zu sein. Das wird jetzt immer häufiger.
Im Git (https://github.com/sparkfun/SparkFun_AS7265x_Arduino_Library/tree/main) bekommst Du dafür recht einfache Beispiele.
Das ist zwar alles Arduino, aber den eigentlichen Knoten (das TWI-Handling) hast Du ja jetzt mit dem Lesen/Schreiben der virtuellen Register zumindest grob gelöst.
Die Lösung fand sich in der Datenblattsammlung von AMS ( OSRAM ) (https://ams-osram.com/products/boards-kits-accessories/kits/ams-as7265x-dem-sn-demonstrator-kit#User.guides).
Dort sind auch die einzelnen Sequenzen zum Lesen und Schreiben beschrieben.
Sparkfun hat da anscheinend abgekupfert, aber nicht wirklich gut.
Was noch zu sagen wäre: Nach der Auswahl des Chips und der Sequenz zum Sampeln aller 6 Werte eines Chips ist eine Delay Zeit von ( Samplezyklen * 2,8 * 1,5ms ) einzufügen, sonst werden nur 0x00 oder falsche Werte ausgelesen.
Bei mir funktioniert es nun so einigermassen!
(https://ams-osram.com/products/boards-kits-accessories/kits/ams-as7265x-dem-sn-demonstrator-kit#User.guides)
Holomino
27.09.2024, 07:41
Hmmm?!?
Das ist aber nicht die ominöse Delay-Konstante aus dem Quellcode?
#define AS7265X_POLLING_DELAY 5 //Amount of ms to wait between checking for virtual register changes
Haben die so ziemlich in jede Registerzugriffsroutine mit einem Delay() reingeknallt.
Das ist aber nicht die ominöse Delay-Konstante aus dem Quellcode?
Nein, diese Polling Konstante wird in der while(1) schleife beim Auslesen des Status Registers benutzt um den Prozess zu verlangsamen.
Ich hab da "_delay_ms(AS7265X_POLLING_DELAY);" draus gemacht.
Beim Auslesen der Sensorwerte müssen die Anzahl der gewünschten Samples * 2,8 und dann noch mal 1,5 ( also *4,2 ) in ms genommen werden.
Ich hab's der Einfachheit halber gleich mal 5 genommen.
Die Sequenz zum Auslesen der 3 Sensoren sieht dann so aus:
void getchanneltop(void)
{
uint8_t pointer = 0;
uint8_t buffer = 0;
//Readout Data
selectDevice(AS72651_NIR);
setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_ ONE_SHOT); //One-shot reading of VBGYOR
_delay_ms(AS7265X_INTERGRATION_TIME*5);
for(uint8_t i=0x08;i<0x14;i+=2) //Für i hab Ich dann gleich die richtigen Register Adressen benutzt.
{
//selectDevice(AS72651_NIR);
buffer=i2m_AS72xx_topread(i);
Top_read[pointer]=buffer;
Top_read[pointer]=(Top_read[pointer]<<8);
buffer=i2m_AS72xx_topread(i+1);
Top_read[pointer]|=buffer;
pointer++;
}
//Readout Data
selectDevice(AS72652_VISIBLE);
setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_ ONE_SHOT); //One-shot reading
_delay_ms(AS7265X_INTERGRATION_TIME*5);
for(uint8_t i=0x08;i<0x14;i+=2)
{
//selectDevice(AS72652_VISIBLE);
buffer=i2m_AS72xx_topread(i);
Top_read[pointer]=buffer;
Top_read[pointer]=(Top_read[pointer]<<8);
buffer=i2m_AS72xx_topread(i+1);
Top_read[pointer]|=buffer;
pointer++;
}
//Readout Data
selectDevice(AS72653_UV);
setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_ ONE_SHOT); //One-shot reading
_delay_ms(AS7265X_INTERGRATION_TIME*5);
for(uint8_t i=0x08;i<0x14;i+=2)
{
//selectDevice(AS72653_UV);
buffer=i2m_AS72xx_topread(i);
Top_read[pointer]=buffer;
Top_read[pointer]=(Top_read[pointer]<<8);
buffer=i2m_AS72xx_topread(i+1);
Top_read[pointer]|=buffer;
pointer++;
}
}
Das Ganze war schon ein längerer Kampf.
Was immer noch passiert ist, das der Sensor ab und zu kein ACK auf dem I2C bus sendet, aber da hab Ich dann Zeitschleifen rein gemacht, damit der Controller nicht blockiert wird.
Watchdog wäre auch ein Alternative gewesen, allerdings würde der Controller die komplette Initialisierung durchlaufen und das wollte Ich vermeiden.
Nachtrag:
Was noch zu den AS7265x Sensoren zu sagen wäre ist, das Sie schnelle Abfragen auf dem I2C Bus nicht mögen.
In der Arduino lib sind vermutlich dehalb auch sehr viele Zeitschleifen eingefügt.
Manchmal hägen sich die Module dermassen auf, das auch ein Softwarereset nichts mehr hilft.
In der endgültigen Version werd Ich wohl den Reset Pin ansteuern um da wieder raus zu kommen.
Die Abfrage eines Moduls dauert im Besten Fall bei mir 1/2 Sekunde für die 18 möglichen Wellenlängen.
Ob das mit der seriellen Schnittstelle besser geht hab Ich nicht getestet.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.