MATLAB Answers


How do I return a byte array from an S-Function (made from C code) to Simulink?

Asked by Philip
on 4 Oct 2012

Let me start by saying that I am new to creating S-functions from C code and I have never really been great at C code. Therefore, please excuse my ignorance on the subject. Your help is appreciated!

The task I have is to create a Simulink model that will work on xPC Target to communicate with a serial device that accepts ANSII commands. In order to create these commands, my Simulink model is going to pass an enumerated value into this "middle block". The middle block is going to build the ANSII string, convert the string to a byte array, and then output the byte array. The byte array is then going to be sent over serial to the hardware I’m trying to control.

for the "midle block". I currently have an Embedded MATLAB (or whatever it’s called now in 2011a) block that builds the command strings and convert the string to a byte array. However, I would like to replace the Embedded MATLAB block with an S-Function block (created from C code) so that I can more easily manipulate the string using C code (not fun in EML). I apologize for not giving details... hard to explain why.

The problem I’m facing is that I am having a hard time generating the mex file for the S-function because I’m not too familiar with passing arrays into and out of an S-function. My idea was that I could pass a byte array (all zeros) into the S-function and have the S-function output the updated byte array out (meaningful string convert to byte array).

However, it seems that it's not that simple with the code I have written because passing arrays to and from a C-function requires pointers... and the Legacy Code Tool is not compatible with function pointers.

The C code and the Legacy Code Tool script I'm using to generate the S-function are below. The output from LCT is shown at the end of this post.

I know for a fact that the input argument (type char/uint8) is working correctly. It's only the char* type for the function output that is causing a problem. I have included the c driver for this code as well.

I’ve heard about using mxArray, but have no experience using this. Plus it seems like it would involve some invasive changes to the C code. Since I want to use the C code in another environment, I'd like to avoid putting anything involving MATLAB in the code, even if I were to wrap it with #ifdefs and set an environment flag.

Is there any simple way for me to output a byte array without using mxArray?

Your help is much appreciated, and again, please pardon my ignorance… I didn’t go to school for a Computer Science degree… but now I wish I had!


-------------------------- writeString.h ---------------------------------

// writeString.h START
#include "string.h"
#include "stdio.h"
//char writeStringValue(char cmdStr[]);
char* writeStringValue(char cmdStr[]);
#endif /* _WRITESTRING_H_ */
// writeString.h END

-------------------------- writeString.c ---------------------------------

// writeString.c START
#include "writeString.h"
//char writeStringValue(char cmdStr[])
char* writeStringValue(char cmdStr[])
    int n = sprintf(cmdStr, "NEW STRING VALUE");
      //return (char) cmdStr[1];
      return cmdStr;
  // writeString.c END

-------------------------- C TEST DRIVER ---------------------------------

// writeString_DRIVER.c= START
#include "stdio.h"
#include "string.h"
#include "writeString.h"
int main()
    char cmdStr[] = "ORIGINAL STRING VALUE";
      strcpy(tempStr, cmdStr);
      printf("Original TempStr Value = %s\n", tempStr);
      //char a = writeStringValue(&cmdStr[0]);
      tempStr = writeStringValue(&cmdStr[0]);
      //printf("New TempStr Value = %d\n",a);
      printf("New TempStr Value = %s\n",tempStr);
      return 1;
  // writeString_DRIVER.c END

-------------------------- LCT SCRIPT ---------------------------------

% Create wrapper S-function using Legacy Code Tool
def = legacy_code('initialize');
def.SourceFiles = {'writeString.c'};
def.HeaderFiles = {'writeString.h'};
def.SFunctionName = 'String_Builder';
def.OutputFcnSpec = 'uint8 y1 = writeStringValue(uint8 u1[])';
legacy_code('sfcn_cmex_generate', def);
legacy_code('compile', def);
legacy_code('slblock_generate', def);

-------------------------- LCT OUTPUT ERROR ---------------------------------

>> createSFunction
### Start Compiling String_Builder
    mex('String_Builder.c', 'C:\Users\ppesce\Desktop\MDriveTestDriver\TestSingleChar\writeString.c', '-IC:\Users\ppesce\Desktop\MDriveTestDriver\TestSingleChar')
Error String_Builder.c: 246  operands of = have illegal types `unsigned char' and `pointer to char' 
1 errors, 0 warnings 
    C:\PROGRA~1\MATLAB\R2011B\BIN\MEX.PL: Error: Compile of 'String_Builder.c' failed. 
Error using mex (line 206)
Unable to complete successfully.
Error in C:\Program Files\MATLAB\R2011b\toolbox\simulink\simulink\+legacycode\@LCT\compile.p>compile (line 268)
Error in C:\Program Files\MATLAB\R2011b\toolbox\simulink\simulink\+legacycode\@LCT\legacyCodeImpl.p>legacyCodeImpl (line 84)
Error in legacy_code (line 87)
[varargout{1:nargout}] = legacycode.LCT.legacyCodeImpl(action, varargin{1:end});
Error in createSFunction (line 8)
legacy_code('compile', def);


1 Answer

Answer by Kaustubha Govind
on 5 Oct 2012
 Accepted answer

Since Simulink allocates and manages the memory for the input and the output, in general, the engine will maintain separate memory locations for the input and output. So, unless you configure the input buffer to be reused by the output (using ssSetInputPortOverWritable) and confirm that Simulink is indeed reusing the input buffer for the output (see ssGetInputPortBufferDstPort), you cannot make your function writeStringValue to write to the input.

The simplest solution would be to pass the input and output pointers to the function, assuming your input and output are of fixed length:

def.OutputFcnSpec = 'writeStringValue(uint8 u1[20], uint8 y1[20])';

Your function will also need to be appropriately re-defined:

writeStringValue(char source[], char destination[]); 


Kaustubha Govind
on 9 Oct 2012

Strange. I just ran this code:

def = legacy_code('initialize');
def.SourceFiles = {'writeString.c'};
def.HeaderFiles = {'writeString.h'};
def.SFunctionName = 'String_Builder';
def.OutputFcnSpec = 'writeStringValue(uint8 u1[20], uint8 y1[20])';
legacy_code('sfcn_cmex_generate', def);

And got this in String_Builder.c:

static void mdlOutputs(SimStruct *S, int_T tid)
   * Get access to Parameter/Input/Output/DWork/size information
  uint8_T *u1 = (uint8_T *) ssGetInputPortSignal(S, 0);
  uint8_T *y1 = (uint8_T *) ssGetOutputPortSignal(S, 0);
     * Call the legacy code function
    writeStringValue( u1, y1);

Do you see something different?

on 10 Oct 2012

Oh man... I'm an idiot. I didn't notice that you put the output (y1) in the argument list. This works! Thank you for your help and your patience!

Kaustubha Govind
on 11 Oct 2012

Glad it helped! Could you accept my answer if you're satisfied with it, please?

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply today