Skip to main content
ECast.12
Associate II
August 5, 2021
Solved

STM32G0 Variable square wave generation

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.

This topic has been closed for replies.
MM..1
MM..1Best answer
Super User
August 6, 2021

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);
 }
 }

12 replies

TDK
August 5, 2021

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""."
Tesla DeLorean
Guru
August 5, 2021

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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
ECast.12
ECast.12Author
Associate II
August 6, 2021

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
ECast.12Author
Associate II
August 6, 2021

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
Super User
August 6, 2021

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...

ECast.12
ECast.12Author
Associate II
August 6, 2021

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.

MM..1
Super User
August 6, 2021

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
ECast.12Author
Associate II
August 6, 2021

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);
 }
 }
}

MM..1
MM..1Best answer
Super User
August 6, 2021

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);
 }
 }

ECast.12
ECast.12Author
Associate II
August 6, 2021

Many thanks MM..1, not it works. I didn't see HAL_TIM_OC_DelayElapsedCallback() function inside main.c. that is the clear solution.

Sorry for my mistake and thanks again for your time. Emanuele.