STM32G0 Variable square wave generation
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 7:49 AM
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.
Solved! Go to Solution.
- Labels:
-
STM32G0 Series
-
TIM
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-06 2:29 AM
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);
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 7:57 AM
Follow the example code:
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 7:57 AM
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
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 11:22 PM
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);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 11:26 PM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-05 11:41 PM
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...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-06 12:12 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-06 12:43 AM
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);
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-06 1:26 AM
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);
}
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-06 2:29 AM
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);
}
}
