Overview

The C Function Call block enables seamless integration of custom C functions into Simulink models with automatic code generation for Microchip targets. This page provides practical examples demonstrating common use cases.

Why Use C Function Call?

The C Function Call block solves several integration challenges:

  • Legacy Code Integration: Reuse existing C libraries without rewriting in Simulink
  • Compiler Built-ins: Access device-specific functions like __delay_ms(), __builtin_dmaoffset()
  • Optimized Algorithms: Call hand-tuned assembly or C routines for performance
  • Vendor Libraries: Interface with third-party SDKs and drivers
  • Custom Peripherals: Wrap low-level register access in callable functions

Key Capabilities

  • Automatic parsing of user C files to list available functions
  • Support for function inputs, outputs, and return values
  • Automatic GPIO configuration for custom code requirements
  • Flexible execution timing (every update or once at initialization)
  • Optional header file declaration management

For complete block documentation, see C Function Call Block Reference .


Example 1: Simple Delay Function

Goal: Add a software delay using Microchip XC16/XC-DSC __delay_ms() built-in function.

[Constant: 10] โ”€โ”€> [C Function Call]
                   (__delay_ms)
                         โ†“
                   [Delay Applied]

Block Configuration

Function Configuration Tab:

ParameterValue
Parse user file to list functionsโ˜ Unchecked (built-in function)
Function Declarationextern void __delay_ms(unsigned long t_ms);
Execute functionwhen block is updated (default)
Block Input(leave empty - parameter name is t_ms)
Function returned value is a block outputโ˜ Off
Sample Time-1 (inherited)

Pins Configuration Tab: (No configuration needed)

Generated Code

// In model_step() function
void model_step(void) {
    // ... other code ...
    __delay_ms(rtb_Constant);  // Executes every sample time
    // ... other code ...
}

Notes

  • Built-in functions do not require source files or parsing
  • Delay executes every time the block updates
  • Input parameter name t_ms matches the function signature

Example 2: Custom Sensor Read Function

Goal: Create a custom function that reads a sensor value and returns processed data through output parameters.

User C File (user_functions.c)

#include <stdint.h>

void read_sensor(int16_t input1, int16_t input2, int16_t *output) {
    // Combine two analog inputs and average them
    *output = (input1 + input2) / 2;
}
[ADC Channel 0] โ”€โ”€โ”
                  โ”œโ”€โ”€> [C Function Call] โ”€โ”€> [Display Output]
[ADC Channel 1] โ”€โ”€โ”˜     (read_sensor)

Block Configuration

Function Configuration Tab:

ParameterValue
Parse user file to list functionsโ˜‘ Checked
Functions foundread_sensor (auto-detected)
Function Declarationextern void read_sensor(int16_t input1, int16_t input2, int16_t *output);
Execute functionwhen block is updated (default)
Block Inputinput1, input2
Function returned value is a block outputโ˜ Off
Block Outputoutput
Add this function declaration in header fileโ˜‘ On

Pins Configuration Tab: (No configuration needed - ADC blocks handle pin setup)

Generated Code

// In model_private.h (if AddHeaderDeclaration enabled)
extern void read_sensor(int16_t input1, int16_t input2, int16_t *output);

// In model_step() function
void model_step(void) {
    // ... other code ...
    read_sensor(rtb_ADC_Ch0, rtb_ADC_Ch1, &rtb_SensorOutput);
    // ... other code ...
}

Code Generation Details

  • Input Parameters: Passed by value (scalar inputs)
  • Output Parameters: Passed by address (pointer parameters)
  • Function Declaration: Added to generated header if enabled

Example 3: One-Time Initialization Function

Goal: Call a custom hardware initialization function only once at power-up.

User C File (hardware_init.c)

#include <xc.h>

void init_custom_hardware(void) {
    // Configure custom peripheral registers
    // This code runs once during startup
    
    // Example: Configure an external chip
    // Set up SPI communication
    // Load calibration values
}
[Model Initialization]
         โ†“
