Forum: Mikrocontroller und Digitale Elektronik STM32F4 + Timer 3 + DMA1 schreibt nicht ins ODR => TEIF


von Ben H. (Firma: Student) (benhogan)


Lesenswert?

Moin, Leute!

Ich möchte mit dem folgenden Code durch den Timer 3 den Speicherinhalt 
aus LED_PIN_SET[10] in das OutputDatenRegister (ODR) von GPIO Port E 
schreiben.

Einfach, schon tausend mal gelesen ... möchte man glauben...
1
#include "stm32f4xx.h"
2
#include "stm32f4xx_tim.h"
3
#include "stm32f4xx_dma.h"
4
5
// togglet Pin 5 bei jedem CC4 Event
6
uint16_t LED_PIN_SET[10] = { GPIO_Pin_5, 0x0000,
7
                             GPIO_Pin_5, 0x0000,
8
                             GPIO_Pin_5, 0x0000,
9
                             GPIO_Pin_5, 0x0000,
10
                             GPIO_Pin_5, 0x0000 };
11
12
13
void TIM3_Config()
14
{
15
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
16
17
  DMA_InitTypeDef       DMA_InitStructure;
18
19
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
20
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
21
22
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
23
  TIM_TimeBaseStructure.TIM_Period = 42000-1; // 1Hz
24
  TIM_TimeBaseStructure.TIM_Prescaler = 1000-1; // 42 kHz
25
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
26
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
27
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
28
29
  TIM_SetCompare4(TIM3,41999);
30
31
  TIM_DMAConfig(TIM3,TIM_DMABase_SR,TIM_DMABurstLength_1Transfer);
32
33
  TIM_DMACmd(TIM3,TIM_DMA_CC4,ENABLE);
34
35
  DMA_InitStructure.DMA_Channel = DMA_Channel_5;
36
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOE->ODR);
37
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&LED_PIN_SET;
38
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
39
  DMA_InitStructure.DMA_BufferSize = 10;
40
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
41
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
42
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
43
44
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
45
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
46
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
47
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
48
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
49
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
50
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
51
52
  DMA_Init(DMA1_Stream2, &DMA_InitStructure);
53
  DMA_Cmd(DMA1_Stream2, ENABLE);
54
55
  //TIM_Cmd(TIM3, ENABLE);
56
57
}
58
59
void init_gpio( void ) {
60
61
  GPIO_InitTypeDef      GPIO_InitStructure;
62
63
  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOE, ENABLE);
64
65
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
66
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
67
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
68
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
69
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
70
71
  GPIO_Init(GPIOE, &GPIO_InitStructure);
72
73
}
74
75
int main( void ) {
76
77
    uint32_t i;
78
79
    i = 0;
80
81
    SystemInit();
82
83
    RCC_ClocksTypeDef rcctdef;
84
85
    RCC_GetClocksFreq(&rcctdef);
86
87
    init_gpio();
88
89
    TIM3_Config();
90
91
    TIM_Cmd(TIM3, ENABLE);
92
93
    while(1) {
94
95
         i++;
96
    }
97
98
    // sollte nicht erreicht werden
99
    return 1;
100
101
}




ABER:

er tut einfach nicht. Sobald der Timer aktiviert wird und der erste 
Request über die Bühne geht (ob manuell ausgelöst (TIM3->EGR) oder 
automatisch) stirbt das DMA enable Flag und es erscheint ein hässliches 
Transfer Error Interrupt Flag (TEIF) an dessen Statt.

Jetzt kommt es: Nach einem Tag verzweifelten Hin- und Herschieben, 
Fremdcodewälzen versuche ich es mal mit dem Timer 1 auf DMA2Stream4.

Und tada ... alles läuft wie es soll.

Leider verstehe ich immer noch nicht, was an obiger Konfiguration so 
daneben geht? Kann mir jemand erklären, warum der Code dort oben nicht 
funktioniert?

von WP (Gast)


Lesenswert?

Ben Hogan schrieb:
>
1
> ...
2
> DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
3
> ...
4
>
Memory zu Memory mag der DMA1 wohl nicht so gerne. Hast du es schonmal 
mit Memory zu Peripheral versucht?

von Uwe B. (derexponent)


Lesenswert?

das hier ist wohl auch nich im Sinne des Erfinders :
1
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;

