Recently I am starting to look at real-time OS for embedded systems. Most books and articles only have trivial examples, such as a "keypad task" to control a "flashing led task." In real-world applications, are there guidelines on how to break an application into tasks, such as
I learned all this on the job, so I can't recommend a book.
Create a task when the situation has changed enough to warrant it, and when it's safe. For all of the systems I've worked on this time is system startup, and none other. Memory is bulky, expensive, and power- hungry, so one is generally resource constrained -- the last thing you want is to set yourself up for the OS crashing in the field when one extra task is created, or even for that one extra thing failing because the OS couldn't allocate memory.
Task granularity is a judgment call, but be guided by the word 'task': assign a task to carry out a task. An example that I can give from personal experience is having to drive three axes on closed-loop in a coordinated fashion: I'll have one task for each axis, and one task to do the coordination.
The maximum number of tasks is also a judgment call, and heavily dependent on your application, but it's aided by the fact that task switches take time and tasks take memory. I'd say it's the minimum number that is sensible for you, unless for some reason you have oceans of memory.
Control systems and communications consulting
The first cut, for me, is to determine logical tasks. A logical task is something that has sequential processing and it fairly independent of other processing within the system. An example is controlling an interactive front panel with display and keypad while the system is controlling something independently of the keypad entries.
If everything in the system is done sequentially, single task code works fine. If the system has the appearance of several things happening simultaneously that are not in lock step, there are several logical tasks, one for each independent process.
The easiest mapping is one OS task for each logical task. A logical task can sometimes be done entirely in an interrupt routine, eliminating the need for an OS task. Sometimes there are constraints, such as memory or timing, that require a single OS task to perform more than one logical task.
My suggestion is to create a new task only when it is associated with sequential behavior over a period of time that can't be done while everything else, except interrupt processing, is on hold.