Embedded system 1: Basics of Embedded C Programming

Embedded system 1: Basics of Embedded C Programming

Embedded system consists of both Hardware and Software

The main Hardware module is Processor, the processor is the heart of the embedded system and it can be anything like a microprossor, microcontroller, DSP, CPLD and FPGA

All this device have one thing in common, They are programmable we can write a program (which is the SW part of embedded system) to define how the device actually works.

Embedded Software or Programm allow Hardware to monitor external events (Input) and control external devices (outputs) accordingly. During this process, the program for embedded system may have to directly manipulate the internal architecture of the Embedded Hardware such as LEDs, Serial Communications, Interrupt Handling, and I/O Ports

Embedded Systems programming is different from developing applications on a desktop computers:

  • Embedded devices have ressource constraints (limited ROM, limited RAM, limited stack space, less processing power)
  • Embedded Systems typically uses smaller, less power consuming components. are more tied to the hardware.

Factors for Selecting the Programming Language

  • Size: The memory that the program occupies is very important as Embedded Processors like Microcontrollers have a very limited amount of ROM.
  • Speed: The programs must be very fast i.e., they must run as fast as possible. The hardware should not be slowed down due to a slow running software.
  • Portability: The same program can be compiled for different processors.
  • Ease of Implementation
  • Ease of Maintenance
  • Readability

Language use for embedded systems programming

  • Machine Code
  • Low level language: assembly
  • High level language : C, C++, JAVA, ADA...
  • Application level language like visual Basic, scripts, access

Assemby Language programming

Assembly language maps mnemonic words with the binary machine codes that the processor uses to code the instructions. Assembly language seems to be an obvious choice for programming embedded devices. However, use of assembly language is restricted to developing efficient codes in terms of size and speed. Developing small codes are not much of a problem, but large programs/projects become increasingly difficult to manage in assembly language. Finding good assembly programmers has also become difficult nowadays. Hence high level languages are preferred for embedded systems programming.

Assembly is also used but mainly to implement those portions of the code where very high timing accuracy, code size efficiency, etc., are prime requirements. As assembly language programs are specific to a processor, assembly language didn’t offer portability across systems. To overcome this disadvantage, several high-level languages such as C were developed.

C programming & Embedded C Programming

The C Programming Language, developed by Dennis Ritchie in the late 60’s and early 70’s, is the most popular and widely used programming language. The C Programming Language provided low-level memory access using an uncomplicated compiler (a software that converts programs to machine code) and achieved efficient mapping to machine instructions. The C Programming Language became so popular that it is used in a wide range of applications ranging from Embedded Systems to Super Computers.

Embedded C Programming Language, which is widely used in the development of Embedded Systems, is an extension of C Program Language. The Embedded C Programming Language uses the same syntax and semantics of the C Programming Language like main function, declaration of data types, defining variables, loops, functions, statements, etc.

The extension in Embedded C from standard C Programming Language includes I/O Hardware Addressing, fixed point arithmetic operations, accessing address spaces, etc.

Use of C in Embedded systems is driven by following advantages

  • It is small and reasonably simpler to learn, understand, program and debug.
  • C compilers are avaible for almost all embedded devices
  • C has advantage of processor-independence
  • C combines functionality of assemby language and features of high level languages
  • It is fairly efficient

Compared to assembly language, C Code written is more reliable and scalable, more portable between different platforms (with some changes). Moreover, programs developed in C are much easier to understand, maintain and debug.

Compared to other high level languages, C offers more flexibility because C is relatively small, structured language; it supports low-level bit-wise data manipulation.

Objected oriented language, C++ is not apt for developing efficient programs in resource constrained environments like embedded devices. Virtual functions & exception handling of C++ are some specific features that are not efficient in terms of space and speed in embedded systems. Sometimes C++ is used only with very few features, very much as C.

Java is another language used for embedded systems programming. It primarily finds usage in high-end mobile phones as it offers portability across systems and is also useful for browsing applications. Java programs require Java Virtual Machine (JVM), which consume lot of resources. Hence it is not used for smaller embedded devices.

Difference between C and Embedded C

C is used for desktop computers, while embedded C is for microcontroller-based applications. Accordingly, C has the luxury to use resources of a desktop PC like memory, OS, etc. While programming on desktop systems, we need not bother about memory. However, embedded C has to use the limited resources (RAM, ROM, I/O, OS) on an embedded processor. Thus, program code must fit into the available program memory. If code exceeds the limit, the system is likely to crash.

