Forum: Mikrocontroller und Digitale Elektronik stm32f103 1 us Systick-timer delay


von Roland H. (blacksmoke)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,
ich habe ein stm32f103-board hier (blue-pill, falls das jemand was 
sagt). Dort ist ein 8MHz Quarz drauf. Um einen Sensor vom Typ DHT22 
abfragen zu können, benötige ich unter anderem ein 1uS delay. Nun habe 
ich versucht mir eine Library zu basteln, die mir mithilfe des 
Systick-Timers 2 Delays macht (ms und us). Dabei bin ich grob wie folgt 
vorgegangen:

http://embeddedsystemengineering.blogspot.de/2016/01/arm-cortex-m3-stm32f103-tutorial-system.html

Das ms-delay hat dabei gut funktioniert (noch nicht nachgemessen ob das 
wirklich so genau stimmt). das uS delay funktionierte so gar nicht. Ich 
habe dann angefangen an den Anzahl an Ticks zwischen 2 Interrupts zu 
spielen. Dabei bin ich darauf gestoßen, dass es bis zu einem Wert von 33 
(x 125nS) noch funktioniert, bei 32 nicht mehr. Dies Wären dann ein 
minmales delay von 4,125uS

Kann sich einer das erklären ? Ich werde daraus einfach nicht schlau

die Clock-konfiguration in der main sieht wie folgt aus: (kommt aus 
stm32cubemx so raus)

void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(_FILE_, _LINE_);
  }

    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != 
HAL_OK)
  {
    _Error_Handler(_FILE_, _LINE_);
  }

    /**Configure the Systick interrupt time
    */
  //HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}


Außerdem habe ich noch die Delay-file angehängt.
So wie ich das sehe läuft der Clock ohne PLL mit dem HSE (Quartz) wenn 
ich mich da nicht vertue

über eine Hilfe wäre ich sehr dankbar

das Programm bleibt einfach in der while-schleife beim delay scheinbar 
hängen

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

Also den Systick Interrupt alle µs aufzurufen ist schon extrem 
sportlich. Das sind normalerweise 1ms oder 10ms normal. Kurze Delays 
solltest du besser ohne Interrupts machen oder wenn die Zeiten genau 
stimmen müssen über einen speziellen Timer.

von Roland H. (blacksmoke)


Angehängte Dateien:

Lesenswert?

also du meinst einen extra timer dafür zu verwenden ? ist der 
systick-timer nicht auch einfach nur ein timer ? ich habe mit dem TIM2 
mal n bisschen rum gespielt(hat auch funktioniert, allerdings habe ich 
nicht getestet, wie weit man runter gehen kann), da ich bei stm32 bisher 
noch nicht viel mit timern gemacht hab.

ein Datenbit ist physikalisch 80uS lang( nachgemessen). ganz genau muss 
es vermutlich nicht sein. ich hatte halt gehofft, dass ich das reltiv 
modular hinbekomme (also am besten als lib die man nur noch einbinden 
kann wenn man sie braucht, der uC soll am ende so variable wie möglich 
für evtl. verschiedene Temperatursensoren, ... verwendbar sein

von Johannes S. (Gast)


Lesenswert?

Da müssen aber nur die Bitlängen gemessen werden, das geht mit Timer 
Capture oder einem frei laufenden Timer und bei Pegelwechsel liest man 
den Zählerstand und bildet die Differenz.

Beitrag #5358054 wurde vom Autor gelöscht.
von STM Apprentice (Gast)


Lesenswert?

temp schrieb:
> Also den Systick Interrupt alle µs aufzurufen ist schon extrem
> sportlich.

Jawoll, das ist grob gesprochen Mist.

Ist doch alles da, man braucht nur danach suchen:

https://www.mikrocontroller.net/articles/STM32_f%C3%BCr_Einsteiger#Taktzeitberechnung_und_.C3.9Cberwachung

Gibt es auch beim STM32F103.

von Roland H. (blacksmoke)


Lesenswert?

Ich müsste also einen timer im Input Capture-Mode verwenden, der bei 
jedem flankenwechsel die zeit misst. allerdings müsste ich dann zu jeder 
messung wissen ob 1 oder 0

von Daniel B. (daniel_3d)


Lesenswert?

Ich habe mir vor einiger Zeit mal was gebastelt und es funktioniert 
bisher sehr gut. Hier für den STM32F103:
1
//********************** DWT_Delay_us ********************************
2
#define DWT_Korrekturwert 20 
3
uint32_t DWT_Scale;
4
inline static void DWT_Delay_us(uint32_t microseconds) {
5
  uint32_t DWT_Startwert = DWT->CYCCNT;
6
  uint32_t ticks = (microseconds * DWT_Scale) - DWT_Korrekturwert; 
7
  while ((DWT->CYCCNT - DWT_Startwert) < ticks);     
8
}
9
//********************** DWT_Delay_us ********************************
10
11
int main(void)
12
{
13
   //********************** DWT_Delay_us ********************************
14
  DWT_Scale = (HAL_RCC_GetHCLKFreq() / 1000000);   // Berechnung des "Vorteilers", für Takt pro Microsekunde
15
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;  // Initialisierung des DWT
16
  DWT->CYCCNT = 0;                  // Initialisierung des DWT-Counter
17
  DWT->CTRL = ENABLE;                // Initialisierung des DWT-Counter
18
  //********************** DWT_Delay_us ********************************
19
20
while (1)  {
21
22
   DWT_Delay(5);
23
}
24
}

: Bearbeitet durch User
von STM Apprentice (Gast)


Lesenswert?

Daniel B. schrieb:
> Hier für den STM32F103:

Man muss in DWT_Delay_us (...) noch die Interrupts global
disablen sonst gibts manchmal pöhse Delay-Verschiebungen.

von Daniel B. (daniel_3d)


Lesenswert?

Danke für den Tipp, werde ich noch einbauen.
Bisher hatte ich immer Glück :-)

