Forum: Mikrocontroller und Digitale Elektronik STM32F4: Datenübertragung extrem langsam


von Daniel V. (voda) Benutzerseite


Angehängte Dateien:

Lesenswert?

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
void adns3090_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_t adns3090_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
  return reset;
39
}

In der Main rufe ich dann folgendes auf:
1
int main (void)
2
{
3
 uint8_t picture[1536];
4
 int image_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 (int n=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

: Bearbeitet durch User
von Stefan K. (stefan64)


Lesenswert?

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

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von holger (Gast)


Lesenswert?

SystemCoreClockUpdate();

Es reicht wenn du das einmal machst.

    delay_nms(10/1000);

10/1000 ergibt 0!

Was macht deine Funktion wenn du 0 übergibst?

von Daniel V. (voda) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hi Stefan,

Stefan K. schrieb:
> Was ist das für ein Uart und wie ist der eingestellt:
> USART_SendData(USART3,image_data);
1
/*USART-Schnittstelle */
2
USART_InitTypeDef USART_InitStructure;    
3
USART_InitStructure.USART_BaudRate            = 256000;
4
USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
5
USART_InitStructure.USART_StopBits            = USART_StopBits_1;
6
USART_InitStructure.USART_Parity              = USART_Parity_No;
7
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
8
USART_InitStructure.USART_Mode        = USART_Mode_Rx | USART_Mode_Tx;
9
USART_Init(USART3, &USART_InitStructure);
10
USART_Cmd(USART3,ENABLE);

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

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Uhrmacher (Gast)


Lesenswert?

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

von Daniel V. (voda) Benutzerseite


Angehängte Dateien:

Lesenswert?

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");

rennt alles.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>Ich habe gerade die Quick&Dirty-Lösung
>"wait_nms(10.0 / 1000.0" implementiert.

Dann zeig den Kram doch mal. Ist das denn so schwer?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Uhrmacher (Gast)


Lesenswert?

Oscar in der Tonne, vielleicht.

von Uhrmacher (Gast)


Lesenswert?

Meine Glaskugel sagt: wait_nms(<integer type>)

"Oscar" - meine Güte.

von Daniel V. (voda) Benutzerseite


Lesenswert?

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
1
#include <delay.h>
2
3
static __IO uint32_t sysTick_zaehler;
4
5
void SysTick_Init(void)
6
{
7
 while (SysTick_Config(SystemCoreClock/1000000) != 0)
8
 {
9
 }
10
}
11
12
void timer_Decrement(void)
13
{
14
  if (sysTick_zaehler != 0x00)
15
  {
16
   sysTick_zaehler--;
17
  }
18
}
19
20
void delay_ns(uint32_t n)
21
{
22
 sysTick_zaehler = n;
23
  while (sysTick_zaehler != 0) 
24
  {
25
  }
26
}
27
28
void delay_ms(void)
29
{
30
 sysTick_zaehler = 1000;
31
  while (sysTick_zaehler != 0) 
32
  {
33
  }
34
}
35
36
void delay_nms(uint32_t n)
37
{
38
  while (n--)
39
  {
40
   delay_ms();
41
  }  
42
}

und die dazugehörige Header:
1
#ifndef DELAY_H
2
 #define DELAY_H
3
  
4
#include <stm32f4xx.h>  
5
6
extern void SysTick_Init(void);
7
extern void timer_Decrement(void);
8
extern void delay_ns(uint32_t n);
9
extern void delay_ms(void);
10
extern void delay_nms(uint32_t n);
11
#endif

: Bearbeitet durch User
von Uhrmacher (Gast)


Lesenswert?

Schön. Und zeigst Du uns jetzt BITTESCHÖN die Implementierung von 
wait_nms? DANKESCHÖN.

von Daniel V. (voda) Benutzerseite


Lesenswert?

Die wait_nms() heißt delay_nms() daher habe ich einfach
1
delay_nms(10.0/1000.0);

ausprobiert. Die Funktion siehe oben.

Auch habe ich die Funktion delay_ns(5000) für 5 µs ausprobiert. Selbes 
Fehlerbild.

Danke und Gruß
Daniel

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von holger (Gast)


Lesenswert?

__IO entspricht volatile.
Da wollte ein ganz schlauer bei STM nur wieder was neues erfinden.

von Uhrmacher (Gast)


Lesenswert?

[MOD: unsachliches Lamento gelöscht]

WAS ERGIBT 10.0 durch 1000.0 gecasted auf eine Integer-Typ? Das kann 
doch nicht so schwer sein.

Ich bin raus.

: Bearbeitet durch Moderator
von Daniel V. (voda) Benutzerseite


Lesenswert?

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

von aSma>> (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

Daniel V. schrieb:
1
void adns3090_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
}

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?

von holger (Gast)


Lesenswert?

>ganzen Delays dort offenbar in ns sind

Nein, es sind n x µs;) Der Name der Funktion ist nur
schlecht gewählt.

von Daniel V. (voda) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von aSma>> (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Daniel V. (voda) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.