Compilers for C (ANSI C) typically generate OS-dependent executables. Embedded C requires compilers to create files to be downloaded to the microcontrollers/microprocessors where it needs to run. Embedded compilers give access to all resources which is not provided in compilers for desktop computer applications.

Embedded systems often have the real-time constraints, which is usually not there with desktop computer applications.

Embedded systems often do not have a console, which is available in case of desktop applications.

So, what basically is different while programming with embedded C is the mindset; for embedded applications, we need to optimally use the resources, make the program code efficient, and satisfy real-time constraints, if any. All this is done using the basic constructs, syntaxes, and function libraries of ‘C’.

Embedded C compiler working

Article content

Source code file, it contains the source program to be compiled. The source code is usually developed in C.

Embedded C compiler generates a series of output files during compilation.

Preprocessor works on .c file and generates .i file. This file contains source text as expanded by the preprocessor like all macros are expanded and all comments are deleted.

Compiler works on .i file and generates .src files.

.src: these are assembly source files.

Assembler converts assembly file to .obj file.

.obj file contains the relocatable object code. These files are used to link with other .obj, library (.Lib) files by linker to generate an absolute

object file. OH converter object code to hex converter generates

.hex file this is a binary file used to program the processor.

Article content

Variables in Embedded C

Article content

  • Global variable stored in R/W storage scratch pad
  • Local variable stored in Register Bank and Stack

Article content

A variable is an addressable storage location to information to be used by the program. Each variable must be declared to indicate size and type of information to be stored, plus name to be used to reference the information.

Automatic variables stocké dans registers

Static variables stocké dans RAM

Automatic variables

  • Declare within a function/procedure
  • Variable is visible (has scope) only within that function
  • Space for the variable is allocated on the system stack when the procedure is entered: De-allocated, to be re-used, when the procedure is exited
  • If only 1 or 2 variables, the compiler may allocate them to registers within that procedure, instead of allocating memory
  • Values are not retained between procedure calls

void delay()
{
    int i, j; // automatic variables – visible only within delay()

    for (i = 0; i < 100; i++) // outer loop
    {
        for (j = 0; j < 20000; j++) // inner loop
        {
            // do nothing
        }
    }
}        

Variables i and j must be initialized each time the procedure is entered since values are not retained when the procedure is exited.

Static variables

Retained for use throughout the program in RAM locations that are not reallocated during program execution.

Declare either within or outside of a function:

  • If declared outside a function, the variable is global in scope, known to all functions of the program. Use "normal" declarations.
  • If declared within a function, insert the keyword static before the variable definition. The variable is local in scope, i.e., known only within this function. Example: static int count;

void math_op()
{
    int i; // automatic variable – allocated space on stack when function entered
    static int j; // static variable – allocated a fixed RAM location to maintain the value

    if (count == 0)
    {
        j = 0; // initialize static variable j first time math_op() entered
    }
    i = count; // initialize automatic variable each time math_op() entered
    j = j + i; // change static variable j – value kept for next function call
}
// return & de-allocate space used by automatic variable i.        

C control structures

Control order in which instructions are executed:

Conditional execution

  • Execute a set of statements if some condition is met.
  • Select one set of statements to be executed from several options, depending on one or more conditions.

Iterative execution

Repeated execution of a set of statements:

  • A specified number of times, or
  • Until some condition is met, or
  • While some condition is true.

Function in Embedded C

C function

A function is a group of statements that together perform a task. A function declaration tells the compiler about a function's name, return type, and parameters.

A function definition provides the actual body of the function.

Functions partition large programs into a set of smaller tasks:

  • Helps manage program complexity
  • Smaller tasks are easier to design and debug
  • Functions can often be reused instead of starting over
  • Can use of "libraries" of functions developed by 3rd parties, instead of designing your own
  • The function may return a result to the caller
  • One or more arguments may be passed to the function/procedure

#include <reg51.h>

int math_func(int k, int n); // Function Declaration

void main()
{
    int a, b, c;
    a = 10; 
    b = 20;
    c = math_func(a, b); // Function call
}

int math_func(int k, int n) // Function definition
{
    int j; // local variable
    j = n + k - 5; // function body
    return (j); // return the result
}        

Constant/ literal in Embedded C

Constant in C programming language, as the name suggests are the data that doesn't change. Constants are also known as literals.

Integer constants

123    /* decimal constant */
0x9b   /* hexadecimal constant */
0456   /* octal constant */

