2023-09-06 02:47 AM
Hello,
I was working on a project and came across a rather strange behaviour with a triggered slave timer. I have recreated the behaviour in a separate isolated CubeIDE project as not to distract you with the irrelevant parts. So, forgive the contrived scenario since it lacks the context of my project (which is communicating with an external ADC but that is besides the point). This isn't a big problem for me since it is a relatively minor bug that came about when I was playing around. However, I am really more curious as to why this happens, and I thought that I can at least document it here.
In this example, I am using a NUCLEO-H723ZG board, and I have two timers, TIM2 and TIM3, both with PWM outputs. The former is set up as a basic PWM generator, and the latter is set up in one-pulse-mode as a slave triggered by the former's update, i.e. TRGO. TIM3's source is thus ITR1 as according to Table-355 in the reference-manual (RM0468) The timing is such:
TIM2 | TIM3 | |
PSC | 0 | 0 |
ARR | 499 | 399 |
CRR1 | 250 | 390 |
To be honest, the exact values do not matter too much, but I thought that I would give as much detail so that one of you can replicate it.
This is all created in CubeMX. Attached is the generated tim.c file and the .ioc file (never mind, can't attach it).
Here is the code in main():
/**
* @brief The application entry point.
* @retval int
*/
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_TIM2_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
//TIM3->CCR1 = 200;
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
So, what happens is that TIM2_CH1 outputs a PWM signal with a duty of about 50%. Then, TIM3_CH1 goes high for 390 bus-clock-cycles and then low for the remaing 10 cycles and then high again. This is for each rising edge of TIM2_CH1. I know that this is an odd scenario, but bear with me; the point of this post is not the exact timing by what happens to the registers. So, the result is seen below; this is expected.
However, if one wanted TIM3's pulse-length to be shorter, i.e. CRR1 = 200 instead of 390, and one changed it by overwriting the CCR1 register directly (i.e. uncomment the line above HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1)), then the result is somewhat expected as seen below (also attached). All other pulses are 200 bus-clock-cycles long as expected (I will admit that this is a bit hard to see in the screenshot). However, the first triggered pulse is still 390 cycles long as configured in CubeMX even though the CCR1 should be 200. It is almost as if the CCR1 has some kind of 'memory' of the original CubeMX value. Keep in mind that this is before both timers are started. The same thing happens when I put a breakpoint at the line where TIM3 is started and manually wait before resuming; so, it does not seem to be a race-condition.
Here are both timers' registers after CCR1 is overwritten as 200. Clearly, the CCR1 is 200 before both timers are started.
A similar thing happens with TIM8's RCR. I had TIM8 outputting repeated short pulses triggered by TIM2 like before with TIM3. In CubeMX, I originally had the number of repetitions as 64 but changed it to 32 by directly writing to the RCR. The result is 64 pulses on the first rising edge and then intended 32 for the rest.
Of course, this is all simply fixed by sticking to making changes in CubeMX. But it is nevertheless startling and a bit disconcerting.
Am I missing something? I thought that after writing to a register, that's it. Its former content should not matter. Is there some kind of FIFO buffer before the register? Does it need some kind of event for the content to update?
Let me know if you need any more information. I would be interested if anyone had an explanation or could at least replicate it.
Thanks
Solved! Go to Solution.
2023-09-06 07:38 AM
Hello @Claegtun, welcome to ST Community,
I would like to thank you for the quality of your explanation!
I believe your question in tightly related to @Jan Waclawek gotcha.
I will try to replicate the use case and get back to you!
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-09-06 04:06 AM
TIM_AUTORELOAD_PRELOAD_ENABLE?
2023-09-06 07:38 AM
Hello @Claegtun, welcome to ST Community,
I would like to thank you for the quality of your explanation!
I believe your question in tightly related to @Jan Waclawek gotcha.
I will try to replicate the use case and get back to you!
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-09-06 04:06 PM
This seems to be it. Silly me not researching enough. Thanks
2023-09-06 04:08 PM
Yeah, as Sarra.S said, it seems to be because the registers are preloaded. I will have to try that out to see if that is the case, but I am willing to accept this. Thanks