[Back] [Top] [Next]

1 Introduction

C can be a rather terse and mystifying language. Widely quoted as being a high level language, C does indeed contain many such features like structured programming, defined procedure calling, parameter passing, powerful control structures etc.

However much of the power of C lies in its ability to combine simple, low-level commands into complicated high-level language-like functions and allow access to the actual bytes and words of the host processor. To a great extent then, C is a sort of universal assembly language. Most programmers who are familiar with C will have been used to writing programs within large machines running Unix or latterly MS-DOS. Even in the now cramped 640KB of MSDOS, considerable space is available so that the smallest variable in a program will be an int (16 bits). Most interfacing to the real world will be done via DOS Ints and function calls. Thus the actual C written is concerned only with the manipulation and processing of variables, strings, arrays etc.

Within the modern 8 bit microcontroller, however, the situation is somewhat different. Taking the 8051 as an example, the total program size can only occupy 4 or 8K and use only 128bytes of RAM. Ideally, real devices such as ports and special function registers must be addressed from C. Interrupts have to be serviced, which require vectors at absolute addresses. Special care must be taken with a routine's data memory allocation if over-writing of background loop data is to be avoided. One of the fundamentals of C is that parameters (input variables) are passed to a function (subroutine) and results returned to the caller via the stack. Thus a function can be called from both interrupts and the background without fear of its local data being overwritten (re-cutrancy).

A serious restriction with the 8051 family is the lack of a proper stack; typically with a processor such as the 8086, the stack pointer is 16 bits (at least). Besides the basic stack pointer, there are usually other stack relative pointers such as a base pointer etc..

With these extra demands on the stack control system, the ability to access data on the stack is crucial. As already indicated, the 8051 family is endowed with a stack system which is really only capable of handling return addresses. With only 256 bytes of stack potentially available, it would not take too much function-calling and parameter-passing to use this up.

From this you might think that implementing a stack-intensive language like C on the 8051 would be impossible. Well, it very nearly has been! While there have been compilers around for some years now that have given C to 8051 users, they have not been overly effective. Most have actually been adapted from generic compilers originally written for more powerful micros such as the 68000. The approach to the stack problem has largely been through the use of artificial stacks implemented by using 8051 opcodes.

Typically, an area in external RAM is set aside as a stack; special library routines manage the new stack every time a function is called. While this method works and gives a re-entrant capability, the price has been very slow runtimes. The net effect is that the processor spends too much time executing the compiler's own code rather than executing your program!

Besides the inherent inefficiency of generating a new stack, the compiled program code is not highly optimised to the peculiarities of the 8051. With all this overhead, the provision of banked switch expanded memory, controlled by IO ports, becomes almost a necessity!

Therefore, with the 8051 in particular, the assembler approach to programming has been the only real alternative for small, time-critical systems.

However, as far back as 1980, Intel produced a partial solution to the problem of allowing high-level language programming on its new 8051 in the shape of PLM51. This compiler was not perfect, having been adapted from PLM85 (8085), but Intel were realistic enough to realise that a full stack-based implementation of the language was simply not on.

The solution adopted was to simply pass parameters in defined areas of memory. Thus each procedure has its own area of memory in which it receives parameters and passes back the results. Provided the passing segments are internal the calling overhead is actually quite small.

Using external memory slows the process but is still faster than using an artificial stack.

The drawback with this "compiled stack" approach is that re-entrancy is now not possible. This apparently serious omission in practice does not tend to cause a problem with typical 8051 programs. However the latest C51 versions do allow selective re-entrancy, so that permitting re-entrant use of a few critical functions does not compromise the efficiency of the whole program.

Other noteworthy considerations for C on a microcontroller are:

  1. control of on and off-chip peripheral devices
  2. servicing of interrupts
  3. making the best use of limited instruction sets
  4. supporting different ROM/RAM configurations
  5. a very high level of optimisation to conserve code space
  6. control of registerbank switching
  7. support of enhanced or special family variants (87C751, 80C517 etc..).

The Keil C51 compiler contains all the necessary C extensions for microcontroller use. This C compiler builds on the techniques pioneered by Intel but adds proper C language features such as floating point arithmetic, formatted/unformatted IO etc. It is, in fact, an implementation of the C language ANSI standard specifically for 8051 processors.


[Back] [Top] [Next]