Embedded C Programming is the soul of the
processor functioning inside each and every embedded system we come across in
our daily life, such as mobile phone, washing machine, and digital camera.
Each processor is associated with an embedded software. The first and foremost thing
is the embedded software that decides functioning of the embedded system. Embedded C language is most frequently used to program the microcontroller.
Embedded C Programming
Earlier, many embedded applications were developed using assembly level
programming. However, they did not provide portability. This disadvantage was
overcome by the advent of various high level languages like C, Pascal, and COBOL.
However, it was the C language that got extensive acceptance for embedded systems,
and it continues to do so. The C code written is more reliable, scalable, and portable;
and in fact, much easier to understand.
About C LanguageC language was developed by Dennis Ritchie in 1969. It is a collection of one or more
functions, and every function is a collection of statements performing a specific task.
C language is a middle-level language as it supports high-level applications and low-
level applications. Before going into the details of embedded C programming, we should
know about RAM memory organization.
Salient features of the language C language is a software designed with different keywords, data types,
variables, constants, etc. Embedded C is a generic term given to a programming language written in
C, which is associated with a particular hardware architecture.
Embedded C is an extension to the C language with some additional header files. These header files may change from controller to controller.
The microcontroller 8051 #include<reg51.h> is used.The embedded system designers must know about the hardware architecture to write
programs. These programs play prominent role in monitoring and controlling external
devices. They also directly operate and use the internal architecture of the
microcontroller, such as interrupt handling, timers, serial communication and other
available features.
Differences between C and Embedded C
Differences between C and Embedded CThe basic additional features of the embedded softwareData typesThe data type refers to an extensive system for declaring variables of different types like
integer, character, float, etc. The embedded C software uses four data types that are
used to store data in the memory.
The ‘char’ is used to store any single character; ‘int’ is used to store integer value, and
‘float’ is used to store any precision floating point value.
The size and range of different data types on a 32-bit machine is given in the following
table. The size and range may vary on machines with different word sizes.
Data types
KeywordsThere are certain words that are reserved for doing specific tasks. These words are
known as keywords. They are standard and predefined in the Embedded C.
Keywords are always written in lowercase. These keywords must be defined before
writing the main program. The basic keywords of an embedded software are given
below:
Keywordssbit: This data type is used in case of accessing a single bit of SFR register. Syntax: sbit variable name = SFR bit ; Ex: sbit a=P2^1; Explanation: If we assign p2.1 as ‘a’ variable, then we can use ‘a’ instead
of p2.1 anywhere in the program, which reduces the complexity of the program.
Bit: This data type is used for accessing the bit addressable memory of RAM (20h-2fh). Syntax: bit variable name; Ex: bit c; Explanation: It is a bit sequence setting in a small data area that is used
by a program to remember something.SFR: This data type is used for accessing a SFR register by another name. All the SFR
registers must be declared with capital letters. Syntax: SFR variable name = SFR address of SFR register; Ex: SFR port0=0x80;
Explanation: If we assign 0x80 as ‘port0’, then we can use 0x80 instead of port0 anywhere in the program, which reduces the complexity of the program.
SFR Register: The SFR stands for ‘Special Function Register’. Microcontroller 8051
has 256 bytes of RAM memory. This RAM is divided into two parts: the first part of 128
bytes is used for data storage, and the other of 128 bytes is used for SFR registers. All
peripheral devices like I/O ports, timers and counters are stored in the SFR register, and
each element has a unique address.The Structure of an Embedded C Program comments preprocessor directives global variables main() function{
local variables statements ………….. …………..}
fun(1){
local variables statements ………….. …………..}
Comments: In embedded C programming language, we can place comments in our
code which helps the reader to understand the code easily.
C=a+b; /* add two variables whose value is stored in another variable C*/Preprocessor directives: All the functions of the embedded C software are
included in the preprocessor library like “#includes<reg51.h>, #defines”. These
functions are executed at the time of running the program.
Global variable
A global variable is a variable that is declared before the main function, and can be
accessed on any function in the program.
Global variable
Local variableA local variable is a variable declared within a function, and it is valid only to be used
within that function.
Local variable
Main () functionThe execution of a program starts with the main function. Every program uses only one
main () function.
Advantages of embedded C program Its takes less time to develop application program. It reduces complexity of the program. It is easy to verify and understand. It is portable in nature from one controller to another.Examples of a few Embedded C ProgramsThe following are a few simple Embedded C programs used for microcontroller-based projects.
Example-1
Example-2
Example-3
Example-4
PROGRAMMING TIMERS 0 AND 1 IN 8051 C
SECTION 9.3: PROGRAMMING TIMERS 0 AND 1 IN 8051 C
In Chapter 7 we showed some examples of C programming for the 8051. In this section we
study C programming for the 8051 timers. As we saw in the examples in Chapter 7, the general-
purpose registers of the 8051, such as RO – R7, A, and B, are under the control of the C
compiler and are not accessed directly by C statements. In the case of SFRs, the entire RAM
space of 80 – FFH is accessible directly using 8051 C statements. As an example of accessing
the SFRs directly, we saw how to access ports PO – P3 in Chapter 7. Next, we discuss how to
access the 8051 timers directly using C statements.
Accessing timer registers in C
In 8051 C we can access the timer registers TH, TL, and TMOD directly using the reg51 .h header file. This is shown in Example 9-20. Example 9-20 also shows how to access the TR and TF bits.
Example 9-20
Write a 8051 C program to toggle all the bits of port P1 continuously with some delay in
between. Use Timer 0, 16-bit mode to generate the delay.
Solution:
Calculating delay length using timers
As we mentioned in Chapter 7, the delay length depends on three factors: (a) the crystal
frequency, (b) the number of clocks per machine cycle, and (c) the C compiler. The original
8051 used 1/12 of the crystal oscillator frequency as one machine cycle. In other words, each
machine cycle is equal to 12 clocks periods of the crystal frequency connected to the XI – X2
pins. The time it takes for the 8051 to execute an instruction is one or more machine cycles, as
shown in Appendix A. To speed up the 8051, many recent versions of the 8051 have reduced
the number of clocks per machine cycle from 12 to four, or even one. For example, the
AT89C51/52 uses 12, while the DS5000 uses 4 clocks, and the DS89C4xO uses only one clock
per machine cycle. As we mentioned earlier in this chapter, the 8051 timers also use the crystal
frequency as the clock source. The frequency for the timer is always l/12th the frequency of the
crystal attached to the 8051, regardless of the 8051 version. In other words, for the
AT89C51/52, DS5000, or DS89C4xO the duration of the time to execute an instruction varies,
but they all use 1/12th of the crystal’s oscillator frequency for the clock source to the timers. This
is done in order to maintain compatibility with the original 8051 since many designers use timers
to create time delay. This is an important point and needs to be emphasized. The C compiler is
a factor in the delay size since various 8051 C compilers generate different hex code sizes. This
explains why the timer delay duration is unknown for Example 9-20 since none of the other
factors mentioned is specified.
Delay duration for the AT89C51/52 and DS89C4xO chips
As we stated before, there is a major difference between the AT89C51 and DS89C4xO chips in
term of the time it takes to execute a single instruction. Although the DS89C4xO executes
instructions 12 times faster than the AT89C51 chip, they both still use Osc/12 clock for their
timers. The faster execution time for the instructions will have an impact on your delay length.
To verify this very important point, compare parts (a) and (b) of Example 9-21 since they have
been tested on these two chips with the same speed and C compiler.
Timers 0 and 1 delay using mode 1 (16-bit non auto-reload)
Examples 9-21 and 9-22 show 8051 C programming of the timers 0 and 1 in mode 1 (16-bit
non-auto reload). Examine them to get familiar with the syntax.
Timers 0 and 1 delay using mode 2 (8-bit auto-reload)
Examples 9-23 through 9-25 shows 8051 C programming of timers 0 and 1 in mode 2 (8-bit
auto-reload). Study these examples to get familiar with the syntax.
Example 9-21
Write an 8051 C program to toggle only bit PI.5 continuously every 50 ms. Use Timer 0, mode 1
(16-bit) to create the delay. Test the program (a) on the AT89C51 and (b) on the DS89C420.
Solution:
Example.9-22
Write an 8051 C program to toggle all bits of P2 continuously every 500 ms. Use Timer 1. mode
1 to create the delay.
Solution:
NOTE THAT 8051 TIMERS USE 1/12 OF XTAL FREQUENCY, REGARDLESS OF MACHINE CYCLE TIME.
Example 9-23
Write an 8051 C program to toggle only pin PI.5 continuously every 250 ms. Use Timer 0, mode
2 (8-bit auto-reload) to create the delay.
Solution:
256-23 = 233
23 x 1.085 us = 25 us
25 us x 250 x 40 = 250 ms by calculation.
However, the scope output does not give us this result. This is due to overhead of the for loop in
C. To correct this problem, we put 36 instead of 40.
Example 9-24
Write an 8051 C program to create a frequency of 2500 Hz on pin P2.7. Use Timer 1. mode 2 to
create the delay.
Solution:
Example 9-25
A switch is connected to pin PI.2. Write an 8051 C program to monitor SW and create
the following frequencies on pin PI.7:
SW=0: 500 Hz
SW=1: 750 Hz
Use Timer 0, mode 1 for both of them.
Solution:
C Programming of timers 0 and 1 as counters
In Section 9.2 we showed how to use timers 0 and 1 as event counters. A timer can be used as
a counter if we provide pulses from outside the chip instead of using the frequency of the crystal
oscillator as the clock source. By feeding pulses to the TO (P3.4) and Tl (P3.5) pins, we turn
Timer 0 and Timer 1 into counter 0 and counter 1, respectively. Study the next few examples to
see how timers 0 and 1 are programmed as counters using the C language.
Example 9-26
Assume that a 1-Hz external clock is being fed into pin Tl (P3.5). Write a C program for counter
1 in mode 2 (8-bit auto reload) to count up and display the state of the TL1 count on PI. Start the
count at OH.
Solution:
Example 9-27
Assume that a 1-Hz external clock is being fed into pin TO (P3.4). Write a C program for counter
0 in mode -1 (16-bit) to count the pulses and display the THO and TLO registers on P2 and PI,
respectively.
Solution:
Example 9-28
Assume that a 2-Hz external clock is being fed into pin Tl (P3.5). Write a C program for counter
0 in mode 2 (8-bit auto reload) to display the count in ASCII. The 8-bit binary count must be
converted to ASCII. Display the ASCII digits (in binary) on PO, PI, and P2 where PO has the
least significant digit. Set the initial value of THO to 0.
Solution:
To display the TL1 count we must convert 8-bit binary data to ASCII. See Chapter 7 for data
conversion. The ASCII values will be shown in binary. For example, ’9′ will show as 00111001
on ports.
Example 9-29
Assume that a 60-Hz external clock is being fed into pin TO (P3.4). Write a C program for
counter 0 in mode 2 (8-bit auto-reload) to display the seconds and minutes on PI and P2,
respectively.
Solution:
For Examples of Timer 2, see the www.MicroDigitalEd.com Web site.
SUMMARY
The 8051 has two timers/counters. When used as timers they can generate time delays. When used as
counters they can serve as event counters. This chapter showed how to program the timers/counters
for various modes.
The two timers are accessed as two 8-bit registers: TLO and THO for Timer 0, and TL1 and TH1 for Timer
1. Both timers use the TMOD register to set timer operation modes. The lower 4 bits of TMOD are used
for Timer 0 and the upper 4 bits are used for Timer 1.
There are different modes that can be used for each timer. Mode 0 sets the timer as a 13-bit timer,
mode 1 sets it as a 16-bit timer, and mode 2 sets it as an 8-bit timer.
Basics of Embedded C ProgramNow that we have seen a little bit about Embedded Systems and Programming Languages, we will dive in to the basics of Embedded C Program. We will start with two of the basic features of the Embedded C Program: Keywords and Datatypes.
Keywords in Embedded C
A Keyword is a special word with a special meaning to the compiler (a C Compiler for example, is a software that is used to convert program written in C to Machine Code). For example, if we take the Keil’s Cx51 Compiler (a popular C Compiler for 8051 based Microcontrollers) the following are some of the keywords:
bit sbit sfr small large
These are few of the many keywords associated with the Cx51 C Compiler along with the standard C Keywords.
Data Types in Embedded C
Data Types in C Programming Language (or any programming language for that matter) help us declaring variables in the program. There are many data types in C
Programming Language like signed int, unsigned int, signed char, unsigned char, float, double, etc. In addition to these there few more data types in Embedded C.
The following are the extra data types in Embedded C associated with the Keil’s Cx51 Compiler.
bit sbit sfr sfr16
The following table shows some of the data types in Cx51 Compiler along with their ranges.
Data Type Bits (Bytes) Range
bit 1 0 or 1 (bit addressable part of RAM)
signed int 16 (2) -32768 to +32767
unsigned int 16 (2) 0 to 65535
signed char 8 (1) -128 to +127
unsigned 8 (1) 0 to 255
float 32 (4) ±1.175494E-38 to ±3.402823E+38
double 32 (4) ±1.175494E-38 to ±3.402823E+38
sbit 1 0 or 1 (bit addressable part of RAM)
sfr 8 (1) RAM Addresses (80h to FFh)
sfr16 16 (2) 0 to 65535
Basic Structure of an Embedded C Program (Template for Embedded C Program)The next thing to understand in the Basics of Embedded C Program is the basic structure or Template of Embedded C Program. This will help us in understanding how an Embedded C Program is written.
The following part shows the basic structure of an Embedded C Program.
Multiline Comments . . . . . Denoted using /*……*/
Single Line Comments . . . . . Denoted using //
Preprocessor Directives . . . . . #include<…> or #define
Global Variables . . . . . Accessible anywhere in the program
Function Declarations . . . . . Declaring Function
Main Function . . . . . Main Function, execution begins here{Local Variables . . . . . Variables confined to main functionFunction Calls . . . . . Calling other FunctionsInfinite Loop . . . . . Like while(1) or for(;;)Statements . . . . .….….}
Function Definitions . . . . . Defining the Functions{Local Variables . . . . . Local Variables confined to this FunctionStatements . . . . .
….….}
Before seeing an example with respect to 8051 Microcontroller, we will first see the different components in the above structure.
Different Components of an Embedded C Program
Comments: Comments are readable text that are written to help us (the reader) understand the code easily. They are ignored by the compiler and do not take up any memory in the final code (after compilation).
There are two ways you can write comments: one is the single line comments denoted by // and the other is multiline comments denoted by /*….*/.
Preprocessor Directive: A Preprocessor Directive in Embedded C is an indication to the compiler that it must look in to this file for symbols that are not defined in the program.
In C Programming Language (also in Embedded C), Preprocessor Directives are usually represented using #include… or #define….
In Embedded C Programming, we usually use the preprocessor directive to indicate a header file specific to the microcontroller, which contains all the SFRs and the bits in those SFRs.
In case of 8051, Keil Compiler has the file “reg51.h”, which must be written at the beginning of every Embedded C Program.
Global Variables: Global Variables, as the name suggests, are Global to the program i.e. they can be accessed anywhere in the program.
Local Variables: Local Variables, in contrast to Global Variables, are confined to their respective function.
Main Function: Every C or Embedded C Program has one main function, from where the execution of the program begins.
Related Post: “EMBEDDED SYSTEM PROJECT IDEAS“.
Basic Embedded C ProgramTill now, we have seen a few Basics of Embedded C Program like difference between C and Embedded C, basic structure or template of an Embedded C Program and different components of the Embedded C Program.
Continuing further, we will explore in to basics of Embedded C Program with the help of an example. In this example, we will use an 8051 Microcontroller to blink LEDs connected to PORT1 of the microcontroller.
Example of Embedded C Program
The following image shows the circuit diagram for the example circuit. It contains an 8051 based Microcontroller (AT89S52) along with its basic components (like RESET Circuit, Oscillator Circuit, etc.) and components for blinking LEDs (LEDs and Resistors).
In order to write the Embedded C Program for the above circuit, we will use the Keil C Compiler. This compiler is a part of the Keil µVision IDE. The program is shown below.
#include<reg51.h> // Preprocessor Directivevoid delay (int); // Delay Function Declaration
void main(void) // Main Function{P1 = 0x00; /* Making PORT1 pins LOW. All the LEDs are OFF.(P1 is PORT1, as defined in reg51.h) */
while(1) // infinite loop{P1 = 0xFF; // Making PORT1 Pins HIGH i.e. LEDs are ON.delay(1000); /* Calling Delay function with Function parameter as 1000.This will cause a delay of 1000mS i.e. 1 second */
P1 = 0x00; // Making PORT1 Pins LOW i.e. LEDs are OFF.delay(1000);}}
void delay (int d) // Delay Function Definition{unsigned int i=0; // Local Variable. Accessible only in this function. /* This following step is responsible for causing delay of 1000mS (or as per the value entered while calling the delay function) */
for(;d>0;d–){for(i=250;i>0;i – -);for(i=248;i>0;i – -);}}
Delay using 8051 timer.
The 8051 microcontroller has two independent 16 bit up counting timers named Timer 0 and Timer 1 and this article is about generating time delays using the 8051 timers. Generating delay using pure software loops have been already discussed here but such delays are poor in accuracy and cannot be used in sensitive applications. Delay using timer is the most accurate and surely the best method.
A timer can be generalized as a multi-bit counter which increments/decrements itself on receiving a clock signal and produces an interrupt signal up on roll over. When the counter is running on the processor’s clock , it is called a “Timer”, which counts a predefined number of processor clock pulses and generates a programmable delay.
When the counter is running on an external clock source (may be a periodic or aperiodic external signal) it is called a “Counter” itself and it can be used for counting external events.
In 8051, the oscillator output is divided by 12 using a divide by 12 network and then fed to the Timer as the clock signal. That means for an 8051 running at 12MHz, the timer clock input will be 1MHz. That means the the timer advances once in every 1uS and the maximum time delay possible using a single 8051 timer is ( 2^16) x (1µS) = 65536µS. Delays longer than this can be implemented by writing up a basic delay program using timer and then looping it for a required number of time. We will see all these in detail in next sections of this article.
Designing a delay program using 8051 timers.
While designing delay programs in 8051, calculating the initial value that has to be loaded inot TH and TL registers forms a very important thing. Let us see how it is done.
Assume the processor is clocked by a 12MHz crystal.
That means, the timer clock input will be 12MHz/12 = 1MHz
That means, the time taken for the timer to make one increment = 1/1MHz = 1uS
For a time delay of “X” uS the timer has to make “X” increments.
2^16 = 65536 is the maximim number of counts possible for a 16 bit timer.
Let TH be the value value that has to be loaded to TH registed and TL be the value that has to
be loaded to TL register.
Then, THTL = Hexadecimal equivalent of (65536-X) where (65536-X) is considered in decimal.
Example.
Let the required delay be 1000uS (ie; 1mS).
That means X = 1000
65536 – X = 65536 – 1000 = 64536.
64536 is considered in decimal and converting it t0 hexadecimal gives FC18
That means THTL = FC18
Therefore TH=FC and TL=18
Program for generating 1mS delay using 8051 timer.
The program shown below can be used for generating 1mS delay and it is written as a subroutine so that you can call it anywhere in the program. Also you can put this in a loop for creating longer time delays (multiples of 1mS). Here Timer 0 of 8051 is used and it is operating in MODE1 (16 bit timer).
DELAY: MOV TMOD,#00000001B // Sets Timer 0 to MODE1 (16 bit timer). Timer 1 is
not used
MOV TH0,#0FCH // Loads TH0 register with FCH
MOV TL0,#018H // LOads TL0 register with 18H
SETB TR0 // Starts the Timer 0
HERE: JNB TF0,HERE // Loops here until TF0 is set (ie;until roll over)
CLR TR0 // Stops Timer 0
CLR TF0 // Clears TF0 flag
RET
The above delay routine can be looped twice in order to get a 2mS delay and it is shown in the program below.
MAIN: MOV R6,#2D
LOOP: ACALL DELAY
DJNZ R6,LOOP
SJMP MAIN
DELAY: MOV TMOD,#00000001B
MOV TH0,#0FCH
MOV TL0,#018H
SETB TR0
HERE: JNB TF0,HERE
CLR TR0
CLR TF0
RET
Few points to remember while using timers.
Once timer flag (TF) is set, the programmer must clear it before it can be set again.
The timer does not stop after the timer flag is set. The programmer must clear the TR bit in
order to stop the timer.
Once the timer overflows, the programmer must reload the initial start values to the TH and TL
registers to begin counting up from.
We can configure the desired timer to create an interrupt when the TF flag is set.
If interrupt is not used, then we have to check the timer flag (TF) is set using some conditional
branching instruction.
Maximum delay possible using a single 8051 timer is 65536µS and minimum is 1µS provided
that you are using a 12MHz crystal for clocking the microcontroller.
Square wave generation using 8051 timer.
Square waves of any frequency (limited by the controller specifications) can be generated using the 8051 timer. The technique is very simple. Write up a delay subroutine with delay equal to half the time period of the square wave. Make any port pin high and call the delay subroutine. After the delay subroutine is finished, make the corresponding port pin low and call the delay subroutine gain. After the subroutine is finished , repeat the cycle again. The result will be a square wave of the desired frequency at the selected port pin. The circuit diagram is shown below and it can be used for any square wave, but the program has to be accordingly. Programs for different square waves are shown below the circuit diagram.
Square wave generation using 8051 timer
1KHz Square wave using 8051 timer.
MOV P1,#00000000B
MOV TMOD,#00000001B
MAIN: SETB P1.0
ACALL DELAY
CLR P1.0
ACALL DELAY
SJMP MAIN
DELAY: MOV TH0,#0FEH
MOV TL0,#00CH
SETB TR0
HERE: JNB TF0,HERE
CLR TR0
CLR TF0
SETB P1.0
RET
END
2 KHz Square wave using 8051 timer.
MOV P1,#00000000B
MOV TMOD,#00000001B
MAIN: SETB P1.0
ACALL DELAY
CLR P1.0
ACALL DELAY
SJMP MAIN
DELAY: MOV TH0,#0FCH
MOV TL0,#018H
SETB TR0
HERE:JNB TF0,HERE
CLR TR0
CLR TF0
SETB P1.0
RET
END
10 KHz square wave using 8051 timer.
MOV P1,#00000000B
MOV TMOD,#00000001B
MAIN: SETB P1.0
ACALL DELAY
CLR P1.0
ACALL DELAY
SJMP MAIN
DELAY: MOV TH0,#0FFH
MOV TL0,#0CEH
SETB TR0
HERE:JNB TF0,HERE
CLR TR0
CLR TF0
SETB P1.0
RET
END