cancel
Showing results for 
Search instead for 
Did you mean: 

TIM15 can't be enabled when OPM bit is set, why?

CLeo.1
Senior II

Hi everyone,

I am having issues getting One Shot Mode enable when using the TIM15. I am currently using the STM32H743ZI as the MCU.

When setting the OPM bit in the TIM15_CR1 register the CEN bit in same register no longer can be enabled for whatever reason. I tried disabling the Auto-reload preload enable (ARPE bit) in the CR1 register but nothing changed.

Code:

LL_DMA_InitTypeDef      WS2812B_Config_DMA;
	LL_GPIO_InitTypeDef     WS2812B_Config_GPIO;
	LL_TIM_InitTypeDef      WS2812B_Config_TIM15;
	LL_TIM_OC_InitTypeDef   WS2812B_Config_OC;
	LL_TIM_BDTR_InitTypeDef WS2812B_Config_BDTR;
 
	LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
	LL_TIM_StructInit(&WS2812B_Config_TIM15);
	LL_TIM_OC_StructInit(&WS2812B_Config_OC);
	LL_TIM_BDTR_StructInit(&WS2812B_Config_BDTR);
	LL_GPIO_StructInit(&WS2812B_Config_GPIO);
	LL_DMA_StructInit(&WS2812B_Config_DMA);
 
	//Configure GPIO
	WS2812B_Config_GPIO.Alternate  = LL_GPIO_AF_4;
	WS2812B_Config_GPIO.Mode       = LL_GPIO_MODE_ALTERNATE;
	WS2812B_Config_GPIO.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	WS2812B_Config_GPIO.Pin        = LL_GPIO_PIN_2;
	WS2812B_Config_GPIO.Pull       = LL_GPIO_PULL_NO;
	WS2812B_Config_GPIO.Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	LL_GPIO_Init(GPIOA, &WS2812B_Config_GPIO);
 
	//Configure DMA
	WS2812B_Config_DMA.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
	WS2812B_Config_DMA.FIFOMode = LL_DMA_FIFOMODE_DISABLE;
	WS2812B_Config_DMA.MemBurst = LL_DMA_MBURST_SINGLE;
	WS2812B_Config_DMA.MemoryOrM2MDstAddress = (uint32_t)WS28128B_BUFF;
	WS2812B_Config_DMA.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
	WS2812B_Config_DMA.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
	WS2812B_Config_DMA.Mode = LL_DMA_MODE_NORMAL;
	WS2812B_Config_DMA.NbData = NUM_LED;
	WS2812B_Config_DMA.PeriphBurst = LL_DMA_PBURST_SINGLE;
	WS2812B_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&TIM15->CCR1;
	WS2812B_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
	WS2812B_Config_DMA.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
	WS2812B_Config_DMA.PeriphRequest = LL_DMAMUX1_REQ_TIM15_UP;
	WS2812B_Config_DMA.Priority = LL_DMA_PRIORITY_LOW;
 
	//Interrupt: DMA
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_4);
 
	NVIC_EnableIRQ(DMA1_Stream4_IRQn);
	NVIC_SetPriority(DMA1_Stream4_IRQn, 0);
 
	LL_DMA_Init(DMA1, LL_DMA_STREAM_4, &WS2812B_Config_DMA);
 
	WS2812B_Config_TIM15.Autoreload    = 240e6/LED_FREQ;
	WS2812B_Config_TIM15.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
	WS2812B_Config_TIM15.CounterMode   = LL_TIM_COUNTERMODE_UP;
	WS2812B_Config_TIM15.Prescaler     = 0;
	WS2812B_Config_TIM15.RepetitionCounter = 0;
	LL_TIM_Init(TIM15, &WS2812B_Config_TIM15);
 
	WS2812B_Config_OC.CompareValue = 0;
	WS2812B_Config_OC.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
	WS2812B_Config_OC.OCMode = LL_TIM_OCMODE_PWM1;
	WS2812B_Config_OC.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
	WS2812B_Config_OC.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
	WS2812B_Config_OC.OCNState = LL_TIM_OCSTATE_DISABLE;
	WS2812B_Config_OC.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
	WS2812B_Config_OC.OCState = LL_TIM_OCSTATE_ENABLE;
	LL_TIM_OC_Init(TIM15, LL_TIM_CHANNEL_CH1, &WS2812B_Config_OC);
 
	WS2812B_Config_BDTR.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
	WS2812B_Config_BDTR.Break2Filter = LL_TIM_BREAK2_FILTER_FDIV1;
	WS2812B_Config_BDTR.Break2Polarity = LL_TIM_BREAK2_POLARITY_LOW;
	WS2812B_Config_BDTR.Break2State = LL_TIM_BREAK2_DISABLE;
	WS2812B_Config_BDTR.BreakFilter = LL_TIM_BREAK_FILTER_FDIV1;
	WS2812B_Config_BDTR.BreakPolarity = LL_TIM_BREAK_POLARITY_LOW;
	WS2812B_Config_BDTR.BreakState = LL_TIM_BREAK_DISABLE;
	WS2812B_Config_BDTR.DeadTime = 0;
	WS2812B_Config_BDTR.LockLevel = LL_TIM_LOCKLEVEL_OFF;
	WS2812B_Config_BDTR.OSSIState = LL_TIM_OSSI_DISABLE;
	WS2812B_Config_BDTR.OSSRState = LL_TIM_OSSR_DISABLE;
	LL_TIM_BDTR_Init(TIM15, &WS2812B_Config_BDTR);
 
	LL_TIM_OC_EnablePreload(TIM15, LL_TIM_CHANNEL_CH1);
	LL_TIM_EnableARRPreload(TIM15);
	LL_TIM_EnableDMAReq_UPDATE(TIM15);
	LL_TIM_SetOnePulseMode(TIM15, LL_TIM_ONEPULSEMODE_SINGLE);
	LL_TIM_GenerateEvent_UPDATE(TIM15);
	LL_TIM_EnableAllOutputs(TIM15);

 Where TIM15 is enable. Essentially fill up the buffer before enabling the TIMER

