cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G0 Variable square wave generation

ECast.12
Associate II

Hi all, I'm using a NUCLEO-G071RB board and I simply want to generate a variable frequency square wave. I started from project TIM_OCToggle found on the examples.

My problem is that TIM1 channel 1 has a fixed frequency and even if I change it through the sConfigOC struct with HAL functions, and I see changes in htim1->Instance registers after my function SetOutputFrequency(), nothing seems to happen on the ouput PA8, I see only some periods ON or OFF with the oscilloscope. Where are my errors? Is there a simple way to do what I need without using HAL libraries (portability and readability doesn't walk together...) ? Thanks in advance, Emanuele.

1 ACCEPTED SOLUTION

Accepted Solutions

As first you cant rename or modify HAL Callkback names , this is defined by HAL. And you need understand how interrupts work.

In main simply

...
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
	// HAL_Delay(50);
 
	// Change frequency with blue pushbutton
	if(!HAL_GPIO_ReadPin(BLUE_BUT_GPIO_Port, BLUE_BUT_Pin))
	{
		TIM1_pulses += 10;
		// Toggle green led at any change
		HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
		changes++;
	}
 
	/* USER CODE END 3 */
  }
}

In callback user code

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
  uint16_t autoreload_value;
  uint32_t uhCapture = 0;
  
  /* Get configured autoreload value */
  autoreload_value = __HAL_TIM_GET_AUTORELOAD(htim);
 
  /* TIM1_CH1 toggling with frequency = 800 Hz */
  if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  {
    uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
 
    /* Set the Capture Compare Register value */
    if (uhCapture + TIM1_pulses < autoreload_value)
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + TIM1_pulses));
    }
    else
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + TIM1_pulses) - autoreload_value);
    }
  }

View solution in original post

12 REPLIES 12
TDK
Guru

Follow the example code:

https://github.com/STMicroelectronics/STM32CubeG0/blob/c6c0046d9278a7107261c6bf38327345df4c3efd/Projects/STM32G0C1E-EV/Examples/TIM/TIM_PWMOutput/Src/main.c

Once the PWM is running, you can adjust frequency by changing TIMx->ARR and TIMx->PSC and adjust duty cycle by changing TIMx->CCMRy (for channel y). Note that ARR and PSC are preloaded by default.

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

TIM1->ARR would be the FREQUENCY divider, for DIVIDE BY N, write the value N-1

>>even if I change it through the sConfigOC struct with HAL functions

Changing the CCRx registers changes phase, not frequency

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

Hi Tesla, thanks for your answer but CCR1 contains PULSE_VALUEx that determines the output frequency if the ST code sample is correct, while AAR contains 65535 that corresponds to  htim1.Init.Period = 65535;

But maybe the answer is at page of RM0444:

...

Bits 15:0 CCR1[15:0]: Capture/Compare 1 value

If channel CC1 is configured as output: CCR1 is the value to be loaded in the actual

capture/compare 1 register (preload value).

It is loaded permanently if the preload feature is not selected in the TIMx_CCMR1 register

(bit OC1PE). Else the preload value is copied in the active capture/compare 1 register when

an update event occurs.

The active capture/compare register contains the value to be compared to the counter

TIMx_CNT and signaled on OC1 output.

If channel CC1 is configured as input: CR1 is the counter value transferred by the last

input capture 1 event (IC1). The TIMx_CCR1 register is read-only and cannot be

programmed.

...

Below you can find the MX_TIM1_Init as done by ST and in main.h the values to generate different frequencies on channels 1,2,3,4.

static void MX_TIM1_Init(void)
{
 
  /* USER CODE BEGIN TIM1_Init 0 */
 
  /* USER CODE END TIM1_Init 0 */
 
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
 
 
  /* USER CODE BEGIN TIM1_Init 1 */
 
  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = PRESCALER_VALUE;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_OC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse = PULSE1_VALUE; 	
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.Pulse = PULSE2_VALUE;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.Pulse = PULSE3_VALUE;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.Pulse = PULSE4_VALUE;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.BreakFilter = 0;
  sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
  sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
  sBreakDeadTimeConfig.Break2Filter = 0;
  sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */
 
  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);
 
}

ECast.12
Associate II

Hi TDK, many thanks for your answer. I will take a look to the example, but my case is different. I don't need PWM, but just a variable frequency with duty cycle at 50% with the toggle mode output.

