Forum: Mikrocontroller und Digitale Elektronik STM32F4: SysTick etwas ungenau


von Daniel V. (voda) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo Forum,

ich habe eine Frage zum SysTick. Dieser ist etwas ungenau. Für eine 
Delay-Funktion die ich für das Auslesen eines optischen Sensors 
(ADNS3090) benötige  habe ich folgende delay.c erstellt:
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 natürlich die delay.h
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

Nun habe ich eine Funktion geschrieben welche meine "DEBUG"-LED zum 
blinken bringt.
1
void Run_Idle (void)
2
{
3
  SysTick_Init();  
4
    
5
  delay_nms(1000);
6
    GPIOA->BSRRL |= (1<<YELLOW_LED);
7
  delay_nms(1000);
8
    GPIOA->BSRRH |= (1<<YELLOW_LED);  
9
}

Dann natürlich noch die Pin-Konfigurationen zusammengefasst:
1
void startUp(void)
2
{
3
 SystemInit();
4
 SystemCoreClockUpdate();
5
 RCC_config();
6
 GPIO_config(); 
7
}

Setze ich jetzt delay(1000) ein, so messe ich mit dem Oscar 988 ms. Ist 
das normal das dieser etwas daneben liegt?

vielen lieben Dank und Gruß
Daniel

PS: µC läuft auf 80 MHz mit einem extern Quarz. PLL wurden eingestellt.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>ich habe eine Frage zum SysTick. Dieser ist etwas ungenau.

Vieleicht ist dein Oscar auch etwas ungenau;)

von aSma>> (Gast)


Lesenswert?

Servus,
wo ist dein void SysTick_Handler(void); ?

Sonst musst du ein wenig Kopfrechnen. Dein µC verweilt im Interrupt!

Normalerweise setzt man den systick takt auf 1ms. Um eine delay Zeit von 
µs zu erreichen kann man den Counter "SysTick->VAL" zur Hilfe nehmen.

von Jan Heynen (Gast)


Lesenswert?

Und wie sicher sind sie dat den F4 auch mit den externen Quarz lauft ? 
System_init() alles richtig einestellt ? Den F4 start immer mit den 
interne osc, nur wen die software richtig ist und das signal von 
externen quarz ist forhanden, schaltet er nach externen quarz.

von Daniel V. (voda) Benutzerseite


Lesenswert?

holger schrieb:
> Vieleicht ist dein Oscar auch etwas ungenau;)

Hmmm, doch so eine Messungenauigkeit des Rigols? Okay, morgen auf der 
Arbeit mal an einem Agilent hängen ;)

von hp-freund (Gast)


Lesenswert?

Wie immer mein Tip im Falle von Taktproblemen:

MCO benutzen

von Daniel V. (voda) Benutzerseite


Lesenswert?

hp-freund schrieb:
> Wie immer mein Tip im Falle von Taktproblemen:
>
> MCO benutzen

Da liegen 80 MHz an. Den SysTick_Handler hatte ich vergessen zu posten, 
ist aber drin. Mittlerweile hängt an den Port mein ADNS3090.

PS. Leider hängt mein Sensor dran messe aber um die 80 MHz, bricht aber 
immer wieder ein. (vermute mal durch den Sensor)

An anderer Stelle hatte ich den SPI-Takt eingestellt, ausgehend von 80 
MHz. Dort hat alles gepasst.

Danke und Gruß
Daniel

: Bearbeitet durch User
von hp-freund (Gast)


Lesenswert?

Wenn die Zeit zu kurz ist muss die Frequenz irgendwie zu hoch sein oder 
es gibt für die Zeitzählung zusätzliche Impulse.
Ist mir jetzt aber zu schwierig das nach zu vollziehen.

von Daniel V. (voda) Benutzerseite


Lesenswert?

Ganz vergessen den Fix zu posten:

In der while-Schleife der main.c sollte die Funktion

1
SystemCoreUpdate();

aufgerufen werden.

Gruß
Daniel

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Parallel zu Timer-Decrement toggle eine LED.

Wenn Du dann 500kHz misst, ist Dein Timer richtig aufgesetzt.

Moment mal: Du hast einen Interrupt pro µs? Vermutlich hast einen Teiler 
von 80 und ein off-by-one-Problem. Brauchst Du diese hohe Auflösung?

Normalerweise incrementiert man den Systicker immer und wartet so:

[c]
typedef uint32_t TTick;
TTick SysTicker;

interrupt Timerx(void)
{
    SysTicker++;
}

void delay_ns(unsigned int n)
{
TTick startTick = SysTicker;

   while((SysTicker-startTick) <n) {};
}

Der Vorteil: Du kannst in verschiedenen Tasks oder in verschiedenen 
Programmteilen (dann if statt while) parallel mit einem Systicker 
warten. Oder Zeiten messen. Wichtig ist nur, dass Du immer 
(SysTicker-Startwert) geklammert rechnest und dann vergleichst etc.

Meist braucht man für den Systicker-Increment nichtmal ne 
Interrupt-Routine.

von STler (Gast)


Lesenswert?

Ich empfehle, grundsätzlich bei den STM32ern den DWT Cycle Counter zu 
nutzen. Der ist sowieso präziser und wird auch später nicht gebraucht, 
falls man mal einen Scheduler oder ein RTOS einsetzt.

Infos bei ARM:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/BABJFFGJ.html

von Daniel V. (Gast)


Lesenswert?

Hmmm, interessant was ihr da ansprecht...

Gruß
Daniel

von W.S. (Gast)


Lesenswert?

Achim S. schrieb:
> while((SysTicker-startTick) <n) {};

wohl eher nicht. Den meisten fällt sowas eher nicht auf, aber auch ein 
32 Bit Zähler läuft mal über. Deshalb eher so:

unsigned long L;
L = Tick;
while (n)
{ if (L!= Tick)
  { L = Tick;
    n--;
  }
};



Daniel V. schrieb:
> ich habe eine Frage zum SysTick. Dieser ist etwas ungenau.

Nein. Stattdessen sind deine geposteten Funktionen grauenhaft. Laß den 
eigentlichen SysTick-Counter in Ruhe, schreib dir eine Variable 'Ticks', 
die du im SysTick-Handler hochzählst und die die verflossene Zeit in ms 
anzeigt. Aber die darfst du nur lesen und nirgendwo anders schreiben als 
im SysTick-Handler. Obendrein sollte der Handler 'Ticks' bei 86400000 
zurücksetzen und ggf. ne Variable 'Tag' inkrementieren.
Delay-Routinen müssen das natürlich berücksichtgen, indem sie NICHT 
einfach if(Ticks>n) benutzen.

