Date: agosto 25, 2025
Author: Guillermo Garcia
Categories: Embedded C programming Tags: Embedded C programming
We begin this series where we learn C programming with the necessary focus to apply it in the development of embedded systems.
Table of Contents
Programming is quite well-known today, and even more people can learn to program thanks to high-level languages like Python. When we see the diverse range of languages available, we may wonder why there is such a variety of languages when, ultimately, the goal is the same: telling a machine what to do for you. Well, the reality is that each language is created for specific tasks, in which that language usually excels more than others.
An Embedded System can be best described as a system which has both the hardware and software and is designed to do a specific task. Embedded systems programming:

An embedded system is a computer system with a specific, dedicated function that is not designed so that it should ever need to be reprogrammed (i.e. engine control units, implantable medical devices, appliances) The most common type of embedded system is a microcontroller.
The C Programming Language is the most popular and widely used programming language, embedded C Programming Language is an extension of C Program Language.
C is a flexible, well-structured and very compact language. It was designed to provide low-level access to memory, to provide language constructs that map efficiently to machine instructions, and to require minimal run-time support. Therefore, C was useful for many applications that had formerly been coded in assembly language, such as in system programming.
In conclusion knowing the compilation process for a program for a C compiler is essential for embedded development. Before knowing the process, we will define key concepts that the compiler performs when building an executable file for an embedded system.
Machine languages can be understood directly by the computer. As different processors have different hardware architectures, a computer can only directly understand its own machine language.
A program written in machine language constists of a sequence of binary processor instructions where every instruction consists of a sequence of bits.
10100101 01100000 01100101 01100001 10000101 01100010
The piece of code shown above is a program for a 6502 processor that adds the content of memory location 0x60 (hexadecimal) to the content of memory location 0x61 and stores the result in memory location 0x62. To improve readability, these bit sequences are often written in hexadecimal notation resulting for this example in:
A5 60 65 61 85 62
Assembly languages were developed to make the instructions more readable for humans. Therefore, the sequences of bits were replaced by symbolic codes (usually based upon English like abbreviations).
Unfortunately this results in code that is incomprehensible to computers until translated to machine language. The translator programs needed to accomplish this task are called assemblers.
The translation of the previous example in hexadecimal in assembly language results in the following which is more understandable by humans:
LDA 060 (load content at memory address 0x60) ADC 061 (add with carry to the content of memory address 0x61) STA 062 (store result in memory address 0x62)
One of the disadvantages of assembly languages is that each assembly language instruction corresponds to exactly one machine language instruction. As such, programs written in assembly can easily become very long. To speed up the process of writing programs, several high-level languages were developed. In all of these languages, single statements can accomplish substantial tasks.
The syntax used resembles normal written language (mostly English), but with very strict regulations.
A program written in a high-level language is incomprehensible to a computer until translated into machine language. Depending on the highlevel languages used, this translation is accomplished with a compiler or an interpreter. A compiler translates the complete source program at once. An interpreter translates one instruction at the time and executes that instruction directly.
C language as a high-level language
well-structured and very efficient programming language available for most types of machines. It is widely used to develop systems that demand high performance, such as operating systems, embedded systems, real-time systems and communication systems as well as application software.
Using a text editor we can create a file type program1.c where the source code of the program is written.
In the second phase, the source code is compiled (translated) into machine language instructions resulting in an object code (program1.obj or program1.o). In a C system, this step is preceded by the execution of the preprocessor. The preprocessor searches for special directives written in the source code
and performs the actions described.
After the execution of the preprocessor, the source code is translated into machine language instructions. When the compiler cannot recognize a certain statement, because of a syntax error for instance, the compiler issues an error message. These types of errors are called syntax errors or compile errors and must be corrected by editing the source code.
All library functions referred inside the source code are added to the object file to create an executable program. (program1.exe). This process is called linking and is performed by a linker. Errors occurring during this step are called link errors (such as calling non existing functions). Also in this case, the source code needs to be corrected and all steps starting from phase 1 have to be executed again.
Before execution, the program must be placed into memory. This is done by the loader. Additional components from shared libraries are loaded into memory as well.
Finally, the computer can execute the program. Errors occurring at this stage are called run time errors. These errors are caused by mistakes in the program logic like e.g. errors in the solution method used.

We begin by writing a simple program that prints the text “Hello, world” on the screen.
/*
My first program
this program will print the line "Hello, world" to the screen
*/
#include <stdio.h>
int main(void)
{
printf("Hello, world\n");
return 0;
}
All text placed in between /* and */ is regarded as comments. The C compiler will ignore this portion of the code. Since C is very well suited to write big programs, it is very important to make sure every part of the program is started with meaningful comments.
These should at least indicate how the code works and for sure describe the outcome of that portion of the program.
Every line starting with # is a preprocessor directive. Before compiling, the preprocessor will look for these directives and process them first. “#include ” tells the preprocessor to include the header file “stdio.h” into the program. This file contains some function declarations (not definitions) for standard input/output functions like printf, scanf, … The function definitions are located in the C standard library. The linker will take care of including all used functions into the executable. Next to function declarations, this header file also contains some definitions of constants (e.g. EOF, NULL) and macro’s.
Every C program contains one or more functions. A function can be recognized by the parentheses () following the function name. One of those functions must be the function main(). Every C program will automatically start by executing this main function. When calling a function, extra information can be passed on to that function. This extra information is called a function parameter and is put in between the parentheses ().
To indicate that the main function does not have any parameters, the keyword void is used.
Functions can also return a result. The keyword int to the left of main indicates that the main function returns an integer value. The full content of the main function is written in between an opening brace
( { ) and a closing brace ( } ). Every step of the function is described in a statement. The full collection of statements in between braces is called a block.
The main function in this example contains 2 statements: “printf(“…”);” and “return 0;”.
A statement consists of an expression, followed by a semicolon (;). In C, every statement, even the last statement of a function, ends in a semicolon. There is no semicolon after “int main()” because here we start the definition of the function main, hence “int main()” is not a statement. However, if a function call is made, a semicolon needs to be added at the end.
It will write all text in between the double quotes (“”) literally to the screen, except if an escape character () is used. When such an escape character is encountered in a string, the compiler takes the next character into account and combines it with the backslash to determine what to do. \n for instance means a newline.
Therefore adding \n to the string in the printf function causes the cursor to position itself at the beginning of the next line. The statement “return 0;” is added at the end of the main() function to set the return value of that function to the integer value 0.
Deja una respuesta