[C Function Call]
 (init_custom_hardware)
         โ†“
[Application Logic]

Block Configuration

Function Configuration Tab:

ParameterValue
Parse user file to list functionsโ˜‘ Checked
Functions foundinit_custom_hardware (auto-detected)
Function Declarationextern void init_custom_hardware(void);
Execute functiononly once at power-up time
Block Input(leave empty - no parameters)
Function returned value is a block outputโ˜ Off
Add this function declaration in header fileโ˜‘ On

Pins Configuration Tab: (Configure if function needs specific GPIO)

Generated Code

// In model_private.h
extern void init_custom_hardware(void);

// In model_initialize() function
void model_initialize(void) {
    // ... other initialization ...
    init_custom_hardware();  // Called once at startup
    // ... other initialization ...
}

Execution Timing Comparison

SettingExecution LocationUse Case
when block is updated (default)model_step()Periodic operations, data processing
only once at power-up timemodel_initialize()Hardware setup, calibration, one-time config

Example 4: Function with Return Value

Goal: Monitor CPU utilization and display the current load percentage.

User C File (system_monitor.c)

#include <stdint.h>

// Global variable updated by ISR or background task
volatile uint32_t cpu_load_percent = 0;

uint32_t get_cpu_load(void) {
    // Return current CPU utilization percentage
    return cpu_load_percent;
}
[C Function Call] โ”€โ”€> [Display/Scope]
  (get_cpu_load)       (CPU Load %)

Block Configuration

Function Configuration Tab:

ParameterValue
Parse user file to list functionsโ˜‘ Checked
Functions foundget_cpu_load (auto-detected)
Function Declarationextern uint32_t get_cpu_load(void);
Execute functionwhen block is updated (default)
Block Input(leave empty - no parameters)
Function returned value is a block outputโ˜‘ On
Sample Time0.1 (update every 100ms)

Pins Configuration Tab: (No configuration needed)

Generated Code

// In model_private.h
extern uint32_t get_cpu_load(void);

// In model_step() function
void model_step(void) {
    // ... other code ...
    rtb_CPULoad = get_cpu_load();  // Return value assigned to output
    // ... other code ...
}

Return Value Handling

  • Return value becomes first output port when “Function returned value is a block output” is enabled
  • Return value must be a scalar (not a pointer or array)
  • Data type must match Simulink signal type

Example 5: GPIO Configuration with Custom Function

Goal: Control an LED pattern using custom GPIO manipulation code with automatic pin configuration.

User C File (led_control.c)

#include <xc.h>
#include <stdint.h>

void set_led_pattern(uint8_t pattern) {
    // Write pattern to PORTB pins 0-7
    LATB = pattern & 0xFF;
}
[Pattern Generator] โ”€โ”€> [C Function Call] โ”€โ”€> LED Hardware
    (0-255)              (set_led_pattern)      (PORTB 0-7)

Block Configuration

Function Configuration Tab:

ParameterValue
Parse user file to list functionsโ˜‘ Checked
Functions foundset_led_pattern (auto-detected)
Function Declarationextern void set_led_pattern(uint8_t pattern);
Execute functionwhen block is updated (default)
Block Inputpattern
Function returned value is a block outputโ˜ Off

Pins Configuration Tab:

ParameterValue
List of port used as Digital OutputB[0:7]
List of port used as Digital Input(empty)
List of port used as Analog Input(empty)

Generated Code

// In model_private.h
extern void set_led_pattern(uint8_t pattern);

