C Function Call Block Icon
Interface to custom C functions from Simulink models, enabling integration of legacy code, vendor libraries, and optimized routines with automatic code generation.

When to use:

When to use:

  • Integrating legacy C code or existing libraries into Simulink model
  • Hand-optimized assembly or C routines needed for performance-critical sections
  • Vendor SDKs or third-party libraries required (CAN stacks, sensor drivers)
  • Compiler built-ins needed (__delay_ms(), __builtin_dmaoffset(), etc.)
  • Custom peripheral register access not available in standard MCHP blocks

When NOT to use:

  • Standard MCHP blocks already provide the functionality (use peripheral blocks)
  • Complex C code easier to write directly in generated code (use Custom Code blocks)
  • Function signature unclear or undocumented โ€” parsing may fail
  • Excessive CPU overhead from frequent calls โ€” optimize algorithm in Simulink first

Overview

The MCHP_C_function_Call block enables integration of custom C functions into Simulink models with automatic code generation for Microchip targets. It provides a bridge between Simulink visual programming and custom C code.

Key Features:

  • Call external C functions from Simulink
  • Automatic parsing of user C files to list available functions
  • Support for function inputs, outputs, and return values
  • Pin configuration for GPIO requirements
  • Execution timing control (every update or once at initialization)
  • Optional header file declaration management

Typical Use Cases

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

Block Dialog

C Function Call Block โ€” Function Tab
Pins Configuration TabC Function Call Block โ€” Pins Configuration Tab

Parameters

Function Configuration (Tab 1)

ParameterVariableDescription
Parse user file to list functionsParseFilesChkbxWhen enabled, scans user C files and populates function list dropdown
Functions foundListFunctions1Dropdown populated with parsed functions or “Custom Function” for manual entry
Function DeclarationFunction1C function signature (e.g., extern void my_func(int x);)
Add this function declaration in header fileAddHeaderDeclarationWhen enabled, adds declaration to generated header
Execute functionFunction1Typewhen block is updated (default): Called every sample time
only once at power-up time: Called once during initialization
Block Input (comma separated function parameters)Fct1BlockInComma-separated list of parameters passed from Simulink input ports
Function returned value is a block outputFctReturnBlockOutWhen enabled, function return value becomes a Simulink output port
Block Output (comma separated function parameters)Fct1BlockOutComma-separated list of parameters passed by pointer/reference for outputs
Block execution orderingORDERINGNone: Default Simulink ordering
Input: Force execution after input dependencies
Output: Force execution before outputs consumed
Input & Output: Explicit ordering control
Sample TimeSampleTimeBlock sample time (-1 for inherited)

Pins Configuration (Tab 2)

ParameterVariableDescriptionFormat
List of port used as Digital InputPinDigitalInputGPIO pins configured as digital inputse.g., A[1 2] B12
List of port used as Digital OutputPinDigitalOutputGPIO pins configured as digital outputse.g., A[1 2] B12
List of port used as Analog InputPinAnalogInputGPIO pins configured as analog inputse.g., A[1 2] B12

Note: Pin configuration automatically generates TRIS and ANSEL register initialization code.

Common Issues

Function Not Found During Build

Symptom: Linker error undefined reference to 'my_function'

Causes:

  • User C file not in model directory
  • User C file not added to CustomSource in Code Generation settings
  • Function defined in header file only (no implementation)

Solution:

  1. Ensure user C file is in the model directory, or
  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 file

Incorrect Parameter Values

Symptom: Function receives unexpected values or garbage data

Causes:

  • Parameter names in Block Input/Output don’t match function signature
  • Data type mismatch between Simulink signal and C function parameter
  • Missing comma in Block Input parameter list

Solution:

  1. Verify parameter names in Block Input/Output match exactly (case-sensitive)
  2. Check data types: Simulink signals must match C parameter types
  3. Use comma-separated list: param1, param2, param3 (spaces optional)

GPIO Not Configured

Symptom: Pin remains in default state (usually input)

Causes:

  • Pin name not added to Pins Configuration tab
  • Pin name format incorrect
  • Device datasheet uses different pin naming

Solution:

  1. Add pin to appropriate list: Digital Input, Digital Output, or Analog Input
  2. Use correct format: A0 or A[0] for single pin, A[0 1 2] for multiple
  3. Verify pin names match device datasheet (some use RA0, others A0)

Function Executes at Wrong Time

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

Cause: Incorrect Execute function setting

Solution:

  • For functions that run every sample time: select “when block is updated (default)”
  • For functions that run once at startup: select “only once at power-up time”
  • Verify Sample Time is set correctly (-1 for inherited)

Examples

Example 1: Using Compiler Built-in Delay Function

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

Configuration:

Function Declaration: extern void __delay_ms(unsigned long t_ms);
Execute function: when block is updated (default)
Block Input: (leave empty - parameter name is t_ms)
Function returned value is a block output: OFF
Sample Time: -1

Simulink Model:

[Constant: 10] โ”€โ”€> [C Function Call] 
                   (__delay_ms)

Generated Code:

// In model_step() function
__delay_ms(rtb_Constant);

Example 2: Calling Custom Function with Multiple Parameters

User C File (user_functions.c):

void process_data(int16_t input1, int16_t input2, int16_t *output) {
    *output = (input1 + input2) / 2;
}

Block Configuration:

Function Declaration: extern void process_data(int16_t input1, int16_t input2, int16_t *output);
Block Input: input1, input2
Block Output: output
Add declaration in header: ON

Simulink Model:

[Signal A] โ”€โ”€โ”
             โ”œโ”€โ”€> [C Function Call] โ”€โ”€> [Output]
[Signal B] โ”€โ”€โ”˜     (process_data)

Generated Code:

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

// In model_step()
process_data(rtb_SignalA, rtb_SignalB, &rtb_Output);

Example 3: One-Time Initialization Function

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

User C File:

void init_custom_hardware(void) {
    // Hardware initialization code
}

Configuration:

Function Declaration: extern void init_custom_hardware(void);
Execute function: only once at power-up time
Block Input: (leave empty - no parameters)
Add declaration in header: ON

Generated Code:

// In model_initialize() function
init_custom_hardware();

Example 4: Function with Return Value

User C File:

uint32_t get_cpu_load(void) {
    // Calculate CPU utilization percentage
    return cpu_load_percent;
}

Block Configuration:

Function Declaration: extern uint32_t get_cpu_load(void);
Function returned value is a block output: ON
Block Input: (leave empty - no parameters)
Block Output: (leave empty - return value becomes output)

Simulink Model:

[C Function Call] โ”€โ”€> [Display]
  (get_cpu_load)

Generated Code:

// In model_step()
rtb_Output = get_cpu_load();

Example 5: GPIO Configuration with Custom Function

User C File:

void set_led_pattern(uint8_t pattern) {
    LATB = pattern & 0xFF;
}

Block Configuration - Function Tab:

Function Declaration: extern void set_led_pattern(uint8_t pattern);
Block Input: pattern

Block Configuration - Pins Configuration Tab:

List of port used as Digital Output: B[0:7]

Generated Code:

// In model_initialize()
TRISBbits.TRISB0 = 0;
TRISBbits.TRISB1 = 0;
// ... (B2-B6) ...
TRISBbits.TRISB7 = 0;
ANSELBbits.ANSB0 = 0;  // If device has ANSEL registers
ANSELBbits.ANSB1 = 0;
// ... (B2-B6) ...
ANSELBbits.ANSB7 = 0;

// In model_step()
set_led_pattern(rtb_Pattern);
Implementation Details (click to expand)

Implementation Details

Function Parsing

When “Parse user file” is enabled, the block scans C files specified in:

  • CustomSource (Configuration Parameters โ†’ Code Generation โ†’ Custom Code โ†’ Source files)
  • CustomHeaderCode (Configuration Parameters โ†’ Code Generation โ†’ Custom Code โ†’ Additional includes)
  • CustomSourceCode (Configuration Parameters โ†’ Code Generation โ†’ Custom Code โ†’ Source code)

The parser uses regular expressions to match function declarations of the form:

[extern] [unsigned|signed] <return_type> [*] <function_name>(<parameter_list>);

Recognized data types include:

  • Standard C types: void, char, int, short, long, float, double
  • Fixed-width types: int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t
  • Simulink types: boolean_T, int16_T, uint16_T, etc.
  • Custom types: Simulink.Bus objects defined in base workspace

Code Generation

The block generates code using the TLC file MCHP_C_function_Call.tlc:

1. Header Declaration (Optional)

If AddHeaderDeclaration is enabled, the function prototype is added to the generated header:

// In model_private.h
extern void my_function(int x);

2. Function Call Location

Depends on Function1Type parameter:

When “when block is updated (default)”:

// In model_step() via Outputs() section
void model_step(void) {
    // ... other code ...
    my_function(input_value);
    // ... other code ...
}

When “only once at power-up time”:

// In model_initialize() via Start() section
void model_initialize(void) {
    // ... other initialization ...
    init_function();
    // ... other initialization ...
}

Parameter Passing

ConfigurationGenerated CodeDescription
Scalar input parameterLibBlockInputSignal()Pass by value
Pointer/array input parameterLibBlockInputSignalAddr()Pass by address
Scalar output parameterLibBlockOutputSignal()Direct assignment (error if used as parameter)
Pointer/array output parameterLibBlockOutputSignalAddr()Pass by address
Literal value in Block InputDirect valueConstant passed directly
Function return valueoutput = function(...)Assigned to first output port if enabled

Example with mixed parameter types:

Function: extern int my_func(int a, float *b, uint16_t c);
Block Input: a, b, c
Function returned value is a block output: ON

Generated call:
rtb_Output = my_func(rtb_InputA, &rtb_InputB, rtb_InputC);

Pin Configuration Details

The Pins Configuration tab generates initialization code based on port type:

Port TypeTRIS ConfigurationANSEL Configuration
Digital InputTRISx = 1 (input)ANSELx = 0 (digital mode)
Digital OutputTRISx = 0 (output)ANSELx = 0 (digital mode)
Analog InputTRISx = 1 (input)ANSELx = 1 (analog mode)

Pin Format:

  • Single pin: A5, B12
  • Multiple pins: A[0 1 2], B[5:10]
  • Mixed: A[1 2] B5 C[7:9]

Generated code example:

PinDigitalInput: A[0 1] B5
โ†’ Generates in model_initialize():
  TRISAbits.TRISA0 = 1;
  TRISAbits.TRISA1 = 1;
  TRISBbits.TRISB5 = 1;
  ANSELAbits.ANSA0 = 0;  // If device supports ANSEL
  ANSELAbits.ANSA1 = 0;
  // ANSELB may not exist depending on device

References

  • User Functions Overview: Introduction to custom code integration

  • Master Block: Device configuration and compiler settings

  • Code Generation: Custom code integration options

  • Embedded Coder: Custom code integration

  • S-Function Builder: Advanced custom block functionality