von Jacko (Gast)


Lesenswert?

Hochsprache - und ein Delay von wenigen Systemtakten.

Bei ASM ruft man dann (und nur dann) einfach ein paar
Dummy-Befehle (NOP) auf, und schon ist eine µs vergangen.

Die Compiler sind aber mittlerweile so schlau, dass sie Dummy-
Befehle gar nicht ausführen lassen.

Die DWT-Einheit wird wahrscheinlich auch nur was über Laufzeiten
der vom Compiler für sinnvoll erachteten Befehle erzählen
können...

Inline-Assembler mit ein paar Dummy-Befehlen?

von STM Apprentice (Gast)


Lesenswert?

Jacko schrieb:
> Bei ASM ruft man dann (und nur dann) einfach ein paar
> Dummy-Befehle (NOP) auf, und schon ist eine µs vergangen.

Und woher weisst du wieviele NOPs fuer eine usec gebraucht
werden? Das ist nämlich abhängig von der Clock-Konfiguration,
also unterschiedlich für ein und denselben Controller.

Also ein STM32F103 braucht eben nicht 72 Clock-Zyklen für
eine usec weil er mit 72 Mhz getaktet ist.

Musst du für jeden Konfigurations-Fall getrennt herausmessen.

von Daniel B. (daniel_3d)


Lesenswert?

Viele Wege führen nach Rom, aber von ASM habe ich keine Ahnung.
Die obige Lösung funktioniert bei mir bisher tadellos.

Ich habe mich natürlich verschrieben.
Das Delay wird natürlich so aufgerufen:
1
DWT_Delay_us(5);

Nachgemessen habe ich indem ich einen Pin getoggelt habe. Die Zeiten 
stimmen ziemlich genau.
Natürlich indem ich direkt das BRR und BSRR-Register beschrieben habe.
Verwendet man die HAL-Funktionen zum Pin toggeln, merkt man auf einmal 
wie langsam die sind.

: Bearbeitet durch User
von Matthias (Gast)


Lesenswert?

>spielen. Dabei bin ich darauf gestoßen, dass es bis zu einem Wert von 33
>(x 125nS) noch funktioniert, bei 32 nicht mehr. Dies Wären dann ein
>minmales delay von 4,125uS

Wundert mich nicht. Der Cortex-M3 Core sichert beim Eintritt in einene 
ISR
(Tailchaining, etc. mal außen vor) alle 32 Register in Hardware auf den 
Stack. Das dürften schon 32 Taktzyklen sein. Bei 8 MHz bzw. 125ns
dauert der Context-Switch für einen IRQ schon 32 x 125ns = 4000ns.
Beim Beenden würde dann prinzipiell nochmal soviel anfallen.
Da könnte dann ggf. das Tailchaining greifen, dass es etwas 
"beschleunigt" wird.

von STM Apprentice (Gast)


Lesenswert?

Daniel B. schrieb:
> Verwendet man die HAL-Funktionen zum Pin toggeln ...

... dann braucht man auch keine delay_us-Funktion mehr.

SCNR

von Roland H. (blacksmoke)


Lesenswert?

Also den DWT kannte ich bisher noch nicht. Ich werde vielleicht erst mal 
kucken ob ich nicht einen der Timer im Input-Capture verwenden kann 
(wenn man sie schon hat).

Mir stellt sich nur die frage ob der Input-Capture-Mode der richtige 
ist. Ich habe da im Reference-Manual auch noch was von External-Trigger 
gelesen. Das gesamtbild wie dies alles zusammenhängt ist mir noch nicht 
ganz klar. ist der ETR ein extra mode/ eingang oder was soll das ganze.

