How to generate two signals with variable frequency and constant phase difference?

87 views Asked by At

I have an STM32F411 Black Pill board, and I am trying to simulate a rotary encoder with this.

I want to generate two PWM signals, which are always 90 degrees out of phase, but have variable frequency. I tried the approach at How can I have Variable Frequency PWM with STM32? using two timers but this fails for some reason. The second channel (TIM2) ends up being a constant low.

I am able to get an output at constant frequency, but trying to change it doesn't work for the TIM2 channel.

Configuration

  • System clock (HCLK): 100 MHz
  • Both timers run at this frequency (APB1 and APB2)

TIM1

  • Channel 1: PWM Generation CH1
  • Channel 2: Output Compare No Output
  • Prescaler: 999, AutoReload register: 999 (resulting in 100 Hz PWM)
  • Output compare mode: Active Level on match

TIM2

  • Slave mode: Trigger Mode
  • Trigger source: ITR0
  • Channel 1: PWM Generation CH1
  • Prescaler: 999, AutoReload register: 999 (resulting in 100 Hz PWM)

Code main.c

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    TIM1->ARR = 999;
    TIM2->ARR = 999;
    TIM1->CCR1 = 499;
    TIM1->CCR2 = 249;
    TIM2->CCR1 = 499;

    HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_SET);
    HAL_Delay(1000);

    TIM1->ARR = 499;
    TIM2->ARR = 499;
    TIM1->CCR1 = 249;
    TIM1->CCR2 = 124;
    TIM2->CCR1 = 249;

    HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_RESET);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Expected screenshot

Expected Waveform

Logic analyzer screenshot

Blue is TIM1 CH1, orange is TIM2 CH1 enter image description here

EDIT 1: Narrowed down the issue

Just as a debugging step, I tried to just vary the phase shift by varying TIM1->CCR2. This doesn't work. The graph is at a constant phase difference as shown in the diagram below.

EDIT 1 Code main.c

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

  HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_SET);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    for (size_t i = 249; i < 999; i++)
    {
      TIM1->CCR2 = i;
      HAL_Delay(10);
    }


    HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_RESET);
    HAL_Delay(100);
    HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_SET);
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

EDIT 1 Graph from logic analyzer

Blue is TIM1 CH1, orange is TIM2 CH1

Debugging logic analyzer graph

EDIT 2: Simon's method

Tried to change just the frequency of the PWM signals. This doesn't work.

EDIT 2 Code main.c

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

  HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_SET);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    TIM1->ARR = 999;
    TIM2->ARR = 999;

    HAL_GPIO_TogglePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin);
    HAL_Delay(1000);

    TIM1->ARR = 499;
    TIM2->ARR = 499;

    HAL_GPIO_TogglePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin);
    HAL_Delay(1000);
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

EDIT 2 Logic analyzer screenshot

Variable frequency screenshot

1

There are 1 answers

0
GerwazyMucha On

Firstly try without slave mode to trigger the second timer. The way to do so i like that:

  • Init both timers with the same PSC ARR and CRR values
  • Set CNT register of one of the timers to ARR/4.
  • Start both timers.

Depending of generated frequency the delay that is the result of asynchronous start may or may not be crucial.

If it turns out that the delay of triggering timers is a thing just use the same trigger source to start generation to trigger both timers.