W.S.

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> wohl eher nicht. Den meisten fällt sowas eher nicht auf, aber auch ein
> 32 Bit Zähler läuft mal über.

Mein Konstrukt ist sehr, sehr robust. n darf nur nicht zu groß werden. 
Wenn die Zeile im worst case erst nach y Ticks aufgerufen wird, so darf 
n nur maximal (MAX_VALUE - y) groß sein. Beispiel: Wenn der Systicker 
8bit breit ist und millisekündlich inrementiert wird und die Zeile im 
Worst Case erst nach 20ms erneut aufgerufen wird, so muss n<=234 sein.

Dein Konstrukt muss vollständig blockieren, ist also weder task- noch 
interruptfest.

Falls Du darauf anspielst, dass der Systicker inkonsistent sein könnte 
(also während eines lesens überläuft), das muss tatsächlich vermieden 
werden, sollte aber zentral beim incrementieren passieren.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Der Tipp von STLer ist on-the-sport, genauer als mit dem Cycle Counter 
geht's eh nicht (auf dem ST kannst Du alternativ auch free running timer 
einsetzen und den jeweiligen count abfragen, aber eben Überlauf 
beachten!)

Bei deinem Code musst Du auch noch Interruptprioritäten in Betracht 
ziehen; läufst Du z.B. unter FreeRTOS, ist zu beachten, dass der SysTick 
Interrupt immer auf der niedrigsten Interruptpriorität liegt, also von 
höher priorisierten interrupts ausgebremst wird. Für das OS ist sein 
Systemtask das Maß aller Dinge (also damit auch der SysTick counter), 
aber wenn der in der "absoluten" Zeit off ist, kann das OS nichts 
dagegen tun.

Immer das Gesamtsystem im Hinterkopf haben...

: Bearbeitet durch User
von Daniel V. (Gast)


Lesenswert?

Heiß also, die Funktionen wegwerfen (sie habe ich mir geklaut, muss ich 
gestehen, da ich Quick&Dirty-mäßig eine Lösung brauchte und ich erst 
gerade mich einarbeite in die ARM-Kiste) und W.S. seine Funktionen 
implementieren und ggf noch umrechnen auf ms.

Hier habe ich "meine" Lösung her:
http://patrickleyman.be/blog/stm32f407-delay-with-systick/

Gruß
Daniel

von W.S. (Gast)


Lesenswert?

Daniel V. schrieb:
> Heiß also, die Funktionen wegwerfen...

Nun, du hättest dir von vornherein ein anderes Konzept zurechtlegen 
können. Ich hab in eigentlich jeder Firmware eine Systemuhr drin, die 
auf dem SystemTick basiert. Dazu arbeite ich auch gern mit Events und 
delayed events. Geht einfach, wenn man mal das simple Prinzip versteht:

1. ein Event wird dargestellt durch eine Zahl, je nach Plattform und 
Bedarf 8, 16 oder 32 bittig. Beispiel: #define isSekundeUm  47110815
Natürlich kann man auch was Anderes nehmen, z.B. nen kleineren struct 
oder was man will.