Ich will mir einen Sensorknoten bauen, der über eine weitere serielle 
Schnittstelle mit anderen Sprechen kann(kleiner Linux-Rechner / andere 
uC). Dafür soll der Knoten mit verschiedenen Sensoren arbeiten können 
(was halt grad da ist). Die Abfrage sollen quasi über seriell von 
anderen Stellen kommen, so nach dem Frage-Antwort-Prinzip(die Abfrage 
des Sensors, muss also nicht in einer Endlosschleife immer wieder 
passieren)

Den DWT kann ich mir ja mal anschauen für was der denn überhaupt da ist

: Bearbeitet durch User
von John Doe (Gast)


Lesenswert?

Matthias schrieb:
>>spielen. Dabei bin ich darauf gestoßen, dass es bis zu einem Wert
> von 33
>>(x 125nS) noch funktioniert, bei 32 nicht mehr. Dies Wären dann ein
>>minmales delay von 4,125uS
>
> Wundert mich nicht. Der Cortex-M3 Core sichert beim Eintritt in einene
> ISR
> (Tailchaining, etc. mal außen vor) alle 32 Register in Hardware auf den
> Stack. Das dürften schon 32 Taktzyklen sein. Bei 8 MHz bzw. 125ns
> dauert der Context-Switch für einen IRQ schon 32 x 125ns = 4000ns.


Bullshit. Lies die Doku.

von Peter D. (peda)


Lesenswert?

Die ARM kehren bei hoher Interruptlast nicht mehr zum Main zurück, das 
Main stoppt quasi.
Im Unterschied zu den 8051 oder AVR, wo nach jedem RETI mindestens ein 
Befehl des Main ausgeführt wird.

von Roland H. (blacksmoke)


Lesenswert?

Ich bin eigentlich eher ein Fan von direktem verwenden der CMSIS. Das 
ist das was mir beim AVR früher immer gefallen hat. es war zwar etwas 
mehr einarbeitung erforderlich, aber dabei hat man das soweit verstanden 
wie die Dinge funktionieren.

Bei st geht es eher in die richtung sich alles generieren zu lassen. In 
letzter Zeit befasse ich mich wieder mehr damit direkt die Register zu 
beschreiben wenn möglich.

Vielleicht muss ich mir echt mal ein template-projekt pro stm32-typ 
basteln, dass nur das nötigste (makefile, startup, cmsis, ...) enthält 
und diese mit libs nach und nach erweitern, bei denen ich dann weiß was 
sie tun (und vor allem dass sie tun)

: Bearbeitet durch User
von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Roland H. schrieb:
> Also den DWT kannte ich bisher noch nicht. Ich werde vielleicht erst mal
> kucken ob ich nicht einen der Timer im Input-Capture verwenden kann
> (wenn man sie schon hat).
>
> Mir stellt sich nur die frage ob der Input-Capture-Mode der richtige
> ist. Ich habe da im Reference-Manual auch noch was von External-Trigger
> gelesen. Das gesamtbild wie dies alles zusammenhängt ist mir noch nicht
> ganz klar. ist der ETR ein extra mode/ eingang oder was soll das ganze.
>
> Ich will mir einen Sensorknoten bauen, der über eine weitere serielle
> Schnittstelle mit anderen Sprechen kann(kleiner Linux-Rechner / andere
> uC). Dafür soll der Knoten mit verschiedenen Sensoren arbeiten können
> (was halt grad da ist). Die Abfrage sollen quasi über seriell von
> anderen Stellen kommen, so nach dem Frage-Antwort-Prinzip(die Abfrage
> des Sensors, muss also nicht in einer Endlosschleife immer wieder
> passieren)
>
> Den DWT kann ich mir ja mal anschauen für was der denn überhaupt da ist

Hallo,

die DWT (Data Watchpoint and Trace Unit) ist im ARM Cortex Reference 
Manual dokumentiert.

Wie der Name schon impliziert, ist es eigentlich ein Submodul des Cortex 
Kerns, dass zum Debuggen und nicht zum normalen Programmablauf genutzt 
wird. Der Cycle Counter ist dazu da, einen Watchpoint nach einer 
programmierbaren Anzahl von Zyklen nach Auftreten eines Events (z.B. 
schreibenden oder lesenden Zugriffs auf eine definierbare 
Speicherstelle) auszulösen.

