cancel
Showing results for 
Search instead for 
Did you mean: 

How to re-enable the timer after a One pulse goes off (OPM)?

CLeo.1
Senior II

Hi guys I am using the stm32h753zi MCU and writing my own drivers to use the WS2812B led strip.

It uses a PWM to determine how much of the RGB you want.

I got the PWM part down and the OPM done, the problem is that I want to send let say a duty cycle of 80% follow by a 32% duty Cycle. I got it working for sending one burst but noted in the datasheet the OPM disables the CEN, however I can never re-enable it?

Is that how OPM works? One time and thats it?

Essentially sending a duty cycle of 80% = 1 and sending a duty cycle of 32% = 0, so thats why I want to send it in pulses in PWM form.

EDIT: 1 Using the TIM15 configured in PWM mode with DMA. The DMA will transfer from a buffer into CCR1 to alter the PW on the fly.

Code:

Timer Setup with DMA

/*
 * Timer_Driver.c
 *
 *  Created on: Jan. 8, 2021
 *      Author: Christopher
 */
 
 
#include "Timer.h"
 
void init_Timer(uint16_t * RGB_Array) {
 
	RCC->APB2ENR |= RCC_APB2ENR_TIM15EN;
 
	GPIOA->MODER &= ~GPIO_MODER_MODE2;
	GPIOA->MODER |=  GPIO_MODER_MODE2_ALT;
 
	GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED2;
	GPIOA->OSPEEDR |=  GPIO_OSPEEDR_OSPEED2_VERY_HIGH;
 
	GPIOA->AFR[0] &= ~GPIO_AFRL_AFSEL2;
	GPIOA->AFR[0] |=  GPIO_AFRL_AFSEL2_TIM15;
 
	TIM15->CCMR1 &= ~TIM_CCMR1_CC1S;
	TIM15->CCMR1 |=  TIM_CCMR1_CC1S_OUTPUT_MODE;
 
	TIM15->CCMR1 &= ~TIM_CCMR1_OC1M;
	TIM15->CCMR1 |= TIM_CCMR1_OC1M_PWM1;
 
	TIM15->ARR = 0x12B;
	TIM15->PSC = 0x00;
 
	TIM15->AF1 &= ~TIM15_AF1_BKINE;
	TIM15->CCMR1 |= TIM_CCMR1_OC1PE;
	TIM15->CR1 |= TIM_CR1_ARPE;
	TIM15->CR1 |= TIM_CR1_URS;
	//TIM15->CR1 |= TIM_CR1_OPM;
	TIM15->DIER |= TIM_DIER_UDE;
 
 
	//TIM15->CCMR1 &= ~TIM_CCMR1_OC1PE;
 
	TIM15->BDTR |= TIM_BDTR_MOE;
	TIM15->CCER |= TIM_CCER_CC1E;
	//TIM15->CR1 |= TIM_CR1_CEN;
 
	RCC -> AHB1ENR |= RCC_AHB1ENR_DMA1EN;
 
 
	         DMAMUX1_Channel2 -> CCR |= DMAMUX_CxCR_DMAREQ_ID_TIM15_CH1;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_CT;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_PL;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_MSIZE;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_PSIZE;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_MINC;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_CIRC;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_DIR;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_PFCTRL;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_TCIE;
	         DMA1_Stream2 -> CR &= ~DMA_SxCR_HTIE;
 
	         DMA1_Stream2 -> CR |= DMA_SxCR_CT_MEM0;
	         DMA1_Stream2 -> CR |= DMA_SxCR_PL_Very_High;
	         DMA1_Stream2 -> CR |= DMA_SxCR_MSIZE_32BIT;
	         DMA1_Stream2 -> CR |= DMA_SxCR_PSIZE_32BIT;
	         DMA1_Stream2 -> CR |= DMA_SxCR_MINC;
	         DMA1_Stream2 -> CR |= DMA_SxCR_CIRC;
	         DMA1_Stream2 -> CR |= DMA_SxCR_DIR_M_TO_P;
	         DMA1_Stream2 -> CR |= DMA_SxCR_PFCTRL_DMAFLOW;
	         //DMA1_Stream2 -> CR |= DMA_SxCR_TCIE;
 
	         DMA1_Stream2 -> NDTR = 25;
 
	         DMA1_Stream2 -> PAR = (int) &TIM15->CCR1;
	         DMA1_Stream2 -> M0AR = (int) RGB_Array;
 
	         DMA1_Stream2 -> CR |= DMA_SxCR_EN;
 
 
 
 
}

