This lecture note covers the embedded 'c' programming constructs based on 8051 microcontroller. Although the same concepts can be used for other advanced microcontrollers with some modifications.
Embedded C programming based on 8051 microcontroller
1. EMBEDDED ‘C’
Gaurav Verma
Assistant Professor
Department of Electronics and Communication Engineering
Jaypee Institute of Information and Technology
Sector-62, Noida, Uttar Pradesh, India
1 Copyright to gaurav verma
2. What is Embedded ‘C’
Whenever the conventional ‘C’ language and
its extensions are used for programming
embedded systems, it is referred to as
“Embedded C” programming.
3. Advantages
Š It is a ‘mid-level’, with ‘high-level’ features (such as support for
functions and modules), and ‘low-level’ features (such as good access
to hardware via pointers).
Š C is the most common Embedded language 85%, of embedded
applications are coded in C.
Š C , when used correctly is as safe and robust as any other high level
language.
ŠŠ It directly manipulates the hardware and memory addresses.
Š It is very efficient, It is popular and well understood.
Š Good, well proven compilers are available for every embedded
processor(8-bit to 32-bit)
Š Cx51 Cross compiler supports all of the ANSI Standard C directives.
4. ‘C’ Versus Embedded ‘C’
y ‘C’ is a well structured, well
defined and standardized
y Embedded ‘C’ can be
id d b f
generalpurpose programming
language.
l f f
considered as a subset of
conventional ‘C’ language.
y A software program called
yA platform specific
application , known as ,
compiler is used for the
f
p g
‘Cross compiler’ is used for
the conversion of programs
conversion of programs ritten in Embedded ‘C’ to
written in C to the target
processor specific binary
written C target processor/controller
specific instructions(machine
files. language).
5. ‘p Compiler’ Versus ‘Cross p
Compiler’
y C il i ft t lth t t d itt
Compiler is a software tool that converts a source code written
in a high level language on top of a particular operating system
running g on a specific target processor architecture.
y Cross compilers are software tools used in cross platform
development applications. (In cross platform development , the
compiler running on a particular target processor/OS converts
the source code to machine code for a target processor whose
architecture and instruction set is different from the current
development environment OS).
6. y
Keywords
¾These are the reserved names used by the ‘C ’ language
language.
¾All keywords should be written in ‘lowercase’ letters.
Examples- int, char, double, float, void, while, for,
long etc.ANSI ‘C’ supports 32 such keywords.
7. Identifiers
¾ Identifiers are user defined names and labels.
¾ Can contain letters of English alphabet(both upper and lower
case) and numbers.
¾¾Note: The starting character of an identifier should be a letter
and the only special character allowed in identifier is
underscore(_).
8. g
Storage Classes
y Keywords related to storage class provide information on the
scope i.e. visibility or accessibility and lifetime i.e. existence
of a variable.
yy ‘C’ supports four types of storage classes .
9. Storage Class Meaning Comments
auto Variables declared inside
a function
Scope and accessibility is
function. restricted within the
Default storage class is
auto.
function where the
variable is declared.
No initialization.
register Variables stored in the
CPU register of
Same as auto in scope
and access
processor.
Reduces access time of
variable.
access.
The decision on whether
a variable needs to be
kept in CPU register of
the processor depends
on the compiler.
static Local variable with i h l
lifetime same as that of
the program.
Retains the value
throughout the program.
By default initializes to
zero on variable
creation.
10. Cont…
Storage Class Meaning Comments
static Accessibility
depends on where
the variable is
declared.
extern Variables Can be modified
accessible to all
functions in a file
by any function
within a file or
and all files in a
multiple file
program.
across multiple
files.
p g
11. Data Types
Data types Bits Bytes Value range
y Bit 1 0 to 1
y Signed char 8 1 -128 to +127
y Unsigned char 8 1 0 to 255
y enum 816 12 -128 to +127 or -32768 to +32767
y Signed short 16 2 -32768 to +32767
y Unsigned short 16 2 0 to 65535
y Signed int 16 2 -32768 to +32767
y Unsigned int 16 2 0 to 65535
y Signed long 32 4 -2147483648 to 2147483647
yy Unsigned long 32 4 0 to 4294967295
y Float 32 4 ±1.175494E-38 to ±3.402823E+38
y sbit 1 0 to 1
y sfr 8 1 0 to 255
Note: The storage size may vary for data type depending on the cross compiler in
use for embedded applications.
12. Unsigned Char
y 8 bits range :0 to 255
y Character data type is preferred data type for many
applications, like setting a counter value and ASCII
characters instead of signed char.
y It is important to specify the keyword unsigned in front of
the char else compiler will use the signed char by default.
y As 8051 has a limited number of registers and data RAM
locations, using the int in place of char can lead to a larger
size hex file.
13. yy Example : Write an 8051 C program to send hex values for
ASCII characters of 0, 1, 2, 3, 4, 5, A, B, C, and D to port P0.
y #include <reg51.h>
void main( )
{
unsigned char num[ ]=“012345ABCD”;
unsigned char z;
for (z = 0 ; z <= 10; z++)
P0=num[z];
}
14. y Write an 8051 C program to toggle all the bits of P1
continuously.
#include <reg51.h>
void main(void)
{
for (;;) ;repeat forever
{
P1=0x55;
P1=0xAA;
}
}
15. Signed Char
y 8 bit data type MSB D7 to represent or +value Range 128
8-– value. –to +127.
y Use unsigned char unless the data needs to be represented as
signed numbers.
16. y Write an 8051 C program to send temperature range of
–4 to +4 to port P1.
#include <reg51.h>
void main(void)
{
char mynum[]={0,+1,-1,+2,-2,+3,-3,+4,-4};
unsigned char z;
for (z = 0; z<=8 ; z++)
P1=mynum[z];
}
Note: The negative values will be displayed in the 2’s complement
form as 1, FFH, 2, FEH, 3, FDH, 4, FCH.
17. Unsigned int:
y 16-bit data type range : 0 to 65535 (0000 – FFFFH).
yy Used to define 16-bit variables such as memory addresses, set counter
values of more than 256.
y Registers and memory accesses are in 8-bit chunks, the misuse of int
variables will result in a larger hex file.
y 8051 programming, unsigned char will do the job of unsigned int .
NOTE : C compiler uses signed int as default if unsigned keyword is
not used.
Signed int:
y Signed int is a 16-bit data type that uses the MSB D15 to represent –
or +value. We have 15 bits for the magnitude of the number from –
32768 to +32767
18. sbit (Single bit):
y 8 bit keyword is a 8051 C data types used to access single-bit
addressable register.
y Allows access to the single bits of the SFR registers, that are bit
addressable.
bit and sfr :
y The bit data type allows access to single bits of bit-addressable
memory spaces 20 – 2FH. To access the byte-size SFR registers, we
use the sfr data type.
19. y Example :Write an 8051 C program to toggle bit D0 of the
port P1 (P1.0) 50,000 times.
#include <reg51.h>
sbit MYBIT=P1^0;
void main()
{
unsigned int z;
for (z = 0; z<50000; z++)
{
MYBIT=0;
MYBIT=1;
}
}
20. y Example :Write an 8051 C program to toggle only bit P2.4
continuously without disturbing the rest of the bits of P2.
#include <reg51.h>
sbit mybit = P2^4;
void main(void)
{
while (1)
{
mybit=1; //turn on P2.4
mybit=0; //turn off P2.4
}
}
21. yy Example :Write an 8051 C program to monitor bit P1.5. If it
is high, send 55H to P0; otherwise, send AAH to P2.
#include <reg51.h>
sbit mybit=P1^5;
void main(void)
{
mybit=1 //mybit (P1.5)an input pin
while (1)
{
if (mybit==1)
P0=0x55;
else
P2=0xAA;
}
}
22. y Write an 8051 C program to get the status of bit P1.2, save it,
and send it to P2.5 continuously.
#include <reg51.h>
sbit inbit=P1^2;
sbit outbit=P2^5;
bit mbit; //use bit to declare bit- addressable
void main(void)
{
while (1)
{
mbit=inbit; //get a bit from P1.2
outbit=mbit; //send it to P2.5
}
}
23. Time Delays
y Using the 8051 timer
y Using a simple for loop
y Delays can be observed either on the oscilloscope or using a simulator.
y In creating a time delay using for loop factors need to be considered
1. Number of machine cycles and number of clock periods per machine
cycle.
2. Crystal frequency connected between XTAL1 and XTAL2. Duration
of the clock period for the machine cycle is the function of crystal
frequency.
3. Compiler selected. Accuracy of the time delay is mainly due to the
compiler used .
24. yy In assembly language programming, delay generated can be controlled
by the user, as the number of instructions and the cycles per
instruction are known.
y In case of C program, the C compiler will convert the C statements
and functions to assembly language instructions.
y Different compilers produce different delay.
25. y Write an 8051 C program to toggle bits of P1 continuously
with some delay.
#include <reg51.h>
void main(void)
{
unsigned int x;
for (;;)
{
P1=0x55;
for (x=0;x<20000;x++); //delay size unknown
P1=0xAA;
for (x=0;x<20000;x++);
}
}
26. y Write an 8051 C program to toggle bits of P1 ports continuously with a
250ms.
#include <reg51.h>
void Delay1(unsigned int);
void main(void)
{
while(1) //repeat forever
{
P1=0x55;
Delay1(250);
P1=0xAA;
Delay1(250);
}
}
void Delay1(unsigned int itime)
{
unsigned int i,j;
for (i=0;i<itime;i++)
for (j=0;j<1275;j++);
}
27. y Write an 8051 C program to get a byte of data from P1,wait
½ second, and then send it to P2.
#include <reg51.h>
void Delay(unsigned int);
void main(void)
{
unsigned char mybyte;
P1=0xFF; //make P1 input port
while (1)
{
mybyte=P1; //get a byte from P1
Delay(500);
P2=mybyte; //send it to P2
}
}
28. y Write an 8051 C program to get a byte of data form P0. If it is less than
100, send it to P1; otherwise, send it to P2.
#include <reg51.h>
void main(void)
{
unsigned char mybyte;
P0=0xFF; //make P0 input port
while (1)
{
mybyte=P0; //get a byte from P0
if (mybyte<100)
P1=mybyte; //send it to P1
else
P2=mybyte; //send it to P2
}
}
29. Accessing SFR Addresses 80 – FFH
y Another way to access the SFR RAM space 80H-FFH is to use the sfr
data type. When this data type is used no need of using the header file
reg51.h. Table 4 shows the single bit addresses of ports.
Ports D7 D6 D5 D4 D3 D2 D1 D0
PO 87
(P0.7)
86 85 84 83 82 81 80
(P0.0)
P1 97
(P1.7)
96 95 94 93 92 91 90
(P1.0)
P2 A7
(P2.7)
A6 A5 A4 A3 A2 A1 A0
(P2.0)
P3 B7 B6 B5 B4 B3 B2 B1 B0
(P3.7) (P3.0)
30. Write an 8051 C program to toggle all the bits of P0 and P2 continuously
with a 250 ms delay. Use the sfr keyword to declare the port addresses.
If reg51.h is not included in the program, keyword sfr is used to define the port
address
sfr P0=0x80;
sfr P1=0x90;
sfr P2=0xA0;
void Delay(unsigned int);
void main(void)
{
while (1)
{
P0=0x55;
P2=0x55;
Delay(250);
P0=0xAA;
P2=0xAA;
Delay(250);
}
}
31. Logic Operation in 8051 C
y The most important feature of the C language is its ability to
perform bit manipulation.
y The bitwise operators used in C are
y AND (&),
y OR ( | ),
y EX-OR ( ^ ),
y inverter (~),
y Shift right (>>)
y Shift left (<<)
32.
33. y Example : The following program will explain the different
logical operations that are run on simulator.
#include <reg51.h>
void main(void)
{
P0=0x6E & 0x0F; //ANDing
P1=0x34 | 0x68; //ORing
P2=0x54 ^ 0x88; //XORing
P0=~0x55; //inversing
P1=0xCA >> 3; //shifting right 3
P2=0x77 >> 4; //shifting right 4
P0=0x5 << 4; //shifting left 4
}
34. y Example : Write an 8051 C program to toggle all the bits of
P0 and P2 continuously with a 250 ms delay. Use the
inverting and Ex-OR operators.
#include <reg51.h>
void Delay(unsigned int);
void main(void)
{
P0=0x55;
P2=0x55;
while (1)
{
P0=~P0;
P2=P2^0xFF;
Delay(250);
}
}
35. y Example : Write an 8051 C program to get bit P1.0 and send
it to P2.7 after inverting it.
#include <reg51.h>
sbit inbit=P1^0;
sbit outbit=P2^7;
bit mbit;
void main(void)
{
while (1)
{
mbit=inbit; //get a bit from P1.0
outbit=~mbit; //invert it and send to P2.7
}
}
36. y Example :Write an 8051 C program to read the P1.0 and P1.1 bits and
issue an ASCII character to P0 that is if P1.1 and P1.0 is 00 send ‘0’ if 01
send ‘1’, if 10 send ‘2’ .
yy Make P1 as input port. Read P1.
y Mask all bits except D0 & D1 of P1 ad put the masked value in x.
y If x=0; send ‘0’ to P0, else if x=1; send ‘1’ to P0, else if x=2; send ‘2’ to P0,. (use
Repeat from step 2.
#include <reg51.h>
void main(void)
{
unsigned char z;
z=P1; //read P1
z=z&0x3; //mask the unused bits
38. Data conversion programs in 8051 C
y Packed BCD to ASCII conversion
y ASCII to Packed BCD conversion
yy Binary (hex) to decimal and ASCII conversion
39. Packed BCD to ASCII conversion
Example : Write an 8051 C program to convert packed BCD number 0x45
to ASCII and display on P1 and P2.
Get the packed BCD number. Convert it to unpacked BCD by masking the lower and
upper digit. Add 30H individually and send the ASCII number to P1 and P2
#include <reg51.h>
void main(void)
{
unsigned char x,y,z;
unsigned char mbyte=0x45;
x=mbyte&0x0F;
P1=x|0x30;
y=mbyte&0xF0;
y=y>>4;
P2=y|0x30;
}
40. ASCII to Packed BCD conversion
Example : Write an 8051 C program to convert ASCII digits to packed BCD
and display them on P1.
Get the 1st ASCII number and mask the higher nibble. Shift the number to get the
higher nibble of BCD. Get the 2nd ASCII number and mask the higher nibble. Add the
result to the first number. Display the BCD number on P1.
#include <reg51.h>
void main(void)
{
unsigned char bcdbyte;
unsigned char w=‘4’;
unsigned char z=‘7’;
w=w&0x0F;
w=w<<4;
z=z&0x0F;
bcdbyte=w|z;
P1=bcdbyte;
}
41. Binary (hex) to decimal and ASCII conversion
Example : Write an 8051 C program to convert 11111101(FDH) to decimal and
display the digits on P0, P1 and P2.
Divide the binary number by 10 and save it in x. Modulo divide the binary by 10 & save
the result in units. Divide x by 10 & save it in hundred. Modulo divide x by 10 & save it
in tens Display units, tens & hundreds on P0,P1 & P2.
#include <reg51.h>
void main(void)
{
unsigned char x,binbyte,d1,d2,d3;
binbyte=0xFD;
x=binbyte/10;
d1=binbyte%10;
d2=x%10;
d3=x/10;
P0=d1;
P1=d2;
P2=d3;
}
42. Delay Using Timers
y Example : Write an program to toggle all the bits of port continuously with some delay in between. Use Timer 0, 16-bit mode to
generate the delay.
E l W it 8051 C t t l ll th bit f t P1
#include <reg51.h>
void Delay(void);
void main(void)
{
while (1)
{ //repeat forever
P1=0x55;
Delay(); //toggle all the bits of P1
P1=0xAA; // delay unknown
Delay();
}
}
43. Void Delay()
{
TMOD=0x01; // timer 0 mode 1
TL0=0x00;
TH0=0x35; // load TH and TL
TR0=1; // turn on T0
while (TF0==0); // wait for TF0 to roll over
TR0=0; // turn off T0
TF0=0; // clear TF0
}
44. y Example 11: Write an 8051 C program to toggle only bit P1.5 continuously
every 50ms. Use Timer 0, mode 1 (16-bit) to create the delay. Assume
XTAL=11.0592 MHz=> T=1.085μs Count=50ms/1.085μs =46083
Initial count = 65536-46083 =19453, Count in Hex = 4BFDH
#include <reg51.h>
void Delay(void);
sbit mybit=P1^5;
void main(void)
{
while (1)
{
mybit=~mybit; //toggle P1.5
Delay();
}}
46. Example : A switch is connected to pin P1.2. Write an 8051 C program to
monitor SW and create the following frequencies on pin P1.7:SW=0: 500Hz,
SW=1: 750Hz, use Timer 0, mode 1 Values to be loaded into TH and TL for
500Hz and 750Hz can be calculated as in previous ex.
#include <reg51.h>
sbit mybit=P1^5;
sbit SW=P1^7;
void Delay(unsigned char);
void main(void){
SW=1; // make P1.7 as input
while (1) {
mybit=~mybit; // toggle P1.5
if (SW==0) // check switch
Delay(0);
else
Delay(1);
}}
47. Void Delay(unsigned char c)
{
TMOD=0x01;
if (c==0) {
TL0=0x67;
TH0=0xFC;
}
else
{
TL0=0x9A;
TH0=0xFD;
}
TR0=1;
while (TF0==0);
TR0=0;
TF0=0;
}
48. 8051 C programming of Timers 0 and 1 Delay Using Mode 2
Write an 8051 C program to toggle only pin P1.5 continuously every 250 ms.
For the delay of 250ms the count exceeds 256. hence, count for 25μs is calculated which is
23 .Therefore for 250ms =>25μs × 250 × 40 = 250 ms
#include <reg51.h>
Void Delay(void);
sbit mybit=P1^5;
void main(void){
unsigned char x,y;
while (1) {
mybit=~mybit;
for (x=0;x<250;x+ +)
for (y=0;y<36;y+ +) //we put 36, not 40
Delay();
}}
50. Problem: Write an 8051 C program to create a frequency of
2500 Hz on pin P2.7. UseTimer 1, mode 2 to create delay
Time Period=1/2500Hz = 400μs
Ton=Toff= 400μs /2 = 200μs
Count = 200μs / 1.085μs = 184
Initial count =256-184 = 72
In hex = 48H
51. Counter Programming:
y Timers can also be used as counters, counting events happening real
world. When it is used as a counter, it is an external pulse that
increments theTH,TL register.
y TMOD and TH, TL registers are used as in timer programming except
for the source of the frequency.
y C/T bit inTMOD register
y The C/T bit in the TMOD registers decides the source of the clock for
the timer. When C/T = 1, the timer is used as a counter and gets its
pulses from outside. The counter counts up as pulses are fed from pins
14 and 15, these pins are called T0 (timer 0 input) and T1 (timer 1
input).
52.
53. y Example : Assuming that clock pulses are fed into pin T1, write a
program for p g counter 1 in mode 2 to count the pulses and display the
state of theTL1 count on P2, which connects to 8 LEDs.
MOV TM0D,#01100000B ;counter 1, mode 2,C/T=1
MOV TH1,#0 ;clear TH1
SETB P3.5 ;make T1 input
AGAIN: SETB TR1 ;start the counter
BACK: MOV A,TL1 ;get copy of TL
MOV P2,A ;display it on port 2
JNB TF1,Back ;keep doing, if TF = 0
CLR TR1 ;stop the counter 1
CLR TF1 ;make TF=0
SJMP AGAIN ; keep doing it
Notice in the above program the role of the instruction SETB P3.5.
54. Example : Assume that a 1-Hz external clock is being fed into pin T1 (P3.5).
Write a C program for counter 1 in mode 2 to count up and display the state
of the TL1 count on P1. Start the count at 0H.
#include <reg51.h>
sbit T1=P3^5;
void main(void){
T1=1;
TMOD=0x60;
TH1=0;
while (1) {
do {
TR1=1;
P1=TL1;
} while (TF1==0);
TR1=0;
TF1=0;
}}
55. Example : Assume that a 1-Hz external clock is being fed into pin T0 (P3.4). Write a
C program for counter 0 in mode 1 (16-bit) to count the pulses and display the state
of the TH0 and TL0 registers on P2 and P1,
#include <reg51.h>
void main(void){
T0=1;
TMOD=0x05;
TL0=0
TH0=0;
while (1) {
do {
TR0=1;
P1=TL0;
P2=TH0;
} while (TF0==0);
TR0=0;
TF0=0;
}}
56. Memory in 8051 C Programming
8051 Memory Types
y The 8051 separates the data segments from the code
segments in memory. The register related data, interrupt
service data, stack data and any frequently accessed data are
kept in on-chip internal memory but the code and large data
structures such as arrays and lookup tables are kept in
EPROM or extended RAM off-chip memory.
y 8051 C allows C programmers to decide which memory
segment to assign the data items to. The capacity of on-chip
memory is very limited (256 bytes). The on-chip memory is
faster but more expensive while the extended RAM can be
up to 64K byte which is slower but cheaper.
57. 8051 Memory Areas
The 8051 architecture supports a number of physically separate memory areas for
program and data. Each memory area offers certain advantages and disadvantages.
Program Memory code
Internal Data memory bdata, data, idata
External Data memory xdata, pdata
58. Explicitly declared Memory types
code: Program memory (64 Kbytes); accessed by opcode
MOVC A, @A+DPTR.
data: Directly addressable internal data memory; fastest access to full
internal address space (256 bytes).
idata: Indirectly addressable internal data memory; accessed across the
internal address space (128 bytes).
bdata: Bit-addressable internal data memory; allows mixed bit and byte
access (16 bytes).
xdata: External data memory (64 Kbytes); accessed by opcode
MOVX @DPTR.
pdata: Paged (256 bytes) external data memory; accessed by opcode
MOVX @Rn.
59. Contd.
There are five typical
memory data types used for
data memory in 8051 C:
code, data, idata, bdata,
xdata and pdata.
There is one typical code
memory type for code, but
you can also assign large,
constant data items in the
code type segment (64kB).
The 8051 Memory Space
60. data type memory
y The data type memory is a 128 byte on-chip memory
segment with an address starting at 000H(0) and ending at
07FH(127).
yy This directly accessible internal memory segment is mostly
used to store frequently used variables in the program such
as parameter variables, temp variables, function variables
and especially interrupt service variables which must be
quickly accessible otherwise program performance will be
affected.
y From 080H(128) to 0FFH(255) in RAM is the Special
Function Register (SFR) memory segment which are used to
control timer/counter, Acc, interrupts, and I/O ports.
61. Bdata, idata, xdata
y Within the data segment there is a 16-byte special segment called
bdata with an address from 020H to 02FH which is bit
addressable. Each individual bit in this segment has its unique bit
address for fast access and manipulation.
y The idata segment (128 byte) is the second memory segment at
080H-0FFH available in the extended on-chip RAM(8052). It is
indirect accessible. it must be accessed with the indirect address
mode (address is held in a register).
y The xdata segment (64KB) from 00000H-0FFFFH (65,535) is
available on off-chip RAM which augments the internal memory.
62. pdata
y The paged pdata segment is within the first 256 bytes of
the xdata which accommodate up to 256 bytes for data
variables although it is not as efficient as internal memory
access.
y The code segment (64kB) available on off-chip external
EPROM is used for program storage. It can also be used to
store lookup tables and other large but less frequently
changed data.
63. Memory Models
y With memory models you can simply determine the default
memory type used for function parameters and variables
declared with no explicit memory type. You can override the
default memory type by explicitly declaring variables with
specified memory types.
y The three memory models are SMALL, COMPACT, LARGE.
64. SMALL model
In the small memory model all variables default to the
internal data memory of the 8051.This is the same as if they
were declared explicitly by the data memory type.
y The small mode data access is the fastest because the direct
memory access is used on the on-chip RAM, but is limited
to 128 bytes(256 bytes for 8052). You always try to use
small memory in your application unless it can not work out
before trying other two memory models.
y 8051 programmers with a single 8051 chip can only use the
SMALL memory model unless external memory is utilized.
65. COMPACT model
In the compact memory p y model all variables default to one page
(256 bytes off-chip) of external data memory. This is the same as if
they were explicitly declared as the pdata memory type. The
programmer can still access internal data memory. The compact
mode data access is slower than the small mode access because byte
registers R0 and R1(@R0,@R1 in assembly) are used to indirectly
address the data which can only reach 28 = 256 bytes.
66. LARGE model
In the large model all variables default to external off-chip
xdata memory(up to 64k bytes). The data in this model as if
were explicitly declared as the xdata memory type (64k). The
memory access is slower than the small and compact models
because it uses 2 bytes data pointer (DPTR) to address the
external memory.
67. Accessing Code ROM Space in 8051 C
y Three spaces to store data:
y The 128 bytes of RAM space with address range 00-7FH
y The 64KB of code space with address 0000-FFFFH
- used for storing program & under control of PC
- data accessed using MOVCA,@A+DPTR
yy The problems using this code space for data:
y Can burn predefined data and tables but can’t write during the
execution of program. More of code space is used for data and less for
program code
68. y Intel created another memory space called external memory especially
for data.
yy The 64KB of external memory – RAM & ROM
y The 8051 C compiler allocates RAM locations as
y Bank 0 – addresses 0 – 7
y Individual variables – addresses 08 and beyond
y Array elements – addresses right after variables
y Array elements need contiguous RAM locations and that limits the size
of the array due to the fact that we have only 128 bytes of RAM for
everything
y Stack – addresses right after array elements
69. Example : Compile and single-step the following program on your
8051simulator. Examine the contents of the 128-byte RAM space to
locate the ASCII values.
#include <reg51.h>
void main(void)
{
unsigned char mynum[]=“ABCDEF”; //RAM space
unsigned char z;
for (z=0;z<=6;z++)
P1=mynum[z];
}
70. Example : Write, compile and single-step the following program on
your 8051simulator. Examine the contents of the RAM space to locate
the values.
#include <reg51.h>
void main (void)
{
unsigned char mydata[100]; //RAM space
unsigned char x, z=0;
for (x=0;x<100;x++)
{ z--;
mydata [x]=z;
P1=z;
}
}
71. Example : Compile and single-step the following program on your
8051simulator. Examine the contents of the code space to locate the
ASCII values.
#include <reg51.h>
void main(void)
{
code unsigned char mynum[]=“ABCDEF”;
unsigned char z;
for (z=0;z<=6;z++)
P1=mynum[z];
}
72. #pragma
y You can use the #pragma directive in your C program to
specify the memory model. The default memory model is
SMALL.
y The compiler is directed to use the LARGE memory model
but each individual data variable can override the LARGE
memory model (default to xdata type) by explicit
specification with data type or bdata type.
#pragma LARGE
unsigned int data data1; // override data1 to data type
int data2; // default xdata type
bit bdata bit1; // override bit1 to bdata type
73. Mix C and Assembly Code
y Trade off between C and assembly language in the
embedded software development.
y You can take advantage of assembly code to call an assembly
routine from your C program or embed assembly code
inline in your C program.
y For example, some particular standard assembly functions
are available to use, some time critical task needs to be
written in assembly for fast execution, or you need to access
SFRs or memory-mapped I/O devices directly using
assembly code.
y It is not very difficult to call assembly code from C and vice-versa
because the C51 provides the mechanisms to do so.
y You can use pair of inline assembly directive #pragma ASM
and #pragma ENDASM to include the assembly code in
your C program
74. #include <reg51.h>
void sub(unsigned char x);
void main()
{ . . .
sub(10);
. . .}
sub(unsigned char x)
{
#pragmaASM
movA, #x //or use R7 register to get the value of
//parameter and put in Acc
mov@R0,A // put it in buffer
#pragma ENDASM
}
75. Calling an assembly code from C51 program
extern void a_func(unsigned int x);
void main()
{
int y;
y =_func();
}
Create a separate C file with the definition of a_fun() as follows:
#pragma SRC //let C compiler generate a .src file
//instead of obj file
unsigned int a_func(unsigned int x)
{ return x-1; //this may be a fake statement,
//the purpose of it to generate a skeleton
//for your true assembly code replacement
}
76. y Then use c51 compiler command to compile this C file with
option of src to generate the assembly source code in a .src
file.
y Modify the source code in assembly to fit your need and
rename it to .a51 file and use a51 command assemble it into
objective code.
y Finally, link this object code with the object code from the
main program to get the target code.
77. Function Declaration
Cx51 provides you with a number of extensions for standard C function
declarations.These extensions allow you to:
- Specify a function as an interrupt procedure
- Choose the register bank used
- Select the memory model
return_type funcname (args) [{small | compact | large}]
[interrupt n] [using n]
where:
return_type is the type of the value returned from the function. If no
type is specified, int is assumed.
78. y funcname is the name of the function.
y args is the argument list for the function.
y small, compact, or large is the explicit memory model for the
function.
y interrupt indicates that the function is an interrupt function.
y using specifies which register bank the function uses.
79. Interrupt Functions
y An interrupt is a triggered event that temporarily suspends
the foreground g program and lets another background
program called an interrupt service routine (ISR) or
interrupt handler to deal with the event.
y An interrupt driven system can do multiple tasks
simultaneously. It allows the system to respond to an event
and handle the event asynchronously. After the ISR
completes its job, control returns to the foreground
program.
80. yy Most embedded system software is time critical so
programmers adopt the interrupt (event) driven approach
instead of inefficient busy polling.
While(TF0 != 1) { . . . }
Or while( p7^1 == 0){ . . . }
yy Here the program continuously checks all ““service request
flags”, waiting for the events to occur. Once it finds a
request, it services the device and keeps polling. In an
interrupt driven program, the main foreground program
can handle other things without wasting time waiting for the
event.
81. How Interrupt Works
In an interrupt system, when the CPU is informed of an
interrupt, it first completes its current instruction and saves
the Program Counter (PC) and current status on the stack.
The CPU then fetches the ISR address (interrupt vector) and
puts it into the PC and runs the ISR until it reaches the RETI
instruction.
After that, the CPU pops up the old PC from the stack and
resumes the interrupted program.
Most embedded systems are interrupt driven since the
interrupt can not be predicted in advance. Meanwhile, the CPU
is free to do other things.
82. y A foreground program is interrupted g p g p by a level 1 interrupt
and the CPU switches the control to its ISR0. Somehow
during the execution of ISR0 another level 1 interrupt
occurs which interrupts the ISR0. After ISR1 completes,
ISR0 is resumed and in turn the original program is
resumed.
Interrupt Example
83. Simple interrupt application
yy with an external interrupt #2 which is connected to p3^2
(bit 3 of pin 2 for INT0, see reg51.h). Once there is an
interrupt caused by a edge trigger, the interrupt 0 event is
fired and ISR myISR ( ) is called.
#include <reg51.h>
void myISR() interrupt 0
{
. . .
}
main()
{
EA = 1; // enable global interrupt
EX0 = 1; // enable external interrupt 0
IT0 = 1; // 0 level triggered, 1 edge triggered
while(1)
{
. . .
}
}
84. Example using Timer 0 ISR connected to INT1
#include <reg51.h>
unsigned int count;
void myRT0() interrupt 1
{
TF0=0; //Timer 0 overflow flag
count++;
}
main()
{
TMOD = 0x02; // timer 0 in mode 2(8 bits), counts up to 256
TH0 = 56; // reload value, 256-56=200, Overflows every
// 200 CPU clocks
TF0 = 0;
ET0 = 1; // enable Timer 0 interrupt
EA = 1; // enable global interrupt
TR0 = 1; // start timer 0
While(1); // wait for INT
} }
85. Register bank
y An interrupt p function has a “using” attribute to specify the
register bank. The "using" attribute tells the compiler to switch
register banks on entry to an interrupt routine.
y The 8051 has a register bank of eight general-purpose registers
(R0-R7). Due to the time spent on the stack for saving the status
and risk of stack damage while switching to an ISR, the 8051
provides four register banks you can use.
86. y Register bank 0 is used by default; the registers R0 to R7 are
used extensively for the temporary storage of library routines
and for locals.
y Register banks 1, 2, or 3 are best used by ISRs to avoid saving
and restoring registers on the stack and to avoid the risk of stack
damage because the same priority interrupts can share a register
bank.
y Here is the syntax of ISR declaration with the “using” attribute
void <ISR_name>(void) interrupt <interrupt_handler__number> [ using
<register_bank> ]
87. y The “using” g attribute in an ISR definition above is optional.
Small interrupt routines are not recommended to use the
“using” attribute, since they use the default register bank 0.
Complex and time critical interrupts which call other
functions must use the “using” attribute.
y A simple example of an ISR for Timer0 using register bank
#2.
unsigned int count;
void timer0 (void) interrupt 1 using 2
{
if (++count == 1000)
count = 0;
}
88. Reentrant Functions
y In embedded system software, a function may need to call
itself recursively, or to be interrupted by another function
which calls the same function indirectly, or to be shared by
multiple processes simultaneously.
y The interrupted function must save its status including the
Program Counter(PC) and values of all register and local
variables so that the function can be resumed correctly later.
y The normal C51 function can not be invoked recursively
because the context data is stored in a fixed memory
location and the recursive calls will corrupt the stack data
area.
89. y This requires such functions to be reentrant. The extension
of reentrant function is used to force the compiler to make a
separate stack area for each instance of the function so that
the function will always work correctly regardless of the
circumstances.
y The cost of the reentrant function is the consumption of the
memory space.
90. y A normal 8051 C function does not guarantee such features
because it can not protect its own current status well when
it switches.
y The reentrant function extension attribute allows you to
declare functions to be reentrant. The stack for a reentrant
function is simulated in internal or external memory
depending on the memory model.
y The first example given here is a function A() interrupted by
a ISR which calls this function A() recursively so that the
function A() should be declared as a reentrant function.
92. Table lookup function shared by multiple processes
char data[] ={‘A’, ‘B’, ‘C’};
char lookup(int i) reentrant
{
char x;
x = data[i];
return (x);
}
This function may also be interrupted by another ISR which
calls this function as well so that this lookup function is
declared as a reentrant function.
93. Simple reentrant function which calls itself
void recursive(unsigned int value) reentrant
{if (value>0) recursive(--value);
}
If a reentrant function calls another function, the latter must
also be reentrant as well.
94. Real Time Function
y The C51 provides the _task_ and _priority_ keywords to
the function extensions
y the _task_ tells the function as a real time task function
y _priority_ tells the priority level of the task if task is run
in a priority-based multi-tasking scheduling environment.
E.g.
Void myFuction (void) _task_ 2 _priority_ 3.
y You can define up to 256 real time functions numbering
from 0 to 255.
y You can specify up to 5 priority levels from 0 to 4.
95. Modular Programming in C
y It is clear that a single source file or “module” is sufficient to
hold the entire C program.
y Modular programming, p g g however, is a “divide and conquer”
software design and programming technique which breaks
up a large program into manageable subprogram units such
as functions and subroutines.
y Each module represents a separation of concerns which
improves software maintainability and reusability by
enforcing logical boundaries between modules.
96. Contd.
y An embedded system is composed of several modules,
each with a unique function, so the embedded software
system is built from a number of discrete tasks and then
finally assembled into a complete, working software.
97. y A modular program consists of a main module and many
auxiliary modules, such as device drivers in embedded
software systems.
y In reality each module is a separate source file. If you need
to revise or update functions in one module, you only need
to work on that file containing the function.
y For example, most embedded systems have an initialization
and configuration function called init() which is included in
one of the modules.
y Each individual module is much shorter than the entire
program, hence it is much easier to read and understand.
98. Contd.
y Modular programming also helps debugging and testing
programs and reduces the likelihood of bugs.
y Local scopes of variables in modules and smaller size of
the individual modules make it easier to understand the
effects and impacts of changing a variable.
99. yy Modular programming in C is to organize a program in
multiple files. There is a main module which contains the
main() function. This is the entrance of the entire program
and many other modules in formats of C source code files or
header files.
y First, you must decide how to divide your program into
multiple files or modules. For example, you can make each
interrupt service subroutine as a separate module, same as
other interrupt handlers.
100. Contd.
y Next, after you determine all modules you need to decide
how to interface with each other. In other words, you need
to decide the visibilities of the variables and functions in
each module.
y For these private data variables or helper functions, you
should define them as private visibility by C static scope to
make them accessible within the module file where they are
defined.
y For most functions, data variables and data types, you need
to make them public to expose to all other modules that
need to access or share and reuse these resources.
101. Scope of Functions and Variables
y In a multi-module C program, one module may need to call
functions defined in other modules. Multiple modules may
share some data, and some data may need to be transferred
from one module to other modules.
y The scope of data and functions must be specified properly
to avoid data and program corruption.
y A typical 8051 application consists of many modules in
multiple source files where each has a number of functions
accessing variables in RAM.
y Each function has some temporary variables which will be
used to store intermediate process values for internal use,
but some functions need to access global variables shared by
multiple functions.
102. y C uses the extern keyword to make an external reference
to a variable or function residing in other modules.
y The keyword static is used to specify the scope of variable
of function to be the enclosing module.
y A variable defined outside any function is treated as a global
variable which is often used for main program and interrupt
service routines to share or exchange data.
y You can find that the variable “a” is defined outside any
function in main.c so that it can be accessed everywhere in
this module and can also be seen from any other module
which makes an external reference to this variable.
103. yThe main.c module makes an external reference to exfunc()
defined in module1.c so that exfunc() can be called within this
module.
yThe scopes of variables “b” and “c” are within the main()
function because they are defined inside the main() function.
main.c:
int a ;
extern int exfunc(int) ;
int func( int x)
{
return(x*x);
}
main()
{
Int b;
int c;
b = exfunc(c);
a = func(b);
}
104. MODULE1.c
extern int a;
int exfunc(int x)
{
int d ;
static int count ;
count++ ;
d = x * a ;
return(d) ;
)
The module1.c makes an external reference to the global
variable a to use it in exfunc().
The local variable count is defined as a static variable so that it
will remember its value after the exfunc() function call exits.
When the exfunc() function is called again, the count variable
will operate on its previous kept value, which is different from
local variables in a function that doesn’t keep their data once
the function exits.
105. Header Files
y The main model includes the reg51.h in order to use port #1
defined in reg51.h.
y You can place public (global) parts of a module in a header
(.h) file. This header is included by other files that use its
corresponding module.
y A header file typically contains global variable definitions,
typedef of data type declarations and function prototypes.
This ensures that the module and all the files that use the
module agree on data types, global data, and functions.
106. Contd.
y It is also common for one header to include other headers. If
both header files include a common header file and a C
source code file includes these two header files, it will cause a
compiler error due to multiple declarations of a type or
macro.
y The conditional compilation directive of the C preprocessor
directives can be used to solve this kind problem
107. delay.h
#ifndef _DELAY_
#define _DELAY_
/* body of header */
#endif
y This directive checks to see if the identifier is not currently
defined. If the _DELAY_ identifier has not been defined by a
#define statement, then the code immediately following
the command will be compiled. This prevents any duplicated
inclusion.
y Notice that for a function, the header file can only contain
the prototype of a function, but never contains its complete
implementation code.
y The header plays a decoupling role between the function
implementation in its source file and the function execution
in the destination file where the function is called.
108. Multi‐module C Programming
y The header file plays a role of interface between the modules
with data and function definition and the modules
referencing these data and functions. Here is a simple
example with two modules connected by a header file.
y The main program has an infinite loop calling a function f()
which is defined in another module named module1. The
MyHeader file provides a constant identifier PI, a type
definition of “Real” and a function prototype of f().
109. main.c
#include ”MyHeader.h”
void main( void )
{
int x;
while(1){
f(x);}
}
MyHeader.h
#ifndef _MYHEADER_H
#define _MYHEADER_H
#DEFINE PI 3.14259 //
typedef double Real; //
void f( Real ); // external function prototypes
#endif
110. module1.c
#include <stdio.h>
#include <math.h>
#include "MyHeader.h"
void f( int x )
{
int i;
for (i=1; i<x; i++) circle(i);;
}
static void circle( Real R ) // internal scoped function
{
Static count; //static variable
count ++;
Printf(“%f %d”, PI*sqr(R), count);
}
Because neither main.c nor module1.c has the _MYHEADER_H identifier
defined, the header file will be included and compiled.
111. y For a large embedded system it is not practical to have a
single header file incorporated in each source file regardless
of whether the incorporated files need them or not.
y A good practical solution is to generate module-specific
include header files as shown in the next diagram. Each
module is paired with its corresponding header file.
112. y The header file consists of two parts:
y Part1: original data, type, macro definitions and function
prototypes used for host modules
y Part2: external references to the items listed in part1 for
referencing modules
y The template for module1.c is shown below:
#ifdef _module_ //_module_ is a identifier used for
//conditional compilation
// part1
#else
//part2
#endif
113. Assume there are two modules, module1.c and module2.c.
The module1.c is shown below.
#define _module1_
#include <module1.h>
#include <module2.h>
.
.
.
function1(){ . . . }; //function1 definition
.
.
.
function2(); //call function2
114. The corresponding module1.h is shown below.
#ifdef _module_ //_module_ is a identifier used for
// conditional compilation
function1(); // prototype of function2
#else
extern function1();
#endif
The module2.c is shown as below.
#define _module1_
#include <module1.h>
#include <module2.h>
. . .
Function1(){ . . .; function2(); }
// call function2 in the definition of function1
115. y The header file for module2.c is listed below.
#ifdef _module2_ //_module_ is a identifier used for
//conditional compilation
function2(); // prototype of function2
#else
extern function2();
#endif
yy Although both header files are included in both modules, the
original function prototype declarations are compiled in the
host modules and external references are compiled in the
referencing modules by the conditional compilation
directives in the header files.
116. Practice is the key to success in
embedded ‘C’
THANK YOU
116 Copyright to gaurav verma