2. es gibt einen Event-Puffer, der regelmäßig von der Grundschleife in 
mail abgefragt wird:
  if (EventAvailable() DispatchEvent(GetEvent());

3. Events können von diversen Firmwareteilen in den Puffer eingestellt 
werden zwecks späterer Bearbeitung

4. Die Systemuhr schickt z.B. regelmäßig einen Event "isSekundeUm" o.ä. 
in den Puffer. Obendrein führt die Event-Verwaltung eine zweite kleine 
Liste für verzögerte Events - und diese Liste wird von der Systemuhr 
regelmäßig abgeklappert nach abgelaufenen Events. Findet sie einen, dann 
schmeißt sie selbigen aus der Liste der verzögerten Events raus und 
packt ihn in den normalen Event-Puffer. Dazu gibt es noch ne Funktion 
zum Löschen von solchen verzögerten Events, falls man so einen nicht 
mehr braucht.

So kann man Zeitüberwachungen und nicht blockierende Warteschleifen 
bauen indem man an recht beliebiger Stelle etwa sowas schreiben kann:

 AddDelayedEvent(dieZeitIstUm, 1500); // 1.5 Sekunden

und wenn man die Sache erledigt hat, die Zeit noch nicht um ist oder der 
Event noch nicht von der Grundschleife gelesen worden ist, schmeißt man 
ihn wieder raus mit
 KillDelayedEvent(dieZeitIstUm);

ansonsten landet der Event nach 1500 ms im Event-Puffer und wird von der 
Grundschleife gelesen und den zuständigen Programmteilen zur Bearbeitung 
übergeholfen.

Das Ganze ist recht aufwandsarm und kostet auch kaum Rechenzeit und man 
kann - während man auf irgendwas wartet - noch andere Dinge erledigen.

Prinzip verstanden?

W.S.

von W.S. (Gast)


Lesenswert?

W.S. schrieb:
> in
> mail

in main und nicht in mail! Warum muß so eine bescheuerte 
Rechtschreibautomatik einem immer wieder sowas einbrocken? Steht die auf 
Sanskrit oder was?

W.S.

von aSma>> (Gast)


Lesenswert?

Die Autokorrektur uriniert bei mir auch immer alles.

von Daniel V. (Gast)


Lesenswert?

W.S. schrieb:
> Prinzip verstanden?

Hi W.S.,

jetzt gerade fliegt mir diese Geschichte um die Ohren, da ich die 
Bilddaten  meines ADNS3090 nur tröpfchenweise über die USART übertragen 
werden (bei 256000 Baud), da wohl die Timer immer dazwischenschlagen und 
ich warten muss.

Kannst Du mir zum Nachvollziehen etwas Beispielcode zeigen?
Wenn ich zuhause bin, zeige ich mal meinen derzeitigen Code.

Danke und Gruß
Daniel

von Felix F. (wiesel8)


Lesenswert?

@ W.S.
Könntest du deinen Dispatcher hier hochladen? Würde mich auch 
interessieren.

mfg

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Daniel V. schrieb:
> Kannst Du mir zum Nachvollziehen etwas Beispielcode zeigen?

Ja.
Das angehängte Zeugs sollte das Prinzip klar genug machen.

W.S.

von Daniel V. (voda) Benutzerseite


Lesenswert?

Das ist ein sehr interessantes Konzept. Vielen Dank!

Gruß
Daniel

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> Das angehängte Zeugs sollte das Prinzip klar genug machen.

Hallo W.S. eine Frage: Warum machst Du den Aufwand mit Millisekunden pro 
Tag? Das ist sehr fehlerträchtig und führt bei Dir m.E. zum Problem, 
wenn ein Timer 1ms vor dem Tagwechsel abläuft. Da das ganze ja auch 
nicht auf reale Tage abgeglichen wird, erschließst sich mir der Aufwand 
garnicht. Maximal würde ich die Tagesszähler einfach so mitlaufen 
lassen.

Dass man Überlauf durch Klammerung generell behandeln kann und nicht 
gesondert, habe ich ja hoffentlich gezeigt.

von W.S. (Gast)


Lesenswert?

Achim S. schrieb:
> Hallo W.S. eine Frage: Warum machst Du den Aufwand mit Millisekunden pro
> Tag? Das ist sehr fehlerträchtig

Erstens ist es auf einem 70..100 MHz Cortex so ziemlich das 
Natürlichste, die Tageszeit in Millisekunden zu bemessen und zur 
Mitternacht das Ganze um eine Tageslänge zurückzusetzen. Ist dir 
eigentlich aufgefallen, daß Ticks signed ist?

Mir ist bei dieser Gelegenheit noch ein Bug aufgefallen:
struct TTimerEvent
{ dword endzeit;
  dword aEvent;
};
Man sollte die 'endzeit' ebenfalls signed deklarieren. Bitte beachten.


Zweitens ist das Verfahren durchaus nicht fehlerträchtig. Wie kommst du 
auf so eine Aussage? Deine Vermutung "wenn ein Timer 1ms vor dem 
Tagwechsel abläuft" verstehe ich nicht, denn der Systick-Timer läuft 
genau 1x pro Millisekunde über, also quasi ständig. Von welchem 
(anderen) Timer redest du?

Ich habe den Eindruck, daß hier die von Achtbittern her übliche 
Denkweise zuschlägt, wo man für alle möglichen Zwecke mal eben die 
Interrupts verbietet und gelegentlich damit sich selbst durcheinander 
bringt oder sich eine Ewigkeit in irgend einer ISR aufhält und damit 
alles Andere blockiert. Sowas sollte man generell besser bleibenlassen.

W.S.

von A. S. (Gast)


Lesenswert?

Hallo W.S.:

Du irritierst mich.

W.S. schrieb:
> Erstens ist es auf einem 70..100 MHz Cortex so ziemlich das
> Natürlichste, die Tageszeit in Millisekunden zu bemessen und zur
> Mitternacht das Ganze um eine Tageslänge zurückzusetzen.
nein. Das natürlichste für echte Timer ist ein Free-running-Counter, 
weil der die wenigsten Probleme verursacht, vor allem *nicht beim 
Überlauf*.
a) ich habe übersehen, wo Dein Ticker auf die Uhrzeit abgeglichen wird.
b) ich habe übersehen, wo und wie Sommer-Winter-Zeit und 
Uhrzeitverstellungen gehandhabt werden
> Ist dir eigentlich aufgefallen, daß Ticks signed ist?
nein. Das spielte auch keine Rolle, da endzeit unsigned war und deshalb 
Deine Routine den beschriebenen Bug enthält.

> Man sollte die 'endzeit' ebenfalls signed deklarieren. Bitte beachten.
Ja. Damit träte der von mir beschriebene Fehler nicht mehr auf.
Ich verstehe aber nicht, wieso Du dann all die Argumente aufzählst, die 
gegen Deine Implementierung sprechen

> Ich habe den Eindruck, daß hier die von Achtbittern her übliche
> Denkweise zuschlägt, wo man für alle möglichen Zwecke mal eben die
> Interrupts verbietet und gelegentlich damit sich selbst durcheinander
> bringt oder sich eine Ewigkeit in irgend einer ISR aufhält und damit
> alles Andere blockiert. Sowas sollte man generell besser bleibenlassen.

Genau das machst Du nämlich. Deine Interruptroutine ist zu lang. So 
lange der Free-Running-Counter konsistent gelesen wird, gäbe es keine 
Gründe, irgendwo einen Interrupt zu sperren und all die Auswertung 
könnte dezentral dort geschehen, wo sie notwendig ist. Es entfiele die 
"Gott-Timer-Struktur".

Wobei ich Deinen "Achtbitter" nicht verstanden habe: Wenn man Timer und 
Überläufe mit 8-Bit-Werten einmal verstanden hat, dann kann man das 
robust auf 32-bit skalieren. Wenn man (wie Du) alles mit 32-bit macht, 
wo die Probleme beim Debuggen niemals auftreten, dann jagst Du später 
"glaubhaft versicherten" Geisterfunktionen hinterher, die irgendwie nur 
3 Mal im Jahr auftreten.

von Joe F. (easylife)


Lesenswert?

W.S. schrieb:
> Achim S. schrieb:
>> while((SysTicker-startTick) <n) {};
>
> wohl eher nicht. Den meisten fällt sowas eher nicht auf, aber auch ein
> 32 Bit Zähler läuft mal über. Deshalb eher so:
>
> unsigned long L;
> L = Tick;
> while (n)
> { if (L!= Tick)
>   { L = Tick;
>     n--;
>   }
> };

Das ist ja furchtbar unsicher (Stichwort: L==Tick verpassen).

while((SysTicker-startTick) <n)

funktioniert durchaus auch bei Überläufen, wenn SysTicker und startTick 
unsigned sind:
1
start: 0xfffffffd tick: 0xfffffffd  delta: 0
2
start: 0xfffffffd tick: 0xfffffffe  delta: 1
3
start: 0xfffffffd tick: 0xffffffff  delta: 2
4
start: 0xfffffffd tick: 0x00000000  delta: 3
5
start: 0xfffffffd tick: 0x00000001  delta: 4
6
start: 0xfffffffd tick: 0x00000002  delta: 5

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Joe F. schrieb:
> while((SysTicker-startTick) <n)

