mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik WS2812 per STM32F0 Timer und DMA


Autor: Raphael L. (rleh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo allerseits,

ich habe hier ein STM32F072 Discovery Board (STM32F072RBT6) und versuche 
damit ein paar WS2812B LEDs anzusteuern (konkret ein Ring mit 12 LEDs).

Mein Ansatz ist es einen Timer zu verwenden, disen mittels Prescaler und 
Overflow auf 800kHz [3] zu konfigurieren und dann im PWM Modus nach 
350ns oder 900ns einen Pin togglen lassen. Die passenden Werte für die 
350/900ns sollen per DMA in das entsprechende Capture-Compare Register 
geschrieben werden.
Die berechneten Werte liegen als Array von uint16_t in RAM (ein Element 
für jedes Bit am WS2812B).

Das Problem scheint zu sein, dass der DMA Transfer niemals anläuft, das 
DMA_CNDTR Register zählt niemals herunter.

Die Reihenfolge der Konfiguration von Timer und DMA habe ich aus STs 
Appnote AN4104 [1] und dem Reference Manual (RM0091) [2].

Auch die Kombination von Timer 2, Capture-Compare 2 mit DMA 1 Channel 3 
und GPIO A Pin 1 sollte laut Datenblatt möglich sein.

Ich poste hier mal Codeausschnitte zum Initialisieren der Peripherie und 
aktivieren des DMA-Transfers, den kompletten Code gibt es hier 
https://jufo.mytfg.de/rlleh/stm32f072-dma-pwm-ws2812-demo in einem Git 
Repo.
SystemInit();

// Set flash Latency (1 wait state for 48MHz)
FLASH_SetLatency(FLASH_Latency_1);

// Set up 48 MHz Core Clock using HSI (8Mhz) with PLL x 6
RCC_PLLConfig(RCC_PLLSource_HSI, RCC_PLLMul_6);
RCC_PLLCmd(ENABLE);

// Wait for PLLRDY after enabling PLL.
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != SET);

// Select the PLL as clock source and update SystemCoreClock
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
SystemCoreClockUpdate();

// Config SysTickTimer
SysTick_Config(SystemCoreClock/100);

// Read clock frequency
RCC_ClocksTypeDef clocks;
RCC_GetClocksFreq(&clocks);

// Enable GPIOC, GPIOA, TIM2 and DMA1
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2, ENABLE);

// Enable PA1 as TIM2 PWM output (AF mode)
GPIO_InitTypeDef pwmOut;
GPIO_StructInit(&pwmOut);
pwmOut.GPIO_Pin = GPIO_Pin_1;
pwmOut.GPIO_Speed = GPIO_Speed_50MHz;
pwmOut.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOA, &pwmOut);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_2);


uint32_t cycles = 1.25 * (clocks.SYSCLK_Frequency / 1000000UL);
uint16_t prescaler = (cycles + 65535) / 65536;  // always round up
uint16_t period = (cycles / prescaler) - 1;  // e.g. 100 cycles are from 0 to 99

// reset DMA and TIM2
DMA_DeInit(DMA1_Channel3);
TIM_DeInit(TIM2);
TIM2->CCR2 = 0;

// init DMA
DMA_InitTypeDef dma;
DMA_StructInit(&dma);
dma.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR2;
dma.DMA_MemoryBaseAddr = (uint32_t)(&timerValues[0]);
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_BufferSize = numDmaTransactions;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma.DMA_Mode = DMA_Mode_Circular;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel3, &dma);

DMA_Cmd(DMA1_Channel3, ENABLE);

// Init timer and timer output: pwm
TIM_TimeBaseInitTypeDef tim2;
TIM_TimeBaseStructInit(&tim2);
tim2.TIM_Prescaler = prescaler;
tim2.TIM_Period = period;
tim2.TIM_ClockDivision = TIM_CKD_DIV1;
tim2.TIM_CounterMode = TIM_CounterMode_Up;
tim2.TIM_RepetitionCounter = 1;
TIM_TimeBaseInit(TIM2, &tim2);

TIM_OCInitTypeDef pwm;
TIM_OCStructInit(&pwm);
pwm.TIM_OCMode = TIM_OCMode_PWM1;
pwm.TIM_OutputState = TIM_OutputState_Enable;
pwm.TIM_Pulse = 2;
pwm.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &pwm);

TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);

TIM_Cmd(TIM2, ENABLE);

Hat jemand eine Idee, was ich falsch mache?

Vielen Danke schon mal
Raphael

[1]: 
http://www.st.com/content/ccc/resource/technical/d...
[2]: 
http://www.st.com/content/ccc/resource/technical/d...
[3]: https://www.mikrocontroller.net/articles/WS2812_Ansteuerung

Autor: Markus M. (adrock)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vom Prinzip her würde das funktionieren, aber Du benötigst relativ viel 
Speicher für den Buffer mit den Timerwerten.

Ich würde lieber SPI mit 3 MHz Bitrate nehmen. Dann kannst Du ein WS2812 
Bit aus drei SPI-Bits (100 und 110 für jeweils 0/1) zusammensetzen und 
benötigst weniger Bufferspeicher.

EDIT: Bei 12 LEDs ist das natürlich egal mit dem Speicher.

Muss man mal sehen, ich glaube ich hatte das mit dem DMA schonmal 
programmiert. Falls ich den Code noch finde...

: Bearbeitet durch User
Autor: Raphael L. (rleh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die ineffiziente Speichernutzung ist mit bewusst.

Ich hatte Anfangs meinen Lösungsansatz als den einfachsten eingeschätzt, 
aber dem ist scheinbar nicht so.
Mit SPI ist das natürlich eine effizientere Lösung sofern man noch eine 
SPI Schnittstelle frei hat (Ist bei mir der Fall).

Trotzdem würde mich interessieren wieso meine Idee/mein Code kein 
einziges Signal am Ausgang erzeugt.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.