cancel
Showing results for 
Search instead for 
Did you mean: 

How to use function pointers in STM32CubeIDE

B.Montanari
ST Employee

Summary

This article provides a step-by-step guide and example regarding how a function pointer is implemented and used. The basic usage and a simple example application are provided using the NUCLEO-G071RB board but can be easily tailored to any other STM32.

Introduction

A function pointer is a variable that stores the address of a function. It can be used to share it among different applications with dynamic allocation and pass it as an argument in a callback function. Furthermore, even dynamically change how the firmware responds to events, among other things. However, the primary objective of this guide is to familiarize new programmers with the basic concepts of a function pointer by providing definitions and simple use cases.

There will be two simple code examples to demonstrate the use cases. The first example describes three functions related to LED control (turn on, off, and toggle). The second example one uses one operator to dynamically select which function to use based on the user’s input information.

1.  Development

1.1. Project creation

Start by creating a new project in STM32CubeIDE by clicking [File] → [New]  [STM32 Project]. Select the [Board Selector] and in the empty field of [Commercial Part Number] type [NUCLEO-G071RB]. Select the first option in [Boards List] and then click [Next]. Choose a name for the project and click [Finish]. When the pop-up question appears, select [Yes] for the default mode.

1.2. Basic example 1

For all the code snippets, add them in the main.c file and use the USER CODE XYZ as a reference for the code location.

Let’s start by creating the function pointer. Be cautious, as it needs to match the return type and the passing arguments with the function you want to point to. For the proposed example, the ToggleLED function doesn’t return anything and expects an integer as an argument for the delay in ms. In this case, the pointer declaration that should be used is as follows:

/* USER CODE BEGIN PV */
void (*ptrToggleLED)(int);
/* USER CODE END PV */

The pointer called ptrToggleLED is now created and it can be used to point to our function, but before that, the function needs to be created as well:

/* USER CODE BEGIN PFP */
void ToggleLED(int dlyticks)
{
      HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
      HAL_Delay(dlyticks);
}

Afterwards, the respective function address needs to be assigned to the pointer, this can be easily accomplished by assigning the function to the pointer.

/* USER CODE BEGIN 2 */
ptrToggleLED = ToggleLED;
/* USER CODE END 2 */

Finally, you can use the pointer as a function as if this was the function itself in the main loop.

/* USER CODE BEGIN WHILE */
while(1)
{
      ptrToggleLED(250);
}

At this point, you can build and program the board. The LED should be blinking normally at the 250ms ratio. If you want to explore more, you can even debug and check that the pointer worked. Place a breakpoint inside the original function and check the pointer in the [Expressions] tab.

 

BMontanari_0-1730142065473.png

It might be necessary to have multiple function pointers of a common type, so it’s possible to create a typedef just for that. This might simplify the implementation and maintainability of the code base. Let’s create a simple typedef of a returning and receiving void function pointer:

/* USER CODE BEGIN PTD */
typedef void (*ptrBlinkLED)(void);
/* USER CODE END PTD */

Now, declare a function with the type just created:

/* USER CODE BEGIN PFP */
void TurnOnLED(void)
{
      HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET);
}

void TurnOffLED(void)
{
      HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_RESET);
}

In the code, we can point these pointers to the functions:

/* USER CODE BEGIN 1 */
ptrBlinkLED LedOn = TurnOnLED;
ptrBlinkLED LedOff = TurnOffLED;

And replace the previous main loop with this code:

LedOn();
HAL_Delay(500);
LedOff();
HAL_Delay(500);

Compile and program to see it working on your board.

1.3. Basic example 2

This example's goal is to change the function called based on the user’s input on the terminal. This application requires the UART usage and to facilitate the code flow, the printf, and scanf functions are used. The default UART settings should be [115200], [8 bits], [No Parity], [1 Stop bit], but this can be changed in the *.ioc file or directly in the MX_USART2_UART_Init function.

Add these code snippets:

/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PFP */
int __io_putchar(int ch)
{
      HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
      return ch;
}

int __io_getchar(void)
{
      uint8_t ch[1];
       __HAL_UART_CLEAR_OREFLAG(&huart2);
      HAL_UART_Receive(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
      return ch[0];
}
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
// Function for addition
int add(int a, int b) {
    return a + b;
}

// Function for subtraction
int subtract(int a, int b) {
    return a - b;
}

/* USER CODE END 0 */
/* USER CODE BEGIN 1 */
setvbuf(stdin, NULL, _IONBF, 0); //avoid buffering problems
// Declare a function pointer
int (*operation)(int, int);
// Variables to store user input
int choice, x, y;
/* USER CODE END 1 */
       /* Infinite loop */
       /* USER CODE BEGIN WHILE */
       while (1)
       {
             //Wait for user input
             printf("Enter two integers and remember to press enter after typing each number: \r\n");
             scanf("%d", &x);
             scanf("%d", &y);
             printf("Type the desired operation: 1 for addition, 2 for subtraction: \r\n");
             scanf("%d", &choice);
             // Assign the function pointer based on user choice
             if (choice == 1) {
                    operation = add;
             } else if (choice == 2) {
                    operation = subtract;
             } else {
                    printf("Invalid choice, 'add' will be used as default\r\n");
                    operation = add;
             }

             // Call the function using the function pointer
             int result = operation(x, y);
             printf("Result: %d\r\n", result);
             /* USER CODE END WHILE */
             /* USER CODE BEGIN 3 */
       }

       /* USER CODE END 3 */

2.  Example 2 results

Press [Ctrl + B] to build the project; there should be no errors or warnings. Program the board and open your preferred terminal. In this case, we use Tera Term with the 115200, 8, N, 1 setting. Running the code, we can play with the simple example by adding or subtracting:

BMontanari_1-1730142065475.png

Conclusion

Pointers are an important feature of the C language and used for a variety of common applications. The example codes provide an elementary usage of function pointer and a couple of simple examples to familiarize readers to use them in their own projects.

Related Links

Here are some useful links that can help you learn more about the low level functions used, such as the printf and scanf when using the GCC compiler:

 

Version history
Last update:
‎2024-10-29 02:36 AM
Updated by: