0% found this document useful (0 votes)
6 views29 pages

Unit 3 Material

Uploaded by

mohanssaissai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views29 pages

Unit 3 Material

Uploaded by

mohanssaissai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 29

III.

Programming concepts and embedded programming in C, C++


Software programming in assembly language and in High-level language
Assembly Language Programming (ALP): Assembly Language Programming (ALP) involves
writing code that directly corresponds to the instructions a CPU executes. It is closer to
machine code, which gives a programmer fine control over the hardware.
Advantages of ALP:
1. Processor and Hardware Sensitivity: Assembly codes are tightly coupled with the
underlying hardware, giving complete control over the processor's internal
components such as registers, memory, and I/O devices. This allows for full
exploitation of the processor’s instruction set and addressing modes.
2. Compact Machine Code:Since assembly directly translates to machine code, the
resulting program is highly efficient in terms of memory usage. The program doesn't
need data type declarations, conditions, or rules that are required in high-level
languages (HLL).
3. Modular Programming in Assembly:Assembly supports modular programming,
where a program is broken into reusable components. Each module performs a
specific task, and a set of functions can be reused across different parts of the
program.

4. Top-Down Design Approach: The top-down design approach starts with designing
the main program, followed by modules, sub-modules, and then individual functions.
This helps in managing the complexity of the code by breaking it down into smaller
parts.

High-Level Language Programming (HLL): High-level language (HLL) programming, like C


or C++, abstracts many of the hardware-specific details, making development faster and
easier. HLLs are commonly used for complex system development.
Advantages of HLL:
1. Short Development Cycle:HLLs offer a short development cycle, even for complex
systems, due to the use of built-in routines, standard libraries, modular programming,
and object-oriented or top-down design approaches. This makes them well-suited for
larger applications such as operating systems, device drivers, and network
communication systems.
2. Use of Functions: Functions in C or methods in Java allow encapsulation of code into
reusable blocks.
3. Library Functions: HLLs come with standard libraries that save development time
by providing pre-written code for common tasks. For instance, mathematical
functions like sqrt () or time functions like delay () and sleep () are readily available in
most compilers. Embedded systems often include system-specific libraries tailored to
specific hardware operations.
4. Reusing Code for Identical Devices: Instead of writing assembly code for each
identical device, HLLs allow reusing code through modular design. For instance, for
UART devices in embedded systems, device drivers written in HLLs can be reused
across multiple systems by calling functions with specific arguments, making the code
more flexible and reusable.
5. Modular Programming: Similar to assembly, HLLs support a modular programming
approach. This involves dividing the program into reusable software components
(modules), which can be tested independently.

6. Top-Down Design Approach:HLLs often follow the top-down design approach,


where the overall system is designed first, followed by the development of individual
modules and functions. This is an efficient way to manage complex applications,
ensuring that the system is structured and easier to debug.
7. Data Type Declarations:HLLs provide the flexibility to declare data types, which
makes it easier to work with different kinds of data. For example, C allows the use of
data types like int, unsigned int, short, and long to represent integers of different sizes
and ranges.Data types also help in defining a set of permissible operations for each
type, making code more readable and maintainable.
8. Bottom-Up Design Approach:In the bottom-up design approach, programming starts
with creating smaller sub-modules for specific tasks and then integrating them into
larger modules. For example, a real-time clock software timer function (RTCSWT)
can be developed and tested independently before being integrated into a larger
system.
Assembly Language High-Level Language
Feature
Programming Programming (C)

Control over Precise and direct control over Indirect control through
Hardware hardware. abstractions.

Difficult to write and debug, Easier to write, debug, and


Ease of Development
processor-specific. maintain.

Slower due to hardware-specific Faster with built-in functions


Development Speed
coding. and libraries.

High portability, can be


Portability Limited, specific to the processor. compiled for different
platforms.

Application Ideal for device drivers, real-time Best for general applications
Suitability operations. and complex system logic.

