2019-05-15 06:05 AM
Hi,
I need a faster 32-bit timer (TIM2 and TIM5 which are 32-bits are on 108MHz AHB1). So idea is to chain 2x 16 bit timers on APB2, choose TIM1 and TIM8. But I just can't configure it to work as it should using CubeMX. I'm also not sure how to adjust it so I get what I need: master timer for MSB and slave timer for LSB. I can easly configure each(separate) timer for the period I desire but getting them to work as chained...is another story...
This is the code I have, TIM1 should be used as master, TIM8 as slave:
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 0xFFFF;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}
static void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {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 = 0xFFFF;
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();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig) != 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();
}
/* USER CODE BEGIN TIM8_Init 2 */
/* USER CODE END TIM8_Init 2 */
}
After that I start TIM8 and then TIM1.
if (HAL_TIM_Base_Start(&htim8) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_Base_Start(&htim1) != HAL_OK)
{
Error_Handler();
}
I get individual counters and calculate the value as this:
uint32_t counter1 = __HAL_TIM_GET_COUNTER(&htim1);
uint32_t counter8 = __HAL_TIM_GET_COUNTER(&htim8);
uint32_t combined = ((counter1 * 0xFFFF) + counter8);
printf("T: %5lu | %5lu | %lu\r\n", counter1, counter8, combined);
But counter1 and counter8 have similar values. So I guess I'm triggering the slave wrongly...
What the heck is wrong? TIM8 trigger source? TIM8 slave mode?
Thnx!
2019-05-15 06:56 AM
Managed to get something which resembles chained timers, but I don't understand why.
TIM8 must be configured like this:
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;
sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
Why must sSlaveConfig.SlaveMode be set to TIM_SLAVEMODE_EXTERNAL1 and why sSlaveConfig.InputTrigger must bne set to TIM_TS_ITR0?
This post helped: http://www.proiotware.com/index.php/9-blogs/9-stm32-chaining-two-16-bit-timers-to-create-32-bit-timer
Thnx!
2019-05-15 07:15 AM
>>What the heck is wrong?
Well the math here is patently wrong for starters
uint32_t combined = ((counter1 * 0xFFFF) + counter8);
The core also has a 32-bit cycle counter that runs at core frequency. ie 216 MHz, or 400/480 MHz on H7
2019-05-15 07:33 AM
Yes, changed the "combined" math. :)
This is F7, can't find 32 bit timer at full speed of 216MHz. :(
Correct me if I'm wrong but TIM1, TIM8, TIM9, TIM10 and TIM11 are on APB2. All are 16 bit. Others are on AHB1, only TIM2 and TIM5 are 32 bit.
2019-05-15 07:58 AM
I said the core has a cycle counter.
It can be used to measure, or delta, elapsed time.
Sorry I don't understand your end goal for using the TIM. Perhaps you can elaborate a little on what you're trying to achieve, and then I can make a judgement call about the appropriate approach.
2019-05-15 08:05 AM
The other issue with a pair of 16-bit reads is that they won't occur atomically, so you'll need to take a more guarded approach to reading them so they are coherent. ie you will want to read the high order register twice, and the low order between them, and be sure the high-order value hasn't changed.
2019-05-17 12:22 AM
Clive: F7 is clocked at 200MHz so we have 5ns for one instruction. We need a timer to calculate time between pulses on several ports. As we are aware timer counts up/down on each clock cycle, so if we count 1000 instructions(cycles) its 1000 * 5ns = 5ms. We used a timer on PICH for similar job so using it here would be a logical choice also, right? Anyways, counting should be precise and deterministic. Several signals should be able to toggle each port and depending on the edge(rising/falling) time stamp should be taken. We don't care how we do it as long as it's doable on precise and deterministic manner. Using a 32 bit timer gives us around 21 seconds for sampling the data after timer overflows. We have specially designed hardware which goes to input pins so basically software is the only part which needs proper handling.
2019-05-17 01:36 AM
Clive: regarding "I said the core has a cycle counter." You meant using DWT->CYCCNT?
2019-05-17 05:31 AM
For test, I used 32 bit timer @ 100MHz and I used CYCCNT.
CYCCNT is 2x value of timer as expected.
Put a toggler device on EXTI pin and measured the time difference. Toggling is done each 100ms (100ms up, 100ms down). Jitter is within 10 micro seconds.
Any way to lower that?
Measured examples (timer | CYCCNT):
20000400 | 40000800
20000190 | 40000380
20000512 | 40001024
20000256 | 40000512
20000362 | 40000724
20000406 | 40000812
20000584 | 40001168
20000290 | 40000580
20000400 / 100MHz = 0,200004 seconds as is 40000800 / 200MHz.
2019-05-17 07:54 AM
Yes, I'd probably lean toward use a timer also. The STM32 designs lack 32-bit TIM for reasons that seem to defy logic. I would have like to have seen more/simpler timing functions implemented at 32-bits, but they seem to have gone for the kitchen sink design with a lot of critical paths limiting high speed/performance.
You might want to look at CPLD/FPGA type implementations, or something like a TDC7200 which can resolve A-to-B timings of different signal edges.
Yes, the DWT's CYCCNT register, good for time-stamping interrupt events, or elapsed time, less good for external events.
There might be a mem-to-mem DMA trick to reading the DWT->CYCCNT into a buffer based on a TIMx_CHx trigger.