I would like to design a time scheduler that would do some specific stuff at specific time using a timer/state machine.
I would like to do something like this :
As you can see on the picture above, I would need to do a request every 4 ms, this request would have a turnaround delay of 1ms and during this window of 4 ms, I would have 3ms to make 6 other request every 500microseconds. And at the end I would like to do this constantly.
What would be the best way to do it in C for STM32 ? Right now I did this :
void TIM2_Init (uint32_t timeout)
{
uint8_t retVal = false;
// Enable the TIM2 clock
__HAL_RCC_TIM2_CLK_ENABLE();
// Configure the TIM2 handle
htim2.Instance = TIM2;
htim2.Init.Prescaler = (uint32_t)(240000000 / 1000000) - 1;
htim2.Init.Period = timeout - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.RepetitionCounter = 0;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_OK == HAL_TIM_Base_Init(&htim2))
{
// Enable update interrupt for TIM2
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
// Enable the TIM2 interrupt in the NVIC
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
// Start the TIM2 counter
if (HAL_OK == HAL_TIM_Base_Start(&htim2))
{
retVal = true;
}
return retVal;
}
void changeTIM2Timeout (uint32_t timeout)
{
// Change the TIM2 period
TIM2->ARR = timeout - 1;
}
I created a timer with a timeout parameter to initialize it. Then I have a function to change the period, cause for example I can have a response timeout of 500us sometimes and also 1ms some other times. Where I'm stuck is having the whole working, meaning first request is done, then 1ms later followed by second request (2), then (3), etc. I think it's on the state machine that would handle the different request with the timer that I need help.
CodePudding user response:
You can create a queue data structure for the tasks and assign each task time there. When it's done, if it's a repetitive task, it can be added to the end of the queue when it's done with its current run and you're fetching the next task. You can have fixed maximum size queue or dynamic maximum size queue, it's up to you.
When the task is invoked, the timer is set to the value, associated with that task. So there will be no waste ticks. This also gives you tickless idle with a bit of effort, if you need it. Also, you can add some more parameters in that queue if you want to customize scheduling somehow, such as priority level.
Edit: this obviously makes the timer unusable as a time measurement device for any other purpose, because you change its settings all the time.
CodePudding user response:
I would configure the timer to 500us on autoreload. Then just keep an index up to 8 that will increment with wrapping. Because 8 is a multiple of 2, you could use an unsigned number and forget about wrapping.
void do_stuff(int idx) {
switch (idx) {
case 0: request(1); break;
case 2: case 3: case 4: case 5: case 6: case 7: etc.
request(idx); break;
}
void TIM2_Init() {
// generate from STM32CubeFX autoreload with 500us
}
void TIM2_IRQHandler(void) {
static unsigned idx = 0;
do_stuff(idx % 8);
}