STM32F4: Datenübertragung extrem langsam

OP Persönliche Seite #4795338
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
Angehängte Dateien:
OP Persönliche Seite #4795362
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
Angehängte Dateien:
Persönliche Seite #4795376
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.
Gast #4795383
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.
OP Persönliche Seite #4795393
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.
Angehängte Dateien:
OP Persönliche Seite #4795415
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
Persönliche Seite #4795441
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.
OP Persönliche Seite #4795557
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
Gast #4796611
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.
Gast #4796621
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?
OP Persönliche Seite #4796719
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?
Gast #4796747
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.
Persönliche Seite #4796762
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.
OP Persönliche Seite #4796790
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren