Forum: Mikrocontroller und Digitale Elektronik STM32 - DMA MemToPerph Timer gesteuert


von Manuel (Gast)


Lesenswert?

Hallo Freunde,

ich hab wieder einmal eine Frage an euch Spezialisten bezüglich STM32 
µController.

Vorweg kurze Information was mich machen will:
Ich will Infrarot-Signale (38kHz Bursts) via PWM über einen Pin 
ausgeben. Dazu gibt es ein Array, nennen wir es BitArray, welches als 
Bitmuster die Burst-zu-Pause Übertragung enthält.
Z.B.: 110010 gibt aus: Burst-Burst-Pause-Pause-Burst-Pause
Ein Burst ist dabei immer 20 PWM-Perioden

Ich könnte das jetzt konventionell ohne DMA implementieren mittels 
TIM_RepetitionCounter. Jedoch muss ich da nach jedem Burst (oder jeder 
Pause) in der Interrupt Routine, das BitArray überprüfen, die PWM 
sperren oder nicht und dann den Timer neu starten. Ist im Prinzip 
möglich, aber noch lieber wäre mir, wenn der Prozessor nur Resourcen 
beim Start und beim Ende verbraucht, nicht während der Übertragung.

Daher meine Überlegung: DMA

Ich hab mir das Ganze so vorgestellt: Es gibt bei dem Timern ja das CCER 
Register, mit welches ich den PWM Ausgang sperren kann. Genau auf dieses 
Register möchte ich via DMA schreiben. Meiner Meinung ist das ein 
MemoryToPeripheral Prozess, der nach jedem Timer TIM_IT_Update den DMA 
ausführen soll.

Vorweg: Ist dies so Möglich wie ich mir das Vorstelle?

Ich habe schon einen Codeauszug, doch irgendwie bleibt der DMA Data 
Counter immer auf der selben Stelle und erhöht bzw. erniedrigt sich 
nicht.


Initialisierung:
1
  // DMA for IR command sending LED
2
  DMA_DeInit(CMD_LED_DMA_STREAM);
3
  DMA_InitStructure.DMA_Channel = CMD_LED_DMA_CHANNEL;
4
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) TIM1->CCER;;
5
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) g_bitarray;
6
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
7
  DMA_InitStructure.DMA_BufferSize = 16;
8
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
9
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
10
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
11
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
12
13
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;    // Kein Autoreload
14
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
15
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
16
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
17
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
18
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
19
  DMA_Init(CMD_LED_DMA_STREAM, &DMA_InitStructure);
20
21
  // Interrupt. Calles if DMA sequence is finished
22
  DMA_ITConfig(CMD_LED_DMA_STREAM, DMA_IT_TC, ENABLE);
23
24
  NVIC_InitStructure.NVIC_IRQChannel = CMD_LED_DMA_IRQ;
25
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
26
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
27
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
28
  NVIC_Init(&NVIC_InitStructure);

DMA IR Sending Start:
1
  DMA_MemoryTargetConfig(CMD_LED_DMA_STREAM, (uint32_t) g_bitarray, DMA_Memory_0); 
2
  DMA_SetCurrDataCounter(CMD_LED_DMA_STREAM, 16);
3
4
5
  DMA_Cmd(CMD_LED_DMA_STREAM, ENABLE);
6
  
7
  // TIMUpdate DMA Request enable.
8
  TIM_DMACmd(IR_TIMER, TIM_IT_Update, ENABLE);
9
10
  // Enalbe PWM Timer.
11
  TIM_CCxCmd(IR_TIMER, PROXIMITY_TIMER_CHANNEL, TIM_CCx_Enable);     
12
  TIM_CtrlPWMOutputs(IR_TIMER, ENABLE);            
13
  TIM_Cmd(IR_TIMER, ENABLE);

Der Code ist noch nicht fertig, mir ist kalr, dass ich so das ganze CCER 
Register überschreibe und nicht das benötigte Bit. Zur Zeit ist mir erst 
mal Wichtig ob mein Vorhaben überhaupt möglich ist.

Hoffe Ihr könnt mir helfen.

lg
Manuel

von Manuel (Gast)


Lesenswert?

Ok, ich hab mal einen gemeinen Fehler gefunden.
Die Zeile
1
  // TIMUpdate DMA Request enable.