auf Dein vollkommen blockierendes Wait möchte ich nicht eingehen. Mit 
Free-Running-Counter könntest Du jedezeit noch weitere Aufgaben 
ausführen oder wärest Task/Interruptfest.
1
void  __irq  SysTick_Handler (void)
2
{ 
3
  ++Ticks;
4
  ...
5
  if (Ticks >= 86400000)      /* wenn 1 Tag um ist */
6
  { 
7
    ...
8
    CurrTimers[i].endzeit = CurrTimers[i].endzeit - 86400000;
9
    ...
10
  }
11
  ...
12
  if (CurrTimers[i].endzeit < Ticks)    /* ob abgelaufen */
13
  ...
14
}

Ich hoffe Du siehtst, dass der Code mit unsigned endzeit nicht 
funktiert, wenn die Endzeit 1Tag - 1ms (bzw. bei Dir -1..-9ms) ist. Das 
meine ich mit fragil.

von Joe F. (easylife)


Lesenswert?

Achim S. schrieb:
> Joe F. schrieb:
>> while((SysTicker-startTick) <n)
>
> auf Dein vollkommen blockierendes Wait möchte ich nicht eingehen.

whaaat?

Das kam ja nicht von mir.
Ich habe lediglich darauf hingewiesen, dass es hier kein Problem mit 
einem Überlauf gibt (was übrigens eine durchaus verbreitete Meinung 
ist).

W.S. schrieb:
> unsigned long L;
> L = Tick;
> while (n)
> { if (L!= Tick)
>   { L = Tick;
>     n--;
>   }
> };

ist ebenfalls blockierend, und eben kritisch, da L==Tick durchaus 
verpasst werden kann.

Und was soll ich denn jetzt mit deinem Codeschnippsel anfangen?

Achim S. schrieb:
> void  __irq  SysTick_Handler (void)
> {
>   ++Ticks;
>   ...
>   if (Ticks >= 86400000)      /* wenn 1 Tag um ist */
>   {
>     ...
>     CurrTimers[i].endzeit = CurrTimers[i].endzeit - 86400000;
>     ...
>   }
>   ...
>   if (CurrTimers[i].endzeit < Ticks)    /* ob abgelaufen */
>   ...
> }

1 Tag 86400000 Ticks? Was hast du denn für eine 1KHz Gurke?
"CurrTimers[i].endzeit = CurrTimers[i].endzeit - 86400000"
Hä?
Also ich habe auch ein Hefe intus, aber das verstehe ich beim besten 
Willen nicht.
endzeit = gestern?
Ich bitte um Erläuterung.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Achim S. schrieb:
> ++Ticks;
>   ...
>   if (Ticks >= 86400000)      /* wenn 1 Tag um ist */

Das ist doch schon vollkommen balla.
1. setzt es voraus, dass der Counter bei 0 startet.
2. Und was ist dann, wenn die Bedingung erfüllt ist? Setzt du irgendwo 
Ticks auf 0? Nö. Also ist die Bedingung jede ms erfüllt, bis der 32-bit 
Counter überläuft (48.7 Tage lang).
Und mit so einem Käse soll ich mich jetzt beschäftigen?

Du kannst ja statt

Achim S. schrieb:
> while((SysTicker-startTick) <n)

auch

if (! (SysTicker-startTick) <n)
{
  // play with your balls
}

schreiben, wenn dich das glücklich macht.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Joe F. schrieb:
> Das kam ja nicht von mir

sorry, das habe ich übersehen. Deine Version

> while((SysTicker-startTick) <n)

ist ja genau, was ich W.S. empfehle. Sein Warte-Code ist ... 
suboptimal.

Das Überlaufproblem ergibt sich bei ihm aber beim Zurücksetzen des 
Tageszählers, darum  habe ich nur die problematischen Zeilen bzw. dessen 
reihenfolge zitiert. Passiert zwar nur selten aber ist halt der 
unterschied zwischen robustem Code und Bastelei. Und dazu völlig 
unnötig, da der direkte Ansatz (wie bei Dir) viel einfacher ist.

von W.S. (Gast)


Lesenswert?

ROTFL!

Also Jungs (Achim und Joe), ihr habt weder das Prinzip der Sache 
verstanden noch das Prinzip des SysTick's kapiert. Guckt einfach mal in 
die Doku von ARM zum Cortex, vielleicht kapiert ihr es dann.

Und nun ratet mal, warum um Mitternacht
"CurrTimers[i].endzeit = CurrTimers[i].endzeit - 86400000;"
gemacht wird...

Und wieso kommt Achim auf "Deine Interruptroutine ist zu lang."? Ich 
hatte die mal gemessen und lag je nach Sensorentprellung bei ca. 5..7 
Mikrosekunden. Mir ist das schnell genug, kostet weniger als 1% der 
Rechenzeit.

Aber ich räume durchaus ein, daß bei einer völlig anderen Hardware auch 
völlig anderer Zeitbedarf herauskommen kann.


Jungs, ich sag's mal so:
1. Dem TO ist geholfen, denn er hat nen Denkanstoß bekommen, den er 
produktiv nutzen kann.
2. Wenn ihr es trotz (hoffentlich gründlichen) Lesens immer noch nicht 
kapiert, dann kann auch ich euch nicht weiter helfen.

So. und nun ist Schluß damit. Lest und begreift oder laßt es bleiben.

W.S.

von Joe F. (easylife)


Lesenswert?

W.S. schrieb:
> dann kann auch ich euch nicht weiter helfen

Das macht nichts.
Du hast das tollste, lesbarste und fehlerfreieste Stück Software in der 
Geschichte der Event-Verarbeitung geschrieben, und wir dumme Jungs 
werden halt weiterhin ein einfaches Delay durch Differenzbildung von 2 
unsigned Werten erledigen.

von aSma>> (Gast)


Lesenswert?

Joe F. schrieb:
> Achim S. schrieb:
>> while((SysTicker-startTick) <n)
>
> auch
>
> if (! (SysTicker-startTick) <n)
> {
>   // play with your balls
> }
>
> schreiben, wenn dich das glücklich macht.

Bei der Subtraktion von unsigned Zahlen wird das Zweier-Kompliment durch 
geführt, sodass man auch schreiben kann:
1
startTick = ~SysTicker +1;
2
while((SysTicker + startTick) <n);
Je nachdem wie der Kompiler reagiert wäre ein cast sinvoll:
1
while((uint32_t)(SysTicker + startTick ) <n);