Der Cycle Counter ist allerdings der am feinsten granuliebare Timer, den 
der Kern bietet (genauer als auf Einzelzyklenbasis geht's nicht), 
weswegen er auch gerne zu Allen möglichen Anderen Zwecken genutzt wird.

Eines der Dinge, die man bei der Nutzung im Auge behalten muss ist dass 
der Cycle Counter von vielen IDEs und Debuggern genutzt und zuweilen auf 
0 gesetzt wird (um Time Stamp Deltas bilden zu können). Also nicht 
wundern, wenn sich ein Programm, das den DWT Cycle Counter nutzt, unter 
Debuggerkontrolle plötzlich Anders verhält als im Normalbetrieb!

von STM Apprentice (Gast)


Lesenswert?

Ruediger A. schrieb:
> Eines der Dinge, die man bei der Nutzung im Auge behalten muss ist dass
> der Cycle Counter von vielen IDEs und Debuggern genutzt und zuweilen auf
> 0 gesetzt wird

Genau das sollte gelinde gesagt eben niemand tun, so wie es
viele andere ungeschriebene Regeln gibt. Was sollte es auch
für einen triftigen Grund geben gerade diesen Zähler auf Null
zu setzen? Kann mann ihn doch durch einfache Differenzbildung
zum Auswerten für seine "eigenen Zwecke" nutzen.

Auch ein Debugger sollte das schaffen. Der ST-Link z.B. verhält
sich diesbezüglich korrekt, der J-Link (denke ich, gerade nicht
überprüft) auch.

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


Lesenswert?

STM Apprentice schrieb:
> Ruediger A. schrieb:
>> Eines der Dinge, die man bei der Nutzung im Auge behalten muss ist dass
>> der Cycle Counter von vielen IDEs und Debuggern genutzt und zuweilen auf
>> 0 gesetzt wird
>
> Genau das sollte gelinde gesagt eben niemand tun, so wie es
> viele andere ungeschriebene Regeln gibt. Was sollte es auch
> für einen triftigen Grund geben gerade diesen Zähler auf Null
> zu setzen? Kann mann ihn doch durch einfache Differenzbildung
> zum Auswerten für seine "eigenen Zwecke" nutzen.
>

Ja, wäre schön, wenn sich Alle daran hielten würden... leider aber hat 
sich auch in "seriösen" Kreisen die Folklore eingebürgert, von 0 aus zum 
Sollwert hochzuzählen, z.B. hier:

http://embeddedb.blogspot.de/2013/10/how-to-count-cycles-on-arm-cortex-m.html
https://stackoverflow.com/questions/36378280/stm32-how-to-enable-dwt-cycle-counter

mglw. auch mit dem Gedanken im Hinterkopf, dass sich damit auch der 
statistisch recht unwahrscheinliche aber mögliche Fall des wraparound 
praktisch elimineren lässt. Ich stimme aber mit Dir darin überein, dass 
sampling on the fly und Deltabildung die bessere Variante ist (zumal die 
Anzahl Zyklen zwischen faktischem Nullsetzen und Anfang des zu 
samplenden Codes auch zwischen Compilern und Optimierungsstufen variabel 
ist und damit das Ergebnis mglw. verfälscht).

> Auch ein Debugger sollte das schaffen. Der ST-Link z.B. verhält
> sich diesbezüglich korrekt, der J-Link (denke ich, gerade nicht
> überprüft) auch.

Ich schrieb von Debuggern, nicht debug probes... Je nach 
"Eigenintelligenz" leiten die probes die Registerzugriffe der Debugger 
mehr oder weniger 1:1 vom Debugger zum Target durch. M.W. nach (könnte 
da aber falsch liegen) hat der ST Link so gut wie keine autarke 
Registersteuerung.

von Thomas E. (picalic)


Lesenswert?

Ist es denn wirklich notwendig, für irgendwelche simplen µs-Delays 
dermaßen in die "Trickkiste" zu greifen und die Debug-Resourcen 
anzuknabbern?
Der Controller hat ja nun wirklich auch ein paar "normale" Timer, die 
das problemlos leisten können. Da scheint es mir schon zweifelhaft, 
sowas wie die Nutzung des DWTs dafür hier Anfängern quasi als 
"Musterlösung" zu präsentieren...

von Roland H. (blacksmoke)


Lesenswert?

Thomas E. schrieb:
> Ist es denn wirklich notwendig, für irgendwelche simplen µs-Delays
> dermaßen in die "Trickkiste" zu greifen und die Debug-Resourcen
> anzuknabbern?
> Der Controller hat ja nun wirklich auch ein paar "normale" Timer, die
> das problemlos leisten können. Da scheint es mir schon zweifelhaft,
> sowas wie die Nutzung des DWTs dafür hier Anfängern quasi als
> "Musterlösung" zu präsentieren...
Das wäre auch immer noch die Lösung die ich aktuell im Auge habe. Ich 
habe doch ein paar Timer. Wozu diese also nicht mal benutzen.

von Roland H. (blacksmoke)


Lesenswert?

Der Input Capture mode nütz mir aber auch nichts wenn ich nur von 
steigender zu steigender flanke messen kann. Das entsprechende 
kapitel/register hab ich im Reference-Manual leider noch nicht gefunden

von Daniel B. (daniel_3d)


Lesenswert?

1.
Es scheint mir der DWT-Counter wird vom Debugger gestartet und wenn kein 
Debugger verwendet wird läuft der DWT-Counter garnicht.
Das schließe ich daraus, dass ich den DWT-Counter einmalig von Hand 
anwerfen muss. Sonst funktioniert die DWT_Delay_us-Funktion nur wenn der 
Debugger angeschlossen ist.

2.
Auch erscheint es mir sinnvoll den DWT-Counter nicht zu manipulieren 
(auf 0 setzten), sondern nur zu lesen und das Delay durch 
Differenzbildung zu realisieren.

3.
Interrupts von wenigen Takten machen nur Probleme.

4.
Eine vorgegebene Taktanzahl in einer Schleife zu vertrödeln mag ich aus 
folgenden Gründen nicht:
-Delay ändert sich bei Änderung des uC-Taktes
-Delay ändert sich je nach Optimierungsgrad

Darum verwende ich das DWT-Counter-Dalay solange ich keine bessere 
Lösung kenne:
Ich benutze einen (ansonsten ungenutzten?) geeigneten Counter den ich 
nicht einmal manipuliere, sondern nur lese.
Es werden keine problematischen Interrupts gebraucht und ich muss mir 
keine Gedanken um Optimierungslevel und Taktfrequenz machen. Ich 
"verbaue" mir keinen Timer, den ich vielleicht doch nochmal brauche.
Und es ist mit 2 copy und paste-Aktionen implementiert.


Roland möchte es gerne mit einer anderen Lösung versuchen.
Ich nutze in Zukunft auch gerne eine andere, "bessere" Lösung.

Dazu müssten aber auch Lösungsvorschläge kommen.

Ein: "...das kannst du so nicht machen, weil..."
ist ja gerechtfertigt und auch gewünscht.
Aber dann sollte folgendes kommen:
"... mach es lieber so, weil..."

Und dieses "... mach es lieber so, weil..."
vermisse ich leider immer öfter.

Ich freue mich auf Lösungs/Verbesserungsvorschläge.

Gruß Daniel

: Bearbeitet durch User
von Leo C. (rapid)


Lesenswert?

Roland H. schrieb:
> Der Input Capture mode nütz mir aber auch nichts wenn ich nur von
> steigender zu steigender flanke messen kann.

Doch. Man kann ja z.B. die Flanke nach jedem Capture Interrupt toggeln.
Oder eleganter: Den "PWM input mode" nehmen. Im Reference Manual Kapitel 
15.3.6, bzw. 14.3.7.

Wenn auf fallende Flanke getriggert wird, bekommt man im CCR1 der 
Zählerwert für die gesamte Bitlänge, und im CCR2 für die Low-Phase. Die 
High-Phase ist dann die Differenz (CCR1-CCR2).

Die Timerwerte können auch per DMA im RAM abgelegt werden und erst nach 
der kompletten Übertragung ausgewertet werden.

von Thomas E. (picalic)


Lesenswert?

Daniel B. schrieb:
> Ich benutze einen (ansonsten ungenutzten?) geeigneten Counter den ich
> nicht einmal manipuliere, sondern nur lese.

Eben, das Fragezeichen setzt Du ja schon selbst - ich würde mal 
vorsichtshalber davon ausgehen, daß der Timer evtl. beim Debuggen 
verwendet und u.U. auch vom Debugger gelöscht werden könnte.

Daniel B. schrieb:
> Es werden keine problematischen Interrupts gebraucht und ich muss mir
> keine Gedanken um Optimierungslevel und Taktfrequenz machen.

Muss man bei einem TIMx auch nicht.

Daniel B. schrieb:
> Ich
> "verbaue" mir keinen Timer, den ich vielleicht doch nochmal brauche.
> Und es ist mit 2 copy und paste-Aktionen implementiert.

"Verbaut" ist der Timer ja auch nicht. Die 4 CC-Units kann man ja für 
andere Zwecke benutzen. Meist hat man ja sowieso mindestens einen frei 
durchlaufenden Timer in Benutzung, den man dann für ein µs-Delay einfach 
mitverwendet. Wenn der (16-bit)Timer zufällig eh auf 1 MHz Takt nach 
Prescaler eingestellt ist, ist usDelay dann einfach:
1
void usDelay (uint16_t us)
2
{
3
  uint16_t temp = TIMx->CNT + us;
4
  while((TIMx->CNT - temp) & 0x8000);
5
}

P.S.: wenn ich das richtig sehe, hat Deine Funktion oben übrigens ein 
Problem mit Timer-Überlauf...

: Bearbeitet durch User
von Daniel B. (daniel_3d)


Lesenswert?

Thomas E. schrieb:
> daß der Timer evtl. beim Debuggen
> verwendet und u.U. auch vom Debugger gelöscht werden könnte.

Kann schon sein. Aber stört das beim debuggen?


Thomas E. schrieb:
> P.S.: wenn ich das richtig sehe, hat Deine Funktion oben übrigens ein
> Problem mit Timer-Überlauf...

Das sehe ich genauso.
Seit zwei Wochen frage ich sekündlich zwei DS18B20 ab und gebe die Werte 
auf ein HD44780.
In beiden Routinen wird das DWT_Delay benutzt wird. Mich wundert auch 
ein wenig, dass ich noch keine einzige Fehlmessung hatte.


Es ist nicht MEINE Lösung.
Es ist nicht mal meine eigene Geistesleistung, den DWT-Counter für sowas 
zu verwenden.

Roland hat nach einer Lösung gesucht und ich habe ihm gesagt, wie es bei 
mir funktioniert.
Wenn er will kann er es verwenden bis er was Besseres gefunden hat.

Ich bin kein Profi, deswegen freue ich mich über jede lehrreiche und 
produktive Diskussion in der die Vor-/Nachteile der unterschiedlichen 
Delays zur Sprache kommen.

Ich probiere dein Timer-Delay auch aus, vielleicht wird es mein neues 
Standard-Delay :-)