Use of Functions and Limited or manual function Extensive use of pre-built


Libraries creation. libraries and reusable functions.

Supports both top-down and


Modular and top-down design bottom-up design, with added
Design Approach
possible. support for object-oriented
design (in C++).

Manual memory management, no Abstracted data types and easier


Data Handling
abstraction. memory management.
C Program Elements: Header and Source Files, and Preprocessor
Directives:
The basic elements of a C program can be broken down into preprocessor declarations, the
main function, and custom functions or interrupt service routines (ISRs). Understanding how
these elements interact is key to efficient program design.
Preprocessor declarations, definitions, and statements: Preprocessor directives are
instructions to the compiler to perform specific tasks before actual compilation starts.
Commonly used preprocessor directives include #include, #define.
Main function: Every C program has a main () function, which acts as the entry point for
execution. This function may call other user-defined or library functions.
Functions, Exceptions, and ISRs: Custom functions are defined by the programmer to break
down tasks into smaller, manageable components. Exceptions and ISRs (Interrupt Service
Routines) handle exceptional conditions or hardware interrupts in embedded systems.
Preprocessor directives handle tasks like including files, defining constants, and global
declarations before the actual program code begins compiling. They help manage repetitive
tasks and improve code organization.
1. The #include directive brings external files into your program. These files could be
standard libraries, user-defined header files, configuration files, or other data files.
Example:
#include <stdio.h>// Standard input-output functions
#include "myheader.h" // User-defined header file
2. Preprocessor definitions can declare global variables, accessible throughout the
program. Global variables persist across functions and can be modified or accessed
anywhere in the program.

3. Constants are defined using the #define directive. This makes code easier to modify,
as changing a constant's value in one place affects its value throughout the program.
Example:
#define PI 3.14159 // Constant for the value of Pi
Key Program Elements: Header and Source Files, Preprocessor Directives
1. Include Directive for the Inclusion of Files
In C, the #include directive is used to insert the contents of a file into the program at compile
time. Files included can be:
Header Files: These files typically end with .h and include function prototypes, macro
definitions, global variables, and constants. Standard C libraries, like stdio.h, provide basic
I/O functions, while user-defined header files allow custom functions and data structures to
be shared across multiple source files.
#include <math.h> // For mathematical functions like sqrt()
#include "network.h" // For custom network-related functions

Source Code Files: These files contain actual program code and typically have .c extensions.
In embedded systems, the application source file will contain the main function and calls to
other functions.
Example:
#include "sysLib.c" // Including system library for system-level functions
Text and Data Files: Non-code files like .txt or .cfg can be included for configurations or
specific data used by the program.
Example:
#include "netDrvConfig.txt" // For network driver configuration details
Source Files: Source files are the backbone of any C application. They contain the actual
implementation of the program's logic, functions, and main execution code. These files are
compiled to generate the final executable binary. Typically, a source file contains:
• Main function (main ()): Entry point for the program execution.
• Function Definitions: Code for any user-defined or library functions that are used in
the application.
• Preprocessor Directives: Instructions for including files or defining constants.
Example:
void main () {
printf ("Hello, World!\n");
// Call other functions here
}
Configuration Files: Configuration files are used to define system parameters and device
configurations. These are especially important in embedded systems where the behavior of
hardware peripherals (e.g., serial communication, network interfaces) must be defined.
• Example: A file called serialLine_cfg.h may contain configuration details for UART
communication in an embedded system:
#include "serialLine_cfg.h"
Preprocessor Directives: Preprocessor directives begin with a # symbol and are
processed before the actual code compilation. These can include
Global Variables: Preprocessor directives can define global variables to be used throughout
the code.
Example: #define volatile int Timer // A volatile global variable Timer
Constants: Preprocessor directives can define constants for values used frequently in the
program. This increases code readability and ease of maintenance.
Example:
#define TRUE 1
#define FALSE 0
Macros: Macros allow the definition of complex expressions that can be reused in the code.
Example: #define SQUARE(x) ((x) * (x))