von A. S. (Gast)


Lesenswert?

aSma>> schrieb:
> Bei der Subtraktion von unsigned Zahlen wird das Zweier-Kompliment durch
> geführt, sodass man auch schreiben kann:startTick = ~SysTicker +1;
> while((SysTicker + startTick) <n);

Das mag korrekt sein, aber wozu?

Auf das grundsätzliche signed/unsigned - Problem mit *undefiniertem 
Verhalten* (nicht einfach nur implementation defined) bei Überlauf des 
einen Typen, möchte ich nicht eingehen.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ruediger A. schrieb:
> dass der SysTick
> Interrupt immer auf der niedrigsten Interruptpriorität liegt, also von
> höher priorisierten interrupts ausgebremst wird.

Spezis - bitte weghören :-P Ich hab das Ding auch mal auf höchste 
Priorität gedreht um (aaarghh) Delays auch in ISR aufrufen zu können 
(war für ein Projekt mit Elektromagneten zum testen). Geht also auch...
Das ist übrigens auch gleichzeitig eine Falle. Wer die Priorität des 
Systick niedrig lässt und delay() in höher priorisierten ISR aufruft, 
fängt sich einen eingefrorenen MC ein.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Wie ist denn das überhaupt mit dem Systick, hab dazu im reference manual 
nichts gefunden:

Also das Counter-Register lädt sich den Wert aus dem Reload-Register und 
zählt mit jedem Takt abwärts, je nach Einstellung zählt als Clock dann 
entweder der CPU-Takt oder CPU-Takt geteilt durch 8.

Wenn der Counter 0 erreicht und man den Interrupt aktiviert hat, wird 
der Interrupt ausgelöst.

An welchem Punkt genau macht das Counter-Register jetzt seinen Reload? 
Sinnvoll (und das würde ich auch vermuten) wäre ja direkt nach dem 
Erreichen der 0. Oder macht er das erst, wenn der Interrupt beendet ist?

von aSma>> (Gast)


Lesenswert?

Achim S. schrieb:
> Das mag korrekt sein, aber wozu?

Für die Unwissenden.

> while((SysTicker-startTick) <n);

Ist ein gutes Konstrukt gegen ein owerflow.

Was hier nicht in die Diskussion eingegangen ist wie man ein delay_us 
entwirft.

Der TE hat wohl nicht verstanden, dass
> while (SysTick_Config(SystemCoreClock/1000000) != 0)

seinen µC fast vollständig auslastet.

Ich habe weiter oben zu Anfang schon einen Ansatz genannt.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

aSma>> schrieb:
> Der TE hat wohl nicht verstanden, dass
>> while (SysTick_Config(SystemCoreClock/1000000) != 0)
>
> seinen µC fast vollständig auslastet.

Allerdings. Für ms ist
1
  /* Setup SysTick Timer for 1 msec interrupts  */
2
  if (SysTick_Config(SystemCoreClock / 1000))
3
  {
4
    /* Capture error */
5
    while (1);
6
  }
 oder so ähnlich deutlich sinnvoller und lässt den anderen Jobs etwas 
Luft.
Kleinere delays könnte man mit einem Taktzyklen Ticker machen, wie das 
in CMSIS vorgeschlagen wird:
1
void _delay_ticks(uint32_t ticker){
2
  do {
3
    ticker--;
4
  } while (ticker);
5
}
Dann muss man aber wieder Zyklen zählen.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Matthias S. schrieb:
> Kleinere delays könnte man mit einem Taktzyklen Ticker machen

Das macht man doch mit dem DWT, dann braucht man keine Taktzyklen zu 
zählen, weil der DWT genau das von sich aus schon tut. Muß man halt nur 
noch wissen, wie lang ein Takt denn ist, um n Takte abzuwarten. Für 
µs-delays aber eine elegante Lösung. Außerdem unabhängig von der 
Optimierungsstufe.

Kommt natürlich an seine Grenzen, wenn man extrem weit runtergeht, weil 
der Overhead der Schleife dann die Genauigkeit wegreißt. Und außerdem 
muß das natürlich inlined werden oder gleich als Makro definiert werden.

von Nop (Gast)


Lesenswert?

Ach ja, und die "Lösung" aus der CMSIS hat den gewaltigen Nachteil, daß 
ein optimierender Compiler die komplette Funktion wegoptimieren kann, 
weil klar ist, daß sie keine Seiteneffekte hat, keinen Returnwert, und 
immer bis 0 zählt.

Wenn man sowas schon macht, dann geht das nur mit einer 
volatile-Variablen.

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Wie ist denn das überhaupt mit dem Systick, hab dazu im reference manual
> nichts gefunden

Für den Fall, daß du hier von einem Cortex sprichst, wirst du die Doku 
auch nicht bei den Chip-Herstellern finden, sondern in der Doku von ARM 
zum jeweiligen Cortex. Der SysTick-Zähler ist nämlich quasi in der CPU 
enthalten. Man lädt ihn mit einem Wert der einem genehm ist (z.B. 
Systemfrequenz/1000) und schaltet ihn ein. Das war's. Ab da faßt man ihn 
nie wieder an. Er liefert nun zyklisch seinen Interrupt (den man 
natürlich im NVIC passend verschalten muß) was ja das Einzige ist, was 
man bezwecken will. Der Interrupthandler ist nun quasi der Kern der 
Systemuhr, der Stunden und Tage zählen oder weiß der Geier was tun kann 
oder bei einem RTOS den Scheduler anstoßen kann und und und.

W.S.

von STMler (Gast)


Lesenswert?

W.S. schrieb:
> Für den Fall, daß du hier von einem Cortex sprichst, wirst du die Doku
> auch nicht bei den Chip-Herstellern finden, sondern in der Doku von ARM
> zum jeweiligen Cortex. Der SysTick-Zähler ist nämlich quasi in der CPU
> enthalten. Man lädt ihn mit einem Wert der einem genehm ist (z.B.
> Systemfrequenz/1000) und schaltet ihn ein. Das war's. Ab da faßt man ihn
> nie wieder an.


Unsinn. Bei ST steht es zum Beispiel im PM0214 Programming Manual 
(Cortex M4) Ausgabe September 2012 auf Seite 228.


