Embedded C and RTOS Unit 1 and 2
Embedded C and RTOS Unit 1 and 2
Unit 1:
Unit 2:
Embedded “C‟: Cx51 Language extensions: data types, bit manipulation, etc.
Preprocessor and preprocessor directives.Cx51 Compiler Library reference.
1
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Introduction to ANSI C
When C was first written the standard was set by its authors Kernighan and
Ritche - hence "K&R C". In 1990, an international ANSI standard for C was
established which differs from K&R C in a number of ways.
int sum(int,int);
meaning sum is an int function which takes two int parameters. The only other
major change is can declare parameter types along with the function as in:
rather than:
int sum(a,b)
int a,b;
was used in the original K&R C. Again, it is just a small change. Notice that even
if we are using an ANSI compiler we don't have to use prototypes and the K&R
version of the code will work perfectly well.
Basics of ANSI C
We briefly list some of C's characteristics that define the language and also
have led to its popularity as a programming language. Naturally we will be
studying many of these aspects throughout the course.
• Small size
2
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
• Structured language
C has now become a widely used professional language for various reasons.
• Preprocessor Commands
• Type definitions
• Variables
• Functions
local variables
C Statements
3
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
/* Sample program */
main()
exit ( 0 );
NOTE:
printf (".\n.1\n..2\n...3\n");
.1
..2
...3
4
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Computers execute instructions one by one, in a strict order set out by the
programmers, this is known as the flow of execution.
o But only having a straight flow would chew up our program memory really
fast, what we need is some loops.
o The two things that make computers useful are the ability to process
data quickly and the ability to use data to influence the program flow.
o C provides a set of flow control statements very much like any other
algebraic language. It gives if, switch, for, and do/while statements. Let’s
look at each one.
IF
5
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
There are two forms of the if statement, one is used to execute a block of code
if a condition is true, the other executes one block of code if a condition is true
and another if the condition is false.
if (condition) {
Statement;
Where the condition is some logical comparison, a bool variable, or function call
that returns a bool. For comparison operators, C has a standard set:
6
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
if ( answerToQuestion == 42) {
DoSomething();
So far we have seen how an if statement can be used to execute a block of code
if the condition is true, bypassing it if it is false. If we wish to do one thing if
the condition is true and another if it is false, we use the else clause.
if ( temperature < 0) {
printf( “frozen\n”);
} else {
Within the curly braces of the if statement, we can put any number of
statements, including other if statements. These are called nested ifs.
if ( temperature < 0) {
printf( “frozen\n”);
} else {
printf( “liquid\n”);
} else {
printf( “gas\n”);
7
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
For our next example, assume that we want to classify characters as vowels or
consonants. We could use something like:
#include <iso646.h>
#include <stdio.h>
char letter;
printf( "Vowel\n");
} else {
printf( "Vowel\n");
} else {
printf( "Vowel\n");
} else {
printf( "Vowel\n");
} else {
printf( "Vowel\n");
} else {
} else {
8
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
printf( "Consonant\n");
SWITCH
switch (var) {
statement;
statement;
break;
case value2:
statement;
statement;
break;
default:
statement;
statement;
break;
9
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
1) If we forget to put in our break statement our code will just continue
through the next case until it hits a break in a trailing case or the closing
curly brace of the switch.
2) Falling through cases because of a missing break is a common bug, be
careful to put break statements at the bottom of every case block.
3) Falling through case statements can be useful if you have multiple cases
that are handled identically.
char letter;
10
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
switch (letter) {
case 'A':
case 'a':
case 'E':
case 'e':
case 'I':
case 'i':
case 'O':
case 'o':
case 'U':
case 'u':
printf( "Vowel\n");
case 'Y':
case 'y':
break;
default:
printf( "Consonant\n");
break;
11
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Another option is to just put a comment where the default code would go
explaining why you think the default case is not necessary.
FOR
For statements are used to iterate over, or individually processes each of, a
bunch of data. The intent is to provide a loop with a way to terminate it, and a
variable that we can use in calculations or as an index to an array.
The for statement has confused syntax made up of three parts separated by
semicolons:
statements;
• Test - the for loop will continue as long as this test is true.
• Increment - this section is used to alter the value of the control variable,
typically increment or decrement it.
12
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
For example, here we have a very common pattern for going through a C array.
Since C arrays starts at 0, we initialize our counter to 0 and continue the loop
while the counter is less than the number of items in the array.
uint16_t total = 0;
uint16_t values[100];
...
This example goes from 0 to 99, which matches the element numbers of the
array. If we don’t need the various sections of the for statement, they can be
left out. We control variables may already be initialized, or something inside of
your loop may change the control variable.
For embedded systems, we will typically have an infinite loop which can be set up
as:
for ( ; ;) {
DoSomething();
or
while ( true) {
DoSomething();
FOREVER {
13
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
DO/WHILE:
The while loop executes a block of statements while a condition is true. There
are two forms, one with the test at the top of the loop, and the other with the
test at the bottom.
while ( moreDataToProcess) {
do {
} while (moreDataToProcess);
14
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
The major difference between the two forms is that the do-while will run at
least once. Sometimes, we need that first loop to get things started, like when
reading status registers.
Unlike if, switch, and for loops with counters, there is no easy way to determine
if a while loop will terminate. It seems to be waiting for the data ready bit on a
UART to become active and it never does because of something happened.
uint32_t counter = 0;
counter = counter + 1;
someOtherCommands;
Pointers in C:
The C equivalent of the indirect instruction is the pointer. The register holding
the address to be indirectly accessed in the assembler examples is a normal C
type, except that its purpose is to hold an address rather than a variable or
constant data value.
It is declared by:
15
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Note the asterisk prefix, indicating that the data held in this variable is an
address rather than a piece of data that might be used in a calculation etc.
In all cases in the assembler example two distinct operations are required:
4. /*indirectly */
5. pointer = &c_variable ;
7. /*pointer */
8. *pointer = 0xff ;
2. void function(void)
3. {
16
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
10. }
Arrays:
Uninitialized Arrays
1. unsigned char x ;
2. unsigned char y ;
1. unsigned int a ;
2. unsigned int b ;
yield four memory locations, two allocated to 'a' and two to 'b'. In other
programming languages it is possible to group similar types together in arrays.
In basic an array is created by DIM a(10).
This has the effect of generating ten sequential locations, starting at the
address of 'a'. As there is nothing to the right of the declaration, no initial
values are inserted into the array. It therefore contains zero data and serves
only to reserve ten contiguous bytes.
Initialized Arrays
17
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
where the initial values are put in place before the program gets to "main()".
Note that the size of this initialized array is not given in the square brackets -
the compiler works-out the size automatically upon compilation.
1. A$ = "HELLO!"
18
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
The second case reads as "get the address of the first element of the array
name and put it into arr_ptr". No implied pointer conversion is employed, just
the return of the address of the array base.
Using Arrays
7. {
8. dest_array[array_index] = source_array[array_index] ;
11. }
Structures:
Structures are perhaps what makes C such a powerful language for creating
very complex programs with huge amounts of data. They are basically a way of
grouping together related data items under a single symbolic name.
19
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
o For each sensor to be catered for there is an input signal with a span and
offset, a temperature coefficient, the signal conditioning amplifier, a
gain and offset.
o The information for each sensor type could be held in "normal" constants
thus:
20
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Where 'N' is the number of the sensor type. A structure is a neat way of
condensing this type of related and repeating data. In fact the information
needed to describe a sensor can be reduced to a generalized:
Unions: Unions allow you to define different data type references for the same
physical address. This way you can address a 32-bit word as a "long" OR as 2
different "ints" OR as an array of 4 bytes.
The number of bytes of RAM used by a union is simply determined by the size
of the largest element, so:
1. union test
2. {
3. char x ;
4. int y ;
5. char a[3] ;
6. long z ;
7. } ;
21
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
requires 4 bytes, this being the size of a long. The physical location of each
element is the base address plus the following offsets:
The C51 Compiler provides a number of directives we may use to control source
file compilation. Directives are composed of one or more letters or digits and,
unless otherwise specified, may be specified after the filename on the command
line or within the source file using #pragma. For example:
or
The source file to compile is testfile.c and SYMBOLS, CODE, and DEBUG are
the directives.
▪ Source controls define macros on the command line and determine the
name of the file to be compiled.
▪ Object controls affect the form and content of the generated object
module (*.OBJ). These directives allow us to specify the optimizing level
or include debugging information in the object file.
22
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
COND1 Listing Includes (in the listing file) conditional source lines
skipped by the preprocessor.
EJECT Listing Inserts a form feed character into the listing file.
▪ Memory Areas
▪ Memory Models
▪ Memory Types
▪ Data Types
23
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
▪ Pointers
▪ Function Attributes
Keywords
To facilitate many of the features of the 8051, the Cx51 Compiler adds a
number of new keywords to the scope of the C language:
Program Memory:
Program (CODE) memory is read only; it cannot be written to. Program memory
may reside within the 8051 MCU, it may be external, or it may be both,
depending upon the 8051 derivative and the hardware design.
24
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
o Program memory may be accessed from your C programs using the code
memory type specifier.
Internal data memory resides within the 8051 MCU and is read/write. Up to
256 bytes of internal data memory are available depending upon the 8051
derivative. The first 128 bytes of internal data memory are both directly and
indirectly addressable. The upper 128 bytes of data memory (from 0x80 to
0xFF) can be addressed only indirectly. There is also a 16 byte area starting at
20h that is bit-addressable.
Access to internal data memory is very fast because it can be accessed using an
8-bit address. However, internal data memory is limited to a maximum of 256
bytes.
Internal data can be broken down into three distinct memory types: data,
idata, and bdata.
o The data memory specifier always refers to the first 128 bytes of
internal data memory. Variables stored here are accessed using direct
addressing.
o The idata memory specifier refers to all 256 bytes of internal data
memory; however, this memory type specifier code is generated by
indirect addressing which is slower than direct addressing.
Memory Types:
The Cx51 Compiler provides access to all 8051 memory areas. Variables may be
explicitly assigned to a specific memory space (by including a memory type
specifier in the declaration) or implicitly assigned (based on the memory model).
25
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Memory
Type Description
far Extended RAM and ROM memory spaces (up to 16MB); accessed
by user defined routines or specific chip extensions (Philips
80C51MX, Dallas 390).
As with the signed and unsigned attributes, we may include memory type
specifiers in the variable declaration. For example:
26
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Be careful when using the old C51 syntax with memory-specific pointers. For
example, the following two declarations are equivalent:
If no memory type is specified for a variable, the compiler implicitly locates the
variable in the default memory space determined by the memory
model: SMALL, COMPACT, or LARGE. Function arguments and automatic
variables that cannot be located in registers are also stored in the default
memory area.
bdata
The bdata memory type may be used to declare variables only. It may not
declare bdata functions. This memory is directly accessed using 8-bit addresses
and is the on-chip bit-addressable RAM of the 8051. Variables declared with
the bdata type are bit-addressable and may be read and written using bit
instructions.
Code: The code memory type may be used for constants and functions. This
memory is accessed using 16-bit addresses and may be on-chip or external.
27
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
For constants (ROM variables), code memory is limited to 64K. Objects are
limited to 64K and may not cross a 64K boundary. Constant variables declared
code are located in the CODE memory class.
For program code (functions), code memory is limited to 64K. Program functions
are stored in the CODE memory class by default. The code memory type
specifier is not required.
return (0);
data
The data memory type may be used to declare variables only. It may not
declare data functions. This memory is directly accessed using 8-bit addresses
and is the on-chip RAM of the 8051. It has the shortest (fastest) access time
but the amount of data is limited in size (to 128 bytes or less).
idata
The idata memory type may be used to declare variables only. It may not
declare idata functions. This memory is indirectly accessed using 8-bit
addresses and is the on-chip RAM of the 8051. The amount of idata is limited in
size (to 256 bytes or less). The lower addresses of idata overlap the
corresponding addresses of data memory.
28
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
pdata
The pdata memory type may be used to declare variables only. It may not
declare pdata functions. This memory is indirectly accessed using 8-bit
addresses and is one 256-byte page of external data RAM of the 8051. The
amount of pdata is limited in size (to 256 bytes).
xdata
The xdata memory type may be used to declare variables only. It may not
declare xdata functions. This memory is indirectly accessed using 16-bit
addresses and is the external data RAM of the 8051. The amount of xdata is
limited in size (to 64K or less).
Memory Models
The memory model determines the default memory type to use for function
arguments, automatic variables, and declarations that include no explicit
memory type. The C51 Compiler provides three memory models:
▪ Small Model
▪ Compact Model
▪ Large Model
The following table lists the default memory areas used for each memory model.
29
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
We may specify the memory model on the compiler command line using
the SMALL, COMPACT, or LARGE directives or using #pragma in the source
file.
Note
Small Model
In this model, all variables, by default, reside in the internal data memory of
the 8051 system—as if they were declared explicitly using the data memory
type specifier.
In this memory model, variable access is very efficient. However, all objects
(that are not explicitly located in another memory area) and the stack must fit
into the internal RAM. Stack size is critical because the stack space used
depends on the nesting depth of the various functions.
Compact Model
Using the compact model, by default, all variables reside in a single page
of external data memory of the 8051 system—as if they were explicitly
declared using the pdata memory type specifier.
30
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
o This memory model is not as efficient as the small model and variable
access is not as fast. However, the compact model is faster than
the large model.
When using the compact model, the C51 Compiler accesses external memory
with instructions that use the @R0 and @R1 operands. R0 and R1 are byte
registers and provide only the low-order byte of the address.
Large Model
DATA TYPES
Data Types: The Cx51 Compiler provides several basic data types we may use in
our C programs. The compiler supports the standard C data types as well as
several data types that are unique to the Cx51 platform.
bit 1 0 to 1
31
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
sbit 1 0 or 1
sfr 8 1 0 — 255
sfr16 16 2 0 — 65535
Note
▪ The bit, sbit, sfr, and sfr16 data types are not provided in ANSI C. They
are unique to the Cx51 Compiler.
Within the 8051 family, the number and type of SFRs vary. The C51 Compiler
provides a number of include files for various 8051 derivatives.
Each file contains declarations for the SFRs available on that derivative. The
Cx51 Compiler provides access to SFRs with the sfr, sfr16, and sbit data types.
sfr: The sfr type defines a special function register (SFR). It is used as
follows:
Where
SFRs are declared in the same fashion as other C variables. The only difference
is that the type specified is sfr rather than char or int. For example:
32
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
P0, P1, P2, and P3 are the SFR name declarations. Names for sfr variables are
defined just like other C variable declarations. Any symbolic name may be used
in a sfr declaration.
The address specification after the equal sign ('=') must be a numeric constant.
Expressions with operators are not allowed. Classic 8051 devices support the
SFR address range 0x80-0xFF.
Note: sfr variables may not be declared inside a function. They must be
declared outside of the function body.
sfr variables are always volatile. The compiler will not optimize accesses to this
type of variables.
sfr16
The sfr16 type defines a 16-bit special function register (SFR). It is used as
follows:
Where
Some 8051 derivatives have 16-bit SFRs there are created using consecutive
addresses in SFR memory to specify 16-bit values. For example, the 8052 uses
addresses 0xCC and 0xCD for the low and high bytes of timer/counter 2
respectively. The Cx51 Compiler provides the sfr16 data type to access two 8-
bit SFRs as a single 16-bit SFR.
Access to 16-bit SFRs using sfr16 is possible only when the low byte
immediately precedes the high byte (little endian) and when the low byte is
33
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
written last. The low byte is used as the address in the sfr16 declaration. For
example:
In this example, T2 and RCAP2 are declared as 16-bit special function registers.
The sfr16 declarations follow the same rules as outlined for sfr declarations.
Any symbolic name can be used in a sfr16 declaration. The address specification
after the equal sign ('=') must be a numeric constant. Expressions with
operators are not allowed. The address must be the low byte of the SFR low-
byte, high-byte pair.
Note: Most 16-bit SFRs are composed of two consecutive addresses that
contain the MSB and LSB (in either order) of the 16-bit register. Depending on
the device, writing to the MSB or LSB "latches" the 16-bit value into the
register. There are four possible configurations for a 16-bit SFR. They are:
The sfr16 keyword may be used to define some (but not all) 16-bit Special
Function Registers. The two SFRs accessed by sfr16 must be organized so that
the low byte is stored in memory first (or at the lower address). For example, if
the LSB is at SFR address 0x9E and the MSB is at SFR address 0x9F, sfr16
may be used.
When we write to a srf16, the code generated by the Keil Cx51 Compiler writes
to the high byte first and then the low byte. In many cases, this is not the
desired order.
If the order in which the bytes are written is important (this is the case with
MANY 8051 devices) we must use the sfr keyword to define and access the
SFRs one byte at a time.
34
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
sfr16 variables may not be declared inside a function. They must be declared
outside of the function body.
sbit
The sbit type defines a bit within a special function register (SFR). It is used in
one of the following ways:
Where
sbit EA = 0xAF;
This declaration defines EA as the SFR bit at address 0xAF. On the 8051, this
is the enable all bit in the interrupt enable register.
Note
35
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Any symbolic name can be used in an sbit declaration. The expression to the
right of the equal sign ('=') specifies an absolute bit address for the symbolic
name. There are three variants for specifying the address:
Variant 1
The previously declared SFR (sfr-name) is the base address for the sbit. It
must be evenly divisible by 8. The bit-position (which must be a number from 0-
7) follows the carat symbol ('^') and specifies the bit position to access. For
example:
sfr IE = 0xA8;
sbit OV = PSW^2;
sbit CY = PSW^7;
sbit EA = IE^7;
Variant 2
A character constant (sfr-address) specifies the base address for the sbit. It
must be evenly divisible by 8. The bit-position (which must be a number from 0-
7) follows the carat symbol ('^') and specifies the bit position to access. For
example:
sbit OV = 0xD0^2;
sbit CY = 0xD0^7;
sbit EA = 0xA8^7;
Variant 3
36
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
sbit OV = 0xD2;
sbit CY = 0xD7;
sbit EA = 0xAF;
Note
▪ Not all SFRs are bit-addressable. Only those SFRs whose address is
evenly divisible by 8 are bit-addressable. The lower nibble of the SFR's
address must be 0 or 8.
▪ The sbit data type declaration may be used to access individual bits of
variables declared with the bdata memory type specifier.
Bit-Addressable Objects
The Cx51 Compiler places variables declared with the bdata memory type into
the bit-addressable area. Furthermore, variables declared with
the bdata memory type must be global (declared outside the scope of a
function). It may declare these variables as shown below:
The variables ibase and bary are bit-addressable. Therefore, the individual bits
of these variables may be directly accessed and modified. Use the sbit keyword
to declare new variables that access the bits of bdatavariables. For example:
37
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
The above example represents declarations, not assignments to the bits of the
ibase and bary bdata variables. The expression following the carat symbol ('^')
in the example specifies the position of the bit to access with this declaration.
This expression must be a constant value.
The range depends on the type of the base variable included in the declaration.
The range is:
▪ 0-7 for char and unsigned char, 0-15 for int, unsigned int,
Note
▪ The C51 Compiler assumes that objects accessed using sbit declarations
are stored in little endian byte order. This is the case for sfr16 types.
However, standard C types like int and long are stored in big endian.
We may provide external variable declarations for the sbit type to access these
types in other modules. For example:
Declarations involving the sbit type require that the base object be declared
with the memory type bdata. The only exceptions are the variants for special
function bits. The following example shows how to change the ibase and bary
bits using the above declarations.
38
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
The bdata memory type is handled like the data memory type except that
variables declared with bdata reside in the bit-addressable portion of the
internal data memory. Note that the total size of this area of memory may not
exceed 16 bytes.
In addition to declaring sbit variables for scalar types, it may also declare sbit
variables for structures and unions. For example:
union lft
float mf;
long ml;
};
char m1;
union lft u;
} tcp;
Note: We may not specify bit variables for the bit positions of a float.
However, you may include the float and along in a union and then declare bit
variables to access the bits in the long type.
39
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
▪ The sbit data type uses the specified variable as a base address and adds
the bit position to obtain a physical bit address. Physical bit addresses
are not equivalent to logical bit positions for certain data types.
▪ Physical bit position 0 refers to bit position 0 of the first byte. Physical
bit position 8 refers to bit position 0 of the second byte. Because int
variables are stored high-byte first, bit 0 of the integer is located in bit
position 0 of the second byte. This is physical bit position 8 when
accessed using a sbit data type.
bit
Where
The bit type may be used for variable declarations, argument lists, and
function-return values. A bit variable is declared like other C data types. For
example:
bit flag2)
40
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
All bit variables are stored in a bit segment located in the internal memory area
of the 8051. Because this area is only 16 bytes long, a maximum of 128 bit
variables may be declared within any one scope.
Functions that disable interrupts (#pragma disable) and functions that are
declared using an explicit register bank (using n) cannot return a bit value. The
Cx51 Compiler generates an error message for functions of this type that
attempt to return a bit type.
Standard C Types
The C Language supports a number of standard data types you may use to define
program variables.
▪ void
▪ char
▪ int
▪ enum
▪ float
▪ double
41
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
void: The void data type defines functions with no return value, functions with
no argument list, and pointers to objects of an undefined type. It is used as
follows:
void *name;
Where
char
The char data type defines a 1-byte binary integer. It is used as follows:
Where
int
enum
The enum keyword defines set of constants of type char or type int depending
on the range of values of the set. It is used as follows:
42
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Where
value is the value to assign to the constant. If the value is missing, then it is
assumed to be the value of the previous constant in the set + 1. The
default value for the first constant in the list is 0.
float
Where
double
Where
43
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Preprocessor
The preprocessor built into the Cx51 Compiler processes the source text of a
source file before it is actually compiled into machine language and object code.
Preprocessing is the first action the compiler performs. The preprocessor's
purpose is to replace or insert additional text into the source file just prior to
compilation.
▪ Header Files,
▪ Macros,
▪ Conditional Compilation.
Header Files
Header files or include files are included and processed by the preprocessor.
They provide us with a convenient way to publish global variables, function
prototypes, manifest constants, and macro definitions that are used throughout
a large development effort.
The #include directive specifies the name of the header file to include.
The #include directive tells the C preprocessor to include the contents of the
file specified in the input stream to the compiler and then continue with the
rest of the original file.
44
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
#include "file.h"
while (1)
printf (func());
while (1)
printf (func());
Header files typically contain variable and function declarations along with
macro definitions. But, they are not limited to only those. A header file may
contain any valid C program fragment.
Macros
Perhaps the most useful aspect of the C preprocessor is the ability to create
and use macros. Macros enable you to assign short names to source code blocks.
45
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
o When we use the macro name in your source file, the preprocessor
replaces it with the source code block specified in the macro definition.
A macro definition includes the name of the macro, the macro body, and may
include macro arguments.
▪ Complex Macros accept one or more arguments and may be used like
functions.
▪ Macro Operators lists special macro operators that may be used in macro
definitions.
Conditional Compilation
46
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Preprocessor Directives:
#pragma PRINT
#include <stdio.h>
#define DEBUG 1
# define myfavnum 45
# include <stdio.h>
The entire proprocessor directive must be contained in a single source line. Line
continuations, backslash ('\') followed by a new-line character, may be used in
preprocessor directives since these are removed by the preprocessor. For
example:
#define mycode \
{ \
47
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
mydef
The following table lists the preprocessor directives and gives a brief
description of each.
Directive Description
#ifndef Same as #ifdef but the evaluation succeeds if the definition is not
defined.
#include Reads source text from an external file. The notation sequence
determines the search sequence of the included files. The compiler
searches for include files specified with less-than/greater-than
symbols ('<', '>') in the include file directory. Include files specified
with double-quotes (" ") are searched for in the current directory.
48
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Library Reference
The C51 run-time library provides more than 100 predefined functions and
macros you may use in your 8051 C programs. The library makes embedded
software development easier by providing routines that perform common
programming tasks such as string and buffer manipulation, data conversion, and
floating-point math operations.
The C51 library includes six different compile-time libraries which are
optimized for various functional requirements. These libraries support most of
the ANSI C function calls.
49
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Programs written for the Philips 80C51MX, Dallas 390 Contiguous Mode, and
Variable Code Banking require a different set of C51 run-time libraries. The
linker automatically includes the correct library.
The following library sets are available in the PK51 Professional Developer's Kit.
Each library set consists of six different library files as explained in the
previous table.
C51B*.LIB Library set for Classic 8051 with far memory type (variable
banking) support.
C51M*.LIB Library set for Mentor M8051EW on-chip program code banking
(IBANKING).
C51N*.LIB Library set for Mentor M8051EW with far memory type support
and on-chip program code banking (IBANKING).
50
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
CH51*.LIB Library set for Philips 80C51MX without ECRM mode and
ROM(HUGE).
CS51*.LIB Library set for Philips 80C51MX with ECRM mode and
ROM(HUGE).
Source code is provided (in the \KEIL\C51\LIB folder) for the following library
routines:
51
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
What is an RTOS?
Every RTOS has a kernel. On the other hand, an RTOS can be a combination of
various modules, including the kernel, a file system, networking protocol stacks,
and other components required for a particular application, as illustrated at a
high level in Figure 2.1.
• Schedulable entities,
• multitasking,
• context switching,
• dispatcher, and
• scheduling algorithms
53
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Each task has its own context, which is the state of the CPU registers required
each time it is scheduled to run.
1) A context switch occurs when the scheduler switches from one task to
another.
2) Every time a new task is created, the kernel also creates and maintains an
associated task control block (TCB).
3) TCBs contain everything a kernel needs to know about a particular task.
When a task is running, its context is highly dynamic.
4) This dynamic context is maintained in the TCB. When the task is not
running, its context is frozen within the TCB, to be restored the next
time the task runs.
As shown in Figure 2.3, when the kernel s scheduler determines that it needs to
stop running task 1 and start running task 2, it takes the following steps:
2 It loads task 2 s context information from its TCB, which becomes the
current thread of execution.
54
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
3. The context of task 1 is frozen while task 2 executes, but if the scheduler
needs to run task 1 again, task 1 continues from where it left off just before
the context switch.
The Dispatcher
The dispatcher is the part of the scheduler that performs context switching
and changes the flow of execution.
At any time an RTOS is running, the flow of execution, also known as flow of
control, is passing through one of three areas: through an application task,
through an ISR, or through the kernel.
• When a task or ISR makes a system call, the flow of control passes to
the kernel to execute one of the system routines provided by the kernel.
• When it is time to leave the kernel, the dispatcher is responsible for
passing control to one of the tasks in the user s application.
Scheduling Algorithms
• Round-robin scheduling.
As shown in Figure 2.4 with this type of scheduling, the task that gets to run at
any point is the task with the highest priority among all other tasks ready to run
in the system.
55
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
Real-time kernels generally support 256 priority levels, in which 0 is the highest
and 255 the lowest. Some kernels appoint the priorities in reverse order, where
255 is the highest and 0 the lowest.
Round-Robin Scheduling
Round-robin scheduling provides each task an equal share of the CPU execution
time. Pure round-robin scheduling cannot satisfy real-time system requirements
because in real-time systems, tasks perform work of varying degrees of
importance.
56
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
With time slicing, each task executes for a defined interval, or time slice, in an
ongoing cycle, which is the round robin.
1) A run-time counter tracks the time slice for each task, incrementing on
every clock tick. When one task s time slice completes, the counter is
cleared, and the task is placed at the end of the cycle.
2) Newly added tasks of the same priority are placed at the end of the
cycle, with their run-time counters initialized to 0.
3) If a task in a round-robin cycle is preempted by a higher-priority task, its
run-time count is saved and then restored when the interrupted task is
again eligible for execution.
4) This idea is illustrated in Figure 2.5, in which task 1 is preempted by a
higher-priority task 4 but resumes where it left off when task 4
completes.
•reliability,
•predictability,
•performance,
•compactness, and
•scalability.
57
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
POSIX: POSIX stands for Portable Operating System Interface. “X” has been
suffixed to the abbreviation to make it sound Unix-like. Over the last decade,
POSIX has become an important standard in the operating systems area
including real-time operating systems. The importance of POSIX can be gauzed
from the fact that nowadays it has become uncommon to come across a
commercial operating system that is not POSIX-compliant. POSIX started as an
open software initiative.
VxWorks
• VxWorks design is hierarchical and well suited for hard real time
applications. It supports kernel mode execution of tasks for fast
execution of application codes.
58
January 9,
EMBEDDED C BY NAGAVELLY SREEDHAR
2018
59