Program Elements: Macros and Functions


The table describes several key elements related to the use of macros and
functions, which are essential in embedded systems or C programming.
Function: Code is compiled once. During execution, the system must save the
current context (like CPU registers, program counters) and restore it after the
function completes.
Example: #include<stdio.h>
int number ()
{
return 10;
}
int main ()
{
printf("%d", number());
return 0;
}

Macro: The compiler replaces the macro name with its defined code during the
pre-processing phase before compilation. There’s no context saving or restoring,
as it is directly inserted inline where the macro is used.
Example: #include<stdio.h>

#define NUMBER 10

intmain ()

printf("%d", NUMBER);

return0;

Characteristics of Macros vs. Functions


Macro:
o Executes a small collection of code.
o No context saving or retrieving overhead.
o Used for short code snippets.
o Inserted directly in place of the macro name.
Function:
o Executes a named set of codes with values passed by the calling
program.
o Can return a data object if not declared as void.
o Includes context-saving and retrieving overheads.
o Can call another function and be interrupted.

MACRO FUNCTION

Macro is Pre-processed Function is Compiled

No Type Checking is done in Macro Type Checking is Done in Function

Using Function keeps the code


Using Macro increases the code length
length unaffected

Use of macro can lead to side effects at Functions do not lead to any side
later stages effects in any case

Speed of Execution using Function


Speed of Execution using Macro is Faster
is Slower

Before Compilation, the macro name is During function call, transfer of


replaced by macro value control takes place

Macros are useful when small code is Functions are useful when large
repeated many times code is to be written

Macro does not check any Compile-Time Function checks Compile-Time


Errors Errors
PROGRAM ELEMENTS: DATA TYPES, DATA STRUCTURES,
MODIFIERS, STATEMENTS, LOOPS, AND POINTERS

Use of Data Types: When naming a variable, memory is allocated based on the data type.
Different data types allocate varying amounts of memory, and this is fundamental to how the
program interacts with the system's hardware.
Primitive Data Types in C:
1. char (8 bits): Stores characters, equivalent to 1 byte.
2. byte (8 bits): Similar to char, used for storing small numbers or characters (some
compilers do not define "byte" and use "char" instead).
3. unsigned short (16 bits): Stores positive integers, occupying 2 bytes of memory.
4. short (16 bits): Stores integers, including negative values, within the 16-bit range.
5. unsigned int (32 bits): Stores positive integers, using 4 bytes.
6. int (32 bits): Stores signed integers.
7. long double (64 bits): For floating-point numbers with high precision, using 8 bytes.
8. float (32 bits): Stores floating-point numbers, consuming 4 bytes.
9. double (64 bits): A double-precision floating-point number, consuming 8 bytes.
Some compilers do not support certain types (like "byte"). In those cases, typedef can be used
to create custom types.
Hardware-Specific Data Types:
Example: A 16-bit timer can only use an unsigned short data type because it matches the
hardware architecture, which can count values from 0 to 65535.
Using typedef:The typedef keyword allows the creation of new data types. For instance, if a
compiler doesn't support "unsigned byte", you can define it as:
Example: typedef unsigned char portAdata;
****Demonstrates how to use basic data types such as char, int, and long in embedded C****

#include <stdio.h>
void delay_ms(unsigned int ms) // Function to simulate delay
{
while (ms--) // Simulate a delay by looping
{
// Do nothing
}
}
int main()
{
unsigned char led State = 0x01; // 8-bit value to represent the LED state
unsigned int delay = 1000; // 16-bit delay value (in milliseconds)
while (1) { // Infinite loop to simulate LED blinking
printf("LED ON: 0x%02X\n", ledState);
delay_ms(delay); // Wait for some time
ledState = ~ledState; // Toggle LED state
printf("LED OFF: 0x%02X\n", ledState);
delay_ms(delay); // Wait for some time
}
return 0;
}

Use of Pointers and Null Pointers