so landet nur das erste Wert im ODR-Register

von Ben H. (Firma: Student) (benhogan)


Angehängte Dateien:

Lesenswert?

Oh ja, danke für den Tip. Was ihr hier seht, ist das Ergebnis eines 
taglangen Experimentes. Da wurden schon viele Werte ausprobiert.

Da stand vorher
1
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

und eigentlich sollte dort auch stehen
1
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

Auch die BufferSize stand ursprünglich mal auf 2.

Sorry für die Schlampigkeit.

Im Anhang findet ihr die aufgeräumte, funktionierende Version für Timer 
1 durch DMA2.

Hat jemand eine Idee, was ich übersehen habe bzw. warum das für den 
Timer3 DMA1 nicht funktioniert?

von Ben H. (Firma: Student) (benhogan)


Lesenswert?

Ja, danke auch Dir, Uwe. Ist mir soeben auch aufgefallen.

von Uwe B. (derexponent)


Lesenswert?

poste nochmal den nicht funkionierenden (aber aktuellen) code

von Ben H. (Firma: Student) (benhogan)


Angehängte Dateien:

Lesenswert?

Gern.

Im Anhang findet ihr die korrekte, aber nicht funktionierende 
Implementierung.

Wird der Timer3 in der main aktiviert, so verabschiedet sich der DMA1 
(S2CR->EN = 0x0) mit einem TEIF2 im DMA1->LISR. Das S2NDTR wird um eins 
dekrementiert, also passiert da schon was.

von Ben H. (Firma: Student) (benhogan)


Lesenswert?

So, jetzt hab ich das ganze Spielchen noch für den

(TIM1 DMA2)*läuft*
(TIM8 DMA2)*läuft*

und

(TIM2 DMA1)*zonk*
(TIM3 DMA1)*zonk*

ausprobiert.

Also darf man vermuten, dass DMA1 bzw. der APB1 die Problemkinder sind 
(abgesehen von dem Typ hinter dem Bildschirm :)?

von Uwe B. (derexponent)


Lesenswert?

kann es sein das der DMA1 nicht auf die GPIOs schreiben kann (AHB1) ?!?

bei Memory2Memory kenne ich eine Einschränknung vom DMA1 aber bei GPIO ?

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Hi Ben,
schau bitte mal in das RM0090 10.3.2 Figure 33.
Hier siehst Du welcher DMA-Controller wohin schreiben darf.
10.3.3 zeigt Dir das mögliche Request Mapping - Tables 43/44.
Grüße, Marcus

von Ben H. (Firma: Student) (benhogan)


Angehängte Dateien:

Lesenswert?

@Uwe: ja, dass Mem2Mem bei DMA1 nicht geht habe ich auch festgestellt, 
aber GPIO ... das wirft immer noch Fragen auf.

@Marcus: Die Tabellen für das Request Mapping (Tab. 43/44) kenne ich 
natürlich und musste die ja auch bei der Wahl des DMAs/Streams und 
Channels berücksichtigen. Sonst würde das Event den Request ja gar nicht 
erst auslösen.

Auf meinen Verdacht hin habe ich mir Abb. 33 angesehen konnte dort aber 
keinen Widerspruch zu meiner Implementierung feststellen bzw. dort 
verstehe ich die AHB-APB-bridges nicht so recht ...
( Anhang seht ihr die Abbildung nochmal )
links:Eingang rechts:Ausgang?

Heißt das, dass ich nur TIM1 oder TIM8 verwenden kann, um vom Speicher 
auf die GPIOs zu schreiben?

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Roger that, give the man a cigar!

Scherz beiseite - ich habe eine ähnlich Anwendung auf dem STM32F407 am 
Laufen. Zwei unabhängige Datenpumpen, eine auf TIM1, eine auf TIM8.

von Ben H. (Firma: Student) (benhogan)


Lesenswert?

Danke, aber ich hab das Rauchen vor Jahren aufgegeben.

Nur zwei Timer für die DMA-gesteuerte Ausgabe so vieler Pins ... finde 
ich ein bissl spärlich. Dann sind die auch noch nur 16 Bit "groß".

Enttäuschend.

Danke für die Klärung meiner Verständnisprobleme. Ich denke dieser 
Thread ist damit geschlossen.

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.