Where the Action happens:

uint16_t RED [25] = {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77,
					 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
					 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77, 0x00};
 
uint16_t BLUE [25] = {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77,
				      0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77,
					  0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0x00};
 
uint32_t GREEN [25] = {0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
					   0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77,
					   0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77, 0x00};
 
void setLedLights(uint8_t color, uint16_t * RGB_Array) {
 
if (color == 1) {
 
 
	memcpy(RGB_Array, GREEN, sizeof(uint16_t) * 25);
 
 
 
} else if (color == 2) {
 
	memcpy(RGB_Array, RED, sizeof(uint16_t) * 25);
 
 
 
 
 
} else if (color == 3) {
 
	memcpy(RGB_Array, BLUE, sizeof(uint16_t) * 25);
 
 
}
 
TIM15->CR1 |= TIM_CR1_CEN;
 
}

 Picture:

0693W000006I7vEQAS.png

9 REPLIES 9

> OPM disables the CEN, however I can never re-enable it?

Of course you can, simply write 1 to it.

JW

TDK
Guru

Since pulses need to be back to back, using a DMA-facilitated mode is a better option here. WS2812B is insanely popular, I'm sure you can find code implementations easily.

This bring up at least 5 unique implementation libraries in the first 10 hits:

https://www.google.com/search?q=WS2812B+stm32+library

This one explains it nicely:

http://www.martinhubacek.cz/arm/improved-stm32-ws2812b-library

If you feel a post has answered your question, please click "Accept as Solution".

Yup! After I posted this I found that website, so I am using the DMA PWM way.

What I have now is

An array of pre calculated PW values for sending 1's and 0's that will transfer from the buffer into the CCR1 TIM so a Memory -> peripheral configuration. Theres no Half nor Full complete interrupt as I dont think I need to know when it finishes it. The TIM15 is configured as a PWM so when CCR1 is updated with the pre-calculated PW it should send the correct 1 and 0 pulse widths.

The problem is the DMA needs to be circular or a one shot transfer? and the PWM produced by TIM15 misses pulses and looks messed up

Yeah! Just figured out, I assume it wasnt working because in debug mode it instantly set the CEN = 0 so I couldnt see it turn back on

Updated Post

Updated Post

TDK
Guru

Good progress, keep going.

Pretty sure PFCTRL should not be set here.

It should be a one-shot transfer unless you want it to loop through the buffer again after it's done sending.

You're transferring 32-bit values while your timer is only 16-bits. I'd change the array to uint16_t values and the memory sizes to half-word. It may be transferring 0x77, then 0x00, then 0x77, then 0x00, etc, which would explain the pattern.

Not sure where you're getting DMAMUX_CxCR_DMAREQ_ID_TIM15_CH1, but it's working so it should be right. There is likely a value in the CubeH7 repo which would be more readable. Not a big deal.

I would use the update event (tim15_up_dma) which will do the transfer on update rather than the ch1 event (tim15_ch1_dma) which does the transfer on a ch1 match. For your particular values, they should behave the same, but if you want 0% or 100% duty cycle, update is going to be better.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you appreciate the kind words!, I find working with I2S easier than TIMERS haha

Okay I got an update for you. Yes that 32bit, I fixed it realized CCR1 is a 16 bit register it did fixed some weird PW I was seeing on the line.

PFCTRL is not set, it puts a 0 to make it a DMA flow controller, I see why you thought that. I could remove it.

TIM15 for whatever reason has 2 DMA request TIM15_CH1_DMA, and TIM15_CH1_UP. I tried the latter but didn't work so I had to use the former.

Picture of the Updated Changes, still not perfect.

0693W000006I8ELQA0.png The array for reference:

uint16_t GREEN [25] = {0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
		                          0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77,
					  0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 ,0x77, 0x00};

Like its outputting 9 0xEF? then the rest looks good, and I don't know about that last one

Actually I got it working, I dont know why this fixed this but maybe you can help me explain this but removing "TIM15->CCR1 = RGB_Array[0]" made that extra 0xEF go away. Now everything is working, the 32bit to 16bit fixed the weird data and removing that line fixed the timing. Still dont now why the TIM_CH1_UP didnt work as well

EDIT:

I think I have an explanation as to why it worked once the TIM->15 code was removed. When I set TIM15->CCR1 = RGB_Array[0] I force the TIMER to output a value before the DMA can transfer, I believe once it outputted that it request the DMA to shove another value into it, so it was 0xEF (DMA REQUEST) and that being another 0xEF and so on. So I was just amending the buffer essentially.