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).
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
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_tvolatile)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.
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?
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.
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_tvolatile*)&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.
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_tvolatile*)&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
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.
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."
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...
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?
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.
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...
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.