> Er liefert nun zyklisch seinen Interrupt (den man
> natürlich im NVIC passend verschalten muß)


Nochmal Unsinn. Da muss man gar nichts "verschalten". Nach dem 
Einschalten per TICKINT Bit ist er aktiv, am NVIC muss man nix mehr tun, 
allenfalls die Priorität kann man ändern.

von Nop (Gast)


Lesenswert?

W.S. schrieb:
> Man lädt ihn mit einem Wert der einem genehm ist (z.B.
> Systemfrequenz/1000) und schaltet ihn ein. Das war's. Ab da faßt man ihn
> nie wieder an. Er liefert nun zyklisch seinen Interrupt

Ja wie man ihn benutzt, weiß ich, das ist auch mit ein paar Registern 
und der Vektortabelle einfach genug. Allerdings wird der nicht über den 
NVIC verschaltet, weil der Systick genaugenommen eine Exception und kein 
Interrupt ist, sondern die Prio stellt man in einem der SCB-Register 
ein. Konkret bei F4 geht das in SCB_SHPR3.

Nee, meine Frage zielte mehr darauf ab, ob die Dauer der 
Interruptroutine die Präzision der Zeitgebung beeinflußt oder nicht.

Wenn der Reload direkt beim assert schon passiert, dann ist es ja egal, 
wie lange die Routine dauert - sie muß halt vor allem fertig sein, bevor 
das nächste mal null erreicht wird, als obere Schranke.

von Daniel V. (voda) Benutzerseite


Lesenswert?

So, ich habe mal W.S. sein Konzept mal durchgearbeitet und ich muss 
sagen... ich verstehe nur Bahnhof. Zu viele Magic Numbers usw.

Wie implementiere ich einen ressourcenschonenden SW - Timer, welcher mir 
erlaubt, die SPI-Timings eines ADNS3090 einzuhalten und gleichzeitig die 
Daten, welche auf der MISO liegen auf die USART zu legen?

Die o.a. Lösung funktioniert leider nicht, da der Controller damit 
beschäftigt ist, die Interrups auszulösen.

Danke euch und Gruß
Daniel

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Nee, meine Frage zielte mehr darauf ab, ob die Dauer der
> Interruptroutine die Präzision der Zeitgebung beeinflußt oder nicht.

Also:
1. der SysTick liefert einen zeitzylkischen Interrupt.
2. der Interrupt-Handler für diesen Interrupt sollte natürlich nicht so 
lange trödeln, bis der nächste SysTick-interrupt ausgelöst wird. Aber 
das sollte ja wohl ohnehin klar sein. Kurzum, solange die ISR und alle 
anderen höherpriorisierten ISR's nicht länger dauern als die 
SysTick-Periode, ist die Präzision der Zeitgebung gewahrt. Aber das 
sollte ja ohnehin jedem klar sein, daß man in den Interrupts nicht mehr 
Zeit vertrödeln KANN, als tatsächlich zur Verfügung steht.



Daniel V. schrieb:
> So, ich habe mal W.S. sein Konzept mal durchgearbeitet und ich muss
> sagen... ich verstehe nur Bahnhof.

Seltsam. Eigentlich ist das Konzept doch glasklar. Und wenn du Fragen 
gehabt hättest, dann hättest du sie hier stellen können. Hast du denn 
wenigstens das Beispiel Katzenklappe verstanden? Da gibt es 3 Zustände: 
zu, auf und irgendwo mittendrin. Kann ja passieren, daß beim zu- oder 
aufmachen irgendwas blockiert und dann die Klappe auf halb acht hängt.

W.S.

von Daniel V. (voda) Benutzerseite


Lesenswert?

> Daniel V. schrieb:
>> So, ich habe mal W.S. sein Konzept mal durchgearbeitet und ich muss
>> sagen... ich verstehe nur Bahnhof.
>
> Seltsam. Eigentlich ist das Konzept doch glasklar. Und wenn du Fragen
> gehabt hättest, dann hättest du sie hier stellen können. Hast du denn
> wenigstens das Beispiel Katzenklappe verstanden? Da gibt es 3 Zustände:
> zu, auf und irgendwo mittendrin. Kann ja passieren, daß beim zu- oder
> aufmachen irgendwas blockiert und dann die Klappe auf halb acht hängt.
>
> W.S.

Das Konzept eines Zustandsautomaten, das habe ich schon verstanden nur 
mit Deiner Implementierung tu ich mich etwas schwer :)

Das mit dem Nachfragen im diesem Forum finde ich etwas schwierig. Hab 
schon öfters hier einstecken müssen, deshalb überlege ich zweimal ob ich 
wirklich nachfragen soll.

Gruß
Daniel