Pointers in C are extremely powerful and useful for memory manipulation. A pointer is
essentially a variable that stores the memory address of another variable.
A pointer holds the address of a variable, data structure, or function. It allows direct
manipulation of memory.
Syntax: The * symbol is used to declare a pointer. For example, unsigned char *ptr; declares
a pointer to an unsigned character (8 bits).
Example: unsigned short timer1;
Here, timer1 is a variable of type unsigned short, which requires 2 bytes of memory. A
pointer to timer1 would point to those 2 bytes.
Pointer Arithmetic: In C, if a variable x is incremented with x++, its value is incremented by
1. Similarly, if p is a pointer, p++ increments the pointer to the next memory address
Example with 32-bit Timer: If timer1 were of type unsigned int (32 bits or 4 bytes), then
timer1++ would increment the address by 0x0004 instead of 0x0002.
A NULL pointer is a pointer that points to a predefined invalid memory address, often
0x0000. In C, NULL is typically defined as#define NULL (void*)0x0000.
Pointer Examples:
1. unsigned byte *portA; // This declaration means portA is a pointer to an unsigned
byte, representing the data at some memory address

2. void *portAdata; // allows the compiler to allocate an address for portAdata without
type checking. It is often used for dynamically allocated memory or passing generic
data structures.

3. #define portA (volatile unsigned byte *) 0x1000


#define PIOC (volatile unsigned byte *) 0x1001 // Here, portA points to the memory
address 0x1000, and PIOC to 0x1001. The keyword volatile ensures that the value can
be changed at any time (e.g., by external hardware)

4. unsigned byte portAdata;


unsigned byte *portA = &portAdata;// The first statement allocates memory for
portAdata, while the second uses the ampersand & to assign the address of portAdata
to portA. This means portA points to the memory location of portAdata, allowing
manipulation of the data at that address.

***program demonstrates the use of pointers to manipulate variables***


#include <stdio.h>
int main()
{
int data = 100; // Declare an integer variable
int *p; // Declare a pointer to an integer
p = &data; // Assign the address of 'data' to pointer 'p'

printf("Value of data: %d\n", data);// Print the value of 'data' and its address using the
pointer
printf("Pointer p holds the address: %p\n", p);
printf("Value at the address held by p: %d\n", *p); // Dereference the pointer
*p = 200; // Modify the value of 'data' using the pointer
printf("Modified value of data: %d\n", data);

return 0;
}
Use of Data Structures: Queues, Stacks, Lists, and Trees
In embedded systems programming, data structures are critical tools for efficiently organizing
and manipulating data. They provide mechanisms to store, retrieve, and manage data in
memory in an organized manner.
A data structure is a collection of data elements organized in memory, allowing easy access,
modification, and management of the data. Embedded systems require the careful use of data
structures due to limited resources (memory, CPU speed), making it essential to choose
appropriate structures for specific use cases.
Some of the most commonly used data structures in embedded systems include:
1. Stacks: Used for last-in-first-out (LIFO) data management.
2. Queues: Used for first-in-first-out (FIFO) data management.
3. Lists: A dynamic collection of data elements, often used when the size of the data
collection is not fixed.
4. Trees: A hierarchical structure often used for searching and sorting operations.
Let's break down these data structures and their usage in embedded systems.

Stacks:A stack is a LIFO (Last In, First Out) data structure where the most recent element
added is the first one to be retrieved. When a function call is made, the return address is
"pushed" onto the stack, and when the function returns, the address is "popped" off the stack.
This ensures that the program can return to the correct point in the code.
• Push: Add an element to the stack (increment the stack pointer and place data at the
new top).
• Pop: Remove an element from the stack (retrieve data from the top and decrement the
stack pointer).
Example:
#include <stdio.h>
#define STACK_SIZE 105
int stack[STACK_SIZE];
int top = -1; // This variable top keeps track of the index of the top element of the
stack. It is initialized to -1, meaning the stack is empty.

void push(int data) // Push to stack


