You are on page 1of 9

"

Books and great sites to help you


to be a great embedded software
engineer, enjoy 

1- Become an expert in C : The fundamental language of the hardware that is still


portable (too some degree). Don't just learn it, but become an expert of all it's features
like volatile and why it is important for writing device drivers.
2- Start out with a good development kit like Arduino, but as said before learn other
architectures once you got a good feel for it. Luckily there are some Arduino compatible
boards built with other processors, that way you can rewrite the same design on a
different uC not mess up your whole design while getting a feel for something new.
3- In the learning stage, feel free to re-invent the wheel on device drivers or other pieces
of code. Don't just plop someone else's driver code down in there. There's value in re-
inventing the wheel when you're learning.
4- Challenge yourself to re-write your code more efficiently in terms of speed and
memory usage.
5- Becoming familiar with different styles of embedded systems software architectures.
Start with basic interrupt driven/background loop processing, then move up to
background schedulers, then real-time operating systems.
6- Get good source control! I prefer Mercurial myself.
7- Even sign up for some free source control hosting sites
likeSourceforge.net or Bitbucket.org to host your project even if you're the only one
working on it. They'll back your code up, so you don't have to worry about that
occasional hard drive crash destroying everything! Using a distributed VCS comes in
handy, because you can check in changes to your hard drive then upload to the host
site when ready.
8- Learn your tools well for whatever chip you're working on! Knowing how the compiler
creates assembly is essential. You need to get a feel for how efficient the code is,
because you may need to rewrite in assembly. Knowing how to use the linker file and
interpreting the memory map output is also essential! How else are you going to know if
that routine you just wrote is the culprit of taking up too much ROM/Flash!
9- Learn new techniques and experiment with them in your designs!
10- Assume nothing when debugging. Verify it!
11- Learn how to program defensively to catch errors and verify assumptions (like using
assert)
12- Build a debugging information into your code where you can such as outputting
memory consumption or profiling code with timers or using spare pins on the uC to
toggle and measure interrupt latency on a O-scope.
13- Remember, "There is no silver bullet", don't fall into the trap of believing that there is
one tool, methodology, language or system that can solve all problems.
14- Learn to get by without malloc() and POSIX.
15- Don't get hung up on one architecture, it's easy to become a PIC or AVR or ARM
fanboy by accident.
16- Build stuff, debug it, make it work. Practice makes perfect
17- Learn at least one source control system (SVN/git/etc) and use it.
18- Always be prepared to test your assumptions. The bug is usually in the thing you
are assuming works.
19- Don't become too reliant on debuggers, they're different on every system and of
varying reliability.
20- Think frugally. When problem solving, think about code footprint, RAM footprint and
hardware cost.
21- For more tips check out this great article >>http://www.ganssle.com/startinges.htm
" Here are some books "
>> The Pragmatic Programmer by Andrew Hunt and David Thomas - more or less
required reading for any practical software development
>>Practical Arduino
>>Programming Embedded Systems by Michael Barr
>>Embedded Systems Building Blocks by Jean Labrosse
>>MicroC OS II Real Time Kernel by Jean Labrosse, great intro into RTOS's in general
in there along with his OS.
>>Embedded Software Primer by David Simon - good intro to embedded software
" Here are some websites "
>> www.Embeddedgurus.com
>> www.Embedded.com
>> www.embeddedstar.com
>> www.eg3.com
#ESGeeks
Source : http://electronics.stackexchange.com/…/how-to-become-an-emb…
Scheduling
Two kinds of scheduling techniques are used in Real-Time system:

 Static Scheduling
 Dynamic Scheduling

Static Scheduling
This involves analyzing the tasks statically and determining their timing properties. This
timing property can be used to create a fixed scheduling table, according to which tasks
will be dispatched for execution at run time. Thus the order of execution of the task is
fixed, and it is assumed that their execution time is also fixed.

Round-Robin Scheduling
Round Robin scheduling by Time Slicing is one of the ways to achieve static scheduling.
Round robin is one of the simplest and most widely used scheduling algorithms; in
which a small unit of time known as time slice is defined. Schedulers go around the
queue of ready-to-run processes and allocate a time slice to each such process.

Scheduling with Priority


Priority indicates the urgency or importance assigned to a task. There are two
approaches of scheduling based on priority based execution – when the processor is
idle, the ready task with the highest priority is chosen for execution; once chosen, the
task is run to completion.

Pre-Emptive Scheduling
Preemptive Priority based execution is when the processor is idle, the ready task with
highest priority is chosen for execution; at any time, the execution of a task can be
preempted if a task of higher priority becomes ready. Thus, at all times, the processor is
idle or executing the ready task with the highest priority.
Dynamic Scheduling
Another kind of scheduling mechanism is known as Dynamic Scheduling – In this case, a
real-time program requires a sequence of decisions to be taken during execution of the
assignment of resource to transactions. Here each decision must be taken without prior
knowledge of the needs of future tasks. Dynamic scheduling is not in the scope of this
article, so I am not discussing it in detail here. Perhaps we can discuss it in another
article.

Code Snippet
Let us take a example of a Master-Slave communication system. Master system is
connected to n number of slave systems over serial port (RS 485 network) in multi-drop
architecture. Figure 1 shows the typical configuration of this system. Here only one
system can talk at a time and others are in listen mode. The Master controls the
communication.