// In model_initialize() - Automatic GPIO configuration
void model_initialize(void) {
    // Configure PORTB pins 0-7 as digital outputs
    TRISBbits.TRISB0 = 0;  // Output
    TRISBbits.TRISB1 = 0;
    TRISBbits.TRISB2 = 0;
    TRISBbits.TRISB3 = 0;
    TRISBbits.TRISB4 = 0;
    TRISBbits.TRISB5 = 0;
    TRISBbits.TRISB6 = 0;
    TRISBbits.TRISB7 = 0;
    
    // Disable analog function (if device has ANSEL registers)
    ANSELBbits.ANSB0 = 0;  // Digital mode
    ANSELBbits.ANSB1 = 0;
    ANSELBbits.ANSB2 = 0;
    ANSELBbits.ANSB3 = 0;
    ANSELBbits.ANSB4 = 0;
    ANSELBbits.ANSB5 = 0;
    ANSELBbits.ANSB6 = 0;
    ANSELBbits.ANSB7 = 0;
    
    // ... other initialization ...
}

// In model_step() - Function call
void model_step(void) {
    // ... other code ...
    set_led_pattern(rtb_Pattern);
    // ... other code ...
}

Pin Configuration Format

FormatExampleDescription
Single pinA5Configure single pin (RA5)
RangeB[0:7]Configure pins RB0 through RB7
MultipleA[0 1 2]Configure RA0, RA1, RA2
MixedA[1 2] B5 C[7:9]Multiple ports and ranges

Automatic Pin Setup

The block generates initialization code for:

Port TypeTRIS SettingANSEL Setting
Digital InputTRISx = 1 (input)ANSELx = 0 (digital)
Digital OutputTRISx = 0 (output)ANSELx = 0 (digital)
Analog InputTRISx = 1 (input)ANSELx = 1 (analog)

Common Issues and Solutions

Issue 1: Function Not Found During Build

Symptom: Linker error undefined reference to 'my_function'

Solutions:

  1. Ensure user C file is in the model directory
  2. Add file path to Configuration Parameters โ†’ Code Generation โ†’ Custom Code โ†’ Source files
  3. Verify function is implemented in a .c file (not just declared in .h)
  4. Enable “Parse user file to list functions” to verify detection

Issue 2: Incorrect Parameter Values

Symptom: Function receives unexpected values or garbage data

Solutions:

  1. Verify parameter names in Block Input/Output match function signature exactly (case-sensitive)
  2. Check data types: Simulink signals must match C parameter types
  3. Use comma-separated list: param1, param2, param3 (spaces optional)
  4. For pointer parameters, add parameter name to Block Output (not Block Input)

Issue 3: GPIO Not Configured

Symptom: Pin remains in default state (usually input/analog)

Solutions:

  1. Add pin to appropriate list in Pins Configuration tab
  2. Use correct format: A0 or A[0] for single pin, A[0 1 2] for multiple
  3. Verify pin names match device datasheet
  4. Check generated code in model_initialize() to confirm setup

Issue 4: Function Executes at Wrong Time

Symptom: Initialization function runs every step, or periodic function runs only once

Solutions:

  1. For functions that run every sample time: select “when block is updated (default)”
  2. For functions that run once at startup: select “only once at power-up time”
  3. Verify Sample Time is set correctly (-1 for inherited)
  4. Check generated code location: model_step() vs model_initialize()

Best Practices

Code Organization

  • Separate Files: Keep user functions in separate .c files from model code
  • Header Guards: Use proper include guards in header files
  • Naming Convention: Use descriptive function names (e.g., read_temperature_sensor not read_temp)

Documentation

  • Function Comments: Document purpose, parameters, and return values
  • Block Naming: Give C Function Call blocks descriptive names matching the function
  • Model Documentation: Add notes explaining custom function integration points

Performance

  • Minimize Calls: Avoid calling expensive functions every sample time if not needed
  • Inline Candidates: Consider inline keyword for small, frequently-called functions
  • Data Types: Match Simulink and C data types to avoid unnecessary conversions

Maintenance

  • Version Control: Track user C files alongside Simulink models
  • Testing: Test custom functions independently before Simulink integration
  • Code Review: Have custom code reviewed like any embedded software