{
if (top < STACK_SIZE - 1)
{
stack[++top] = data;
printf("Pushed %d to stack.\n", data);
} else {
printf("Error: Stack overflow.\n");
}
}
int pop()// Pop from stack
{
if (top >= 0) {
int poppedValue = stack[top--];
printf("Popped %d from stack.\n", poppedValue);
return poppedValue;
}
printf("Error: Stack underflow.\n");
return -1; // Error: Stack underflow
}
int main()
{
// Test the push and pop functions
push(10);
push(20);
push(30);
pop();
pop();
pop();
pop(); // This will cause underflow
return 0;
}

Queues:A queue is a FIFO (First In, First Out) data structure where the first element added is
the first one to be retrieved.
Queues are commonly used for
Task scheduling: Handling tasks that are processed in the order they are added.
Buffering data: Handling data streams like network packets or data from sensors.

Example:
#include <stdio.h>
#define QUEUE_SIZE 10
int queue[QUEUE_SIZE];
int front = 0;
int rear = -1;
int count = 0;

void enqueue(int data) // Enqueue (add to queue)


{
if (count < QUEUE_SIZE)
{
rear = (rear + 1) % QUEUE_SIZE;
queue[rear] = data;
count++;
printf("Enqueued %d to queue.\n", data);
}
else
{
printf("Error: Queue overflow.\n");
}
}
int dequeue () // Dequeue (remove from queue)
{
if (count > 0)
{
int data = queue[front];
front = (front + 1) % QUEUE_SIZE;
count--;
printf("Dequeued %d from queue.\n", data);
return data;
}
Printf("Error: Queue underflow.\n");
return -1; // Error: Queue underflow
}

// Main function to test the queue implementation


int main() {
// Test the enqueue and dequeue functions
enqueue(10);
enqueue(20);
enqueue(30);

dequeue();
dequeue();
dequeue();
dequeue(); // This will cause underflow
return 0;
}

Lists:A list is a collection of elements where each element points to the next one or both the
next and the previous one. Lists are useful in embedded systems when you need dynamic
memory allocation or when the size of the data set is unknown or constantly changing.
Example:A list can be used to maintain a collection of tasks in a system, allowing tasks to be
added or removed dynamically.
• Insert: Add a new element anywhere in the list.
• Delete: Remove an element from anywhere in the list.
Trees:A tree is a hierarchical data structure consisting of nodes, where each node has a value
and pointers to child nodes. Trees are useful in operations where sorted data or hierarchical
relationships are needed, such as in:
• Binary Search Trees: Efficient searching and sorting.
• Decision Trees: Used in applications like AI.
• File Systems: Organizing files in a directory hierarchy.
Example:A binary search tree (BST) is a tree where each node has at most two children. The
left child contains a value smaller than its parent, and the right child contains a value larger
than its parent. This allows for efficient searching, insertion, and deletion of elements.
• Insert: Place a new node in the correct position based on its value.
• Delete: Remove a node while maintaining the tree structure.
Use of Modifiers
Modifiers are used to alter the properties of data types or functions, controlling
their behaviour in terms of memory allocation, scope, or optimization.
1.Unsigned Modifier:Unsigned can be applied to short, int, or long data types. It
allows only non-negative values (0 and positive numbers).
Example:
unsigned int positive Value = 100; // Values range from 0 to 2^32 - 1 for 32-bit
unsigned int.
2.Static Modifier Inside a Function Block:When a variable is declared as static
inside a function, its value is persistent across function calls. It retains its value
even after the function finishes execution.
Example:
void counter ()
{
static int count = 0; // Persists across function calls
count++;
printf("%d\n", count);//Every time the counter () function is called, count will
increase, retaining its previous value.
}
3.Const Modifier: The const modifier makes a variable's value read-only. Once
initialized, the variable cannot be modified.
Example:
const int PI = 3.14; // PI cannot be changed&When used in global scope, it
must be initialized during its declaration
4.Register Modifier:The register modifier suggests to the compiler that the
variable should be stored in a CPU register instead of RAM for faster access.
This is often used for loop counters or frequently accessed variables.
Example:
register int counter = 0; // Faster access via CPU register
5.Interrupt Modifier:The interrupt modifier tells the compiler that the function is
an interrupt service routine (ISR). The compiler will save all processor registers
at the entry of the ISR and restore them upon exit.
Example:
void __interrupt ISR_function ()
{
// ISR code
}
6.Extern Modifier:The extern keyword is used to declare a global variable or
function that is defined in another file.
Example:
extern int external_var; // Defined in another file
7.Volatile Modifier:The volatile modifier is used to inform the compiler that the
value of a variable may change unexpectedly.
Example:
volatile int sensor Value; // Can change due to hardware interrupts

Use of Loops, Infinite loops and conditions


The use of loops, especially infinite loops and conditions, is fundamental to
programming, particularly in embedded systems where continuous monitoring
or control is required.
1. For Loop:The for loop is a control structure that allows code to be executed
repeatedly for a fixed number of iterations.
Example:
for (int i = 0; i<= 100; i++)
{
}
2. While Loop:The while loop repeatedly executes a block of code as long as a
specified condition is true.Unlike the for loop, the while loop does not have
built-in initialization or increment steps, so those must be managed manually.
Example:
int i = 0;
while (i<= 100) // A set of statements that execute repeatedly
{
i++;
}
3.Infinite Loops:An infinite loop occurs when the condition in a loop never
becomes false, causing the loop to run indefinitely.In embedded systems,
infinite loops are common and necessary because they ensure that the system is
always in a ready state or waiting to respond to events.
Example:
while (1) // Code that executes continuously
{
}

In devices like embedded controllers, there is often a need to continuously


monitor inputs (e.g., button presses, sensor data). An infinite loop allows the
device to stay active and respond whenever an event occurs.
while (1)
{
if (sensor_input == HIGH)
{
#define false 0
#define true 1

void main(void) {
// Declarations and initializations here

while (true)
{
// Code that continuously executes, e.g., monitoring inputs
}
}
// Perform an action when the sensor is active
}
}

Use of Function Calls


In embedded systems, function calls are used to organize code, improve
reusability, and simplify complex operations into manageable tasks.
Functions break down complex tasks into smaller, more manageable blocks. For
instance, instead of writing all logic directly in themain () function, functions
can handle specific tasks like initializing hardware, reading sensor data, or
processing inputs.
If a certain piece of code is needed multiple times, it can be placed inside a
function and called whenever needed. This reduces code duplication and makes
maintenance easier.
Functions are commonly used to manage interactions with peripheral devices
like sensors, displays, communication interfaces (e.g., UART, SPI, I2C), and
actuators. For example, there might be functions for reading from a temperature
sensor or sending data over a communication interface.
Example:
void init_GPIO ()
{
// Code to configure GPIO pins
}
void init_Timer()
{
// Code to set up a timer
}
int main ()
{
init_GPIO(); // Initialize the GPIO pins
init_Timer(); // Initialize the timer
while (1) {
// Main loop code
}
return 0;
}
// Function Calls for Sensor Readings and Control
int read Temperature()
{
// Read temperature sensor value
return tempValue;
}
void controlMotor(int temperature)
{
if (temperature > 30)
{
// Turn on the motor
} else
{
// Turn off the motor
}
}
int main ()
{
while (1)
{
int temp = read Temperature (); // Get the temperature value
control Motor(temp); // Control the motor based on temperature
}
return 0;
}

Multiple Function Calls in Cyclic Order


In embedded systems, multiple function calls in cyclic order refer to a
programming method where several functions are called sequentially within a
loop, often in an infinite loop. This approach ensures that each function
executes repeatedly and in a specific order, allowing the embedded system to
continually perform tasks or check conditions without stopping.
Functions are executed in a loop, one after the other, in a fixed sequence.After
the last function in the sequence is executed, the flow returns to the first
function, and the cycle repeats.
The example describes a situation where a program continuously executes
multiple functions in a cycle.

