Supporting Information

  • Introduction to the C Programming Language for Embedded Applications
  • Variables in C Programming
  • Arrays in C Programming

Every C program has a main() function. It is certainly possible to write a successful program in which the only function is main(). My guess is that this has been done many times, and it’s true that in certain simple applications no other function is needed.

However, extensive use of functions is an indication that the person writing the code is an experienced firmware developer. Why? Because functions allow us to write better code more quickly, with less work and fewer bugs. For those who spend a significant portion of their professional life writing firmware, these are the sort of advantages that cannot be ignored. Even if we initially resist the use of functions because they seem to require more work, experience gradually teaches us that the benefits greatly outweigh the costs.

What Is a Function?

A C function is a group of instructions that work together to implement a specific type of processor activity. In many cases, a function will perform one specific task, such as retrieving data from an SPI buffer, configuring a timer so that it generates a specified delay, or reading a value from memory and loading it into a DAC register.

However, there is certainly no law stating that a function can perform only one task. You might find it convenient to have one function that updates three unrelated state machines, or you could write a function that transmits a byte via UART, then checks the status bit until a byte is received, then incorporates the value of the received byte into some mathematical computations.

One thing I like about functions is that they enable a fairly direct translation between a flowchart and code.

The “Components” of a Function

A function consists of a name, a list of input parameters, the code statements that implement the required functionality, and a return type.

The following code snippet gives you an example.

char Convert_to_Lowercase(char UppercaseLetter)
     if(UppercaseLetter < 65 || UppercaseLetter > 90)
          return 0x00;
          return (UppercaseLetter + 32);

I like to make my function names very descriptive. This makes code more readable and helps you to keep your thoughts organized.

The instructions are enclosed in curly brackets; this portion of the function definition is called the body of the function. The “return” keyword is used to exit the function and to identify which data should be delivered to the previously executing portion of code.

The return type, placed before the function name, identifies the data type of the information that will be returned. It is perfectly acceptable to have a function that simply performs a task, without any need to return data. In this case you would use the keyword “void” instead of a data type.

Passing Data to a Function

The input parameters, also called arguments, are enclosed in parentheses and placed after the function name. A C function can have multiple arguments, in which case they are separated by commas. Each argument must be accompanied by a data type.

In embedded applications, it is often not necessary to use arguments. I can think of two reasons for this.

First, embedded firmware frequently interacts directly with the device’s hardware, and consequently a function can get the information it needs from configuration registers, communication registers, or port pins.

Second, simple C programs written for microcontrollers can use global variables, i.e., variables that are present throughout the program and can be accessed by any function. As I understand it, the use of global variables in application programming is discouraged, or maybe even “condemned” would be the appropriate word. But in my opinion many firmware projects, especially those written entirely by one programmer, can benefit from the simplicity of global variables.

When defining a function that has no arguments, you can leave the parentheses empty or insert the keyword “void.” Theoretically the “void” approach is better than the empty parentheses, but in the context of embedded development—especially considering how clever modern compilers are—I don’t know how much it really matters.


Let’s briefly examine the function definition shown above.

  • The function name, Convert_to_Lowercase, clearly indicates the purpose of the function: it accepts an eight-bit value corresponding to an uppercase ASCII letter, and it returns an eight-bit value corresponding to the lowercase version of that same letter.
  • There is one input parameter. It has a data type of char and uses a descriptive identifier.
  • The return value, like the input argument, is an ASCII character, and consequently the return type is char.
  • If the input value is outside of the range corresponding to uppercase ASCII letters, the function returns 0x00, which indicates an error. Otherwise, it adds 32 to the input value and returns the sum. If you’re not familiar with ASCII values, the table shown below will help you to understand why I am using the numbers 65, 90, and 32.

Leave a Reply

Your email address will not be published. Required fields are marked *