Gruß Daniel

von Jacko (Gast)


Lesenswert?

STM Apprentice (Gast) fragte:
> Und woher weisst du wieviele NOPs fuer eine usec gebraucht
> werden? Das ist nämlich abhängig von der Clock-Konfiguration,
> also unterschiedlich für ein und denselben Controller.

1) Der TO hat doch eindeutig 8 MHz geschrieben.
   1 NOP pro Systemtakt - wirst du wohl selber ausrechnen können...

2) Es gibt bedingte Kompilierung, da wird die Prozessor-Taktrate
   bekannt gegeben (davon lebt auch der Standard-Delay-Timer) und
   dann werden die NOPs in 2er-Potenzschritten (oder feiner)
   zugeteilt. Was 1 µs Delay braucht, funktioniert meist auch
   mit 2, aber bestimmt mit 1,4 µs Delay...
   - Genauer wird es bei < 20 MHz auch auf andere Weise nicht,
     wenn noch etwas Zeit für das eigentliche Programm gebraucht
     wird.

von Johannes S. (Gast)


Lesenswert?

Jacko schrieb:
> Der TO hat doch eindeutig 8 MHz geschrieben.

...und das es um einen STM32F103 geht, per PLL macht der da bis 72 MHz 
raus.

Timer mit 1us einrichten, Interrupt nach 40 us auslösen. Zum Sensor 
lesen bei Pegelwechsel den us Timer lesen.

