C is a general-purpose programming language with a range of desirable features and rich applications in computing. With its origins in the assembly language, the C language includes constructs that can be efficiently mapped on to typical machine instructions, making the language useful for coding operating systems and many types of application software.
The c language, like so many other brilliant creations in computer electronics, has its origins at Bell Labs. In 1972, American computer scientists Dennis Ritchie and Ken Thompson were building the first iteration of the Unix operating system using assembly language and a 16-bit minicomputer known as the PDP-7. Thompson wished to design a programming language that could be used to write utilities for the new platform. With collaboration from Ritchie, and after several rounds of iteration and improvement, they created the C language along with a C compiler and utilities that were released with Version 2 Unix.
C continued to develop over the years, with new language features being implemented to address changing application requirements:
As a low-level programming language, Embedded C gives developers more hands-on control over factors like memory management. This is useful for programming embedded microcontrollers that frequently face power usage and memory constraints. Embedded C also introduces the in_port and out_port functions for I/O, along with features like fixed-point arithmetic, hardware addressing, and multiple memory areas.
Standard C data types take on new characteristics in Embedded C to deal with the requirements of restricted, resource-limited embedded computing environments. Below, we review the characteristics of the data types available for engineers coding microcontroller systems in Embedded C.
Embedded C programs can deal with both functions and parameters. The function data type determines the type of value that can be returned by a given subroutine. Without a specified return type, any function returns a value of the integer type as its result. Embedded C also supports parameter data types that indicate values that should be passed into a specified function. When a function is declared without any parameters, or when a return value is not expected, the function can be noted as (void).
Embedded C supports three different data types for integers: int, short, and long. On 8-bit architectures, the default size of int values is typically set to 16 bits but Embedded C allows for int sizes to be switched between 8 and 16 bits to reduce memory consumption. The short int data type allows embedded engineers to specify and integer value that is just one or two bytes in size. Using the long int data type allocates twice the standard amount of memory as the int data type - usually 16 bits on most 8-bit platforms.
Embedded C uses two types of variables for bit-sized quantities: bit and bits. A bit value corresponds to a single independent bit while the bits variable data is used to collectively manage and address a structure of 8 bits. Programmers can assign a byte value to a bits variable to allow for the addressing of individual bits within the data.
One of the most salient differences between desktop computer applications and those used to power embedded systems is their treatment of real numbers and floating-point data types. These data types include any quantity that can represent a distance along a line, including negative numbers and numbers with digits on both sides of a decimal point. Real numbers place a significant burden on the computing and memory storage capacities of embedded systems, and thus, these types of data are among the least frequently implemented.
In addition to the conventional data types, there are also complex data types that can be useful for programming microcontrollers for embedded systems applications. These include types like pointers, arrays, enumerated types (finite sets of named values) unions, structures (ways of structuring other data), and more.
With these data types in mind, let's take a look at the structure of a program in Embedded C.
Effective coding requires the use of documentation or commentary to indicate any important details of what the code is doing. An Embedded C program typically begins with some documentation information like the name of the file, the author, the date that the code was created, and any specific details about the functioning of the code. Embedded C supports single-line comments that begin with the characters "//" or multi-line comments that begin with "/*" and end with "*/" on a subsequent line.
Preprocessor directives are not normal code statements. They are lines of code that begin with the character "#" and appear in Embedded C programming before the main function. At runtime, the compiler looks for preprocessor directives within the code and resolves them completely before resolving any of the functions within the code itself. Some preprocessor directives can skip part of the main code based on certain conditions, while others may ask the preprocessor to replace the directive with information from a separate file before executing the main code or to behave differently based on the hardware resources available. Many types of preprocessor directives are available in the Embedded C language.
Global declarations happen before the main function of the source code. Engineers can declare global variables that may be called by the main program or any additional functions or sub-programs within the code. Engineers may also define functions here that will be accessible anywhere in the code.
The main part of the program begins with main(). If the main function is expected to return an integer value, we would write int main(). If no return is expected, convention dictates that we should write void main(void).
An embedded C program file is not limited to a single function. Beyond the main() function, programmers can define additional functions that will execute following the main function when the code is compiled.
Now that we have reviewed the basic structure of programs in Embedded C, we can look at a basic example of an Embedded C program. In keeping with the structure outlined above, commentary on this code will be offered as part of the code structure itself, making use of the documentation conventions previously outlined for the Embedded C language.
/* This Embedded C program uses preprocessor directives, global variables declaration and a simple main function in a simple introductory application that turns a light on when specified conditions are satisfied */
#include <hc705c8.h>
#include <port.h>
/* The code imports header files that contain hardware specifications for the microcontroller system, including port addressing. The following ports are declared within the imported header file: */
#pragma portrw PORTA @ 0x0A;
#pragma portw DDRA @ 0x8A;
/*In the first directive, the name PORTA refers to the data register belonging to Port A, while the suffix "rw" added to "port" indicates both read and write access. In the second directive, the suffix "w" indicates write access only to the DDRA, which is Port A's data direction register. */
#define ON 1
#define OFF 0
#define PUSHED 1
/* ON, OFF and PUSHED are the global variables for this program. They can be called by the main function along with any subsequent functions included by the programmer. */
void wait(registera);
/* A wait function is needed for this program. We didn't code one but you can imagine how it would work. A wait function allows programmers to introduce a time delay between programmed events. */
void main(void){
// Beginning the main function with the expectation of no return
DDRA = IIIIIII0;
/* We have already allowed this program to have "write" access to the DDRA port. Here, we use the assignment to set the DDRA pins to the desired configuration. In this configuration, pin 0 is set to output and pin 1 is set to input. The rest of the pins do not matter for this application. */
while (1){
if (PORTA.1 == PUSHED){
wait(1);
if (PORTA.1 == PUSHED){
PORTA.0 = ON;
wait(10);
PORTA.0 = OFF;
}
}
}
}
/* This basic function checks the value of PORTA.1 to determine whether it is pushed (has a value of 1). If so, the program waits for 1 second and does the check again. If the second check succeeds, the program turns on an LED light by activating PORTA.0, keeps it on for 10 seconds, then shuts the light off again. */
Total Phase supports the efforts of embedded systems engineers building systems with the Embedded C programming language. In addition to our knowledge base and video resources, Total Phase provides industry-leading testing and development tools for use in embedded systems applications. Whether your device uses I2C, SPI, CAN, or USB, Total Phase offers the debugging and analysis tools you need to streamline product testing and reduce your time-to-market.