Forum: Mikrocontroller und Digitale Elektronik STM32 Ersten Timer Wert festhalten bis Flag rückgesetzt


von STM (Gast)


Lesenswert?

Hallo Mikrocontrollerer!
Durch externe Flanken lässt sich ja der Timerwert im STM32 festhalten 
und später auslesen. Das Problem ist dass dieser Wert bei einer späteren 
Flanke mit dem neuen aktuellen überschrieben wird, auch wenn noch kein 
Flag Rückgesetzt wurde. Gibt es eine Möglichkeit den Wert der ersten 
Flanke beizubehalten bis ein Flag rückgesetzt wurde? Momentan löse ich 
das über einen Interrupt der sich nach der ersten Flanke sperrt bis der 
Wert auslesen wurde. Das ist aber erstens umständlich und nicht 100% 
sicher denke ich und zweitens sollte es bei einem so mächtigen 
Controller doch eine besser bereits vorhandene Lösung geben. Vielleicht 
kann mir von euch jemand weiter helfen. Danke

von (prx) A. K. (prx)


Lesenswert?

Schon an DMA gedacht?

von STM (Gast)


Lesenswert?

Noch nicht. Du meinst also die Übertragung mittels DMA erst wieder nach 
dem Auslesen zulassen? Ist das nicht auch wieder eine Bastellösung?

von (prx) A. K. (prx)


Lesenswert?

Capture Event ist DMA-Trigger
DMA: Capture Value => Memory Buffer

Mit einer DMA-Länge von 1 Wort hast du exakt das Gewünschte, solange das 
DMA fixer ist als die nächste Flanke. Aber damit das fehlschlägt müsste 
es schon eine verteufelt kurze Zeit für zwei Flanken sein (wenige 
Core-Takte).

Mit längerem Puffer, ggf. als Zirkularpuffer, kriegst du alle Flanken, 
solange das DMA schneller ist als deine Flanken.

von STM (Gast)


Lesenswert?

Ok super... danke...

von STM (Gast)


Lesenswert?

Ich habe das ganze jetzt mal aufgebaut, ohne es bisher getestet zu 
haben. Mir fällt jetzt gerade auf, dass ich davon ausgegangen bin dass 
der DMA nach der ersten Übertragung gesperrt werden kann. Mein 
eigentliches Problem war ja dass die nachfolgenden Werte den ersten 
überschrieben haben. Mir würde jetzt spontan einfallen die Zieladresse 
ansteigen zu lassen und nur die erste auszulesen, was wieder nur eine 
Bastellösung wäre. Es geht am Ende darum nur den Wert der ersten Flanke 
aufzunehmen und das überschreiben des Wertes erst nach ein paar µs 
manuell wieder freizugeben. Die DMA habe ich erstmal wie folgt 
konfiguriert:

1
  // DMA1 Channel1 Config 
2
  DMA_DeInit(DMA1_Channel1);
3
4
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM2_Value_CC3_Register;
5
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SRC_Buffer;
6
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
7
  DMA_InitStructure.DMA_BufferSize = 1;
8
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
9
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
10
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
11
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
12
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
13
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
14
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
15
16
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
17
18
  // DMA1 Channel1 enable 
19
  DMA_Cmd(DMA1_Channel1, ENABLE
20
21
22
  // TIM1 Update DMA Request enable
23
  TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);

von (prx) A. K. (prx)


Lesenswert?

Wenn der DMA-Kanal für eine Länge von einem Wort ohne automatischem 
Reload konfiguriert wird, dann wird auch nur ein Wort übertragen und der 
Kanal wird danach automatisch gesperrt.

Grundsätzlich ist das Verfahren deiner Interrupt-Lösung ähnlich, als es 
nicht narrensicher ist. Wenn die Zeit zwischen zwei aufeinanderfolgenden 
eingefangenen Flanken kürzer als die Latency vom DMA ist, dann fischt 
das DMA den Wert der zweite Flanke aus dem Register. Nur reagiert DMA 
schneller als der Interrupt und der so mögliche Zeitfehler liegt im 
einstelligen Bereich von Taktzyklen des Core.

DMA_Mode_Circular klingt nach automatischem Reload, was exakt das ist, 
was du nicht haben willst. Dieser Modus ergibt nur Sinn, wenn du in 
einem Zirkularpuffer alle Flanken gespeichert sehen willst.

von (prx) A. K. (prx)


Lesenswert?

PS: Gegen allzu schnelle Pulsfolgen kannst du den Cature-Trigger mit 
einem Filter absichern. Der allerdings die Erkennung der Flanke etwas 
verzögern dürfte.

von STM (Gast)


Lesenswert?

Ja, das Cirular habe ich bereits rausgenommen, das Problem was ich jetzt 
habe ist das neu setzen des Buffers. Laut Beschreibung muss erst die DMA 
deaktiviert werden, dann wird ein neuer Wert geladen und schließlich die 
DMA wieder aktiviert. Ich würde mir das jetzt so zusammen reimen?:
1
DMA_Cmd(DMA1_Channel1, DISABLE);        
2
DMA1_Channel1 -> DMA_CNDTR = 1;        
3
DMA_Cmd(DMA1_Channel1, ENABLE);

von STM (Gast)


Lesenswert?

Nur noch mal zusammenfassend. Klappt jetzt alles super, ich lese jetzt 
direkt den Counter aus und habe zusätzlich noch den Filter der Timer 
aktiviert.
1
//Neu Laden: (deaktivieren, Neu-laden, aktivieren):
2
DMA_Cmd(DMA1_Channel1, DISABLE);
3
DMA1_Channel1 -> CNDTR = 1;
4
DMA_Cmd(DMA1_Channel1, ENABLE);


1
  // DMA1 Channel1 Config
2
  DMA_DeInit(DMA1_Channel1);
3
4
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM4_Value_Counter_Register;
5
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMA_Wertespeicher;
6
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
7
  DMA_InitStructure.DMA_BufferSize = 1;
8
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
9
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
10
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
11
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
12
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;    //Kein Autoreload
13
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
14
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
15
      DMA_Init(DMA1_Channel1, &DMA_InitStructure);


Danke noch mal

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.