Forum: Mikrocontroller und Digitale Elektronik STM32 DMA Byte Transfer zu Word-Peripherie, aber soll Byte bleiben


von Patrick B. (p51d)


Lesenswert?

Hallo zusammen

Ich stehe einmal wieder etwas auf dem Schlauch... Ich möchte ein SK6812 
Led über DMA und Timer (PWM) von einem STM32F4-Discovery ansteuern. Im 
grossen und ganzen läuft das auch so wie es soll.
Einfach nur unter einer Bedingung: Speicher im RAM und Peripherie (CCR 
des Timers) müssen gleich gross sein. Da ich aber nicht doppelt oder 
viermal soviel Speicher (bei einem 32Bit Timer) verschwenden möchte, 
wollte ich im RAM auf ein Byte-Array reduzieren.
Nur verkackt mir der DMA sämtliche Daten beim Transfer.
Im 32Bit CCR Register steht dann z.B. 0x1A1A1A1A und im Speicher war 
alles 0x1A.
Wenn andere Werte genommen werden, haben die Zahlen teilweise überhaupt 
keinen Zusammenhang mehr.
Wenn das Array Word-Grösse hat steht immer schön der Wert 0x0000001A im 
Register des Timers.

Wie kann ich den DMA nun dazu zwingen, dass er mir nur Bytes kopiert und 
der Rest ignoriert?


Genutzt wird Timer2 Ch1, GPIO A pin15, und DMA1 Stream 5 Channel 3. 
Passt alles zusammen, sollange der DMA auf WORD -> WORD eingestellt ist 
und das Array auch WORD Breite hat (wovon aber wirklich nur 8Bit 
verwendet weren und somit 24Bit brach liegen).
1
uint8_t m_au8Sk6812Buffer[LED_BUFFER_SIZE];
2
3
DMA_InitTypeDef dma;
4
DMA_StructInit(&dma);
5
DMA_DeInit(DMA1_Stream5);
6
dma.DMA_Channel = DMA_Channel_3;
7
dma.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR1;
8
dma.DMA_Memory0BaseAddr = (uint32_t)(&m_au8Sk6812Buffer[0]);
9
dma.DMA_DIR = DMA_DIR_MemoryToPeripheral;
10
dma.DMA_BufferSize = LED_BUFFER_SIZE;   // 32 für rgbw und 80 für Treset
11
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
12
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
13
dma.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_Word;
14
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
15
dma.DMA_Mode = DMA_Mode_Circular;
16
dma.DMA_Priority = DMA_Priority_High;
17
dma.DMA_FIFOMode = DMA_FIFOMode_Enable;
18
//dma.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
19
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
20
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
21
DMA_Init(DMA1_Stream5, &dma);

Dabei spielt es irgendwie keine Rolle, wie die Fifo-Einstellungen sind, 
und wie die Speichergrössen aussehen. Sollange diese nicht alle gleich 
dem Maximum (beim Timer2 Word) sind, klappt es nicht.

Wenn ich dann das NRZ Protokoll am Pin anschauen möchte, ist nichts so 
wie es sein sollte.

Es ist leider schon etwas her, dass ich mich zuletzt mit ARM beschäftigt 
habe...

Gruss
Patrick

: Bearbeitet durch User
von Vincent H. (vinci)


Lesenswert?

Wieso setzt du nicht einfach Peripheral- & Memory Größe auf ein Byte?
1
dma.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_Byte;
2
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

In dem von dir geposteten Code steht überhaupt irgendwas vonwegen 
HalfWord...

von Jim M. (turboj)


Lesenswert?

Patrick B. schrieb:
> Nur verkackt mir der DMA sämtliche Daten beim Transfer.
> Im 32Bit CCR Register steht dann z.B. 0x1A1A1A1A und im Speicher war
> alles 0x1A.

Manche Peripherie kann nur 32-bittige Zugriffe korrekt verarbeiten. 
Schau mal nach was bei
1
 (uint16_t volatile)TIM2->CCR1 = 0x1A1B;

hinterher im CCR1 steht. Ich denke Du musst die 3 null Bytes mitführen, 
um einen kompletten 32-Bit Transfer durchzuführen.

von Jim M. (turboj)


Lesenswert?

Patrick B. schrieb:
> dma.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_Word;
> dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

Liest das nicht 2x 16 Bits aus dem Memory und packt es als 32-Bit in die 
Peripherie?

von Patrick B. (p51d)


Lesenswert?

Jim M. schrieb:
> Manche Peripherie kann nur 32-bittige Zugriffe korrekt verarbeiten.
> Schau mal nach was bei (uint16_t volatile)TIM2->CCR1 = 0x1A1B;

