2020-06-02 06:40 PM
I think I am missing something obvious about a difference between channels 2&3 vs channel 4 in the TIM8 after staring at this for some hours now...
I am using the STM32H750 discovery board, and created a project in CubeMX which configures PI2, PB14 and PB15 as PWM outputs for channels 4, 2, 3 of timer 8. (ignore the maximum frequency, I changed it after taking this screenshot)
CubeMX provides a warning about a conflict for PB14 and PB15 assignment, but I can't tell which potential conflict is the real one.
The SDMMC is enabled but not using these pins:
I don't think it is ITRx nor TI1_ED because I'm using the internal clock source and no external triggers:
Break and dead time functions are all disabled in the configuration. Looking in the description of the timer in the reference manual (figure 344 of RM0433) it seems like these channels should be configured with the same behaviors. But when I run my program (even just using the default PWM duty cycle I have configured in the CubeMX) the pins PB14 and PB15 have no PWM output.
I have eliminated everything else from main.c, now it is just initializing the timer base and these 3 channels:
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_FMC_Init();
MX_QUADSPI_Init();
MX_SDMMC1_MMC_Init();
MX_TIM8_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim8);
HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_4);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
This is the timer initialization, as generated by CubeMX:
static void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM8_Init 1 */
/* USER CODE END TIM8_Init 1 */
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 480;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 48;
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(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
sConfigOC.Pulse = 32;
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
sConfigOC.Pulse = 24;
if (HAL_TIM_PWM_ConfigChannel(&htim8, &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.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
/* USER CODE END TIM8_Init 2 */
HAL_TIM_MspPostInit(&htim8);
}
I am initializing the timer interrupt, because I eventually would like to adjust the PWM duty cycle for all 3 channel outputs after the update event. Right now this of course does nothing since 2 of 3 channels produce no PWM output. But this is what is in the callback:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim8)
{
temp_pulse_counter1 = __HAL_TIM_GET_COUNTER(htim8);
//set period equal to counter value
__HAL_TIM_SET_COMPARE(htim8,TIM_CHANNEL_4,temp_pulse_counter1);
__HAL_TIM_SET_COMPARE(htim8,TIM_CHANNEL_3,temp_pulse_counter1);
__HAL_TIM_SET_COMPARE(htim8,TIM_CHANNEL_2,temp_pulse_counter1);
}
I think I am just missing a simple fundamental aspect of configuring the channels or outputs and have become blind to it now. Any help is appreciated!
2020-06-02 10:17 PM
Read out and check the TIM and relevant GPIO registers. I'd bet you don't have CH2N and CH3N enabled in TIMx_CCER (CC2NE/CC3NE bits). IIRC there is some Cube incantation for this, from the Ex group.
I don't Cube/CubeMX.
JW
2020-06-02 10:20 PM
Please post the contents of the RCC->AHB4ENR, GPIOB and TIM8 registers. Never assume that a CubeMX setting or a HAL function does what you think it should do.
Since you have already found the timer chapter, you could as well check the register values against the descriptions in the reference manual. Read the TIMx capture/compare enable register (TIMx_CCER)(x = 1, 😎 description, pay close attention to Table 330. Output control bits for complementary OCx and OCxN channels with break feature.
When you know what the registers should contain, you can get rid of the mess generated by CubeMX, and set the timer registers directly.
2020-06-03 08:10 PM
Fantastic, thanks to you both! It was indeed the CCxNE bits not being set. Since I'm using CubeMX & HAL, I had called HAL_TIM_PWM_Start() for all channels, but really needed to call HAL_TIMEx_PWMN_Start() for channels 2 and 3 using the complementary outputs. That sets the CCxNE bits properly. Thanks!