3

I asked this question on EE forum. You guys on StackOverflow know more about coding than we do on EE so maybe you can give more detail information about this :)


When I learned about micrcontrollers, teachers taught me to always end the code with while(1); with no code inside that loop.

This was to be sure that the software get "stuck" to keep interruption working. When I asked them if it was possible to put some code in this infinite loop, they told me it was a bad idea. Knowing that, I now try my best to keep this loop empty.

I now need to implement a finite state machine in a microcontroller. At first view, it seems that that code belong in this loop. That makes coding easier.

Is that a good idea? What are the pros and cons?

This is what I plan to do :

void main(void)
{
    // init phase

 while(1)
 {
    switch(current_State)
    {
    case 1:
        if(...)
        {
            current_State = 2;
        }
        else(...)
        {
            current_State = 3;
        }
        else
            current_State = 4;
        break;

    case 2:
        if(...)
        {
            current_State = 3;
        }
        else(...)
        {
            current_State = 1;
        }
        else
            current_State = 5;
        break;
   }
}

Instead of:

 void main(void)
    {
        // init phase

     while(1);
    }

And manage the FSM with interrupt

M.Ferru
  • 400
  • 1
  • 6
  • 20
  • `while(1);` no it should be `wihile(1)` no semicolon in the end. semicolon indicates as the line termination in c. `while(1)` is just the infinite loop – danglingpointer Jun 03 '17 at 20:11
  • I don't have any microcontroller experience myself, but it seems to me that it would be pointless to put a second, empty infinite loop after your FSM infinite loop; it would never be reached. – zwol Jun 03 '17 at 20:11
  • @LethalProgrammer --> syntax error without a code block. – Weather Vane Jun 03 '17 at 20:12
  • 3
    @LethalProgrammer No, in this case OP's `while(1);` is correct. That is an infinite loop that does nothing, which is exactly what you want in this particular situation. – zwol Jun 03 '17 at 20:12
  • @zwol While(1) after an infinite loop is pointless. Tru. But I mean, Why shouldn't I put code in it? – M.Ferru Jun 03 '17 at 20:13
  • I said `while(1);` is not infinite loop it will just terminate after executing once. to me, infinite is something goes on forever :D – danglingpointer Jun 03 '17 at 20:13
  • 4
    @LethalProgrammer No, that's not true. It will execute the empty statement forever. – zwol Jun 03 '17 at 20:14
  • No reason, on the face of it your state machine code is reasonable. – Weather Vane Jun 03 '17 at 20:14
  • 2
    @LethalProgrammer It goes for ever.. Just doing idle task. But interrupt are still occuring – M.Ferru Jun 03 '17 at 20:14
  • @M.Ferru I am just guessing, but I can imagine a style rule saying that there had to be a `while(1);` at the end of `main` _always_, just in case somehow the "first" infinite loop exited (maybe someone patched in a `break` without thinking about it). I'd address that by putting a reset in crt1, to be executed in the should-never-happen situation that `main` returns -- that would also catch someone patching in a `return` without thinking about it. But, as I said, I haven't done this kind of work so my idea of good style is questionable. – zwol Jun 03 '17 at 20:15
  • If your code is event driven by interrupts, then the state changes should also be. So why put the FSM in the while loop. Everyone knows that what goes in the idle loop is the code to blink the LEDs. – stark Jun 03 '17 at 20:17
  • 1
    @zwol https://stackoverflow.com/a/28681034/7076153, are you still sure ? `while (1);` seem ok, but this could happen with some infinite loop :p. – Stargateur Jun 03 '17 at 20:20
  • @stark I was just thinking that the code in the while loop was a kind of "guiding" code like FSM use to be – M.Ferru Jun 03 '17 at 20:21
  • To the question title: FSM codes I have written for embedded code do not implement the FSM at the top level of `main`. I use that and its calling hierarchy for the user interface, while the FSM is serviced as a task driven by a regular timer interrupt. One reason is because the FSM is event driven, from timers and external switches, sensors, etc. which are detected by other interrupts. – Weather Vane Jun 03 '17 at 20:26
  • @WeatherVane Okay, then, where should I put that code? in a timer interrupt? I have no free timer left :/ – M.Ferru Jun 03 '17 at 20:33
  • Too long a story, but in the regular heartbeat interrupt I also put keyboard servicing, a local clock, and whatever else is appropriate at that rate. – Weather Vane Jun 03 '17 at 20:35
  • @WeatherVane Ok, but this makes interrupt code longer to execute. What can cause issue to the system at some point – M.Ferru Jun 03 '17 at 20:45
  • Yes but crucial interrupts have a higher priority, and the base level only services user interface. – Weather Vane Jun 03 '17 at 21:03
  • @WeatherVane So I have to execute the FSM code in an periodical low priority interrupt? That seems logic. You can write an answer so what I can accept it if you like to – M.Ferru Jun 03 '17 at 21:11
  • Stop messing about and use a multitasker, freeRTOS or something. 'while(1);' is just a core burner:( – ThingyWotsit Jun 03 '17 at 23:26
  • 1
    @Stargateur That's about loops where the controlling condition isn't a constant. `while(1)` and `for(;;)` are safe. Anyone who writes an intentional infinite loop with a controlling condition that isn't constant has already committed an offense against readability. – zwol Jun 04 '17 at 13:04

4 Answers4

4

It is like saying return all functions in one place, or other habits. There is one type of design where you might want to do this, one that is purely interrupt/event based. There are products, that go completely the other way, polled and not even driven. And anything in between.

What matters is doing your system engineering, thats it, end of story. Interrupts add complication and risk, they have a higher price than not using them. Automatically making any design interrupt driven is automatically a bad decision, simply means there was no effort put into the design, the requirements the risks, etc.

Ideally you want most of your code in the main loop, you want your interrupts lean and mean in order to keep the latency down for other time critical tasks. Not all MCUs have a complicated interrupt priority system that would allow you to burn a lot of time or have all of your application in handlers. Inputs into your system engineering, may help choose the mcu, but here again you are adding risk.

You have to ask yourself what are the tasks your mcu has to do, what if any latency is there for each task from when an event happens until they have to start responding and until they have to finish, per event/task what if any portion of it can be deferred. Can any be interrupted while doing the task, can there be a gap in time. All the questions you would do for a hardware design, or cpld or fpga design. except you have real parallelism there.

What you are likely to end up with in real world solutions are some portion in interrupt handlers and some portion in the main (infinite) loop. The main loop polling breadcrumbs left by the interrupts and/or directly polling status registers to know what to do during the loop. If/when you get to where you need to be real time you can still use the main super loop, your real time response comes from the possible paths through the loop and the worst case time for any of those paths.

Most of the time you are not going to need to do this much work. Maybe some interrupts, maybe some polling, and a main loop doing some percentage of the work.

As you should know from the EE world if a teacher/other says there is one and only one way to do something and everything else is by definition wrong...Time to find a new teacher and or pretend to drink the kool-aid, pass the class and move on with your life. Also note that the classroom experience is not real world. There are so many things that can go wrong with MCU development, that you are really in a controlled sandbox with ideally only a few variables you can play with so that you dont have spend years to try to get through a few month class. Some percentage of the rules they state in class are to get you through the class and/or to get the teacher through the class, easier to grade papers if you tell folks a function cant be bigger than X or no gotos or whatever. First thing you should do when the class is over or add to your lifetime bucket list, is to question all of these rules. Research and try on your own, fall into the traps and dig out.

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • so to your question, you have to answer for yourself, can I and then should I put the state machine in an interrupt handler or can I and then should I put it in the foreground code. we cannot determine that for you not without full visibility into the project, and that wouldnt make sense to do here, add/change a requirement and the answer may change, and on top of all that if it can fit in both places without being insane, some percentage will give one answer and some another. so primarily opinion based. – old_timer Jun 03 '17 at 22:55
  • This is the felling I have about µC / MCU development, many ways can lead to the same result. According on what you said, I might put the FSM in the super loop. The rest of the design is based on interrupt handling that makes the loop empty. By doing so I won't overload timer, ADC or Input interrupt. – M.Ferru Jun 03 '17 at 23:02
1

When doing embedded programming, one commonly used idiom is to use a "super loop" - an infinite loop that begins after initialization is complete that dispatches the separate components of your program as they need to run. Under this paradigm, you could run the finite state machine within the super loop as you're suggesting, and continue to run the hardware management functions from the interrupt context as it sounds like you're already doing. One of the disadvantages to doing this is that your processor will always be in a high power draw state - since you're always running that loop, the processor can never go to sleep. This would actually also be a problem in any of the code you had written however - even an empty infinite while loop will keep the processor running. The solution to this is usually to end your while loop with a series of instructions to put the processor into a low power state (completely architecture dependent) that will wake it when an interrupt comes through to be processed. If there are things happening in the FSM that are not driven by any interrupts, a normally used approach to keep the processor waking up at periodic intervals is to initialize a timer to interrupt on a regular basis to cause your main loop to continue execution.

One other thing to note, if you were previously executing all of your code from the interrupt context - interrupt service routines (ISRs) really should be as short as possible, because they literally "interrupt" the main execution of the program, which may cause unintended side effects if they take too long. A normal way to handle this is to have handlers in your super loop that are just signalled to by the ISR, so that the bulk of whatever processing that needs to be done is done in the main context when there is time, rather than interrupting a potentially time critical section of your main context.

Logan Hunter
  • 397
  • 4
  • 8
0

What should you implement is your choice and debugging easiness of your code. There are times that it will be right to use the while(1); statement at the end of the code if your uC will handle interrupts completely (ISR). While at some other application the uC will be used with a code inside an infinite loop (called a polling method):

while(1) 
{

//code here;

} 

And at some other application, you might mix the ISR method with the polling method.

When said 'debugging easiness', using only ISR methods (putting the while(1); statement at the end), will give you hard time debugging your code since when triggering an interrupt event the debugger of choice will not give you a step by step event register reading and following. Also, please note that writing a completely ISR code is not recommended since ISR events should do minimal coding (such as increment a counter, raise/clear a flag, e.g.) and being able to exit swiftly.

Itzik Chaimov
  • 89
  • 1
  • 10
-1

It belongs in one thread that executes it in response to input messages from a producer-consumer queue. All the interrupts etc. fire input to the queue and the thread processes them through its FSM serially.

It's the only way I've found to avoid undebuggable messes whilst retaining the low latencty and efficient CPU use of interrupt-driven I/O.

'while(1);' UGH!

ThingyWotsit
  • 366
  • 2
  • 4