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
Noch nicht. Du meinst also die Übertragung mittels DMA erst wieder nach dem Auslesen zulassen? Ist das nicht auch wieder eine Bastellösung?
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.
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); |
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.
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.
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); |
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.