PS: Hmmpf, ich gestehe, ich habe die Txt-Datei nicht gelesen :(, ich 
nehme alles zurück. Dennoch harter Tobak

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Daniel V. schrieb:
> Dennoch harter Tobak

Nö.
Eigentlich nicht.
Also: erstens hat man eine Systemuhr. Die besteht im Wesentlichen aus 
der ISR des SystemTick's.

Ich habe sie hier so geschrieben, daß sie alle 1 ms von der HW 
aufgerufen wird, um was zeitkritisches zu erledigen. Der Rest geht in 
der ISR nur alle 10 ms. Dennoch wird die aktuelle Uhrzeit im 1 ms Raster 
gezählt. Units, die die richtige Tageszeit einstellen und einen Kalender 
führen, hab ich hier weggelassen. Wer sowas braucht, kann es sich ja 
leicht selber schreiben.

Natürlich kann man sich je nach konkreter Anwendung das Zeitintervall 
des SysTicks auch anders auswählen. In vielen Fällen reicht alle 10 ms 
völlig aus.

Das System, mit Events zu arbeiten, ist offenbar vielen Leuten hier 
herzlich fremd. Da wird lieber wie per PAP gearbeitet, also blockierend 
irgend ein Algorithmus vom PAP auf nen Quellcode übertragen. Das ist 
einfach, aber eben auch ressourcenvergeudend. Wer schon mal eine 
Anwendung für Windows geschrieben hat weiß hingegen, daß es anders geht, 
nämlich über Events. Die Anwendung (oder der Kommunikations-Thread in 
selbiger) tut normalerweise nix, sondern ist stillgelegt, bis ein Event 
eintrifft, für den sich die Anwendung als zuständig erklärt hat. So kann 
in der Zwischenzeit die Rechenleistung des PC für andere Zwecke 
verwendet werden.

Genau so im Ansatz arbeite ich auf dem µC. Ein Sensor (oder etwas 
Vergleichbares) arbeitet entweder per Interrupt oder er wird im SysTick 
gepollt (sofern dies wirklich zwingend nötig ist).

Aber auf eine erkannte Änderung wird eben NICHT sofort reagiert, sondern 
es wird stattdessen ein entsprechender Event erzeugt, der später dann in 
der Grundschleife (also im User-Mode) ausgewertet wird.

Das hat viele Vorteile:
1. Man hat eine gute Entkopplung zwischen verschiedenen Ebenen in seiner 
Firmware. Insbesondere zwischen den Low-Level-Treibern, die sich mit der 
HW herumplagen müssen und den höheren Ebenen, die sich um 
aufgabenorientierte Dinge kümmern sollen.
Beispiel Katzenklappe:
Wie der Motor funktioniert, ob dafür ein Portpin benötigt wird oder was 
Anderes, ist sache des LowLevel-Treibers. Die algorithmische Ebene (if 
Uhrzeit = nach Feierabend then ÖffneKatzenklappe();) soll ganz dediziert 
sich nicht drum scheren müssen, WIE die Katzenklappe geöffnet oder 
geschlossen wird. Ebenso SOLL es ihr wurscht sein, wie ein anderer 
LL-Treiber (der für die Kontakte an der Klappe) es hinbekommt, 
mitzuteilen, ob die Klappe nun zu oder auf oder mittendrin verreckt ist. 
Sowas wird mit Events gemacht und entkoppelt damit Algo-Ebene, 
Motortreiber und Sensortreiber.

2. Man braucht nicht mehr blockierend zu schreiben und sich dann 
zähneknirschend zu fragen, wie man zwischendurch noch ein paar andere 
nötige Dinge erledigt.

3. Wenn man - wie ich - die in events.h deklarierten Events in sinnvolle 
Gruppen einteilt, dann braucht man nicht jeden Event überall hin zu 
schicken, sondern nur an die Firmwareteile, die dafür in Frage kommen. 
Ein Event aus der Menü- oder Eingabetasten-Gruppe wird sicherlich NUR 
für die Menüführung interessant sein und nicht für den Algo, der das 
Verhalten der Katzenklappe steuert. Und wenn Ein Event nicht sowas wie 
broadcast ist, dann braucht sich auch keine Instanz in der Firmware mehr 
drum zu kümmern, sobald eine Instanz ihn verarbeitet und gelöscht hat.

Nun und schlußendlich bleibt die Grundschleife in main.c angenehm 
überschaubar.

W.S.

von Daniel V. (voda) Benutzerseite


Lesenswert?

Also, eins vorweg, Deine Lösung mit der parallele Abarbeitung finde ich 
sehr charmant und bei einer LabView-Schulung vor ein paar Jahren, wurde 
uns auch das Prinzip des endlichen Zustandsautomaten eingetrichtert um 
paralleles abarbeiten zu ermöglichen.

Ich bin zwar in C mehr oder weniger noch Anfänger (lerne es gerade im 
Unternehmen und auch in meinem Masterstudium an der Fernuni, wobei ich 
gerade meine Masterarbeit zum Thema Optical Flow schreibe (hier setzte 
ich den Code ein)), aber ich höre sehr viel von den alten 
Entwicklerhasen, dass man dies genau so machen soll um robusten Code zu 
erzeugen.

Mein nächster Arbeitsschritt ist es, den Code auf meine Kiste zu 
implementieren und mit meinen Sensoren (ein BMA020, ein ADNS3090 und 
eine USART zur Datenübertragung) anzupassen. Ziel ist es, die Bilddaten 
des ADNS3090, die Beschleunigungsdaten des BMA020 an einem 
Matlabprogramm zu übergeben. Hier entwickle ich gerade mit der 
Imageprocessing-Toolbox einen Algorithmus zu entwickeln.

Danke und Gruß
Daniel

von A. S. (Gast)


Lesenswert?

Daniel V. schrieb:
> Wie implementiere ich einen ressourcenschonenden SW - Timer, welcher mir
> erlaubt, die SPI-Timings eines ADNS3090 einzuhalten und gleichzeitig die
> Daten, welche auf der MISO liegen auf die USART zu legen?

Der Begriff Timer wird vielfältig verwendet ;-)

SPI und USART werden normalerweise im dazugehörigen Interrupt behandelt.

Zudem gibt es eine zyklische Interruptroutine, im einfachsten Fall für 
SysTicker++, meist in der Größenordnung 1ms.
Mit RTOS wird hier auch der Sceduler angeworfen.

Will man keine Interrupts für USART und SPI, dann verarbeitet man diese 
in der SysTicker-Interruptroutine. Beim Senden und beim SPI kein 
Problem, da die Daten dann einfach entsprechend länger brauchen. Will 
man auch USART-Empfangen, so darf die maximale Baudrate bei 
1ms-Zeitraster, 1 Byte Fifo und Parity + 2 Stoppbits nur maximal 9600 
Baud betragen.

Die Auf "SysTicker" basierenden Timer realisiert man zweckmäßig als 
brotlose Timer, die im Rahmen einer Loop abgefragt werden 
"(SysTicker-Timer.Endwert)<0?". Hier werden aber (je nach 
Arbeitsaufkommen) nur Zeiten ab mehreren SysTicker-Incrementen 
verarbeitet. Je näher der Zyklus an 1 SysTicker liegt, umso eher muss es 
in den Interrupt).

von Daniel V. (voda) Benutzerseite


Angehängte Dateien:

Lesenswert?

Okay, als "Quick&Dirty" - Lösung habe ich folgenden Code implementiert:

delay.c
1
#include <delay.h>
2
3
extern __IO uint32_t TimmingDelay;
4
5
void SysTick_Init(void)
6
{
7
    while (SysTick_Config(SystemCoreClock/1000));    
8
}
9
10
void delay_ms(__IO uint32_t time)
11
{
12
  TimmingDelay = time;
13
  while(TimmingDelay !=0);
14
}

delay.h
1
#ifndef DELAY_H
2
  #define DELAY_H
3
  
4
#include <stm32f4xx.h>  
5
6
extern void TimingDelay(__IO uint32_t time);
7
extern void SysTick_Init(void);
8
extern void delay_ms(__IO uint32_t time);
9
10
//extern void timer_Decrement(void);
11
//extern void delay_ns(uint32_t n);
12
//extern void delay_ms(void);
13
//extern void delay_nms(uint32_t n);
14
#endif

In der main toggle ich die "Debug-LED" (Duty cycle 0,5 bei einer 
Impulsdauer von 75 ms). Der Controller läuft  "smooth", vorher hat der 
µC im Debugmodus gehakt und geklemmt. Mit dem Oscar hab ich schnell die 
Timings nachgemessen, passt.

Nun, wie bekomme ich einen us-Timer hin? Denn hier lag das Problem.

Achim S. schrieb:
> SPI und USART werden normalerweise im dazugehörigen Interrupt behandelt.

Verwende ich ebenfalls. Mit SPI-Timer meine ich eher die Timings die ich 
einhalten muss, um den ADNS3090 richtig auszulesen. Dazu habe ich
ebenfalls hier im Forum den Thread erstellt:

Beitrag "STM32F4: Datenübertragung extrem langsam"

Diese eventgesteuerte Lösung von WS finde ich nach wie vor sehr 
charmant, nur muss ich möglichst schnell ein paar Pflichtergebnisse 
liefern und WS-Lösung fällt in der Kategorie "Kür".

Danke und Gruß
Daniel

@ Mods
Eigentlich könnten beide Threads zusammengefasst werden, da diese 
Probleme an der amateurhaften Timerkonfiguration meinerseits entstanden 
sind ;)

: Bearbeitet durch User
von Felix F. (wiesel8)


Lesenswert?

Ein INT alle 1us ist schon ne ziemliche Belastung für den Controller. 
Ich würde einen extra Timer nehmen und den nebenher einfach laufen 
lassen (1 Tick  us oder 10 Ticks  us) und dann in der Delay einfach 
das Count-Register abfragen bzw. in der delay den Counter vorher sogar 
zurücksetzen, um einen OVF zu vermeiden.

mfg

von A. S. (Gast)


Lesenswert?

Daniel V. schrieb:
> Mit SPI-Timer meine ich eher die Timings die ich
> einhalten muss, um den ADNS3090 richtig auszulesen. Dazu habe ich
> ebenfalls hier im Forum den Thread erstellt:

Wenn ich Deinen Thread dort richtig verstehe, ist doch Dein bisheriges 
Problem, dass Deine Warte-Routinen einfach nicht das taten, was sie 
sollten.

Wenn Du blockierend n Bytes über SPI senden oder empfangen willst, 
dann schreibe einmal ein (oder 3) Warteroutinen, die funktionieren, 
prüfe dieses und schreibe den Code runter. Mache blockierendes ns-Warten 
mit NOPs, wenn Du keine Timer dafür aufsetzen kannst oder willst. Und 
wenn er funktioniert, nimm ihn auch für µs.



Wenn Du absolutes Timing haben willst (z.B. LED im 0,5s Takt blinken, 
trotz 200ms Kommunikation zwischendurch, oder alle 10ms ein Telegramm 
starten) dann löse Dich von W.S.s klein-klein-überlauf-jein und mache 
Dich mit dem Sinn eines free-running-counters (SysTicker) vertraut. Und 
dass man diesen immer mit einer Differenz auswertet.

Daniel V. schrieb:
>> SPI und USART werden normalerweise im dazugehörigen Interrupt behandelt.
>
> Verwende ich ebenfalls. Mit SPI-Timer meine ich eher die Timings die ich
> einhalten muss, um den ADNS3090 richtig auszulesen. Dazu habe ich
> ebenfalls hier im Forum den Thread erstellt:

Wenn Du n Bytes SPI überträgst, dann x  µ/ns wartest und dann wieder n 
Bytes SPI, dann macht Interrupt dabei keinen Sinn. Sende ein Byte, 
warte bis es fertig ist und dann sende das nächste oder warte x µ/ns.

Ich denke, Du solltest einmal mit Pseudocode runterschreiben, was Du 
wirklich parallel tun willst.

von Daniel V. (Gast)


Lesenswert?

Achim S. schrieb:
> Wenn Du n Bytes SPI überträgst, dann x  µ/ns wartest und dann wieder n
> Bytes SPI, dann macht Interrupt dabei keinen Sinn. Sende ein Byte,
> warte bis es fertig ist und dann sende das nächste oder warte x µ/ns.
>
> Ich denke, Du solltest einmal mit Pseudocode runterschreiben, was Du
> wirklich parallel tun willst.


Okay, hier mal der Pseudocode:
1
INITIALISIERUNGEN
2
 GPIO;
3
 RCC;
4
 USART;
5
 SPI;
6
 BMA020;
7
 ADNS3090;
8
9
/*Bis hier in alles okay*/
10
11
SCHREIBE/LESE DATEN VOM ADNS390 
12
13
  SCHREIBE AUF REGISTER 0x13 eine 0x80
14
  WARTE (3 FRAMES + 10 us) /*1/2000 s = 0,5 us * 3 + 10 = 1510 us*/
15
  LESE REGISTER 0x40 (Burst Mode)
16
  WARTE(50us)
17
18
  IST STARTBIT = 0x40
19
  {  
20
    For (n=0;n<900;n++)
21
     {
22
     LESE WERTE AUS 0x40  /* 900 Werte */
23
     WARTE (10us)
24
     n++
25
       IST n = 900 
26
       {
27
        WARTE (14 us) /*Exit Burst Mode */
28
       }
29
     }
30
    SONST WARTE BIS STARTBIT = 0x40
31
   }
32
33
/*Funktioniert, die Daten liegen alle auf der MISO*/
34
35
SCHREIBE/LESE DATEN VOM BMA020
36
/* Funktioniert, die Daten liegen auf der MISO */
37
38
SCHREIBE DATEN AUF DIE USART
39
40
  SENDE DATEN VOM ANDS3090
41
  SEMDE DATEN VOM BMA020

Matlab übernimmt die Daten. Die Bilddaten werden benutzt um eine 
30x30-Matrix zu füllen und die BMA020-Daten für die Berechnung der 
Geschwindigkeit verwendet.

Danke und Gruß
Daniel

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.