von Chris J. (Gast)


Lesenswert?

Nimm das und glücklich sein. 1 Mal Init nach SysInit auf dem BPB und es 
läuft, auch mit Debugger
1
#pragma GCC push_options
2
#pragma GCC optimize ("O3")
3
void delayUS_DWT(uint32_t us)
4
{
5
  uint32_t cycles = (SystemCoreClock/1000000L)*us;
6
7
  __disable_irq();
8
  volatile uint32_t start = DWT->CYCCNT;
9
  do  {
10
  } while(DWT->CYCCNT - start < cycles);
11
12
  __enable_irq();
13
}
14
#pragma GCC pop_options
15
16
#pragma GCC push_options
17
#pragma GCC optimize ("O0")
18
uint32_t DWT_Delay_Init(void) {
19
  /* Disable TRC */
20
  CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; // ~0x01000000;
21
  /* Enable TRC */
22
  CoreDebug->DEMCR |=  CoreDebug_DEMCR_TRCENA_Msk; // 0x01000000;
23
24
  /* Disable clock cycle counter */
25
  DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001;
26
  /* Enable  clock cycle counter */
27
  DWT->CTRL |=  DWT_CTRL_CYCCNTENA_Msk; //0x00000001;
28
29
  /* Reset the clock cycle counter value */
30
  DWT->CYCCNT = 0;
31
32
     /* 3 NO OPERATION instructions */
33
     __ASM volatile ("NOP");
34
     __ASM volatile ("NOP");
35
     __ASM volatile ("NOP");
36
37
  /* Check if clock cycle counter has started */
38
     if(DWT->CYCCNT)
39
       return 0; /*clock cycle counter started*/
40
     else
41
       return 1; /*clock cycle counter not started*/
42
}
43
#pragma GCC pop_options

von Matthias (Gast)


Lesenswert?

>Matthias schrieb:
>>>spielen. Dabei bin ich darauf gestoßen, dass es bis zu einem Wert
>> von 33
>>>(x 125nS) noch funktioniert, bei 32 nicht mehr. Dies Wären dann ein
>>>minmales delay von 4,125uS
>>
>> Wundert mich nicht. Der Cortex-M3 Core sichert beim Eintritt in einene
>> ISR
>> (Tailchaining, etc. mal außen vor) alle 32 Register in Hardware auf den
>> Stack. Das dürften schon 32 Taktzyklen sein. Bei 8 MHz bzw. 125ns
>> dauert der Context-Switch für einen IRQ schon 32 x 125ns = 4000ns.

>Bullshit. Lies die Doku.
Wenn Du es schon "besser" weißt, dann hättest Du ja auch gleich die 
korrekten Latenzdaten schreiben können, statt dem Blödsinn.

