1. Embedded C requires compilers to create files that can be downloaded and run on microcontrollers, while C compilers typically generate OS-dependent executables for desktop computers.
2. Embedded systems often have real-time constraints and limited memory/power that are usually not concerns for desktop applications.
3. Programming for embedded systems requires optimally using limited resources and satisfying real-time constraints, which is done using the basic C syntax and function libraries but with an embedded/hardware-oriented mindset.
3. Storage classes
In C language, each variable has a storage class which decides scope, visibility
and lifetime of that variable. The following storage classes are most oftenly
used in C programming,
Automatic variables >>> Local, static and global
External variables
Static variables
Register variables
3
4. Automatic variables
A variable declared inside a function without any storage class
specification, is by default an automatic variable. They are created when a
function is called and are destroyed automatically when the function exits.
Automatic variables can also be called local variables because they are
local to a function. By default they are assigned garbage value by the
compiler.
4
5. External or Global variable
A variable that is declared outside any function is a Global
variable. Global variables remain available throughout the entire program.
One important thing to remember about global variable is that their values
can be changed by any function in the program.
5
Here the global variable number is available to all three
functions.
6. extern keyword
The extern keyword is used before a variable to inform the compiler that this
variable is declared somewhere else.
The extern declaration does not allocate storage for variables.
6
9. Register variable
Register variable inform the compiler to store the variable in register instead
of memory. Register variable has faster access than normal variable.
Frequently used variables are kept in register. Only few variables can be
placed inside register.
NOTE : We can never get the address of such variables.
Syntax :
9
11. Syntax of C's volatile Keyword
declare a variable volatile, include the keyword volatile before
or after the data type in the variable definition. For instance
both of these declarations will declare foo to be a volatile
integer:
volatile int foo;
int volatile foo;
11
12. volatile
Now, it turns out that pointers to volatile variables are
very common, especially with memory-mapped I/O
registers. Both of these declarations declare pReg to be
a pointer to a volatile unsigned 8-bit integer:
volatile uint8_t * pReg;
uint8_t volatile * pReg;
12
pointer to a volatile unsigned 8-bit integer
13. volatile
Volatile pointers to non-volatile data are very rare (I
think I've used them once), but I'd better go ahead and
give you the syntax:
int * volatile p;
13
Volatile pointers to non-volatile data
14. volatile
for completeness, if you really must have a volatile
pointer to a volatile variable, you'd write:
int volatile * volatile p;
14
volatile pointer to a volatile variable
15. Proper Use of C's volatile Keyword
Memory-mapped peripheral registers
Global variables modified by an interrupt service routine
Global variables accessed by multiple tasks within a
multi-threaded application
15
16. Peripheral Registers
Embedded systems contain real hardware, usually with
sophisticated peripherals. These peripherals contain registers
whose values may change asynchronously to the program flow.
As a very simple example, consider an 8-bit status register that is
memory mapped at address 0x1234. It is required that you poll
the status register until it becomes non-zero.
16
17. incorrect implementation 17
uint8_t * pReg = (uint8_t *) 0x1234;
// Wait for register to become non-
//zero while
(*pReg == 0) { } // Do something else
This will almost certainly fail as soon as you turn
compiler optimization on, since the compiler will
generate assembly language that looks something
like this:
mov ptr, #0x1234
mov a, @ptr
loop:
bz loop
assembly
18. To force the compiler to do what we
want, we modify the declaration to:
18
uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;
//The assembly language now looks like this:
mov ptr, #0x1234
loop:
mov a, @ptr
bz loop
19. Interrupt Service Routines 19
int etx_rcvd = FALSE;
void main()
{
...
while (!ext_rcvd)
{
// Wait
}
...
}
interrupt void rx_isr(void)
{
...
if (ETX == rx_char)
{
etx_rcvd = TRUE;
}
...
}
The solution is to declare the
variable etx_rcvd to be volatile
20. Multithreaded Applications 20
int cntr;
void task1(void)
{
cntr = 0;
while (cntr == 0)
{
sleep(1);
}
...
}
void task2(void)
{
...
cntr++;
sleep(10);
...
}
This code will likely fail once the
compiler's optimizer is enabled.
Declaring cntr to be volatile is the
proper way to solve the problem.
23. You will need
Cross Toolchain
MakefileLinker Script
Startup.s
C Code files
23
24. C Startup
It is not possible to directly execute C code, when the processor comes out of
reset. Since, unlike assembly language, C programs need some
basic pre-requisites to be satisfied. This section will describe the
pre-requisites and how to meet the pre-requisites.
We will take the example of C program that calculates the sum of an array as
an example.
And by the end of this section, we will be able to perform the necessary
setup, transfer control to the C code and execute it.
24
26. Before transferring control to C code, the
following have to be setup correctly.
Stack (r13”SP”)
Global variables
Initialized .data
Uninitialized .bss
Read-only data .rodata
Then force the PC register
to jump on the main functions
26
CPU Memory
Peripherals/Modules
27. Stack
C uses the stack for storing local (auto)
variables, passing function arguments,
storing return address, etc. So it is essential
that the stack be setup correctly, before
transferring control to C code.
Stacks are highly flexible in the ARM
architecture, since the implementation is
completely left to the software.
27
28. Stack 28
So all that has to be done in the startup code is to point (r13”SP”) register
at the highest RAM address, so that the stack can grow downwards (towards
lower addresses). For the connex board this can be achieved using the
following ARM instruction.
29. Global Variables
When C code is compiled, the compiler places initialized global variables in the .data
section. So just as with the assembly, the .data has to be copied from Flash to RAM.
The C language guarantees that all uninitialized global variables will be initialized to zero.
When C programs are compiled, a separate section called .bss is used for uninitialized
variables. Since the value of these variables are all zeroes to start with, they do not have
to be stored in Flash. Before transferring control to C code, the memory locations
corresponding to these variables have to be initialized to zero.
29
30. Read-only Data
GCC places global variables marked as const in a separate section, called
.rodata. The .rodata is also used for storing string constants.
Since contents of .rodata section will not be modified, they can be placed in
Flash. The linker script has to modified to accomodate this.
30
33. The startup code has the following parts 33
1.exception vectors
2.code to copy the .datafrom Flash to RAM
3.code to copy zero out the .bss
4.code to setup the stack pointer
5.branch to main
37. 37
What is QEMU ?
− QEMU (short for "Quick EMUlator") is a free and open-source machine
emulator and virtualizer written originally by Fabrice Bellard
− Can emulate 80386, 80486, Pentium, Pentium Pro, AMD64 – from x86
architecture
− PowerPC, ARM, MIPS, SPARC, SPARC64
− Work on FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac
OS X, QNX, Android
40. 40
QEMU Project
“QEMU does not have a high level design description document - only the source code tells
the full story”
41. ARM system emulated with QEMU
qemu-system-arm is the software that emulates a VersatilePB platform
For more information “VersatilePB physical Board’
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dsi0034a/index.html
http://www.arm.com/products/tools/development-boards/versatile-express
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0224i/index.html
41
42. HELLO WORLD FOR BARE METAL 42
tx RX
You will need
Cross Toolchain
Makefile
Linker Script .ld
C Code files
Startup.s
Exectuable File
Test.bin
43. Lab: Steps
The QEMU emulator supports the VersatilePB platform, that contains an ARM926EJ-S
core and, among
other peripherals, four UART serial ports;
the first serial port in particular (UART0) works as a terminal
when using the -nographic or “-serial stdio” qemu option. The memory map of the
VersatilePB board is implemented in QEMU in this board-specific C source;
note the address where the UART0 is mapped: 0x101f1000.
43
44. 44
there is a register
(UARTDR) that
is used to transmit
(when writing in the
register) and receive
(when reading) bytes;
this register is
placed
at offset 0x0, so you
need to read and write
at the beginning of the
memory allocated for
the UART0
45. Lab: Steps
The QEMU emulator supports the VersatilePB platform, that contains an ARM926EJ-S
core and, among
other peripherals, four UART serial ports;
the first serial port in particular (UART0) works as a terminal
when using the -nographic or “-serial stdio” qemu option. The memory map of the
VersatilePB board is implemented in QEMU in this board-specific C source;
note the address where the UART0 is mapped: 0x101f1000.
45
46. To implement the simple “Hello world!”
printing, you should write this test.c file:
46
volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000;
void print_uart0(const char *s) {
while(*s != '0') { /* Loop until end of string */
*UART0DR = (unsigned int)(*s); /* Transmit char */
s++; /* Next char */
}
}
void c_entry() {
print_uart0("Hello world!n");
}
• The volatile keyword is necessary to instruct the compiler that the memory
pointed by UART0DR can
change or has effects independently of the program.
• The unsigned int type enforces 32-bits read and write access.
55. To run the program in the emulator
55
Go to qemu directory
And open the CMD Console
Copy the test.bin on the qemu folder then press this Command
$ qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
56. To Debug the Code
Using gdb, because QEMU implements a gdb connector using a TCP connection. To do so, run
the emulator with the correct options as follows
$ qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel test.bin
This command freezes the system before executing any guest code, and waits for a connection
on the TCP port 1234.
From ARM ToolChan terminal, run arm-none-eabi-gdb and enter the commands:
56
61. DIFFERENCE BETWEEN C AND
EMBEDDED C
Compilers for C (ANSI C) typically generate OS dependent
executables. Embedded C requires compilers to create files to be
downloaded to the microcontrollers/microprocessors where it
needs to run. Embedded compilers give access to all resources
which is not provided in compilers for desktop computer
applications.
61
62. DIFFERENCE BETWEEN C AND
EMBEDDED C
Embedded systems often have the real-time constraints, which is usually not
there with desktop computer applications.
Embedded systems often do not have a console, which is available in case of
desktop applications.
So, what basically is different while programming with embedded C is the
mindset; for embedded applications, we need to optimally use the resources,
make the program code efficient, and satisfy real time constraints, if any. All
this is done using the basic constructs, syntaxes, and function libraries of ‘C’.
62
67. Why Change to C
C is much more flexible than other high-level programming languages:
• C is a structured language.
• C is a relatively small language.
• C has very loose data typing.
• C easily supports low-level bit-wise data manipulation.
• C is sometimes referred to as a “high-level assembly language”.
► When compared to assembly language programming:
• Code written in C can be more reliable.
• Code written in C can be more scalable.
• Code written in C can be more portable between different platforms.
• Code written in C can be easier to maintain.
• Code written in C can be more productive
67
70. 2.memory-mapped devices
Documenting the source code is helpful not only for your
future reference but for those who come after you. For
instance, if you're working on an embedded system, you
need to have a memory map indicating where all the
memory-mapped devices can be found. Listing 8 shows
an example of a memory map.
70
71. Review: The “super loop” software
architecture
Problem
What is the minimum software environment you need to create an
embedded C program?
Solution
71
72. Review: An introduction to schedulers
Many embedded systems must carry out tasks at particular instants
of time. More specifically, we have two kinds of activity to
perform:
• Periodic tasks, to be performed (say) once every 100 ms,
and - less commonly -
• One-shot tasks, to be performed once after a delay of (say)
50 ms.
72
75. Header File
Each .h file should be “stand alone”
▫ It should declare, #define, and typedef anything needed
by prototypes and include any .h files it needs to avoid
compiler errors
In our example prototypes for CircleArea() and
Circumference are placed in circleUtils.h ▫ circleUtils.h
included in circleUtils.c ▫ circleUtils.h included in any
other .c file that uses CircleArea()
75
78. Separate Compilation
If code is separated into multiple .c files
▫ Must compile each .c file
▫ Combine resulting .o files to create executable
78
82. #pragma
The #pragma directive gives special instructions to the compiler. The #pragma
directive is especially useful in embedded C programming and can tell the
compiler to allocate a certain variable in RAM or EEPROM. It can also tell the
compiler to insert a snippet of assembly language code.
The GNU GCC compiler, which is a popular compiler for various embedded
architectures such as ARM and AVR, also uses attributes as an alternative
syntax to the #pragma directive.
82
#pragma GCC dependency
allows you to check the relative dates of the current file and another file. If the other file is more recent than the current file, a warning is issued.
This is useful if the current file is derived from the other file,
and should be regenerated. The other file is searched for using the normal include search path.
Optional trailing text can be used to give more information in the warning message.