Fig. Programming model of multiple function calls


The main function contains the initialization of variables, pointers, and function
declarations.After initialization, the cyclic execution of function calls (e.g., f1,
f2, f3) begins.Each function (f1, f2, f3) represents a block of code that performs
a specific task.
The example describes an infinite loopin which the function calls are
placed.Within the loop, the functions can handle different tasks such as reading
data from a port, processing the data, and outputting results.
A typical use case might involve reading sensor data, processing it, and
transmitting it to another module
▪ f1 could read data from a sensor.
▪ f2 could process the data (e.g., filter or convert it).
▪ f3 could send the processed data to a communication port.
▪ After f3 is executed, the program flow goes back to f1, and
the process repeats.

Function pointers, Function queues and ISR queues


Function Pointers:A function pointer is a pointer that stores the address of a
function. This allows you to call a function indirectly using the pointer. It
enables dynamic selection of functions at runtime, which is particularly useful
in scenarios where the function to be executed might depend on external
conditions or user input.
Example:int (*funcPtr) (int, int); //This declares funcPtr as a pointer to a
function that takes two int arguments and returns an int.
funcPtr = &someFunction; // Here, funcPtr is assigned the address of
someFunction
Function Queues:A function queue is a data structure that stores function
pointers, allowing functions to be queued up for execution. This is useful when
multiple functions need to be executed in a specific order, often in response to
events or tasks managed by a real-time operating system (RTOS).
Example:
typedef void (*FuncPtr) (); // Define a function pointer type for functions with
no arguments and void return
FuncPtr functionQueue[10]; // An array of function pointers
int queueHead = 0, queueTail = 0;

void enqueue (FuncPtr func) {


functionQueue[queueTail++] = func; // Add a function pointer to the queue
}
void executeNext() {
if (queueHead != queueTail) {
FuncPtr func = functionQueue[queueHead++];
func(); // Call the function pointed to by func
}
}

ISR (Interrupt Service Routine) Queues


An ISR queue is a mechanism to manage and handle interrupts using a queue
structure. It helps in organizing the execution of interrupts, ensuring that
multiple interrupt service routines (ISRs) are processed in an orderly manner.
This is crucial in embedded systems where interrupts need to be handled
efficiently without losing important events.
ISR Queue Structure:
• Queue of ISRs: When multiple interrupts occur simultaneously or in
quick succession, an ISR queue can store these interrupts and process
them one by one.
• The queue structure ensures that each ISR is executed in a first-come,
first-served manner or based on priority.
Queuing of Functions on Interrupts
In embedded systems, handling interrupts efficiently is crucial because they
often involve time-sensitive tasks like reading sensor data, handling user input,
or communicating with peripherals.
When an interrupt occurs, the Interrupt Service Routine (ISR) is triggered to
handle the event. ISRs are designed to execute quickly to ensure that the system
can return to its normal operation as soon as possible. However, some tasks
triggered by interrupts may require more processing time. To handle such cases,
we can use function queues.
Interrupt Priorities:In systems with multiple interrupt sources, different
interrupts may have different priorities.A high-priority ISR may pre-empt a
lower-priority ISR. This can cause delays in executing the lower-priority ISR,
potentially missing deadlines if the processing is done directly in the ISR.
Queuing functions helps avoid such delays by allowing the high-priority ISR to
add tasks to a queue rather than directly executing them.
Function Pointers Queue:A function queue is a FIFO (First-In-First-Out)
structure where function pointers are stored.When an interrupt occurs, the ISR
places a function pointer in the queue.The queued function pointers represent
tasks that should be performed later.
Example:
When an interrupt occurs (e.g., data is received at a hardware port), the ISR is
triggered. The ISR might perform a minimal task like reading the data into a
buffer and then enqueue a function for further processing of this data.
When the main program finds a function in the queue, it calls that function.This
allows functions to be executed one by one in a round-robin fashion, ensuring
that all queued tasks are handled in the order they were added.
The provided image shows a flow where multiple function calls are managed
through a function queue system
Step 1: An interrupt occurs at Port A, triggering an ISR called ISR_PortA_Input.
Step 2: Inside the ISR, instead of performing complex operations like
deciphering or encoding data, pointers to the relevant functions
(decipherPortAData, encryptPortAData, etc.) are added to the queue.
Step 3: The operationFunctionQueues () function in the main () loop
periodically checks the queue and executes the functions in the order they were
added.
Step 4: As each function is executed, the head pointer of the queue moves
forward, ensuring that the next function is called in the subsequent iteration.
C C++
Programming Paradigm Procedural Object-Oriented & Procedural