ergibt den Fehler:
error: lvalue required as left operand of assignment

Jim M. schrieb:
> Patrick B. schrieb:
>> dma.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_Word;
>> dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
>
> Liest das nicht 2x 16 Bits aus dem Memory und packt es als 32-Bit in die
> Peripherie?

Das dachte ich auch. Aber wenn man etwas mit den Settings spielt kommt 
das durch den DMA ins Register (Ursprung ist jeweils das entsprechende 
Array mit 0x1A):
Word -> Word           0x0000001A   <= Genau das will ich, aber mit Byte
HalfWord -> Word       0x001A001A
Byte -> Word           0x1A1A1A1A
HalfWord -> HalfWord   0x001A001A
Byte -> HalfWord       0x1A1A1A1A

Der FIFO soll unterschiedliche Datengrössen auf einander abstimmen. Aber 
ich möchte eben NICHT, dass er mir hier 4 Bytes aus dem Speicher holt 
und in das 32Bit Peripherie-Register schreibt. Er soll nur 1Byte holen 
und auch nur das unterste Byte beschreiben.

: Bearbeitet durch User
von Jim M. (turboj)


Lesenswert?

Patrick B. schrieb:
>> Schau mal nach was bei (uint16_t volatile)TIM2->CCR1 = 0x1A1B;
>
> ergibt den Fehler:
> error: lvalue required as left operand of assignment

Mist, geht nur so:
1
*((uint16_t volatile*)&TIM2->CCR1) = 0x1A1B;

In Deiner Aufzählung oben fehlt komischerweise der "Byte->Byte" Fall. 
Der hätte eine gewisse Chance, falls oben was vernünftiges rauskommt.

von Patrick B. (p51d)


Lesenswert?

Jim M. schrieb:
> Patrick B. schrieb:
>>> Schau mal nach was bei (uint16_t volatile)TIM2->CCR1 = 0x1A1B;
>>
>> ergibt den Fehler:
>> error: lvalue required as left operand of assignment
>
> Mist, geht nur so:
>
1
> *((uint16_t volatile*)&TIM2->CCR1) = 0x1A1B;
2
> 
3
>
>

Im Register steht dann: 0x1A1B1A1B
Ich schnalls echt nicht mehr. Also entweder macht der Compiler hier 
etwas komisches oder die Perihperie ergänzt selbständig auf irgendwelche 
Werte.

> In Deiner Aufzählung oben fehlt komischerweise der "Byte->Byte" Fall.
> Der hätte eine gewisse Chance, falls oben was vernünftiges rauskommt.

Ah sorry, habe ich vergesse aufzuschreiben. Getestet hatte ich es 
natürlich:
Byte -> Byte    0x1A1A1A1A

von A. B. (Gast)


Lesenswert?

Das wird prinzipiell nicht funktionieren können:

"18.4 TIM2 to TIM5 registers
Refer to Section 2.2 for a list of abbreviations used in register 
descriptions.
The 32-bit peripheral registers have to be written by words (32 bits). 
All other peripheral registers have to be written by half-words (16 
bits) or words (32 bits). Read accesses can be done by bytes (8 bits), 
half-words (16 bits) or words (32 bits)."

Beim Timer2 müssen also zwingend Word-Schreibzugriffe für CCRx erfolgen. 
Die DMA-Einheit kann zwar "umpacken", also z. B. 4 einzelne Bytes aus 
dem Speicher lesen und daraus einen Word-Schreibzugriff machen, aber 
genau um das "Umpacken" geht's ja gerade nicht. Vielmehr geht es 
sozusagen um ein "unsigned extend byte to word" und so etwas ist einfach 
nicht vorgesehen. Die DMA-Einheit transportiert nur, und bläht nicht auf 
oder so.

Eventuell könnte man den "Chrome-Art Accelerator" dazu verwenden, da 
könnte man wohl eine Pixel-Manipulation finden, die ein Byte mit Nullen 
zu einem Word auffüllt. CLUT mit 256 Einträgen in ARGB Werten.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Patrick B. schrieb:
> Wie kann ich den DMA nun dazu zwingen, dass er mir nur Bytes kopiert und
> der Rest ignoriert?

 So:
1
dma.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_Byte;
2
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

 EDIT:
 Falls dein Compiler meckert, probiere es mal so:
1
#define DMA_PDATA_BYTE          ((uint32_t)0x00000000)        /*!< Peripheral data alignment: Byte     */
2
#define DMA_MDATA_BYTE          ((uint32_t)0x00000000)        /*!< Memory data alignment: Byte     */
3
4
dma.DMA_PeripheralDataSize = DMA_PDATA_BYTE;
5
dma.DMA_MemoryDataSize = DMA_MDATA_BYTE;

: Bearbeitet durch User
von A. B. (Gast)


Lesenswert?

Patrick B. schrieb:
>> Mist, geht nur so:
>> [c]
>> *((uint16_t volatile*)&TIM2->CCR1) = 0x1A1B;

> Im Register steht dann: 0x1A1B1A1B
> Ich schnalls echt nicht mehr. Also entweder macht der Compiler hier
> etwas komisches oder die Perihperie ergänzt selbständig auf irgendwelche
> Werte.

Klar, denn Timer2 hängt am APB1-Bus, und bei den AHB/APB bridges steht 
ausdrücklich:

"Note: When a 16- or an 8-bit access is performed on an APB register, 
the access is transformed into a 32-bit access: the bridge duplicates 
the 16- or 8-bit data to feed the 32-bit vector."

von Bauform B. (bauformb)


Lesenswert?

Man könnte den DMA Stream auf word-word einstellen, aber ohne increment, 
d.h. er lädt immer 32 Bit aus den gleichen 4 Byte im RAM. Ein zweiter 
DMA stream ist auf byte-byte, memory-memory konfiguriert und schaufelt 
die eigentlichen Daten Byte für Byte in diesen Zwischenpuffer. Der muss 
natürlich ganz zu Anfang einmal genullt werden, es werden ja alle 32 Bit 
in den Timer kopiert.

Der zweite DMA stream könnte von einem der Timer TIM3 bis TIM5 
getriggert werden, die lassen sich nämlich synchron mit dem TIM2 
starten. Zunächst würden die DMA-Zugriffe auf den Zwischenpuffer dann 
gleichzeitig (also in zufälliger Reihenfolge) erfolgen. Die Prioritäten 
der einzelnen Streams sind aber programmierbar...

von Patrick B. (p51d)


Lesenswert?

A. B. schrieb:
> Das wird prinzipiell nicht funktionieren können:

Ok, soweit bin ich nun auch schon.
Ich habe in letzter Zeit mit FPGA zu tun gehabt, und da ist für mich 
fragwürdig, wieso so etwas einfaches blockiert wird. Der Schreibzugriff 
würde sowieso immer 32Bit betragen, ob jetzt nur 1 Byte kopiert wird 
oder ob hier noch zusammensetzarbeit o.ä. nötig ist, ist doch egal.

Nun ja, das wird wohl nicht einfach.

Stellt sich für mich nebenbei einfach die Frage, wie die vielen WS2812 
Bibliotheken für STM32 mit DMA und Timer überhaupt laufen können... oder 
hab ich hier nur Pech mit dem STM32F4?

von A. B. (Gast)


Lesenswert?

Patrick B. schrieb:

> Stellt sich für mich nebenbei einfach die Frage, wie die vielen WS2812
> Bibliotheken für STM32 mit DMA und Timer überhaupt laufen können... oder
> hab ich hier nur Pech mit dem STM32F4?

Wieso Pech? Was spricht denn gegen die Verwendung von Timer 3 oder 4? 
Die sind doch weitgehend identisch zu Timer 5 aufgebaut, nur halt 16 Bit 
breit statt 32. Da wäre das Problem nur noch "halb so groß". Wenn die 
also nicht schon anderweitig belegt sein sollten ...

Einen der raren (bei STM32) 32-bit-Timer würde ich gar nicht nutzen, 
wenn nicht zwingend nötig.

von Patrick B. (p51d)


Lesenswert?

Die anderen Timer kann ich schon nutzen.
Aber im Kern löst es das Problem nicht, sondern ist eine kleine 
Symptombekämpfung. Ich verschwende immer noch Massig Speicher, nur damit 
der DMA seine 16Bytes in das 16Byte Zielregister kopieren kann.
Ok, wenn man es ehrlich betrachtet, ist der Speicher heute auch kein 
Problem mehr...

von holger (Gast)


Lesenswert?

>Ok, wenn man es ehrlich betrachtet, ist der Speicher heute auch kein
>Problem mehr...

Für unbenutztes RAM gibt es auch kein Geld zurück;)

von Alexander Daum (Gast)


Lesenswert?

Hmmm ich habe ein ziemlich ähnliches Programm auf einem stm32f103 laufen 
und es geht auch so wie gedacht vom byte aif half word, habs allerdings 
nicht mit dem HAL sondern direkt mi registern gemacht.
Welcher stm32f4 ist denn genau auf dem board drauf, dann kann ich das 
auch noch mal ausprobieren.

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.