4
\$\begingroup\$

I have few questions on software timers and interrupts on a microcontroller. Just for information, I use a dsPIC33E microcontroller.

The final goal is to implement a serial communication protocol: RS485 with Modbus. I managed to transmit and receive a message, and now I have to do a message processing part.

Since I need a bunch of timers for all sorts of different tasks (e.g., 3.5 character delay needed for serial communication, some delay needed for buttons debouncing etc.), I plan to implement timers in a software using a single hardware timer. For example, hardware timer period is set to 100 us, and an interrupt is generated on every "overflow", i.e., every 100 us. In the ISR I just update global counters, which are basically software timers with a resolution of 100 us, whereas the hardware timer ISR has the highest priority. Is this a good way to do this, or is there some better way?

Each counter (i.e., a software timer) has defined its own period, and once the counter reaches its "period value", I want to call some function. Now it is not a very good idea to call this function from inside the hardware timer ISR, because that function will be processed with the highest priority since the calling routine is the hardware timer ISR. What I want is to define some function, let's say:

void Modbus_Protocol(void);

and to be able to define it as an interrupt, which will not be of the highest priority. Only the main hardware timer has the highest priority in this concept. In that way, once the counter in the hardware timer ISR reaches its period value, it wouldn't call its function, but it would rather just set a flag to trigger an interrupt (e.g., void Modbus_Protocol(void)), which will be triggered after the main ISR returns, depending on its priority. Can something like this be done, i.e., can I define software interrupts?

I know there is a possibility to use hardware interrupts which are not used, and to just set an interrupt flag from within the software. But I don't think this is an elegant way to implement interrupts if user defined software interrupts are possible.

\$\endgroup\$
  • \$\begingroup\$ Have you tried implementing your code using a second timer interrupt to set a flag to then carry out your code within your main while loop? \$\endgroup\$ – Nick B Aug 5 '16 at 12:03
  • \$\begingroup\$ I can implement this using a hardware (timer) interrupt without any problems, but I want to use software timers instead, for the reasons mentioned in the question. In that case, I also need software interrupts, since software timers are updated inside the highest-priority ISR, and I don't want to call other functions from inside that interrupt. \$\endgroup\$ – Marko Gulin Aug 5 '16 at 12:07
  • \$\begingroup\$ you can use the interrupt to count out the various times for each task which I think you are saying have different time intervals. if(aflag) { do a } if(bflag) { do b } and so on in the foreground main loop with the interrupt handler setting the flags at the appropriate time. Basically you dont have to use alternate interrupts, you can just have the foreground task, poll these flags. Sampling the buttons you can do in the 100us timer interrupt handler, not every time if needed, every Nth time. then save the result in a flag that the foreground task sees when it gets a chance. \$\endgroup\$ – old_timer Aug 6 '16 at 13:08
  • 1
    \$\begingroup\$ If the handler is just generating prescaled 100us based timer like Batuu's answer, you might not need the interrupt complication at all, might use a hardware prescaler and everyone polls the real timer rather than a global artificial soft counter. First and foremost are you bitbanging serial? Get a different part with a uart if that is the case. \$\endgroup\$ – old_timer Aug 6 '16 at 13:11
  • \$\begingroup\$ I'm not bitbanging serial, I'm using a dedicated UART to do that. What do you mean by "a hardware prescaler" and "the real timer"? I tried Googling on polling CPU timer, but I didn't find anything. Is there a register that is updated on every CPU tick? \$\endgroup\$ – Marko Gulin Aug 6 '16 at 13:27
3
\$\begingroup\$

You do not need special "software interrupts". Have a look at the following code. I neglected some technical stuff like variable declaration for clarity.

Use your ISR to just count some timer (tick). In the main() you are waiting to sync to this timer. Use a dedicated timer for each task you have to process. All these timers are incremented every ISR tick.

If the task timer expire, the dedicated task is processed. There are some special features in this kind of implementation. When you subtract the EXPITED-time from your timer instead setting it to be 0, your software is more robust if any of the tasks takes longer thenn 100µs. It's a kind of soft-realtime criteria.

You get a pseudo multi-tasking system.

isr() //100µs
{
    tick++;
}