Memory Management Manual (malloc/free) new/delete, smart pointers

Rich (STL, containers,


Standard Library Smaller (I/O, strings, math)
algorithms)

Yes (classes, inheritance,


Object-Oriented Features No
polymorphism)

More complex, supports


Syntax Simpler, low-level focus
overloading, templates

Game development, GUI


System programming,
Typical Use Cases applications, large software
embedded systems
systems

Embedded programming in C++


Embedded programming in C++ combines the advantages of object-oriented
programming (OOP) with the features and capabilities of the C language,
making it suitable for embedded systems. C++ extends the procedural
programming of C with object-oriented features, which can help in creating
structured, reusable, and maintainable code for embedded applications.
Object-Oriented Programming (OOP) Features in C++
C++ is an object-oriented language that enables the creation of classes and
objects. A class is like a blueprint that defines the structure and behaviour of
objects.
In embedded systems, classes can be used to manage hardware components or
software modules. For example, a class can represent a real-time clock or
software timers. Each timer object can be initialized with a specific count value
and generate interrupts when that count reaches a certain value.
Method Overloading: In C++, methods (functions) can have the same name as
long as they differ in the number or type of parameters. This is useful for
creating functions that perform similar operations but with different types of
inputs.
Method Overriding: When a derived class defines a method with the same
name, return type, and parameters as a method in the base class, it overrides the
base class's method. This is helpful when a child class needs to provide a
specific implementation of a method.
Encapsulation in C++ allows grouping related variables and methods inside a
class, keeping the implementation details hidden from outside code.This helps
in protecting critical data in embedded systems, such as hardware registers or
configuration settings, by providing controlled access through public methods.
C++ classes can have different access levels: public, private, and protected.For
instance, methods and variables declared as private cannot be accessed outside
the class, which provides a mechanism to restrict access to internal states of an
object.In contrast, C structures (struct) do not provide such access control
features, making C++ classes more secure for certain types of embedded
applications.
C++ supports polymorphism, allowing functions or methods to behave
differently based on the object that invokes them. This is particularly useful in
scenarios where different hardware components share common interfaces but
need different behaviours.
For example, a generic interface for Sensor might be extended by specific
classes like Temperature Sensor or Pressure Sensor, each implementing the read
() method differently.
C++ provides more advanced memory management capabilities compared to C.
This includes features like constructors and destructors for managing resources
like memory or hardware registers when an object is created or destroyed.
In embedded systems, where memory is limited, managing memory usage
through constructors and destructors can help avoid resource leaks and ensure
efficient use of memory.
Disadvantages of C++ in Embedded Systems
Increased Complexity:One of the downsides of using C++ in embedded systems
is the complexity of the generated code. The use of advanced features like
templates, virtual functions, or multiple inheritance can make the code more
difficult to understand and debug.
Question bank
1. Illustrate about pointers concept in programming with suitable examples.
2. Write the advantages and disadvantages of C++ programming.
3. Illustrate about the usage of various data structures in programming.
4. Demonstrate the programming model of multiple function calls with a
neat sketch.
5. Illustrate about the usage of data types, loops and Modifiers in embedded
c programming.

You might also like