Dann sind es halt nur 12 Zyklen beim Eintritt und nicht alle 32 Register 
+
nochmal 12 Zyklen beim Verlassen des IRQ Kontext. Macht in Summe 
trotzdem
noch satte 24 Zyklen bzw. 3000ns allein für den Context-Switch.
Dazu kommen noch ein paar Zyklen für den ISR-Code. Damit sollte klar 
sein,
warum er nicht unter die beobachteten 4us kommt.

von temp (Gast)


Lesenswert?

Der Thread ist zwar schon alt, trotzdem hole ich das hier nochmal hoch. 
Leider stimmt die Aussage nicht, dass das DWT-Delay mit dem jlink 
problemlos ist. Zumindestens nicht in Verbindung mit Segger Embedded 
Studio.
Beim Initialisieren des DWT wird folgendes gemacht:
1
  /* Enable TRC */
2
  CoreDebug->DEMCR |=  CoreDebug_DEMCR_TRCENA_Msk;
Wenn sich jlink connected kommt das System nicht durcheinander, blöd ist 
nur dass genau dieses Bit vom jlink (oder der Segger IDE) beim 
Deconnecten zurück setzt. Damit hängt das Programm in der nächsten 
Delay-Schleife fest.
Ich habe leider noch nicht rausgekriegt ob und wie man das abschalten 
kann und wäre über einen Tip dankbar.

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


Lesenswert?

Naja, wie schon mehrmals hier erwähnt wurde, ist die Nutzung des DWT als 
"normaler" Timer etwas fragwürdig. DWT heißt Data Watchpoint and Trace 
und ist eigentlich ein fürs Debugging dediziertes Modul.

Es läßt sich wirklich jeder Cortex M Timer dafür verwenden, einen 
Zyklengenauen Zähler und Verzögerer zu implementieren. Dazu muß der 
Timer nur als freilaufend konfiguriert und periodisch der Zählerstand 
abgefragt werden. Es muss nicht die DWT sein, die wird im Zusammenhang 
mit aktiven Debuggern mit hoher Wahrscheinlichkeit Probleme bereiten.

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

Ruediger A. schrieb:
> Naja, wie schon mehrmals hier erwähnt wurde, ist die Nutzung des DWT als
> "normaler" Timer etwas fragwürdig. DWT heißt Data Watchpoint and Trace
> und ist eigentlich ein fürs Debugging dediziertes Modul.

Das ist mir klar, und darum ging es nicht bei meiner Frage/Feststellung

Ruediger A. schrieb:
> Es läßt sich wirklich jeder Cortex M Timer dafür verwenden

nur leider hat der STM32F103 nicht einen einzigen der 32bit breit ist.
Ja, es gibt sicher andere Lösungen die kompatibler sind. Das brauche ich 
allerdings nicht und wechsele auch nicht ständig die IDE. Also kann ich 
gut damit leben und helfe mir so:
1
void delay_dwt(const uint32_t ticks) 
2
{
3
  uint32_t start = DWT_CYCCNT;
4
  while((DWT_CYCCNT - start) < ticks)
5
    {    
6
    SCB_DEMCR |= 0x01000000;
7
    }
8
}

von STM Apprentice (Gast)


Lesenswert?

temp schrieb:
> und helfe mir so:

Dauernd auf das SCB_DEMCR-Register ballern?

Und dann noch mit Magic Numbers, sodass jeder erst in den
Magic Documents nachschauen muss was du da machst?

Dein Read-Modify-Write braucht ein "paar Taktzyklen" die
das ganze µs-Timen ungenauer machen.

von temp (Gast)


Lesenswert?

STM Apprentice schrieb:
> Dauernd auf das SCB_DEMCR-Register ballern?
>
> Und dann noch mit Magic Numbers, sodass jeder erst in den
> Magic Documents nachschauen muss was du da machst?
>
> Dein Read-Modify-Write braucht ein "paar Taktzyklen" die
> das ganze µs-Timen ungenauer machen.

Es schaut hier nur einer rein und das bin ich. Ich habe hier niemanden 
etwas aufdrängeln wollen. Die Entscheidung ob die paar Taktzyklen etwas 
ungenauer machen oder nicht kannst du auch mir überlassen. Also was soll 
dein Genöle? Die die's interessiert werden da raus nehmen können was 
wichtig ist. Ich präsentiere hier auch keine Lib die man ungeprüft in 
ein Arduino Dumpfbacken Projekt kopiert. Also troll dich und mach deinen 
eigenen Kram anstelle hier den Oberlehrer zu spielen.

von Peter D. (peda)


Lesenswert?

temp schrieb:
> nur leider hat der STM32F103 nicht einen einzigen der 32bit breit ist.

Wenn Du eh die Zeit verwartest, dann reicht ein 16Bit-Timer dicke und 
den höherwertigen Teil erledigt eine profane Zählschleife drumrum.

