STM32L4 PWM complementario utiliza solo el canal negativo

Tengo problemas al usar la función PWM complementaria del STM32-L432KC. El pin no complementario ch1 está generando la señal correcta, pero el pin ch1n siempre está en GND.

Quiero usar los canales 1, 2 y 3 en el temporizador 1, de manera óptima solo el canal inverso, por lo que puedo ver, esto debería ser posible. Para las pruebas también se ha habilitado el ch1 que está funcionando.

He configurado la HAL usando CubeMx, mi código de inicialización es:

void MX_TIM1_Init(void)
{

  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 80;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 20000;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  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(__FILE__, __LINE__);
  }

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 1000;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  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.Break2State = TIM_BREAK2_DISABLE;
  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
  sBreakDeadTimeConfig.Break2Filter = 0;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  HAL_TIM_MspPostInit(&htim1);

}

¿Hay algo mal configurado? ¿Es posible usar solo el canal inverso?

Empiezo el cronómetro llamando:

HAL_TIM_Base_Start(&htim1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL. Quién sabe lo que hay allí. ¿Por qué no configura los registros directamente en su lugar?
Me encantaría configurarlo todo yo solo, pero necesito configurar muchos periféricos (I²C con DMA, UART, SPI...) y no tengo tiempo para hacerlo todo a mano.
¿Y cuál es el problema? Es más rápido que el bloatware HAL. Ahora perderás días pidiendo las cosas elementales. Por lo general, HAL requiere mucho más tiempo que el enfoque de registro simple. Este "ahorro de tiempo" es una leyenda. Ahora desperdiciaste 5 horas. Su problema se puede ordenar en 3 minutos sin HAL
¿Puedes mostrar tus llamadas de PWM Start también?
Agregué el código de inicialización a mi pregunta original.

Respuestas (2)

no ha iniciado NChannels. Hazlo asi:

   HAL_TIM_MspPostInit(&htim1);


   /* Start channels x*/

   if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3) != HAL_OK)
   {
     /* Starting Error*/
     Error_Handler();
   }
   /* Start channel xN*/
   if(HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3) != HAL_OK)
   {
    /*Starting Error*/
     Error_Handler();
   }

   if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
   {
     /* Starting Error*/
     Error_Handler();
   }
   /* Start channel xN*/
   if(HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
   {
    /*Starting Error*/
     Error_Handler();
   }

   if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
   {
     /* Starting Error*/
     Error_Handler();
   }
   /* Start channel xN*/
   if(HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
   {
    /*Starting Error*/
     Error_Handler();
   }

Cuando llama a la función HAL_TIM_PWM_Init, llama a HAL_TIM_PWM_MspInit que comúnmente se coloca en stm32...._hal_msp.c en las fuentes del proyecto. Hay inicialización de GPIO para los temporizadores de trabajo periféricos. Algo como esto

  GPIO_InitStruct.Pin = TIM_LED_GPIO_PIN_CHANNEL1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = TIM_LED_GPIO_AF;
  HAL_GPIO_Init(TIM_LED_GPIO_CHANNEL1_PORT, &GPIO_InitStruct);

Entonces, ¿posiblemente no tienes init allí?

Tengo la función init (en mi código está en el archivo tim.c), pero mis parámetros de inicialización son ligeramente diferentes: el parámetro Pull está configurado en GPIO_NOPULL y el parámetro Speed ​​en GPIO_SPEED_FREQ_LOW, pero para los puertos de trabajo, el parámetro es el mismo
¿Puede mostrar el código completo aquí para ambos puertos?