Ai Ho

Jet Station πŸš€

Menu

🏠 Home | πŸš€ Embedded Systems | 🧰 Development Toolbox | πŸŽ“ Training Courses | πŸ“š Documents

Embedded C Function

Embedded C Function Introduction

Did You already Know?

In this article, I will address these crucial concerns for you.

How Functions are Used in Embedded C

πŸ‘‰ Functions are fundamental building blocks in embedded C programming. They allow you to organize code into reusable modules, making programs easier to read, maintain, and debug.

πŸ‘‰ In embedded systems, functions are used to implement hardware control, handle interrupts, process data, and manage system resources efficiently.

πŸ‘‰ Understanding how to define, use and debug a functions is essential for writing reliable and efficient embedded software.

Example of function declaration, defintion and function call:

/**
  * @brief  Reset counter value
  * @param  None
  * @retval None
  */
void Counter_resetCounter(void);
/**
  * @brief  Reset counter value
  * @param  None
  * @retval None
  */
void Counter_resetCounter(void) 
{
	g_counter_u32 = 0U;
}
Counter_resetCounter();

What is a Function in C Program?

Key Points

Demostration Project

πŸš€ In this Embedded C Function Demo Project, I start to count up and check if the counter is overed the threshold. If yes, I reset the counter and turn on on-board LED to indicate the user that counter is overed, otherwise, I keep the on-board LED off to indicate user that the counter is counting up.

Software Design

Where is the Function stored in Microcontroller Memory?

πŸ’‘ The functions are compiled into binary code and stored in the flash memory.

❓ How can you confirm this?

πŸ‘‰ For example, with the Counter_countUp function, the address shown in the Watch Window is 0x0800027C. Comparing this to the flash memory range 0x0800 0000 - 0x0801 FFFF specified for the STM32F103C6 in the STM32F103C6 data sheet and the target controller’s Read/Only Memory Areas used for the demo project, you can see that the function address is within the flash memory range.

Function Code Storage

Similarly, you can double check for other functions.

How the Function is called?

πŸ‘‰ First, let discuss about an example that how Counter_getCounterThres function is called.

Calling a Function

πŸ‘‰ After that, the program jumps to the Counter_getCounterThres function and execute the instructions inside of the function, like reading the value of g_counterThres_u32 variable.

Entering a Function

πŸ‘‰ After executed the instructions, the Counter_getCounterThres function is terminated by the program and return back with the value of g_counterThres_u32 and store it in l_counterThres_u32 variable. Terminating a Function

What is Stack in Embedded Software?

πŸ’‘ The stack in embedded software is a special region of memory used to store temporary data such as function parameters, local variables, and return addresses during program execution. When a function is called, its context (including local variables and the return address) is pushed onto the stack. When the function returns, this context is popped off the stack, restoring the previous state.

πŸ’‘ The stack operates in a Last-In, First-Out (LIFO) manner, meaning the last item pushed onto the stack is the first to be removed. The stack is managed by the Stack Pointer register, which keeps track of the top of the stack.

πŸ‘‰ In the demo with STM32F103C6 target controller, the stack is located in the SRAM area of the memory. It is designed starting from 0x2000 0000. You can double check with STM32F103C6 data sheet, Read/Write Memory Areas, Register View and Memory View to have more details.

STM32F103C6 Stack

What is Function Call Stack?

πŸ’‘ The function call stack is a data structure that keeps track of active function calls during program execution. Each time a function is called, a new stack frame is created and pushed onto the stack. This frame contains information such as the function’s parameters, local variables, and the return address.

πŸ’‘ When a function completes, its stack frame is popped off the stack, and control returns to the calling function. This process allows the program to manage nested function calls efficiently.

Call Stack Explanation with Demostration

🎯 In this demo, I would like you focus in the growing of the stack and function call stack by looking to the Stack Pointer - R13(SP) in the Registers View and the Function Call Stack in the Call Stack + Local View when calling and terminating the functions. You can set a Break Point inside of the function to stop the program and analyze the Call Stack.

πŸ‘‰ When calling the functions, the program create a context for the function by allocating the memory for its parameters, local variables and return address. The push it on the top of the stack and making the stack is growing up. Stack Pointer is also updated to track the top of the stack. The called function is pushed in the Call Stack.

[!NOTE] πŸ’‘ In the ARM-Cortex, stack growthing is equal with the decreasing of Stack Pointer value because the top of the stack is initialized with allowed biggest address of the SRAM memory.

Call Stack Growing Up

πŸ‘‰ When terminating the functions, the function context includes parameters, local variables is popped off the stack, restoring the previous state. The stack is growing down, the Stack Pointer is updated to track the top of the stack and the called function is popped out of the Call Stack.

Call Stack Growing Down

Why You Need to Know the Stack and Call Stack?

πŸ”‘ Understanding the stack and call stack is essential in embedded software development for several reasons:

By keeping track of stack and call stack usage, you can design safer, more efficient, and more maintainable embedded software.

Best Practices

Think About the Design

βœ”οΈ Before writing any function, consider the big picture:

Avoid Overusing Functions

Functions help organize software, making it easier to understand and maintain.

❌ However, excessive use can lead to problems:

Proper Stack Management

βœ”οΈ Proper stack management is crucial in embedded systems, as stack overflows can lead to unpredictable behavior, crashes, or corruption of program data. The size of the stack is typically limited by the microcontroller’s memory resources, so functions should be designed to use stack space efficiently.

Use Inline Functions

βœ”οΈ Function calls involve context switching, which can add overhead. To improve execution speed, consider implementing simple functions as inline functions. This reduces call overhead and can optimize performance. (More details on this topic will be provided in a future article.)

Recursive Functions

❌ Avoid using recursion unless absolutely necessary, as it can quickly lead to stack overflow due to the rapid increase in function call depth.

βœ”οΈ There are techniques to eliminate recursion, such as using loops or iterative algorithms, but these will be discussed in a future article.

Design a Function

βœ”οΈ Before declaring and defining a function, consider these basic questions:

Notes

✏️ Here I would like to further explain the usage scope of a function, also known as the function’s storage class.

Local Function

πŸ’‘ A local function is intended for use only within the source file or module where it is defined and cannot be called from outside. The static keyword is added before the function declaration and definition to specify this scope.

Example:

/**
  * @brief  Count up counter
  * @param  None
  * @retval None
  */
static void Counter_countUp(void) 
{
	g_counter_u32++;
}

### Global Function

πŸ’‘ A global function, on the other hand, can be used both inside and outside the source file or module. It does not require any prefix.

Example:

/**
  * @brief  Reset counter value
  * @param  None
  * @retval None
  */
void Counter_resetCounter(void);

Explore More Topics

|πŸ‘ˆ Previous | Next πŸ‘‰|

Repositories

πŸš€ My Repositories

Contact & Discussion

If you have any thing would like to discuss or cooperate with me, please don’t hesitate to contact me via:

Home Page

🏠 Home