Main Routine
Collapse | Copy Code

void main(void)
{
/* Initialise all register of processor and the peripheral devices
*/
InitMain();

/* Register the event handler */


RegisterTask(MainEventHandler);

RegisterTask(CheckDataIntegrity);
..............

/* Turn on all the leds for 1 sec as lamp test */


TurnOnLed(LED, 1000);

/* Call the application event manager - no return */


EventManager();
}

In the above case, the RegisterTast() and EventManager() are two


important functions. For any application, we have number of tasks and a function
represents the entry point of a task, like 'CheckDataIntegrity' . When a device
receives a complete data packet, it goes for data
checking. RegisterTask() function creates a link-list of function pointers where
each node represents a single task. Here I have passed the function
pointerMainEventHandler or CheckDataIntegrity as an argument.

Main.h should have the following lines:

Collapse | Copy Code

/* Application event handler function pointer */


typedef void (*tEventHandler)(unsigned short *);

/* Link-list definition */
typedef struct TaskRecord
{
tEventHandler EventHandler;
struct TaskRecord *pNext;
}tTaskRecord;

static tTaskRecord *mpTaskList = NULL;

Here mpTaskList represents a link-list of function pointers. Considering each node


of link list as a entry point of a task, this will execute one by one
in EventManager() function. Below is the definition
of RegisterTask()function which adds the function pointer into the link-list.

Collapse | Copy Code

void RegisterTask(tEventHandler EventHandlerFunc)


{
tTaskRecord *pNewTask;

/* Create a new task record */


pNewTask = malloc(sizeof(tTaskRecord));
if(pNewTask != NULL)
{
/* Assign the event handler function to the task */
pNewTask->EventHandler = EventHandlerFunc;
pNewTask->pNext = NULL;

if(mpTaskList == NULL)
{
/* Store the address of the first task in the task list */
mpTaskList = pNewTask;
}
else
{
/* Move to the last task in the list */
mpActiveTask = mpTaskList;
while(mpActiveTask->pNext != NULL)
{
mpActiveTask = mpActiveTask->pNext;
}

/* Add the new task to the end of the list */


mpActiveTask->pNext = pNewTask;
}
}
}

For this type of application, after initialization there should be an infinite loop for
continuous execution. The function EventManager() at the end of
the main which is nothing but a infinite loop always checks for active tasks or events.
If any event occurs, then it passes that event flag as an argument of the function which
is already added into the mpTaskList. So EventManager() function
calls MainEventHandler() function with eventIDas an
argument. MainEventHandler will check the eventId and do the necessary
action or execute the corresponding code. Here the event should be unique for each
event-handler function, i.e. two event-handler functions should not check the
same eventID.
Definition of EventManager Function
Collapse | Copy Code

void EventManager(void)
{
unsigned short AllEvents;
tTaskRecord pActiveTask

/* No return */
while(1)
{
/* Read application events */
AllEvents = mEventID;

/* Process any application events */


pActiveTask = mpTaskList;
while((AllEvents != 0) && (pActiveTask != NULL))
{
if(pActiveTask->EventHandler != NULL)
{
/* Call the task's event handler function */
(mpActiveTask->EventHandler)(&AllEvents);

/* Read application events */


AllEvents = mEventID;
}

/* Move to the next event handler */


pActiveTask = pActiveTask->pNext;
}
}
}

Event can be generated from interrupt service routine or by checking the status of an
input pin in polling mode.SerialReceiveISR function generates an event after
receiving the complete packet. Since the variablemEventID is modified in the
interrupt service routine, it is recommended to disable interrupt while reading.

Collapse | Copy Code

#pragma interrupt_level 0
void interrupt IService(void)
{
/* Receive bit is set when a byte is received */
if(Receivebit == 1)
{
SerialReceiveISR()
}
...........
/* code for other interrupt */
}

SerialReceiveISR Function
Collapse | Copy Code

#pragma inline SerialReceiveISR


void SerialReceiveISR(void)
{
static char RxMsgDataCount;

/* If a framing or overrun error occurs then clear the error */


if(Error == 1)
{
/* Indicate a problem was seen */
mEventID = mEventID | ATTENTION_REQ_FLG;
}
else if( RxMsgCount == DataLength)
{
/* Packet receive complete */
mEventID = mEventID | DATA_RECEIVE_COMPLETE;
}
else
{
/* Store data in memory */
Store(RxByte);
RxMsgCount++;
}
}

Here ATTENTION_REQ_FLAG & DATA_RECEIVE_COMPLETE flags are two


bits of a Global variable mEventID, which is 16 bits and each bit triggers the
corresponding event when set.

Collapse | Copy Code


#define ATTENTION_REQ_FLAG 0x0008
#define DATA_RECEIVE_COMPLETE 0x0001

When the flag is set, the EventManager will call all the registered functions with
the eventID as argument.

Collapse | Copy Code

void MainEventHandler(unsigned short *Event)


{
if(*Event & DATA_RECEIVE_COMPLETE)
{
/* Do the corresponding action */
.........
/* Reset the flag */
*Event &= ~DATA_RECEIVE_COMPLETE;
}
}

Now we can change the variable type to increase the number of flags. If you want to
generate multiple number of events, then use a structure rather than a single variable.

You might also like