Embedded C Notes
Embedded C Notes
Embedded C Notes
Advantages of C language :
Extends to newer systems architecture
High efficiency and performance
Low-level access
Disadvantages of C languages
No exceptions
No range checking
No automatic garbage collection
No support for OOP
Multiple incursion :
If (f.c) includes (h1.h and h2.h) and (h1.h) includes h2.h , this produces a
development error
This is solved by
#ifndef h2_h_
#define h2_h_
#endif
Sources of Interrupts
Internal interrupts generated by on-chip peripherals such as timers EEPROM ,
serial and parallel ports
External interrupts generated by peripherals connected to the ECU
Exceptions thrown by the processors
Software interrupts
Interrupt service routine (ISR) or interrupt handler :
A program run by the ECU when an interrupt is invoked
Interrupt nesting :
The ability to leave the current interrupt and serve another interrupt
Interrupt timing :
Interrupts latency : the maximum amount of time interrupts are delayed before
executing the 1st instruction in the ISR
Interrupt response time : the interrupt latency + Time to save the CPU context
before the interrupt
Interrupt recovery time : time to restore the CPU context
#define :
Creates symbolic names (symbols) for expressions #define led 3
Creates Macros
#define min(A,B) if( (A) < (B) ){ \
return A; \
}\
else{ \
return B; }
#define min(A,B) ( (A)<(B)? (A) : (B) )
#define is a text replacement , so don’t terminate it with a (;) to keep its symbols
usability
#include
#include <header.h>
Searches for (header.h) in the include paths
#include ”header.h”
Searches for (header.h) in the include paths and the current directory of the
project
Conditional preprocessors
They can control which lines are compiled (Conditional Compilation)
Used in header files to ensure that declarations done (Header guards)
Keywords (#if , #ifdef , #ifndef , #elif , #else , #endif)
Bit-Manipulation :
Using bit-mask macros
#define SET(port,mask) port|=mask
#define SET(port,pin) port|=(1<<pin)
#define CLEAR(port,mask) port&=~mask
#define CLEAR(port, mask) port&=~(1<<pin)
Using structs
#pragma
it tells the compiler to do something, set some option, take some action, override
some default, etc. that may or may not apply to all machines and operating
systems.
Pragma is implementation specific directive
There are many type of pragma directive and varies from one compiler to
another compiler
If compiler does not recognize a particular pragma , it simply ignore that pragma
statement without showing any error or warning message
Compilation process :
The compiler allocates memory for definitions and generates opcodes for
executable statements
The compiler works with one translation unit (parsed C file) at a time
The compiler and assembler create relocatable object file
compilation stages :
Front end (source code parsing)
Middle end (optimiation)
Back end (code generation)
Code generation :
Converts the intermediate representation code structure into target opcodes
Memory allocation :
The compiler allocates memory for code and data in sections
These sections are defined by name or attributes of info stored in them as
follows :
1) Code is stored in (.text) section
2) Globally declared variables without initialization are stored in (.bss) section
3) Globally declared variables with initialization are stored in (.data) section
4) Constants are stored in (.rodata) section
5) Automatic (local) variables are stored in (.stack) section or general registers
(R#)
6) Dynamic data are stored in (.heap) section
Attributes are used by linker for locating sections in memory
Linking Process
Combining object files into a single executable file
Stages :
1) Symbol resolution
2) Section Concatenation
3) Section location
4) Data initialization
Symbol Resolution :
Resolve References between object files
Search for unresolved symbols in libraries to resolve them
No resolution = unresolved symbol error
If the linker finds the same symbol defined in two object files , it will report a
“redefinition” error
Section concatenation :
Concatenating like-named sections from the input object files
Program addresses are adjusted to take account of concatenation
Section location
Each section is given an absolute address in memory
There’s a base address in non-volatile memory for persistent sections and
there’s another base address in volatile memory for non-persistent sections
Data initialization
Any initialized data is stored in the non-volatile memory
Linker must create extra sections to enable copying data from ROM to RAM to
speed up execution
Each initialized section by copying is divided into a section in ROM (shadow
section) and another in RAM
(.bss) section has no shadow section . It’s initialized by startup code
If manual initialization isn’t used , the linker arranges for the startup code to
perform initialization
Linker control :
The detailed operation of the linker can be controlled by invocation options
(command-line) or linker control file (linker script , linker configuration file or
scatter-loading)
LCF(linker control file) defines physical memory layout and placement of
different memory regions
LCF syntax is compiler-dependent
When an IDE is used , linking options can be relatively friendly specified
The output of linking stage is a loadable file in a platform-independent format
(.ELF or DWARF)
Loading process :
ELF or DWARF are target-independent format
The ELF must be converted into a native (Flash or PROM) format (.bin or .hex) to
be loaded into the target
Memory alignment :
A memory address , is said to be n-byte aligned when it’s a multiple of n bytes
A memory access is said to be aligned when the datum being accessed is n bytes
long and the datum address is n-byte aligned
When a memory access is not aligned, it is said to be misaligned.
A memory pointer that refers to primitive data that is n bytes long is said to be
aligned if it is only allowed to contain addresses that are n-byte aligned,
otherwise it is said to be unaligned.
Data types :
Reentrant functions :
Allow different concurrent invocations from different context
Examples : Functions shared between different tasks in a multi-tasking system
Pointers arithmetic :
*pn++ : fetches what (pn) points to , then increments the pointer (pn)
*++pn : increments the pointer (pn) , then fetches wht it’s pointing to
*(arr+7) : means accessing the 7th element in an array using a pointer notion
7[arr] is the same as *(arr+7)
Pointers Casting
Casting explicitly is needed when moving data among pointers of different types
int *p;
Float *pf =(float*) p;
Casting implicitly is done while moving to and from a void pointer
Pointer to pointer
A ( pointer to pointer ) addresses a location of an address in memory
int n=4;
int *pn =&n;
int **ppn=&pn;
It’s needed for
1) Pointer array
int *arr[20];
2) Multi-dimensional array
int world[20][30];
*(world +5)[4] is the same as *(*(world +5)+4)
int **p=world //this is illegal
3) String array
Typedef
A facility to allow creating a new name for an existing data type
typedef char * string ; //string is a new name for char *
string x ; // x is a variable of type char *
Helps to improve the readability of the program
“typedef”s are usually placed in header files
Arrays :
A constant pointer to a block of contiguous data in memory
C has no range checking for arrays , so we can mistakenly access a something out
of the array bounds without a compilation error
A macro calculating the length of an array
#define ArrSize(A) (sizeof(A)==0 ? 0: sizeof(A)/sizeof(A[0]))
A 2-D array is represented in memory in a row-major way (arranged as row by
row)
Unions :
Holds objects of different types in the same memory location
Union size is equal to the size of its largest element
A non-homogenous array is an array of unions that can hold elements of
different types
Enumeration (enum)
A set of integers referenced symbolically
If an enum has a starting value , each symbol in turn represents the next int
enum days{sun=2, mon , tue , wed , thu , fri , sat=1 };
enum days today , yesterday , tomorrow;