cancel
Showing results for 
Search instead for 
Did you mean: 

PWM Frequency Issue in STM32WB55CGU6TR with RTOS

pippalla_chakravarthi
Associate III

Hello,

I am currently working with the STM32WB55CGU6TR MCU and using an RTOS. I've created two threads:

  1. PWM Thread: This thread generates PWM signals using the GPIO toggle method at frequencies of 60Hz and 600Hz.
  2. Read FIFO Thread: This thread reads data from the IMU FIFO. The IMU triggers an interrupt when data is ready, and the MCU waits for this interrupt via the EXTI_CALLBACK function to read the data.

Issue:

The PWM thread is producing signals at 50Hz and 500Hz instead of the intended 60Hz and 600Hz. Additionally, the duty cycle is not constant. Here are the steps I have tried:

Case 1: Both threads have the same priority, resulting in the PWM thread giving 50Hz and 500Hz.

Case 2: Increasing the PWM thread priority by 1 causes the Read FIFO thread to stop working entirely.

I would appreciate any insights or suggestions on how to resolve these issues.

Thank you!

 

 

 

/* USER CODE BEGIN Header_Startpwm_imuTask */
/**
  * @brief  Function implementing the pwm_imuTask thread.
  * @PAram  argument: Not used
  * @retval None
  */
/* USER CODE END Header_Startpwm_imuTask */
void StartPWM_Task(void *argument)
{
	int time=INTIAL_VALUE;
	uint16_t time_delay=INTIAL_VALUE;
	/* USER CODE BEGIN 5 */
	/* Infinite loop */
        for(;;)
	{
                  GPIO_SET_PIN(GPIOA, GPIO_PIN_0);//CAM_TRIG
		  for(time=1;time<=5;time++)
		  {
			  GPIO_SET_PIN(GPIOA, GPIO_PIN_8);//INS_TRIG
			  GPIO_SET_PIN(GPIOA, GPIO_PIN_9);//IMU1_TRIG

			  delay_us(833);
			  GPIO_CLEAR_PIN(GPIOA, GPIO_PIN_8);//INS_TRIG
			  GPIO_CLEAR_PIN(GPIOA, GPIO_PIN_9);
			  delay_us(833);
		  }
		  GPIO_CLEAR_PIN(GPIOA, GPIO_PIN_0);//CAM_TRIG
		  for(time=1;time<=5;time++)
		  {
			  GPIO_SET_PIN(GPIOA, GPIO_PIN_8);//INS_TRIG
			  GPIO_SET_PIN(GPIOA, GPIO_PIN_9);//IMU1_TRIG
			  delay_us(833);
			  GPIO_CLEAR_PIN(GPIOA, GPIO_PIN_8);//INS_TRIG
			  GPIO_CLEAR_PIN(GPIOA, GPIO_PIN_9);//IMU1_TRIG
			  delay_us(833);
		  }
	  }

}

 


 

 

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @PAram  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartReadFIFOTask(void *argument)
{
uint32_t flagsWaiter=0;
uint32_t waitFlags = 0x01; // Wait for flag 0 to be set
uint8_t rawdata1[16]={0};
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
#if 1
reset();
ICM42688_enableFifo();
ICM42688_enableLN_mode();
delay_ms(3);
ICM42688_enableDataReadyInterrupt();
delay_ms(3);
whoAmI();
#endif

while(1)
{
#if 1
	//printf("while\n\r");
	if(data_1_ready == 1)
	{
		data_1_ready = 0;
		printf("Data ready interrupt 1... \n\r");
		multi_i2c_read((0x69<<1),0x30,rawdata1,16);

		for (size_t i=0; i<16; i++) {
		      	  printf("FIFO Data %#x.. \n\r", rawdata1[i]);
		 }
	}
#endif
}
}

 

 

11 REPLIES 11
Bob S
Principal

What does your delay_us() function do?  If it does not yield then lower priority tasks will never run because your PWM task is always running.

Using software to generate PWM signals like that will never work with any guaranteed precision.  You need to use hardware timers.  Either to generate the PWM directly (that is what they are made for), or to generate interrupts that then trigger the PWM task to do something (via RTOS events/mutex/etc.).

Hi @Bob S  ,

1 .delay_us() function wiil generate the delay using hardware timer2 . If i used this timer without rtos i can get precisely 60hz and 600hz !.

/*Microsecond delay function definition*/
void delay_us(uint16_t time)
{

/* change your code here for the delay in microseconds */

__HAL_TIM_SET_COUNTER(&htim2, 0);

while ((__HAL_TIM_GET_COUNTER(&htim2))<time);

}

 2.I used the hardware timers directly to generate the PWM signal But there is phase shift after two or three pulses

Below is the configuration for PWM using slave trigger concept.

pippalla_chakravarthi_0-1719843314095.png

pippalla_chakravarthi_1-1719843417112.png

 

 

pippalla_chakravarthi_2-1719843575470.png

 

 

There is a phase shift when using the timer hardware?  Is this between the 3 signals from TIM1 or between the TIM1 signals and the TIM2 signal?  Either way, if the timers are free running I would not expect any change in phase relationship.  Are you starting or stopping the timers to toggle your "CAM_TRIG" signal?

Hello @Bob S ,

 

Thanks for the reply,

The phase shift between the timer1(three channels ) and timer2 singlechannel

  1. Are you starting or stopping the timers to toggle your "CAM_TRIG" signal?

     A)Yes i am starting the timer base once and PWM like below

pippalla_chakravarthi_0-1720506473982.png

I am attaching video FYR

 

Your TIM2 prescaler needs to be 31, not 32.  See how you set the TIM1 prescaler (64-1).  The actual clock divisor is "precsaler+1".


@pippalla_chakravarthi wrote:

1 .delay_us() function wiil generate the delay using hardware timer2 . If i used this timer without rtos i can get precisely 60hz and 600hz !.

 

/*Microsecond delay function definition*/
void delay_us(uint16_t time)
{

/* change your code here for the delay in microseconds */

__HAL_TIM_SET_COUNTER(&htim2, 0);

while ((__HAL_TIM_GET_COUNTER(&htim2))<time);

}

 

 While it is true the hardware timer is being used to measure the time, that code is blocking which means if the PWM task is higher priority, the read FIFO task will never run.  Your tasks need a way to yield to other tasks, whether with task delay or waiting on a semaphore/mutex/queue etc.

But your PWM task timing is so tight that you probably won't be able to add a task delay and get the PWM frequency you want unless you change the RTOS timebase to microsecond.  That might work for your application.

Otherwise I think it's better to set up TIM2 to interrupt every 833us and toggle the GPIO pin in the ISR.  Or, release a semaphore in the ISR that the PWM task blocks on, and toggle the pin in the task.

Hello @Bob S ,

Does it mean that changing the prescaler to 31 will make it work? I modified it later, but there is still a phase shift between the two timers' PWM.

 

That delay is not thread safe, read the starting count and subtract that from the current, and compare that with the delay time desired. Don't zero the count.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

> I modified it later

What does that mean?  Fix it in CubeMX and re-gen the code.  Or hand-edit MX_TIM2_Init() as a quickie test and THEN fix it in CubeMX.

WAIT - You are using TIM2 as BOTH the us delay and a PWM generator??????  NOOOOoooooooo....... First off, as @Tesla DeLorean said, don't (ever) clear the timer count value.  And second, even using differential timer counts, this will fail when the timer auto-reloads from its ARR value to zero (which is has to do in order to generate the PWM).  Use a different timer exclusively for your delay function, **IF** indeed you still need that us delay.