main()
{    
  while(true)
  {
    while(tick == last_tick);
    last_tick = tick;

    modbus_timer++;
    task1_timer++;
    task2_timer++;

    if(modbus_timer >= MODEBUS_TIMER_EXPIRED)
    {
      modbus_timer -= MODEBUS_TIMER_EXPIRED;
      Modbus_Protocol();
    }

    if(task1_timer >= TASK1_TIMER_EXPIRED)
    {
      task1_timer -= TASK1_TIMER_EXPIRED;
      Task1();
    }

    if(task2_timer >= TASK2_TIMER_EXPIRED)
    {
      task2_timer -= TASK2_TIMER_EXPIRED;
      Task2();
    }
  }//forever-loop
}// main()
\$\endgroup\$
  • \$\begingroup\$ Of course, this is another way to implement "software timers". But in this way, I can't assign priority levels on "interrupts". E.g., I want that a software interrupt Modbus_Protocol() has higher priority than some other hardware interrupt. I don't think I can do that using your example. One other thing - I will not use these timers constantly, so I'm not really looking for a real-time operation. What I mean by this is that a Modbus timer will be started only when the UART is receiving a message and once the message is received, the timer will stop and reset. \$\endgroup\$ – Marko Gulin Aug 5 '16 at 13:17
  • \$\begingroup\$ Two points: Think about your software architecture. Modbus_Protocol() seems to be a kind of interpreter/parser for modbus messages. Usually this is a low priority task. Use polling or usart isr to receive the chars/frames on the hardware I/O and put this frames into a software queue ore fifo. If the frame is complete call your interpreter (low prio). \$\endgroup\$ – Batuu Aug 5 '16 at 13:23
  • \$\begingroup\$ Second: depending on your specific controller you can mask some interrupts to realize main()-level task priority over hardware interrupts. But I usually prefer the first approach, because it is more deterministic. \$\endgroup\$ – Batuu Aug 5 '16 at 13:25
  • \$\begingroup\$ That is exactly how I'm going to implement the Modbus part - high priority interrupt (UART_RX ISR) will fill a buffer, and once the whole message is received, a Modbus interpreter (low priority) will be called to empty the buffer and to interpret the message. But it would be much more flexible if I could define a function (e.g. a Modbus interpreter or something else) that will be declared as an interrupt. In that way, I don't have to worry which ISR is calling which function, since function is called by setting its interrupt flag. Again, I know that I can use other unused hardware interrupts. \$\endgroup\$ – Marko Gulin Aug 5 '16 at 13:40
  • 1
    \$\begingroup\$ Modify my example. Declare a flag "modbus_frame_received" and set it to true in your usart-handler if so. Istead of waiting for a modbus_timer just do if(modbus_frame_received) { Modbus_Protocol(); modbus_frame_received = false; }. Here you get a max delay/jitter of 100µs between receive completed and interpreter-call. IMHO there is no need to do it on any interrupt-priority. Handling other hardware resources is usually more important then software protocol stuff. In most cases communication I/O is an async process. \$\endgroup\$ – Batuu Aug 5 '16 at 13:52
0
\$\begingroup\$

After some Googling and discussion, I realized that I tried to achieve prioritized tasks, which is basically an RTOS (real-time operating system). Although an RTOS can be implemented on a microcontroller, it requires a huge amount of resources (RAM etc.).

The other option is to use a scheduler as Batuu explained. In that way, tasks cannot be prioritized (there is no multitasking), and all tasks should finish within a single tick.

\$\endgroup\$
  • 2
    \$\begingroup\$ This is 'cooperative multitasking' en.wikipedia.org/wiki/Cooperative_multitasking \$\endgroup\$ – Bruce Abbott Aug 6 '16 at 11:56
  • \$\begingroup\$ Your are right, that you basically ask for an RTOS. One remark: Using a scheduler as explained, it is not required that every task completes within a single tick. You can device tasks into multiple sub-tasks with a simple state-maschine for example. This way you can pretend using a multitasking system by running an static schedular. In my applications, this runs really well. The need for true RTOS raises by system complexity and dynamic processing requirements. \$\endgroup\$ – Batuu Aug 11 '16 at 6:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.