void WS2812B::setColour(uint8_t GREEN, uint8_t RED, uint8_t BLUE) {
 
	for (uint32_t LED = 0; LED < NUM_LED; LED++) {
 
		for (int8_t G_bit = 7; G_bit >= 0; G_bit--) {
 
			if ((GREEN & (1<<(7 - G_bit)))) {
 
				WS28128B_BUFF[G_bit] = 0xC0;
			} else {
				WS28128B_BUFF[G_bit] = 0x60;
			}
		}
 
		for (int8_t R_bit = 15; R_bit >= 8; R_bit--) {
 
			if ((RED & (1<<(15 - R_bit)))) {
 
				WS28128B_BUFF[R_bit] = 0xC0;
			} else {
				WS28128B_BUFF[R_bit] = 0x60;
			}
		}
 
		for (int8_t B_bit = 23; B_bit >= 16; B_bit--) {
 
			if ((BLUE & (1<<(23 - B_bit)))) {
 
				WS28128B_BUFF[B_bit] = 0xC0;
			} else {
				WS28128B_BUFF[B_bit] = 0x60;
			}
		}
	}
 
	WS28128B_BUFF[(NUM_LED * 24)] = 0x2EE0;
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_4);
	LL_TIM_EnableCounter(TIM15);
}

3 REPLIES 3
TDK
Guru

> When setting the OPM bit in the TIM15_CR1 register the CEN bit in same register no longer can be enabled for whatever reason.

Why do you think that?

Probably what is happening is you are enabling the timer in one shot mode, the timer completes and turns itself off (as one shot mode does) and you inspect CEN at a later time and assume it was never enabled.

You don't enable TIM15 anywhere in the code that you've presented.

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

I suspected that it's been turned on and turned off at a fast rate, but I have a digital analyzer hooked up to see if it spouts out any PWM and it doesnt, so I can ensure that the TIM15 never been turned on.

I do enable CEN bit but elsewhere, ill include it.

Does One Shot Mode mean one cycle will be outputted or will it output all the varying Pulse widths that the DMA transferred over?

Alright, I found a work around but is this suppose to be an easier way with using OPM?

Essentially I have my DMA in One shot mode, and I have the Timer in "Circular Mode". Every time a "pulse" is completed a CC1IF is set I count how many times that flag gets set and when it exceeds a certain threshold thats when I disable the timer and I get my nice "One Shot" PWM with varying duty cycles