Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

To resolve issues starting MATLAB on Mac OS X 10.10 (Yosemite) visit: http://www.mathworks.com/matlabcentral/answers/159016

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!

Phil

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

// writeString.h START
#ifndef _WRITESTRING_H_
#define _WRITESTRING_H_
#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";
    char *tempStr = "YOU SHOULD NEVER SEE THIS STRING";
      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);

0 Comments

Philip

Products

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[]); 

6 Comments

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?

Philip 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?

Kaustubha Govind

Contact us