MM..1
Chief II

Freq is based on CLK Period and pulse is half for duty 50.

Example CLK 48M DIV1

Period 65535 +1 N.. 65536

Pulse 32767(8)

Then min freq is 48M/65536 = around 732Hz

Max is Period 1 ... 48M/2 or other setup for OUT CLK then 48M...

Hi MM..1, thanks. I understood it. The static example of ST works at selected frequency, but I'm not able to change dinamically the frequency, this is my problem.

Your main code changes nothing because IT routine override your change.

And your code uses other method when Period stay 65536 and IT routine steps with togle times.

Then you need in main only change __IO uint32_t TIM1_pulses

and implement it here instead PULSE1_VALUE constant

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
  uint16_t autoreload_value;
  uint32_t uhCapture = 0;
  
  /* Get configured autoreload value */
  autoreload_value = __HAL_TIM_GET_AUTORELOAD(htim);
 
  /* TIM1_CH1 toggling with frequency = 800 Hz */
  if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  {
    uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
 
    /* Set the Capture Compare Register value */
    if (uhCapture + PULSE1_VALUE < autoreload_value)
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + PULSE1_VALUE));
    }
    else
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + PULSE1_VALUE) - autoreload_value);
    }
  }

ECast.12
Associate II

Hi MM..1 and thanks. I do that but it doesn't work, maybe I didn't understand how I have to do.

This is my new code:

...
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
	// HAL_Delay(50);
 
	// Change frequency with blue pushbutton
	if(!HAL_GPIO_ReadPin(BLUE_BUT_GPIO_Port, BLUE_BUT_Pin))
	{
		if(HAL_TIM_OC_Stop_IT(&htim1, TIM_CHANNEL_1) != HAL_OK)
		{
			// Stopping Error
		    Error_Handler();
		}
		TIM1_pulses += 10;
		if (TIM1_pulses <= PULSE0_VALUE) TIM1_pulses=PULSE0_VALUE;
		if (TIM1_pulses >= PULSE1_VALUE) TIM1_pulses=PULSE1_VALUE;
		// SetOutputFrequency(&htim1,TIM1_pulses,TIM_CHANNEL_1);
		//HAL_TIM_MspPostInit(&htim1); // Doesn't change anything...
		if(HAL_TIM_OC_Start_IT(&htim1, TIM_CHANNEL_1) != HAL_OK)
		{
			// Starting Error
		    Error_Handler();
		}
 
		// Toggle green led at any change
		HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
		changes++;
	}
 
	// Refresh output frequency
	My_HAL_TIM_OC_DelayElapsedCallback(&htim1, TIM1_pulses);
 
	/* USER CODE END 3 */
  }
}
 
/* USER CODE BEGIN 4 */
void My_HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim, uint32_t pulses)
{
  uint16_t autoreload_value;
  uint32_t uhCapture = 0;
 
  /* Get configured autoreload value */
  autoreload_value = __HAL_TIM_GET_AUTORELOAD(htim);
 
  /* TIM1_CH1 toggling with frequency = 800 Hz */
  if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  {
    uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
 
    /* Set the Capture Compare Register value */
    if (uhCapture + pulses < autoreload_value)
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + pulses));
    }
    else
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + pulses) - autoreload_value);
    }
  }
}

As first you cant rename or modify HAL Callkback names , this is defined by HAL. And you need understand how interrupts work.

In main simply

...
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
	// HAL_Delay(50);
 
	// Change frequency with blue pushbutton
	if(!HAL_GPIO_ReadPin(BLUE_BUT_GPIO_Port, BLUE_BUT_Pin))
	{
		TIM1_pulses += 10;
		// Toggle green led at any change
		HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
		changes++;
	}
 
	/* USER CODE END 3 */
  }
}

In callback user code

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
  uint16_t autoreload_value;
  uint32_t uhCapture = 0;
  
  /* Get configured autoreload value */
  autoreload_value = __HAL_TIM_GET_AUTORELOAD(htim);
 
  /* TIM1_CH1 toggling with frequency = 800 Hz */
  if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  {
    uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
 
    /* Set the Capture Compare Register value */
    if (uhCapture + TIM1_pulses < autoreload_value)
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + TIM1_pulses));
    }
    else
    {
      __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (uhCapture + TIM1_pulses) - autoreload_value);
    }
  }