/* For decimal literals: no prefix is used. */
/* Prefix used for hexadecimal: 0x / 0X */
/* Prefix used for octal: 0 */        

Character constants

Character constants hold a single character enclosed in single quotations marks

m = 'a';    // ASCII value 0x61
m = 0x01;        

Character can hold an integer value as well (maximum value character constants can hold in 127 and 255 for unsigned character constants). Also, different characters have associated integer values (like 'A' is 65, 'a' is 97). These values are standard and known as ASCII.

String constants/Literals

String constants consist of any number of consecutive characters in enclosed quotation marks (").

String (array) of characters:

char my_string[] = "My String";

// Compiler will interpret the above statement as

char my_string[10] = {'M', 'y', ' ', 'S', 't', 'r', 'i', 'n', 'g', '\0'};        

In C programming language, system internally stores the string as a character array with a null character (\0) as the terminator. null character marks the end of a string.

Data types

Basic data types in C51 compiler

Article content

Data types simply refers to the type and size of data associated with variables.

  • char: The most basic data type in C. It stores a single character and requires a single byte of memory in almost all compilers. char for characters and strings.
  • int: As the name suggests, an int variable is used to store an integer. int for most variables and "countable" things (for loop counts, variables, events).

Most types are signed by default (short, int, long long), char is unsigned by default.

  • float: For general measurable things (seconds, distance, temperature). Most embedded processors do not natively support floating point math. This means the compiler must use software to support these types. On small, 8-bit architectures, floating point math takes up too much code space and execution time to be practical.

Basic data types in ARM C compiler

Article content

Because the natural data-size for an ARM processor is 32 bits, its is much more preferable to use int as a variable than short, the processor may actually have to use more instructions to do a calculation on a short than an int.

In code ported from other platforms, especially 8-bit or 16-bit platforms, the data types may have had different sizes. For example, int may have been represented as 16 bits.

Arithmatic Operations in Embedded C

int i, j, k;           // 32-bit signed integers
uint8_t m, n, p;       // 8-bit unsigned numbers

i = j + k;             // add 32-bit integers
m = n - 5;             // subtract 8-bit numbers
j = i * k;             // multiply 32-bit integers
m = n / p;             // quotient of 8-bit divide
m = n % p;             // remainder of 8-bit divide
i = (j + k) * (i - 2); // arithmetic expression

/* 
*, /, % are higher in precedence than +, - (higher precedence applied 1st)
Example: j * k + m / n = (j * k) + (m / n)
*/        

Bitwise operations in Embedded C

Bit level Operations in C

  1. Bitwise OR operator denoted by |
  2. Bitwise AND operator denoted by &
  3. Bitwise Complement or Negation Operator denoted by ~
  4. Bitwise Right Shift & Left Shift denoted by >> and << respectively
  5. Bitwise XOR operator denoted by ^

Article content

unsigned char A, B, C; // we can declare an 8-bit number as a char

A = 0x66; // binary A = 01100110;

B = 0xB3; // binary B = 10110011;

C = A & B; // binary C = 00100010; i.e., 0x22;

Article content

unsigned int A, B, C;

A = 0x64; // binary A = 01100100

B = 0x10; // binary B = 00010000

C = A | B; // C = 0x74 which is binary 01110100

Article content

unsigned int A, B, C;

A = 0x64; // binary A = 01100100

B = 0xB3; // binary B = 10110011

C = A ^ B; // C = 0xD7 which is binary 11010111

Article content

unsigned int A, B;

A = 0x64; // binary A = 0b01100100

B = ~A; // B = 0x9B which is binary 0b10011011

Shift operators

A >> B (right shift operand A by B bit positions) A << B (left shift operand A by B bit positions)

Vacated bits are filled with 0’s.

Shift right/left fast way to multiply/divide by power of 2

B= A<<2;

Article content

B= A>>4;

Article content

8051 is the 8-bit microcontroller originally developed by Intel in the mid-80s. A typical 8051 microcontroller also known as Intel MCS-51 and here are the configuration of that particular microcontroller

  • RAM 128 bytes
  • ROM 4 Kbytes
  • 2 16 bit timers
  • 1 serial port 4 input-output port
  • 8-bit data width
  • 16-bit address width

Article content

Since 8051 is 8-bit microcontroller it can read-write and process 8 bit of data. There are a bunch of manufacturers like Atmel, NXP, Texas Instruments who manufacturer their own versions of 8051 microcontrollers. Irrespective of manufacturers internal hardware design i.e internal architecture of 8051 microcontroller remains more or less the same. Here a diagram shows the architecture of 8051 in block diagram style.

Article content

Central Processing Unit is the heart of microcontroller mainly contains ALU and CU. ALU stands for the arithmetic logic unit as the name suggest it used to perform arithmetic and logical operations. CU nothing but a control unit responsible for all the timing of communication process between CPU and other peripherals.

Program memory is also known as code memory is a read-only memory used to store the CPU instructions. A program written into the memory will retain even if the power is down or the system is reset. The data memory is also known as Random Access Memory or RAM is responsible for storing the value of variable temporary data and immediate result for the proper operation of the microcontroller. RAM is a volatile memory and generally organized as a register and user accessible memory locations.

Input-output ports provide microcontroller a physical connection with the outside world. These ports can be configured as an input port or output port. Input provides a gateway for passing data from the outside world to the microcontroller with the help of sensors and output port allows the microcontroller to control external devices like motor, LED etc.

The oscillator is used to generate a clock signal. Clock signal allows the operation inside the microcontroller and other parts to be synchronous. The clock generator is an integral part of microcontroller architecture and the user has to provide additional timing circuitry in the form of a crystal.

Timer hardware is used to generate delay and the same hardware can be used to count external event on T0 and T1 pin of the microcontroller.

serial port used for serial communication between the microcontroller and other serial devices Interrupt control block, this block is used to control external and internal interrupt in 8051 microcontrollers.

Article content

Program memory, data memory, timer, serial port, input-output ports, interrupt control block and CPU all interface together through the system bus.

A microcontroller programmer or microcontroller burner is a hardware device accompanied with software which is used to transfer the machine language code to the microcontroller ROM from the PC.

The compiler converts the code written in languages like assembly, C, etc., to machine language code and stores it in a hex file.

The API/software of the programmer reads data from the hex file stored on the PC and feeds it into the controller’s memory.

The software transfers the data from the PC to the hardware using serial, parallel, or USB port.

Article content

Depending on the way it interacts with PC, there are 3 types of microcontroller programmers:

  • Parallel Programmer: use the parallel port of the PC. they are low cost programmer but not widely used.
  • Serial Programmer: use the serial port to interact with Pc via RS232 protocols. They are more popular among hobbyist working on PC
  • USB Programmer: uses the USB interface to transfer the data from PC. The main advantage of the USB burner is that they are powered from Pc itself and there is no need of additional supply.

The conventional method to burn a controller is to take it out the circuit, place it on burner and then dump the hex file into the controller using the API.

In order to remove this problem of removing the controller from the circuit every time it needs to be programmed, the controllers have now been upgraded with In System Programmer (ISP) feature.

The latest controllers are coming with the feature like bootloader, which allows self burning capabilities, such microcontroller controller does not need any additional programmer hardware.

What is BootLoader?

A bootloader is a program present in flash memory of microcontroller which enables download of hex-files directly into the flash-memory of a microcontroller.

Generally the bootloader are written to empower a controller with self burning capabilities.

The bootloader program can access any of inbuilt peripherals like USB, USART, CAN, SPI, etc. to exchange the data.

Article content

Conclusion

In this article, we explored the basics of Embedded C programming, a crucial foundation for working with embedded systems. We began by reviewing the key factors for selecting a programming language and highlighted why C is a preferred choice in this field. By differentiating between standard C and Embedded C, we gained a clearer understanding of the unique constraints faced when developing software for embedded hardware.

We also covered essential concepts such as variables, functions, constants, data types, and bitwise operations. These fundamental elements are critical for efficient embedded programming. Additionally, we introduced the 8051 architecture and explained the role of the bootloader in initializing an embedded system.

In the next article, Embedded Systems Programming on ARM Cortex-M3/M4 Processor, we will delve deeper into embedded systems development by focusing on programming ARM Cortex-M3 and M4 microcontrollers. We will explore the specific features of these advanced architectures and discuss techniques to fully leverage their capabilities.

Raghavendra Reddy

ECE 24 || Verilog || System Verilog || Digital Electronics || Analog Electronics || Static Timing Analysis || Digital IC Design || UVM || LINUX

3mo

Great article and informative. It provides a great overview about Embedded C and 8051 microcontroller..

To view or add a comment, sign in

More articles by NIZAR MOJAB

Insights from the community

Others also viewed

Explore topics