Embedded Internship Report
Embedded Internship Report
EMAIL ID [email protected]
PHONE NO 8792697647
ABOUT COMPANY
Core values:
To incorporate good business practices in order to achieve customer satisfaction and treating
the customers with respect and faith.
To integrate honesty, integrity and business ethics into all aspects of the business function-
ing.
Goals:
To improve, grow and become more efficient in the field electronics engineering and soft-
ware development and develop a strong base of key clients.
To do Innovations in Software field and provide quality services to deliver a range of prod-
ucts.
IT Service
2.4 DOMAINS
TAKE IT SMART (OPC) PVT.LTD have working with several domains like-
IT
Digital marketing
2.5 DEPARTMENTS
Marketing: These are the main section of the market departments:
Sales department is responsible for the sales and distribution of the products to the different
regions.
Promotion department decides on the type of promotion method for the products, arranges
advertisements and the advertising media used.
Mobile: +91-8050104212
Email: [email protected]
Website: www.takeitsmart.in
CONTENTS Page No
1. INTRODUCTION
3.1.1 Architecture 9
3.1.4 Features 13
3.1.6 Ports 15
3.1.8 USART 15
3.3 Relay 17
4. SOFTWARE REQUIREMENTS
4.3 Embedded C 27
CHAPTER 1
INTRODUCTION
The combination of hardware and software either fixed in capability or programmable is know
Embedded system , that is designed for a specific function. Embedded systems is device used
to control, monitor the operation of equipment’s , machinery or plant. “Embedded” refers to
the internal part.
These types of systems are designed with a 8 or 16-bit single microcontroller, that
may be generated by a battery.
For the developing embedded software for this embedded systems, the im-
portant programming tools are editor, assembler, cross assembler and IDE.
Medium scale embedded systems are designed with a 16 or 32-bit MC, RISCs or DSPs.
Both hardware and software complexities are available.
For the developing embedded software for this system, the main tools are C, C+
+, JAVA, Visual,RTOS, debugger, source code engineering tool, simulator and
IDE.
CHAPTER 2
HARDWARE COMPONENTS
2 Power Supply
3 Relay
6 BLUETOOTH transceiver.
7. DC motor
8. Flame Sensor
10. MQ135
12. IR sensor
13. NodeMCU
Atmega328 microcontroller
The microcontroller is at the core of every embedded module. Hence, great care must be exercised
in choosing the right microcontroller without compromising on functionality. Keeping in view many
factors that governed the correct implementation of our project the Atmega48 microcontroller from
Atmel Corporation’s AVR microcontroller family was chosen. Few crucial reasons may be cited so
as to justify our choice of this microcontroller. The first being, that all AVR microcontrollers are
designed to deliver more performance at lesser power consumption. It is compatible with popular
protocols like I2C and SPI. It also has advanced features like an on chip analog to digital converter,
six pulse width modulation channels, and data retention is supported up to a hundred years at 25º C.
Also compilers for the Atmega48/8 are available free of cost from the manufacturer. An added
advantage is that the AVR series can be programmed using the AVRGCC (GNU C compiler), thus
making it an undisputed choice for even GNU/Linux based programmers. The Atmega48
microcontroller has execution speeds of up to one MIPS per MHz of clock frequency. Elucidating
the specifications of the CPU of the AVR, it is an 8 bit microcontroller with advanced RISC
architecture. The CPU is designed for the stellar combination of parallelism and performance. Thus
the CPU uses the Harvard architecture (separate memories and buses for program and data). The
CPU also accommodates a 32 general purpose 8-bit registers.
8
3.1.1 Architecture
The Atmega328 is a low-power CMOS 8-bit microcontroller based on the AVR enhanced RISC
architecture. By executing powerful instructions in a single clock cycle, the ATmega88 achieves
throughputs approaching 1 MIPS per MHz allowing the system designer to optimize power
consumption versus processing speed. The AVR core combines a rich instruction set with 32 general
purpose working registers. All the 32 registers are directly connected to the Arithmetic Logic Unit
(ALU), allowing two independent registers to be accessed in one single instruction executed in one
clock cycle. The resulting architecture is more code efficient while achieving throughputs up to ten
times faster than conventional CISC microcontrollers. The architectural block diagram is as shown
in the next page.
Figure
3.1: Architectural Block Diagram of ATmega 328
3.1.2 AVR CPU Core
This section discusses the AVR core architecture in general. The main function of the CPU core is
to ensure correct program execution. The CPU must therefore be able to access memories, perform
calculations, control peripherals, and handle interrupts.
The Idle mode stops the CPU while the SRAM, Timer/Counters, USART, 2-wire Serial Interface,
SPI port, and interrupt system continue to function. In the Power-down mode, the register contents
are saved but the oscillator is frozen until an interrupt is raised or the hardware is reset. In the
Power-save mode, the asynchronous timer is running while the remaining peripheral components of
the device are sleeping
3.1.6 Ports
The ports of the AVR have read-modify-write functionality when used as general digital I/O ports,
as stated in the datasheet of the device. The ports are bi-directional I/O ports with optional internal
pull-ups. Each port pin mainly has three register bits which are DDxn, PORTxn and PINxn. DDxn is
the data direction bit and indicates input or output at a particular pin of any port .
If DDxn is set to one, the pin is used as output pin, else it is an input pin. If PORTxn is written to a
logic one, and if DDxn is set to zero that particular pin’s internal pull up resistor is activated. The
DDxn is accessed at the DDRx register, the PORTxn is in the PORTx register and the PINxn is at
the PINx register. Writing a logic one to PINxn will toggle PORTxn. The alternate functions of the
port pins and the port registers are explained at the end as part of the datasheets. The pin value can
be read at any time through the PINxn register bit, irrespective of the DDxn pin setting.
3.1.7 Analog to digital converter
The Atmega48 is equipped with a successive approximation analog to digital converter with a
resolution of 10 bits. All the input channels of the ADC are connected to a multiplexer.
3.1.8 USART
15
Serial transmission of digital information (bits) through a single wire or other medium is
much more cost effective than parallel transmission through multiple wires. A UART is used to
convert the transmitted information between its sequential and parallel form at each end of the link.
Each UART contains a shift register which is the fundamental method of conversion between serial
and parallel forms.
The UART usually does not directly generate or receive the external signals used between
different items of equipment. Typically, separate interface devices are used to convert the logic level
signals of the UART to and from the external signaling levels. Communication may be "full duplex"
(both send and receive at the same time) or "half duplex" (devices take turns transmitting and
receiving).
3.1.8.1 Features
The best way to develop quickly an IoT application with less Integrated circuits to add is to choose
this circuit “NodeMCU”. Today,we will give a detailed Introduction on NodeMCU V3. It is an open-
source firmware and development kit that plays a vital role in designing a proper IoT product using a
few script lines.
Arduino Modules and Microcontrollers have always been a great choice to incorporate automation
into the relevant project. But these modules come with a little drawback as they don’t feature a
built-in WiFi capability, subsequently, we need to add external WiFi protocol into these devices to
make them compatible with the internet channel.
This is the famous NodeMCU which is based on ESP8266 WiFi SoC. This is version 3 and it is
based on ESP-12E (An ESP8266 based WiFi module). NodeMCU is also an open-source
firmware and development kit that helps you to prototype your IOT product within a few
LUA script lines, and of course you can always program it with Arduino IDE.
In this article, We will try present useful details related to this WiFi Development Kit, its main
features, pinout and everything we need to know about this module and the application domain.
Introduction NodeMCU V3
NodeMCU V3 is an open-source firmware and development kit that plays a vital role in designing an
IoT product using a few script lines.
Multiple GPIO pins on the board allow us to connect the board with other peripherals and are
capable of generating PWM, I2C, SPI, and UART serial communications.
The interface of the module is mainly divided into two parts including both
Firmware and Hardware where former runs on the ESP8266 Wi-Fi SoC and later is
based on the ESP-12 module.
The firmware is based on Lua – A scripting language that is easy to learn, giving
a simple programming environment layered with a fast scripting language that connects you with a
well-known developer community.
And open source firmware gives you the flexibility to edit, modify and rebuilt
the existing module and keep changing the entire interface until you succeed in optimizing the
module as per your requirements.
USB to UART converter is added on the module that helps in converting USB data
to UART data which mainly understands the language of serial communication.
Instead of the regular USB port, MicroUSB port is included in the module that
connects it with the computer for dual purposes: programming and powering up the board.
The board incorporates status LED that blinks and turns off immediately, giving
you the current status of the module if it is running properly when connected with
the computer.
The ability of module to establish a flawless WiFi connection between two
channels makes it an ideal choice for incorporating it with other embedded devices like Raspberry Pi.
NodeMCU V3 Pinout
NodeMCU V3 comes with a number of GPIO Pins
Power supply is used to energies the whole module. The power supply can be in the form
of wired or battery. In our project 12v battery/adapteris used as a power supply.
16
3.3 Relay
Relay is an electrically operated switch. Relays allow one circuit to switch a
second circuit which can be completely separate from the first. Relays can switch AC and
DC, transistors can only switch DC. Relays can switch higher voltages than standard
transistors. Relays are often a better choice for switching large currents (> 5A). Relays
can switch many contacts at once.
10
Figure 3.5: Circuit diagram of relay
17
The most common type of piezoelectric resonator used is the quartz crystal, so oscillator
circuits designed around them were called "crystal oscillators".A crystal is a solid in
which the constituent atoms, molecules, or ions are packed in a regularly ordered,
repeating pattern extending in all three spatial dimensions.
The initial markets for the BLUETOOTH Alliance include Consumer Electronics,
Energy Management and Efficiency, Health Care, Home Automation, Building
Automation and Industrial Automation.
11
24
Flame Sensor:
A sensor which is most sensitive to a normal light is known as a flame sensor. That’s why
this sensor module is used in flame alarms. This sensor detects flame otherwise
wavelength within the range of 760 nm – 1100 nm from the light source. This sensor can
be easily damaged to high temperature. So this sensor can be placed at a certain distance
from the flame. The flame detection can be done from a 100cm distance and the detection
angle will be 600. The output of this sensor is an analog signal or digital signal. These
sensors are used in fire fighting robots like as a flame alarm.
12
IR SENSOR
DHT11:
The requirement of a sensor is to react for input physical property and convert it
into an electrical signal that is suitable with electronic circuits (Fraden 2010, 2).
Sensors are electronic devices that measure a physical quality such as light or
temperature and convert it to a voltage. Example of digital temperature and
moisture sensor is presented in Graph 9. There are two types of sensors: digital
and analog.
The output of digital sensor where is between 0 and 1 which can translate to
sensors voltage range. Analog sensor can output any value between its voltage
ranges. According to the reading from the sensor changes its output. Digital
sensor output is ON (1) often 5v, or OFF (0), 0v. Analog sensor is used to measure
precise numerical information like temperature or speed. Analog sensors can
13
output almost an infinite range of values. Output pin of sensor connected to
input pinafore denim show the digital form is obtained by the conversion of data.
Some sensors have analog to digital converter embedded to the sensor so the data
is outputted as digital data. After data is processed to digital form, it can be
processed on the microcontroller. (Karvinen & Karvinen, 2014.)
14
Chapter-4
SOFTWARE REQUIREMENTS
You will be needing Arduino IDE software to write and upload the programming logic
onto the Arduino Uno board
4.2 Embedded C
Embedded C is extensive and contains many advanced concepts. The range of modules
covers a full introduction to C, real-time and embedded systems concepts through to the
design and implementation of real time embedded or standalone systems based on real-
time operating systems and their device drivers. Real time Linux (RTLinux) is used as an
example of such a system. The modules include an introduction to the development of
Linux device drivers. Embedded C covers all of the important features of the C language
as well as a good grounding in the principles and practices of real-time systems
development including the POSIX threads (pthreads) specification.
The design of the modules is intended to provide an excellent working knowledge of the
C language and its application to serious real time or embedded systems. Those wanting
in-depth training specifically on RTLinux or Linux kernel internals should contact us to
discuss their requirements; this set of modules is geared more towards providing the
groundwork for approaching those domains rather than as in-depth training on a specific
approach.
15
GENERAL ROLES AND RESPONSIBILITIES
The department where I was interned helped me bridge the gap between
the industry and academia by providing the complete experience to on board
using the various microcontroller boards like AVR microcontroller, nodemcu
microcontroller etc.
16
34
Chapter-5
BIBLIOGRAPHY
1.https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/8-bit-mcus/avr-mcus
2.https://arduino-esp8266.readthedocs.io/en/latest/
17
Take IT Smart Report
Introduction to C:
● C-language is always a preferred choice for Embedded software development because of
the following reasons:
○ C supports low-level access through pointers
○ C-language is processor independent
○ C-data types are suitable for Embedded Systems because these data types are
portable.
○ C supports Bit Manipulation, Memory management, and multithreading fea-
tures.
Difference Between C and Embedded C
S.No. C Embedded C
5 Here, the traditional or Here, we need a specific compiler that can help
standard compilers are used to in generating micro-controller based results.
run the program.
Debuggers:
GDB (GNU Debugger): A powerful command-line debugger.
JTAG (Joint Test Action Group): A standard for testing and debugging integrated
circuits, often used in embedded systems.
In-circuit emulators: Devices that allow debugging without physically having to
disassemble the target device.
Simulators and Emulators:
QEMU: A versatile emulator that can simulate various architectures.
Proteus: A simulator for electronic circuits, allowing you to test the interaction of
your software with the hardware.
MATLAB/Simulink: Used for modeling and simulating real-time systems, includ-
ing embedded systems
Other Tools:
Static analyzers: Tools that analyze code for potential errors and vulnerabilities
before compilation.
Code coverage tools: Help ensure that your code is thoroughly tested.
Profiling tools: Tools that help identify performance bottlenecks in your code.
Build tools: Makefiles or CMake are used to automate the build process.
Real-time operating systems (RTOS): Like FreeRTOS, provide a framework for
scheduling tasks and managing resources in a real-time environment.
Version control systems: Like Git, help manage and track changes to your code.
Fundamentals of C:
19
Key Characteristics of Embedded C
Efficiency: In Embedded C we can create an efficient code to optimize the limited re-
sources available in embedded systems. It aims to minimize memory usage and maximize
performance.
Direct Hardware Interaction: Embedded C allows programmers to interact directly with
hardware components, such as microcontrollers, sensors, actuators, and other peripher-
als. This direct interaction facilitates precise control over the hardware, critical in embed-
ded applications.
Low-level Programming: Embedded C involves low-level programming, which deals with
hardware-specific details like memory addresses, I/O ports, and register manipulation.
This level of control is essential for efficiently managing hardware resources.
Real-time Operations: Embedded systems often operate in real-time environments, re-
quiring precise timing and response to events. Embedded C allows programmers to han-
dle real-time tasks efficiently.
Structure of Embedded C Program
Comments: Comments are readable text written to help user understand the code easily.
They are ignored by compiler and do not take up any memory in the final code. There are
two types of comments, Single line comments and Multiline comments.
Preprocessor Directive: In Embedded C Preprocessor Directives are represented using
#include or #define. Preprocessor Directives are used to indicate a header file specific to
a microprocessor or microcontroller which contains all the functions, SFR's and the bits in
those SFR's. reg51 header file is used in case of 8051 microcontroller.
Global Variables: Global variables as the name suggests are global to program that is
they can be accessed anywhere in the program. Global variables are static variables and
are placed in RAM memory locations.
Local Variables: Local variables in contrast to global variables are confined to their re-
spective functions. Normally these variables are placed in stack or registers. It is only
valid within the function in which it is declared.
Function: Function is a group of statements that together performs a task. A function
declaration tells the compiler about the name, return type and parameter of the func-
tion. A function definition provides actual body of the function.
Main Function: Every Embedded C program has one main function and may contain one
or more functions in the main functions. The program execution starts from the main
function, and it is a core of every execution. If more than one main function is written in
the code, then compiler will confuse from where to start the program execution.
Standard Embedded C Data Types
Data Type Bits Range
bit 1 0-1
sbit 1 0-1
20
sfr 8 0 - 255
sfr16 16 0 - 65535
Text Segment
After we compile the program, a binary file generates, which is used to execute our pro-
gram by loading it into RAM. This binary file contains instructions, and these instructions
get stored in the text segment of the memory.
Text segment has read-only permission that prevents the program from accidental modi-
fications.
Text segment in RAM is shareable so that a single copy is required in the memory for fre-
quent applications like a text editor, shells, etc.
Initialized data segment:
Initialized data segment or data segment is part of the computer's virtual memory space of a C
program that contains values of all external, global, static, and constant variables whose values
are initialized at the time of variable declaration in the program. Because the values of variables
can change during program execution, this memory segment has read-write permission. We can
further classify the data segment into the read-write and read-only areas. const variable comes
under the read-only area. The remaining types of variables come in the read-write area. For
example,
21
const hello* = "Data segment";
Here, the pointer variable hello comes under the read-write area, and the value of the string
literal "Data segment" lies comes under initialized read-only data segment.
#include<stdio.h>
int main() {
// static variable stored in initialized data segment
static int a = 10;
// ...
return 0;
}
In this example, variables global_var and pointer hello are declared outside the scope of main()
function because of which they are stored in the read-write part of the initialized data
segment but, global variable global_var2 is declared with the keyword const and hence it is
stored in the read-only part of initialized data segment. Static variables like a are also stored in
this part of the memory.
Uninitialized data segment
An uninitialized data segment is also known as bss (block started by symbol). The program loaded
allocates memory for this segment when it loads. Every data in bss is initialized to arithmetic
0 and pointers to null pointer by the kernel before the C program executes. BSS also contains all
the static and global variables, initialized with arithmetic 0. Because values of variables stored in
bss can be changed, this data segment has read-write permissions.
#include <stdio.h>
int main()
{
// Uninitialized static variable stored in bss
static int static_variable;
// ..
printf("global_variable = %d\n", global_variable);
printf("static_variable = %d\n", static_variable);
return 0;
}
Output
global_variable = 0
22
static_variable = 0
Here, both the variables global_variable and static_variables are uninitialized. Hence they are
stored in the bss segment in the memory layout in C. Before the program execution begins, these
values are initialized with value 0 by the kernel. This can be verified by printing the values of the
variable as shown in the program.
Stack
The stack segment follows the LIFO (Last In First Out) structure and grows down to the lower
address, but it depends on computer architecture. Stack grows in the direction opposite to heap.
Stack segment stores the value of local variables and values of parameters passed to a
function along with some additional information like the instruction's return address, which is to
be executed after a function call.
Stack pointer register keeps track of the top of the stack and its value change when push/pop
actions are performed on the segment. The values are passed to stack when a function is
called stack frame. Stack frame stores the value of function temporary variables and some
automatic variables that store extra information like the return address and details of the caller's
environment (memory registers). Each time function calls itself recursively, a new stack frame is
created, which allows a set of variables of one stack frame to not interfere with other variables of
a different instance of the function. This is how recursive functions work.
Let us see an example to understand the variables stored in the stack memory segment.
#include<stdio.h>
void foo() {
// local variables stored in the stack
// when the function call is made
int a, b;
}
int main() {
// local variables stored in the stack
int local = 5;
char name[26];
foo();
// ..
return 0;
}
Here, all the variables are stored in a stack memory layout in C because they are declared inside
their parent function's scope. These variables only take the space in memory till their function is
executed. For example, in the above code, the first main() starts its execution, and a stack frame
for main() is made and pushed into the program stack with data of variables local and name.
Then in main, we call foo, then another stack frame is made and pushed for it separately, which
23
contains data of variables a and b. After the execution of foo, its stack frame is popped out, and
its variable gets unallocated, and when the program ends, main's stack frame also gets popped
out.
Heap
Heap is used for memory which is allocated during the run time (dynamically allocated memory).
Heap generally begins at the end of bss segment and, they grow and shrink in the opposite
direction of the Stack. Commands like malloc, calloc, free, realloc, etc are used to manage
allocations in the heap segment which internally use sbrk and brk system calls to change memory
allocation within the heap segment. Heap data segment is shared among modules loading
dynamically and all the shared libraries in a process.
#include <stdio.h>
#include <stdlib.h>
int main() {
// memory allocated in heap segment
char *var = (char*) malloc ( sizeof(char) );
// ..
return 0;
}
Here, we create a variable of data type char by allocation memory of size 1 byte (equal to size
of char in C) at the time of program execution. Because the variable is created dynamically such
variables are initialized in the heap segment of the memory.
Command-line arguments
When a program executes with arguments passed from the console like argv and argc and other
environment variables, the value of these variables gets stored in this memory layout in C.
#include<stdio.h>
return 0;
}
Output:
~$ gcc file_1.c -o file_1
~$ ./file_1 100 23 43 69
File name = ./file_1
Number of arguments passed = 4
Value of Argument_1 = 100
Value of Argument_2 = 23
Value of Argument_3 = 43
Value of Argument_4 = 69
24
This example explains how command-line arguments are passed and used in the program. Here,
this segment stores the value of variables argc and argv where argc stores the number of
arguments passed and argv stores the value of actual parameters along with file name.
What is a Compilation?
Before diving into the traditional definition of compilation, let us consider an example where
there is a person A who speaks Hindi language and person A wants to talk to person B who only
knows English language, so now either of them requires a translator to translate their words to
communicate with each other. This process is known as translation, or in terms of programming,
it is known as compilation process.
The compilation process in C is converting an understandable human code into a Machine
understandable code and checking the syntax and semantics of the code to determine any syntax
errors or warnings present in our C program. Suppose we want to execute our C Program written
in an IDE (Integrated Development Environment). In that case, it has to go through several
phases of compilation (translation) to become an executable file that a machine can understand.
/* This is a
multi-line comment in C */
#include<stdio.h>
int main()
{
// this is a single-line comment in C
return 0;
}
// if we uncomment the below line, then the program will print AGE in the output.
// #define AGE 18
int main()
{
// if `AGE` is defined then print the `AGE` else print "Not Defined"
#ifdef AGE
printf("Age is %d", AGE);
26
#else
printf("Not Defined");
#endif
return 0;
}
OUTPUT:
Not Defined
Explanation:
#ifdef directive checks if the macro AGE is defined or not, and as we have commented
the #define statement the #ifdef AGE block of code will not execute and control flow will move to
the #else block and Not Defined will be printed on the output screen, #endif ensures that the
conditional compilation block ends there.
Now let's see the below figure that shows how a pre-processor converts our source code file into
an intermediate file. Intermediate file has an extension of .i, and it is the expanded form of our C
program containing all the content of header files, macros expansion, and conditional
compilation.
b. Compiling
Compiling phase in C uses an inbuilt compiler software to convert the intermediate (.i) file into
an Assembly file (.s) having assembly level instructions (low-level code). To boost the
performance of the program C compiler translates the intermediate file to make an assembly file.
Assembly code is a simple English-type language used to write low-level instructions (in micro-
controller programs, we use assembly language). The whole program code is parsed (syntax
analysis) by the compiler software in one go, and it tells us about any syntax
errors or warnings present in the source code through the terminal window.
The below image shows an example of how the compiling phase works.
27
c. Assembling
Assembly level code (.s file) is converted into a machine-understandable code (in
binary/hexadecimal form) using an assembler. Assembler is a pre-written program that translates
assembly code into machine code. It takes basic instructions from an assembly code file and
converts them into binary/hexadecimal code specific to the machine type known as the object
code.
The file generated has the same name as the assembly file and is known as an object file with an
extension of .obj in DOS and .o in UNIX OS.
The below image shows an example of how the assembly phase works. An assembly file area.s is
translated to an object file area.o having the same name but a different extension.
d. Linking
Linking is a process of including the library files into our program. Library Files are some
predefined files that contain the definition of the functions in the machine language and these
files have an extension of .lib. Some unknown statements are written in the object (.o/.obj) file
that our operating system can't understand. You can understand this as a book having some
words that you don't know, and you will use a dictionary to find the meaning of those words.
Similarly, we use Library Files to give meaning to some unknown statements from our object file.
The linking process generates an executable file with an extension of .exe in DOS and .out in
UNIX OS.
The below image shows an example of how the linking phase works, and we have an object file
having machine-level code, it is passed through the linker which links the library files with the
object file to generate an executable file.
28
Storage classes:
auto
This is the default storage class for all the variables declared inside a function or a block. Auto
variables can be only accessed within the block/function they have been declared and not
outside them (which defines their scope).
Properties of auto Variables
Scope: Local
Default Value: Garbage Value
Memory Location: RAM
Lifetime: Till the end of its scope
static
This storage class is used to declare static variables that have the property of preserving their
value even after they are out of their scope! Hence, static variables preserve the value of their
last use in their scope.
Properties of static Storage Class
Scope: Local
Default Value: Zero
Memory Location: RAM
Lifetime: Till the end of the program
register
This storage class declares register variables that have the same functionality as that of the auto
variables. The only difference is that the compiler tries to store these variables in the register of
the microprocessor if a free register is available making it much faster than any of the other
variables.
Properties of register Storage Class Objects
Scope: Local
Default Value: Garbage Value
Memory Location: Register in CPU or RAM
Lifetime: Till the end of its scope
extern
Extern storage class simply tells us that the variable is defined elsewhere and not within the
same block where it is used. Basically, the value is assigned to it in a different block and this can
be overwritten/changed in a different block as well.
Also, a normal global variable can be made extern as well by placing the ‘extern’ keyword before
its declaration/definition in any function/block.
Properties of extern Storage Class Objects
Scope: Global
Default Value: Zero
Memory Location: RAM
29
Lifetime: Till the end of the program.
Type Qualifiers
Type qualifiers in C are keywords that modify the properties of variables. They provide additional
information to the compiler about how a variable should be treated, enabling optimizations and
ensuring correct program behavior, especially when dealing with hardware or
concurrency. There are four main type qualifiers in C: const, volatile, restrict,
and _Atomic (introduced in C11).
const
The const qualifier specifies that a variable's value cannot be changed after initialization. It is
used to define constants and to prevent accidental modification of data.
volatile
The volatile qualifier indicates that a variable's value can be changed by external factors outside
the program's control, such as hardware or other threads. This prevents the compiler from
optimizing reads or writes to the variable, ensuring that the most up-to-date value is always
used.
restrict
The restrict qualifier is used with pointers and indicates that the pointer is the only way to access
the memory it points to within a specific scope. This allows the compiler to perform more
aggressive optimizations, as it can assume that there are no other aliases to the memory
location.
void copy_array(int* restrict dest, const int* restrict src, int size);
_Atomic
The _Atomic qualifier (introduced in C11) specifies that access to a variable is atomic, meaning
that it cannot be interrupted by other threads. This is crucial for writing thread-safe code.
Flow control:
30
In Embedded C, control statements are essential for managing program flow, making decisions,
and repeating actions based on conditions. These statements include conditional statements
(like if, else, and switch), loop statements (like for, while, and do-while), and jump statements
(like break, continue, and goto).
EXAMPLE:
#define PIN_LED 13
void setup() {
pinMode(PIN_LED, OUTPUT);
}
void loop() {
// Blink LED
digitalWrite(PIN_LED, HIGH);
delay(1000);
digitalWrite(PIN_LED, LOW);
delay(1000);
Explanation:
The if statement checks the state of a button (defined by BUTTON_PIN).
If the button is pressed (HIGH), one block of code is executed; otherwise, the other block
is executed.
The delay function introduces pauses, controlling the blink rate of the LED.
The code demonstrates how control statements can be used to respond to external in-
31
puts and manage the execution flow of an embedded system.
Operators:
Operators are symbols that perform operations on operands (variables and values). C language
has several types of operators, including:
Arithmetic Operators:
Used for mathematical calculations.
+ (addition)
- (subtraction)
* (multiplication)
/ (division)
% (modulus - remainder of division)
Relational Operators:
Used for comparing values.
== (equal to)
!= (not equal to)
> (greater than)
< (less than)
>= (greater than or equal to)
<= (less than or equal to)
Logical Operators:
Used for combining or negating logical expressions.
&& (logical AND)
|| (logical OR)
! (logical NOT)
Bitwise Operators:
Used for manipulating individual bits in binary numbers.
& (bitwise AND)
| (bitwise OR)
^ (bitwise XOR)
~ (bitwise NOT)
<< (left shift)
>> (right shift)
Assignment Operators:
Used for assigning values to variables.
= (simple assignment)
+= (add and assign)
-= (subtract and assign)
*= (multiply and assign)
/= (divide and assign)
%= (modulus and assign)
&= (bitwise AND and assign)
|= (bitwise OR and assign)
^= (bitwise XOR and assign)
<<= (left shift and assign)
>>= (right shift and assign)
Other Operators:
sizeof (returns the size of a variable or data type in bytes)
? : (ternary or conditional operator)
, (comma operator)
. and -> (member access operators for structures and unions)
& and * (address-of and dereference operators for pointers)
32
Functions:
A function in C is a set of statements that when called perform some specific tasks. It is the basic
building block of a C program that provides modularity and code reusability. The programming
statements of a function are enclosed within { } braces, having certain meanings and performing
certain operations. They are also called subroutines or procedures in other languages.
Syntax of Functions in C
The syntax of function can be divided into 3 aspects:
1. Function Declaration
2. Function Definition
3. Function Calls
Function Declarations
In a function declaration, we must provide the function name, its return type, and the number
and type of its parameters. A function declaration tells the compiler that there is a function with
the given name defined somewhere else in the program.
Syntax
return_type name_of_the_function (parameter_1, parameter_2);
The parameter name is not mandatory while declaring functions. We can also declare the
function without using the name of the data variables.
Example
int sum(int a, int b); // Function declaration with parameter names
int sum(int , int); // Function declaration without parameter names
Function Definition
The function definition consists of actual statements which are executed when the function is
called (i.e. when the program control comes to the function).
A C function is generally defined and declared in a single step because the function definition
always starts with the function declaration so we do not need to declare it explicitly. The below
example serves as both a function definition and a declaration.
return_type function_name (para1_type para1_name, para2_type para2_name)
{
// body of the function
}
33
Function Call
A function call is a statement that instructs the compiler to execute the function. We use the
function name and parameters in the function call.
In the below example, the first sum function is called and 10,30 are passed to the sum function.
After the function call sum of a and b is returned and control is also returned back to the main
function of the program.
Role of functions
1. Library Function
A library function is also referred to as a “built-in function”. A compiler package already exists
that contains these functions, each of which has a specific meaning and is included in the
package. Built-in functions have the advantage of being directly usable without being defined,
whereas user-defined functions must be declared and defined before being used.
For Example:
pow(), sqrt(), strcmp(), strcpy() etc.
Advantages of C library functions
C Library functions are easy to use and optimized for better performance.
C library functions save a lot of time i.e, function development time.
C library functions are convenient as they always work.
// Driver code
int main()
{
int var1 = 3, var2 = 2;
printf("Before swap Value of var1 and var2 is: %d, %d\n",
var1, var2);
swap(var1, var2);
printf("After swap Value of var1 and var2 is: %d, %d",
var1, var2);
return 0;
}
Output
Before swap Value of var1 and var2 is: 3, 2
After swap Value of var1 and var2 is: 3, 2
2. Pass by Reference
The caller’s actual parameters and the function’s actual parameters refer to the same locations,
so any changes made inside the function are reflected in the caller’s actual parameters.
Example:
// Driver code
int main()
{
int var1 = 3, var2 = 2;
printf("Before swap Value of var1 and var2 is: %d, %d\n",
var1, var2);
swap(&var1, &var2);
printf("After swap Value of var1 and var2 is: %d, %d",
var1, var2);
return 0;
}
Output
Before swap Value of var1 and var2 is: 3, 2
After swap Value of var1 and var2 is: 2, 3
Arrays:
37
An array is a variable that can store multiple values. For example, if you want to store 100
integers, you can create an array for it.
int data[100];
How to declare an array?
dataType arrayName[arraySize];
For example,
float mark[5];
Here, we declared an array, mark, of floating-point type. And its size is 5. Meaning, it can hold 5
floating-point values.
It's important to note that the size and type of an array cannot be changed once it is declared.
Declare an Array
Few keynotes:
Arrays have 0 as the first index, not 1. In this example, mark[0] is the first element.
If the size of an array is n, to access the last element, the n-1 index is used. In this exam-
ple, mark[4]
Suppose the starting address of mark[0] is 2120d. Then, the address of the mark[1] will
be 2124d. Similarly, the address of mark[2] will be 2128d and so on.
This is because the size of a float is 4 bytes.
38
Initialize an Array
Here,
mark[0] is equal to 19
mark[1] is equal to 10
mark[2] is equal to 8
mark[3] is equal to 17
mark[4] is equal to 9
Multi-Dimensional Arrays
In C programming, you can create an array of arrays. These arrays are known as multidimensional
arrays. For example,
float x[3][4];
Here, x is a two-dimensional (2d) array. The array can hold 12 elements. You can think the array
as a table with 3 rows and each row has 4 columns.
Initialization of a 2d array
// Different ways to initialize two-dimensional array
Initialization of a 3d array
You can initialize a three-dimensional array in a similar way to a two-dimensional array. Here's an
example,
int test[2][3][4] = {
{{3, 4, 2, 3}, {0, -3, 9, 11}, {23, 12, 23, 2}},
{{13, 4, 56, 3}, {5, 9, 3, 5}, {3, 1, 4, 9}}};
Pointers:
The purpose of Pointers
Defining the pointers
The & and * operators
Pointers Assignments
Pointers Arithmetic
Types of pointers
Array of pointers
Pointers to structure and union
Pointers to Dynamic allocation
40
Pointers type casting
Dynamic memory allocation is the process of allocating and deallocating memory blocks during a
program's runtime, as opposed to static memory allocation which occurs at compile time. It
allows programmers to reserve memory as needed, use it, and then release it back for reuse,
making it more efficient than statically allocating a fixed amount of memory.
To allocate memory dynamically, library functions are malloc(), calloc(), realloc() and free() are
used. These functions are defined in the <stdlib.h> header file.
C malloc()
The malloc() function reserves a block of memory of the specified number of bytes. And, it
returns a pointer of void which can be casted into pointers of any form.
Syntax of malloc()
Example
The above statement allocates 400 bytes of memory. It's because the size of float is 4 bytes.
And, the pointer ptr holds the address of the first byte in the allocated memory.
calloc()
The malloc() function allocates memory and leaves the memory uninitialized, whereas
the calloc() function allocates memory and initializes all bits to zero.
Syntax of calloc()
Example:
The above statement allocates contiguous space in memory for 25 elements of type float.
ree()
41
Dynamically allocated memory created with either calloc() or malloc() doesn't get freed on their
own. You must explicitly use free() to release the space.
Syntax of free()
free(ptr);
This statement frees the space allocated in the memory pointed by ptr.
realloc()
If the dynamically allocated memory is insufficient or more than required, you can change the
size of previously allocated memory using the realloc() function.
Syntax of realloc()
42