Abgesehen davon treiben solche elend langen Delays die CPU-Performance 
in den tiefsten Keller, d.h. Dein 32Bit-Bolide wird von jedem 8Bitter um 
Längen geschlagen.

Der Systick Timer des F103 hat immerhin 24Bit.

von 900ss (900ss)


Lesenswert?

temp schrieb:
> Die Entscheidung ob die paar Taktzyklen etwas ungenauer machen oder
> nicht kannst du auch mir überlassen. Also was soll dein Genöle? Die
> die's interessiert werden da raus nehmen können was wichtig ist

Weshalb gleich so pampig?
Der Hinweis auf diese Ungenauigkeit war sicher nicht bös gemeint und 
würde Anfängern, die das nicht sofort durchschauen, ja durchaus helfen.

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

900ss D. schrieb:
> Weshalb gleich so pampig?
> Der Hinweis auf diese Ungenauigkeit war sicher nicht bös gemeint und
> würde Anfängern, die das nicht sofort durchschauen, ja durchaus helfen.

Es ging mir nicht darum Delays zu implementieren, sondern darum auf die 
Problematik mit dem SCB_DEMCR, jlink und der Segger IDE hinzuweisen.

Peter D. schrieb:
> Dein 32Bit-Bolide wird von jedem 8Bitter um
> Längen geschlagen.

Na dann werde ich wohl auf 8Bitter umstellen wenn die den Vorteil haben 
bei einem 1000µs Delay in 100µs fertig zu sein...

Peter D. schrieb:
> Der Systick Timer des F103 hat immerhin 24Bit.

Der wird aber in der Regel immer verwendet und läuft auch nicht durch.

Peter D. schrieb:
> Wenn Du eh die Zeit verwartest, dann reicht ein 16Bit-Timer dicke und
> den höherwertigen Teil erledigt eine profane Zählschleife drumrum.

Ich kniee ehrfürchtig vor dem Meister nieder für diesen Tip. Auf diese 
Idee wäre außer dir sicher niemand gekommen.

Den freilaufenden 32bit DWT verwende ich als einzigen 32bit Zähler im 
stm32f103 auch für andere Zwecke, ein 16bit Register ist hier kein 
Ersatz.
Alle anderen Cortexe die ich verwende haben wenigstens einen 32bit 
Timer, da verwende ich den. Also was sollen die Belehrungen? Es gibt 
immer viele Lösungen für ein Problem, ich drängle niemanden meine auf 
erwarte aber, dass das umgekehrt auch keiner Macht.

900ss D. schrieb:
> Weshalb gleich so pampig?
> Der Hinweis auf diese Ungenauigkeit war sicher nicht bös gemeint und
> würde Anfängern, die das nicht sofort durchschauen, ja durchaus helfen.

Weil ich Oberlehrer nicht ausstehen kann. Anfänger, die nicht mal raffen 
dass ein paar Befehle in einer Schleife die Ausführung verlängern 
sollten stricken oder häkeln. Diese Arroganz an dieser Stelle erlaube 
ich mir.

von Peter D. (peda)


Lesenswert?

temp schrieb:
> Weil ich Oberlehrer nicht ausstehen kann.

Und das sagt ausgerechnet der Oberlehrer vom Dienst.
Ich wußte nicht, daß Du nen uralten Thread ausgräbst, nur um andere zu 
beleidigen. Dann hätte ich mir meinen Beitrag natürlich verkniffen.
Belehre bitte andere.

von Bauform B. (bauformb)


Lesenswert?

temp schrieb:
> Peter D. schrieb:
>> Der Systick Timer des F103 hat immerhin 24Bit.
>
> Der wird aber in der Regel immer verwendet und läuft auch nicht durch.

Meistens steht ein krummer Wert im reload-Register, deshalb muss man den 
"Überlauf" extra behandeln. Oder bedeutet "läuft nicht durch" noch was 
schlimmeres?

Und vielen Dank für den Tipp mit dem Debugger!

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

Peter D. schrieb:
> Und das sagt ausgerechnet der Oberlehrer vom Dienst.
> Ich wußte nicht, daß Du nen uralten Thread ausgräbst, nur um andere zu
> beleidigen. Dann hätte ich mir meinen Beitrag natürlich verkniffen.
> Belehre bitte andere.

Nein, die Belehrung bei dir ist mehr als angebracht. Lies meinen ersten 
Beitrag und du wirst merken, dass es überhaupt nicht um Delay-Schleifen 
ging, sondern um die Manipulation des DEMCR Registers durch jlink beim 
Deconnecten.
Nicht mehr und nicht weniger. Alles andere kam erst durch die 
versammelte Delayexpertenexpertiese.

Und dann kommen solche Trollos wie du daher und wollen erklären wie man 
Delays programmiert. Und nein, ich belehre dich nicht wie du das machst. 
Du kannst machen was du willst. Aber hör bitte auf anderen deinen 
Programmierstil aufzuschwatzen.

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.