2
  TIM_DMACmd(IR_TIMER, TIM_IT_Update, ENABLE);
gehört natürlich so:
1
  // TIMUpdate DMA Request enable.
2
  TIM_DMACmd(IR_TIMER, TIM_DMA_Update, ENABLE);

Jetzt werden die DMA Requests jedes mal beim Timer_Update ausgeführt und 
der DMACounter zählt runter. Sehr fein.

Doch leider werden die Daten vom BitArray nicht in das TIM1->CCER 
Register geschrieben. :(

von 23mts (Gast)


Lesenswert?

Hm,
MemoryTo-Peripheral hört sich ja erst mal gut an,
aber kann der stm32 per DMA in beliebe Peripheral-Register schreiben?!?
...Docu lesen...

von Manuel (Gast)


Lesenswert?

Hallo,

ja das geht definitiv, das hab ich jetzt rausgefunden.
Es war noch ein kleiner Fehler bei der Adressübergabe der Peripherie:

Das Regsiter muss natürlich als Adresse übergeben werden (& ist 
wichtig):
1
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &TIM1->CCER;;


Probleme hab ich jetzt noch bei der Incrementierung. Das CCER Register 
wird zwar nach jedem TI_Update verändert, jedoch werden die Adressen 
falsch incrementiert. Das Array und das Register sind beides 16-bit. 
Jetzt hab ich die PeripheralDataSize, MemoryDataSize auf
1
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;
2
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

geändert.

Nach jedem TimerUpdate lass ich mir das CCER Register ausgeben. Die 
Werte varieren dann folgendermaßen

3222
0
3232
0
....

Eigentlich sollte 32 oder 22 nach jedem Aufruf in den unteren 8 bit des 
Registers drinnenstehen, aber anscheinen erhöht er nur einmal um 8 bit 
und dann um 24, oder sehe ich das falsch.

Hoff ihr könnt mir helfen den Fehler zu finden; bis auf die 
Incrementierung funktioniert alles, so kurz vorm Ziel darf ich einfach 
nicht mehr scheitern :).

lg
Manuel

von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Moin,

überleg mal was du eingestellt hast. Wie breit ist ein Wort? Was ist 
dann wohl ein Halbwort? Und erst ein Doppelwort.

Sry: aber Warum stellst du Halfword ein wenn du 16bit haben willst??????


MfG

Tec

von Manuel (Gast)


Lesenswert?

Hi,

danke für die Antwort.

Ich habe bei dem Register ja nur die Auswahl:

 - DMA_MemoryDataSize_Byte
 - DMA_MemoryDataSize_HalfWord
 - DMA_MemoryDataSize_Word

Und laut #defines ist das dann
 - uint8_t
 - uint16_t
 - uint32_t

Ich hab ja auch schon alle Kombinationen durchprobiert, komme aber nicht 
auf mein gewünschtes Ergebnis.

Was ist deine Meinung nach bei den HalfWord falsch, ich dachte das wären 
16bit?

Danke & lg
Manuel

von Uwe B. (derexponent)


Lesenswert?

Hi,

>Sry: aber Warum stellst du Halfword ein wenn du 16bit haben willst??????

ein "Halfword" ist beim STM32 (32BitSystem) 16bit breit ... das stimmmt 
also schon


aber ich vermute der Fehler ist hier :

>DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;

müsste das nicht so lauten ?
1
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

Gruss Uwe

von Manuel (Gast)


Lesenswert?

Mannomann,

danke, danke, danke.
So ein blöder CopyPaste - Fehler.

Aber wenn mann mal 2 Tage immer nur auf so eine 
Initialisierungs-Codewurst hinsieht, übersieht man solche Sachen gern.

Arbeite erst seit 2 Wochen mit den STMs und der Perph-Library und da bin 
ich diese noch nicht gewohnt. Muss ich in Zukunft wohl besser aufpassen 
was ich schreibe bzw. kopiere :).

Jetzt jedenfalls wird das Register so wie erwünscht befüllt.
32
22
32
32
...

Danke nochmal,
lg
Manuel

von Uwe B. (derexponent)


Lesenswert?

kein Problem, schön das es jetzt geht

bin auch noch nicht ganz firm mit dem STM32 aber als Debug-Hilfe
kannst du dir die Asserts anzeigen lassen...da würde sowas dann 
vermutlich angemeckert werden

Gruss

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.