Guten Abend liebes Forum,
seit einiger Zeit habe ich Probleme mit einem ADNS3090.
Dieser IC ist normalerweise ein Maussensor und ich möchte diesen für
eine Optical-Flow-Messung einsetzen. Der Sensor hat folgende
bildtechnische Merkmale:
Pixelanzahl: 30 x 30 x 8 Bit CMOS (7200 Bit = 900 Byte)
Framerate: max. 6469 fps
eingestellt: 2000 fps
Laut Datenblatt (S.20, Figure 4) müssen folgende Register eingestellt
werden um die Pixeldaten auszulesen:
Schreibe im Register 0x13 (ADNS3090_FRAME_CAPTURE) den Wert 0x83
Warte 3 Frames + 10 ms (2000 fps/s -> 500 µs * 3 + 10ms = 1510 ms)
Folgendes habe ich ebenfalls eingestellt:
SPI-Takt: 1,25 MHz
USART: 256000 Baud
Diese Funktion sieht so aus:
1
voidadns3090_FrameCapure_Config(void)
2
{
3
ADNS3090_NSS_LOW;
4
delay_ns(121);
5
adns3090_Write(SPI2,ADNS3090_FRAME_CAPTURE|0x80);
6
delay_ns(1);
7
adns3090_Write(SPI2,0x83);
8
delay_ns(1);
9
ADNS3090_NSS_HIGH;
10
delay_nms(1510);
11
}
Die Bilddaten (insgesamt 1536 Werte) können nun über das
Pixel_Burst-Register 0x40 ausgelesen werden. Hier meine
Funktionsimplementierung, zu Figure 24 auf Seite 20 des Datenblattes:
1
uint8_tadns3090_Pixel_Burst(uint8_t*image)
2
{
3
adns3090_FrameCapure_Config();
4
5
ADNS3090_NSS_LOW;
6
adns3090_Read(SPI2,ADNS3090_PIXEL_BURST);
7
delay_nms(50/1000);
8
9
for(i=0;i<900;)
10
{
11
register_val=adns3090_Read(SPI2,0xFF);
12
delay_nms(10/1000);
13
14
if(start==0)
15
{
16
if(register_val&0x40)
17
{
18
start=1;
19
}
20
else
21
{
22
TimeOut++;
23
if(TimeOut==100)
24
{
25
reset=-1;
26
break;
27
}
28
}
29
}
30
31
if(start==1)
32
{
33
image[i++]=(register_val<<2);
34
}
35
}
36
ADNS3090_NSS_HIGH;
37
delay_nms(14/1000);
38
returnreset;
39
}
In der Main rufe ich dann folgendes auf:
1
intmain(void)
2
{
3
uint8_tpicture[1536];
4
intimage_data;
5
6
startUp();/*Controllerinitialisierung*/
7
USART_config();/*Usart*/
8
bma020_SPI_SETTINGS();/*Beschleunigungssensor*/
9
adns3090_STARTUP();/*ADNS3090-Inititialisierung*/
10
SysTick_Init();
11
12
while(1)
13
{
14
SystemCoreClockUpdate();
15
adns3090_Pixel_Burst(picture);
16
17
for(intn=0;n<900;n++)
18
{
19
image_data=picture[n];
20
USART_SendData(USART3,image_data);
21
}
22
}
23
}
Der Sensor und somit auch die Datenübertragung über die USART ist jedoch
extrem langsam (16 Byte/s). Normalerweise habe ich eine viel höhere
Geschwindigkeit erwartet. Warum tröpfeln die Daten vor sich hin?
Kann mir jemand einen Tipp geben?
Vielen Dank und Gruß
Daniel
Was ist das für ein Uart und wie ist der eingestellt:
USART_SendData(USART3,image_data);
256000 Baud ist ja nicht so die Standard-Baudrate, ist das ein CDC-USB?
Ist wirklich (16 Byte/s) gemeint oder eher picture/s?
Mit welchem Programm empfängst Du?
Gruß, Stefan
Daniel V. schrieb:> delay_nms(14/1000);
Was macht diese Funktion, und was macht sie, wenn ihr 0 übergeben wird?
Denn genau das machst Du hier: 14 / 1000 ist 0.
Stefan K. schrieb:> 256000 Baud ist ja nicht so die Standard-baudrate, ist das ein CDC-USB?> Ist wirklich (16 Byte/s) gemeint oder eher picture/s?
Auf der Platine habe ich einen FTDI verbaut.
Stefan K. schrieb:> Mit welchem Programm empfängst Du?
In HTerm tröpfeln die Daten herein.
Rufus Τ. F. schrieb:> Was macht diese Funktion, und was macht sie, wenn ihr 0 übergeben wird?
1
delay_nms(10/1000);
Dies ist meine Wartefunktion in ms (10/1000) = 0,001 ms.
Ich habe W.S.s- Konzept noch nicht implementiert:
Beitrag "STM32F4: SysTick etwas ungenau"
Danke und Gruß
Daniel
Daniel V. schrieb:> Dies ist meine Wartefunktion in ms (10/1000) = 0,001 ms.
Das ist einerseits eine interessante Rechnung (ich würde ja 0.010 ms
erwarten), aber trotzdem 0.
Deine Wartefunktion bekommt tatsächlich einen float -Wert übergeben?
Das ist ... gelinde gesagt ineffizient.
Und 10 / 1000 ist ein int - Ausdruck, der ist konstant 0.
Wolltest Du 0.001 erzeugen, müsstest Du eine float -Division
durchführen,
also "wait_nms(10.0 / 1000.0)" verwenden.
Auch wenn das 'ne Konstante ist: Keine gute Idee. Benenne die Funktion
um in wait_microsec() und übergebe ihr einen int, also hier 10.
> Dies ist meine Wartefunktion in ms (10/1000) = 0,001 ms.
Wenn (und falls) ich annehme, dass die Division, egal aus welchem
Zahlenbereich Dividend und Divisor kommen, eine reale Zahl oder auch
rationale Zahl ergeben, dann stimmt das im Bereich der Mathematik.
Allerdings stimmt das auch in C nicht grundsätzlich. Analog zu Werte-
und Bildmenge in der Mathematik gibt es hier "Datentypen". Auch wenn in
der Mathematik häufig vorkommende Werte- und Bildmengen oft nicht
angegeben werden, so ist das weder in der Mathematik noch in C, - und
dort schon gar nicht -, formal richtig.
Das steht alles in den C-Büchern bzw. spätestens in den Standards.
Wenn Du ein C-Integer durch ein C-Integer dividierst, dann ist das
Ergebnis wieder ein Integer (übrigens eines mit Vorzeichen und nicht
implizit grösser oder gleich Null).
Selbst wenn nicht, - es gibt da gewisse Mittel, zu denen vor allem
eine korrekte Datentyp-Angabe zählt, eine Fliesskomma-Division zu
erzwingen -, dann würde ein dem Sinne nach erwartetes float oder double
Ergebnis spätestens durch den Funktionsparametertyp auf ein Integer
implizit gecasted .
Du musst Dich also entscheiden, in welchem Zahlenbereich (Datentyp) Du
das delay angeben willst und die Entscheidung muss zu der Auflösung und
den Grenzen passen, in denen Du die Dauer der Verzögerung nennen willst.
Du hast vollkommen recht. Ich habe gerade die Quick&Dirty-Lösung
"wait_nms(10.0 / 1000.0" implementiert. Leider keine Besserung, Ich
triggere mit meinem Oscar am Clock-Signal. Auch hier aktualisiert sich
das Oscarbild alle 1 s und läuft synchron so der Aktualisierung bei
HTerm.
Sende ich in der while-Schleife einen Teststring auf die USART:
1
sendText("Es tut mir Leid, Dave, aber das kann ich nicht tun. \n");
Rufus Τ. F. schrieb:> Daniel V. schrieb:>> Ich habe gerade die Quick&Dirty-Lösung "wait_nms(10.0 / 1000.0">> implementiert.>> Zeig doch bitte mal den Quelltext von wait_nms.
Gerne:
delay.c
Daniel V. schrieb:> void delay_nms(uint32_t n)> {> while (n--)> {> delay_ms();> }> }
Diese Funktion wartet die Anzahl der übergebenen millisekunden,
vorausgesetzt, daß delay_ms funktioniert.
Der Wert ist ein Ganzzahlwert. Ein Integer. Da kann man keine Brüche,
keine Nachkommastellen etc. angeben.
> static __IO uint32_t sysTick_zaehler;
Der scheint wohl irgendwo in einem Interrupthandler verändert zu werden.
Da Du darauf auch von außerhalb des Interrupthandlers zugreifen willst,
musst Du diese Variable als volatile deklarieren, weil sonst der
Compiler beim Übersetzen Deiner Funktionen davon ausgeht, daß die
Variable sich nie ändert - und damit die Zugriffe wegoptimiert.
So, ich habe mal den LA drangehängt und die im Datenblatt geforderten
Zeiten stimmen überein.
Es sind ja pro Frame 900 Werte. Diese sind ja auch da, man kann diese
auch sehen, nur das innerhalb der Zeit werden nur 16 Byte/s übertragen.
Der Rest wandert ins Datennirwana. Die Division habe ich nun
herausgenommen und habe mit der ns-Delayfunktion die entsprechende Zeit
eingegeben.
Kann das sein, das ich irgendwas übersehen habe in der Figure 28?
Danke euch und Gruß
Daniel
Daniel V. schrieb:> while (SysTick_Config(SystemCoreClock/1000000) != 0)
Ich habe dir schon in den anderen Thread gesagt, dass das Unfug ist!
Dafür dass man delay_us selten braucht, verbraucht das bei dir sehr viel
an Leistung.
holger schrieb:> __IO entspricht volatile.> Da wollte ein ganz schlauer bei STM nur wieder was neues erfinden.
nein, es gibt auch noch __I, welches im Unterschied zu __IO nämlich
"const volatile" entspricht. Das ist schon nicht blöd, daß man den
Schreibzugriff auf reine Leseregister schon per Compiler abwehrt.
Außerdem ist das auch nicht von ST, sondern das entstammt dem CMSIS von
ARM.
Der Rest stimmt aber.
Also außer dem Tipfehler im Funktionsnamen fällt mir auf, daß Deine
ganzen Delays dort offenbar in ns sind - außer das letzt, das ist in ms.
1500ms wären aber immerhin schon 1.5 Sekunden. Sicher, daß das so sein
soll?
aSma>> schrieb:> Ich habe dir schon in den anderen Thread gesagt, dass das Unfug ist!
Hab ich gelesen und ich schrieb auch im Thread, das ich diese Lösung
implementieren muss. Den Thread habe ich hier ja auch verlinkt und nicht
aus den Augen verloren.
Nop schrieb:> Also außer dem Tipfehler im Funktionsnamen fällt mir auf, daß Deine> ganzen Delays dort offenbar in ns sind - außer das letzt, das ist in ms.> 1500ms wären aber immerhin schon 1.5 Sekunden. Sicher, daß das so sein> soll?
Das ist komplett richtig. Heute Morgen beim Aufwachen ist mir dies auch
als erstes eingefallen. Laut Datenblatt soll t_Capture die Länge von
drei Frames + 10 µs betragen. Ich habe 2000 fps eingestellt, also
beträgt die Zeit für ein Frame 0,0005 s was 500 µs also 1510 µs
entsrpicht. Hier sind es 1,51 s, was man im LA auch sehen kann.
Was mich auch stutzig macht, die Daten liegen ja komplett im Register
und auch auf den Bus.
Ich werde mir erstmal eine neue Delayfunktion schreiben müssen. Bin halt
selber noch ein (Berufs-)Anfänger.
[EDIT]
aSma>> schrieb:> Ich habe dir schon in den anderen Thread gesagt, dass das Unfug ist!
Jau, im anderen Thread hast Du es schon geschrieben. Durch die Zählung
ist der Controller bereits ausgelastet, so kann er die Daten zwar noch
lesen und auf den Bus legen, aber er schafft es nicht weiter auf die
USART, weil die Kapazitäten fehlen. Richtig?
Daniel V. schrieb:> Jau, im anderen Thread hast Du es schon geschrieben. Durch die Zählung> ist der Controller bereits ausgelastet, so kann er die Daten zwar noch> lesen und auf den Bus legen, aber er schafft es nicht weiter auf die> USART, weil die Kapazitäten fehlen. Richtig?
Mit Verlaub, ja freilich. Eine ISR mit 1Mhz bei ca. 80Mhz Takt ist
unschlau. Register sichern, Waitstates & Co da bleibt nicht viel übrig.
Dies klärt man aber besser im anderen Thread.
holger schrieb:> Nein, es sind n x µs;) Der Name der Funktion ist nur> schlecht gewählt.
Das ist neu.
Daniel V. schrieb:> void delay_nms(uint32_t n)> {> while (n--)> {> delay_ms();> }> }
Da wird delay_ms aufgerufen.
Oder sollen das etwa Microsekunden sein?
Dann tu Dir und uns einen Gefallen und hör' mit dem Blödsinn auf,
solche irreführenden Funktionsnamen zu verwenden.
Rufus Τ. F. schrieb:> Dann tu Dir und uns einen Gefallen und hör' mit dem Blödsinn auf,> solche irreführenden Funktionsnamen zu verwenden.
Du hast zu 100% Recht. Ich habe diese Timergeschichte aus irgendeinem
belgischen Blog "geklaut". Im Nachbarthread werde ich morgen die Idee
von WS und den anderen versuchen zu verstehen, zu bearbeiten und zu
implementieren.
Rufus Τ. F. schrieb:> Da wird delay_ms aufgerufen.>> Oder sollen das etwa Microsekunden sein?
Millisekunden.
>Da wird delay_ms aufgerufen.>Oder sollen das etwa Microsekunden sein?
Nein, delay_ms ist schon ok, aber delay_ns macht nicht
was man anhand des Namens erst einmal denkt.