0

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 : enter image description here

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.

PierreP
  • 107
  • 7
  • *What would be the best way to do it in C for STM32?* - is a very broad question. You should start from *some* code. Since you are working with timer, make a simple blinky application that is using timer interrupt. Then add some code to change the blinking period in the runtime. From there you will figure out the rest. – Eugene Sh. Jan 04 '23 at 16:04
  • do you have some code? have you made an attempt which did not work as expected? "What would be the best way...." is a very broad question which is likely to draw downvotes, as it implies that you are wanting others to code the solution on your behalf. – Claies Jan 04 '23 at 16:04
  • @EugeneSh. yep sorry, I thought my code was in, but not at all, its fixed now – PierreP Jan 04 '23 at 16:19
  • @Claies yep sorry, thought it was in, its fixed now – PierreP Jan 04 '23 at 16:20
  • Learn about freeRTOS. Do not reinvent the wheel – 0___________ Jan 04 '23 at 16:47

2 Answers2

0

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.

Ilya
  • 992
  • 7
  • 14
-1

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);
}

   
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 2
    I would not put `do_stuff` in the ISR. I'd rather have a main loop with a fixed time-base triggered by the interrupt. – Eugene Sh. Jan 04 '23 at 16:46
  • 1
    As @EugeneSh. stated doing something in ISRs is a very bad idea,. – 0___________ Jan 04 '23 at 16:48
  • 1
    @EugeneSh. and we are coming into the magic world of synchronisation, events and multitasking. Better use RTOS to have this part done properly – 0___________ Jan 04 '23 at 16:50
  • @0 Depending on the needs, yes. But a simplest scheduler of not-that-concurrent tasks can be implemented with a simple `while(1) { task1(); task2();...; wait_for_timer_interrupt(); }` – Eugene Sh. Jan 04 '23 at 16:53
  • I must use baremetal C so I dont bave any OS. Of course it would have been easier. – PierreP Jan 04 '23 at 16:57
  • @PierreP RTOS is not OS in big computers understanding. It is rather a library providing a sheduler and synchronisation functions. It is part of your app like `printf` or any other function. – 0___________ Jan 04 '23 at 17:19