2019-05-12 07:51 AM
Hi community,
I am trying to get timer3 to trigger on a rising edge, run for one 10ms pulse and then stop until it is next triggered. Application is AC phase control for a simple SCR circuit with 2 optos. I nearly have it working, but essentially, it seems Cube/HAL/...? is configuring the trigger input wrong. Or I am grossly misunderstanding the concept of an external trigger input.
I am using a Nucleo F042k6 for this prototyping. I have also tried this on a standalone chip F030k6 with the same result.
I set it up in CubeMX as timer 3, trigger mode, as show in pic:
I picked 830 to be compatible with 60Hz; this has the impact of a minimum power for 50Hz, but you know... problems for another day...
The code generated to initialise the timer3 is:
static void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 480;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 830;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim3, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchronization(&htim3, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}
In the main() I run:
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim3);
HAL_TIM_OnePulse_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,400);
/* USER CODE END 2 */
Initially, I looped the GPIOA-PA0 to the GPIOA PA6 pin with a wire, and ran the code:
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
HAL_Delay(9);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
HAL_Delay(0);
/* USER CODE END WHILE */
Which seemed to work A-OK. by setting the PWM duty cycle, I could generate the desired phase control signal. Bizarrely, HAL_Delay(x) seems to cause an x+1ms delay... unimportant, the key thing is a rising edge to detect every ~10ms.
But when I connected it to my SCR circuit (verified by the oscilloscope to be generating a zero crossing pulse via open drain and 4k7 pullup), it does not work. The reason seems to be that the trigger input is actually configured as output ?!?! and therefore one pin is fighting the other. If I connect the GPIOA-0 via a 75ohm resistor, it no longer works.
The GPIO settings on CubeMX confirm this: but there is no way I can see of fixing this. ?!?!?!? So it works, but only if I make the signal strong enough to fight the push pull on the pin?????
Help appreciated...
I can push this to my scrap github to see the full code if needed.
David
2019-05-12 07:57 AM
Pictures failed to upload last time, try again...
2019-05-12 08:04 AM
You definitely look to be configuring this as an output.
You would need to configure a channel in an input capture mode to load CCRx with the CNT value as it cycles.
Each TIM has a single counter.
Note also that Prescale and Period values are written as N-1, as they describe the final state. Ie N states, 0 thru N-1
2019-05-12 08:09 AM
So, you want TIM3_CH1 to be output
> HAL_TIM_OnePulse_Start(&htim3,TIM_CHANNEL_1);
or input
> sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
?
I don't use Cube/CubeMX so can't help "fixing" this; OTOH this is a relatively simple task once you read the timer chapter in RM and understand what's one-pulse mode and how the slave-mode controller works.
JW
2019-05-12 10:10 AM
Jan,
So I looked at the reference manual, there is an example on this on page 420/1004, 18.3.10 One-pulse mode.
It seemed to say that broadly what I was doing is correct... via HAL/Cube abstraction, so I removed:
> HAL_TIM_OnePulse_Start(&htim3,TIM_CHANNEL_1);
(as hinted by Jan)
Left everything else exactly the same (the line Jan suggested sSlaveConfig.InputTrigger = TIM_TS_TI1FP1; is already in the timer init) and it worked.
Photo below of it pushing 60% duty cycle into a lightbulb (as a test, getting built into bigger, more complex things later).
Clive,
Thanks for the reminder on N-1 for the prescaler. I already knew this but was being a dumdum.
Re. input capture mode, within CubeMX, there is no combination I can find that does not configure the GPIO as Alternative Function push-pull or Alternative Function Open Drain, including the input capture modes on the channels. This seems to be a confusion within Cube/HAL. However, it seems that the channel is not configured as an output until calling HAL_TIM_OnePulse_Start, at which point the CCx bits for channels 1 and 2 and the MOE bits are set... I'm really not getting this, since timer3 doesn't even have an MOE bit to set or reset.
HAL_StatusTypeDef HAL_TIM_OnePulse_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
{
/* Enable the Capture compare and the Input Capture channels
(in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
in all combinations, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be enabled together
No need to enable the counter, it's enabled automatically by hardware
(the counter starts in response to a stimulus and generate a pulse */
TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
if(IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
{
/* Enable the main output */
__HAL_TIM_MOE_ENABLE(htim);
}
/* Return function status */
return HAL_OK;
}
So many thanks both of you, I'm not sure I'm any the wiser on this one, but it now works at least. Once again, the HAL/Cube abstraction layer is almost a... confusion layer...