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

16 views (last 30 days)
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);

Accepted Answer

Kaustubha Govind
Kaustubha Govind on 5 Oct 2012
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[]);
  7 Comments
Dana Schwanke
Dana Schwanke on 19 May 2023
Is it possible to use a C macro definition for that fixed length? For example, if in the writeString.h, there was a definition
#define MAX_STRING_LEN 20
is there a way to set that as the output function specification
def.OutputFcnSpec = 'writeStringValue(uint8 u1[MAX_STRING_LEN], uint8 y1[MAX_STRING_LEN])';
I have a situation where I have a definition in the header, and I even have that definition imported into a Data Dictionary using the Simulink.Parameter with ImportedDefine set up so that the macro is available to both the legacy code and Simulink, but running
legacy_code('sfcn_cmex_generate', def)
generates the error
Error using legacycode.LCT.legacyCodeImpl
Unsupported identifier "MAX_STRING_LEN":

Sign in to comment.

More Answers (0)

Categories

Find more on Block and Blockset Authoring in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!