[RLP] This works in C# but not in C

Hi,

The following code in C# works but way to slow for our needs:


private void SendData(byte[] redData, byte[] whiteData)
{
    for (byte i = 0; i < 128; i++)
    {
        ShiftOutData(redData[i], whiteData[i]);
    }

    stbPin.Write(true);
    stbPin.Write(false);
}

private void ShiftOutData(byte redByte, byte whiteByte)
{
    for (byte i = 0; i < 8; i++)
    {
        redPin.Write((redByte & 1) == 1);
        whitePin.Write((whiteByte & 1) == 1);
        clkPin.Write(true);
        redPin.Write(false);
        whitePin.Write(false);
        clkPin.Write(false);

        redByte >>= 1;
        whiteByte >>= 1;
    }
}

To make this faster, I figured I’d execute it as an RLP procedure:


void ShiftOutData(unsigned char byte0, unsigned char byte1)
{
		char bit;
		for (bit = 0; bit < 8; bit++)
		{		
			RLPext->GPIO.WritePin(RED_PIN, (byte0 & 1));
			RLPext->GPIO.WritePin(WHITE_PIN, (byte1 & 1));		
			RLPext->GPIO.WritePin(CLK_PIN, 1);
			RLPext->GPIO.WritePin(RED_PIN, 0);
			RLPext->GPIO.WritePin(WHITE_PIN, 0);		
			RLPext->GPIO.WritePin(CLK_PIN, 0);
			
			byte0 >>= 1;
			byte1 >>= 1;
		}
}

int SendData(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
	unsigned char *data0 = generalArray;
	unsigned char *data1 = (unsigned char*)args[0]; 
	
	char i;
	for(i = 0; i < 128; i++)
	{
		ShiftOutData(data0[i], data1[i]);
		i++;
	}

	RLPext->GPIO.WritePin(STB_PIN, 1);	
	RLPext->GPIO.WritePin(STB_PIN, 0);
		
	return 0;	
}

When I call this from C#, sometimes it works, sometimes it doesn’t.
Most of the time, the data is only written to the ports the first time,
after that nothing… unless I place a breakpoint on the RLP call, let VS pauze and then resume the application.

Any ideas what might be causing this kind of behaviour ?
So different behaviour when you put a breakpoint on the RLP call and when you don’t.

Also, I’m no C expert, perhaps I have a mistake in the C code
and it does not offer the same functionality as the C# code?

Thank you for your time

Did you make sure the pins are constructed with OutputPort in C# before you try to use the pins in RLP?

  1. make pin output in C#
  2. toggle the pin and scope it to verify it is actually working
  3. call an RLP that toggles the same pin and it should always work fine

Aha, I did not do that no,
Thank you for the tip, you always seem the first to help :slight_smile:

Is this a typo?

Or is data0 and data1 global as the loop is outputting byte0 and byte1.

Cheers Ian

Sorry was refactoring the names,
the original names and C code came from our hardware engineer.
data0 should be byte0 and data1 should be byte1

(still need to choose better names)

Anyway, my RlpWrapper class now creates the output ports and the scope shows
that they are in fact working now.

However… it would seem the data differs…
Rather hard to debug RLP of course,
but is there any reason why a byte[] sent from C# to C might suddenly
contain different values?

For example:

byte[] emptyData = new byte[128];
someRlpAction.Invoke(emptyData);

In C# we know the array is filled with 0xFF,
but if I pass it to C this way, can I asume the same is true?

The reason I’m asking is that for some reason now,
the C# code an C code are not sending the same data to the connected hardware.

They must be the same.
If you are not sure about something, make a very simple test and show the code here.

For clarity:

This is the C# code:

private void SendData(byte[] redData, byte[] whiteData)
{
    for (byte i = 0; i < 128; i++)
    {
        ShiftOutData(redData[i], whiteData[i]);
    }

    stbPin.Write(true);
    stbPin.Write(false);
}

private void ShiftOutData(byte redByte, byte whiteByte)
{
    for (byte i = 0; i < 8; i++)
    {
        redPin.Write((redByte & 1) == 1); 
        whitePin.Write((whiteByte & 1) == 1);                
        clkPin.Write(true);
        redPin.Write(false);
        whitePin.Write(false);
        clkPin.Write(false);

        redByte >>= 1;
        whiteByte >>= 1;
    }
}

This is the C code in the RLP library:

int SendData(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
	unsigned char *redData = generalArray;
	unsigned char *whiteData = (unsigned char*)args[0]; 
	
	char i;
	for(i = 0; i < SINGLE_COLOR_DISPLAY_DATA_LENGTH; i++)
	{
		ShiftOutData(redData[i], whiteData[i]);
		i++;
	}

	RLPext->GPIO.WritePin(STB_PIN, 1);	
	RLPext->GPIO.WritePin(STB_PIN, 0);

	return 0;	
}

void ShiftOutData(unsigned char redByte, unsigned char whiteByte)
{
		char bit;
		for (bit = 0; bit < 8; bit++)
		{		
			RLPext->GPIO.WritePin(RED_PIN, (redByte & 1 == 1));
			RLPext->GPIO.WritePin(WHITE_PIN, (whiteByte & 1 == 1));		
				
			RLPext->GPIO.WritePin(CLK_PIN, 1);

			RLPext->GPIO.WritePin(RED_PIN, 0);
			RLPext->GPIO.WritePin(WHITE_PIN, 0);		
			
			RLPext->GPIO.WritePin(CLK_PIN, 0);
			
			redByte >>= 1;
			whiteByte >>= 1;
		}
}

Aside from timing… are these two identical ?

If I use the C# version I simply do something like

SendData(redBytes, whiteBytes)

If I use the RLP version I use something like

rlp.SendData(redBytes, whiteBytes)

Where the rlp is simply an instance of an RLP wrapper class.

(Code snippet:)

public RlpWrapper(byte[] elfImage, Cpu.Pin stbPin, Cpu.Pin clkPin, Cpu.Pin oePin, Cpu.Pin redPin, Cpu.Pin whitePin, int numberOfRows = 96)
{
    LoadElf(elfImage);
    StbPin = stbPin;
    ClkPin = clkPin;
    OePin = oePin;
    RedPin = redPin;
    WhitePin = whitePin;
    NumberOfRows = numberOfRows;

    clkPort = new OutputPort(clkPin, false);
    stbPort = new OutputPort(stbPin, false);
    redPort = new OutputPort(redPin, false);
    whitePort = new OutputPort(whitePin, false);
}

public int SendData(byte[] redData, byte[] whiteData)
{
    return SendDataAction.Invoke(redData, whiteData);
}

When compiling the RLP I do get the following warnings:

In function ‘SendData’:

warning: array subscript has type ‘char’

Could this be an indication of possible cause?

Never mind about the warning, it was because I wasn’t using an integer for the indexer of the array

Here is the code to get rid of that warning


int SendData(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
	unsigned char *redData = generalArray;
	unsigned char *whiteData = (unsigned char*)args[0]; 
 
	int i;
	for(i = 0; i < SINGLE_COLOR_DISPLAY_DATA_LENGTH; i++)
	{
		ShiftOutData(redData[i], whiteData[i]);
		i++;
	}
 
	RLPext->GPIO.WritePin(STB_PIN, 1);	
	RLPext->GPIO.WritePin(STB_PIN, 0);
 
	return 0;	
}
 
void ShiftOutData(unsigned char redByte, unsigned char whiteByte)
{
		int bit;
		for (bit = 0; bit < 8; bit++)
		{		
			RLPext->GPIO.WritePin(RED_PIN, (redByte & 1 == 1));
			RLPext->GPIO.WritePin(WHITE_PIN, (whiteByte & 1 == 1));		
 
			RLPext->GPIO.WritePin(CLK_PIN, 1);
 
			RLPext->GPIO.WritePin(RED_PIN, 0);
			RLPext->GPIO.WritePin(WHITE_PIN, 0);		
 
			RLPext->GPIO.WritePin(CLK_PIN, 0);
 
			redByte >>= 1;
			whiteByte >>= 1;
		}
}

you are using char variable to get individual elements of the arrays, I have changed it to int.

P.S. You found it while I was typing this message ;D

When you use Invoke(…), redData should be in arg[0] and whiteData should be in arg[1]. generalArray is empty.

Mike,
Thank you I will try this.

However, please note the following code sample from the GHI docs:

// C#: Foo8.Invoke(byteArray, byteArray2);     // uses a generalArray (byteArray)  // one variable argument (byteArray2)
int Foo8(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
    // argsCount == 1
    // argSize[0] == 10
    unsigned char *byteArray2 = (unsigned char*)args[0];    // same byteArray2 in C#
    // generalArray is the same as byteArray in C#

    return 0;    // Some return value
}

From this I concluded the generalArray would be the first array I passed.

This was in a very old release. Are you using the latest SDK? Where did you get that documentation?
The latest: http://ghielectronics.com/downloads/NETMF/Library%20Documentation/html/f608e398-6573-8adc-5d59-b904dfa3fcee.htm
shows InvokeEX not Invoke()

Oh… this is the first document I got when I googled
I should have a look on my HD actually.

What exactly is the difference between Invoke and InvokeEx ?

All details you need is in the link Mike pointed out above. Reading documented is always a good idea :slight_smile:

I’m sorry, have read it many times,
but I’m not that smart probably :stuck_out_tongue:

This is all the information in that document on InvokeEx:

[quote]InvokeEx(generalArgument, params object[] argList) has the following:
argList is the same as above and has the same rules. This is optional. It is a variable argument list and has some overhead when invoking the Native code. The least overhead is achieved when sending byte[] from C#, as opposed to sending a string or int.
generalArgument has several overloads. It is an array of choice. This array is passed as a pointer, changing numbers in native code will affect numbers in managed code. Also, this argument has minimum overhead on invoking Native C from C# application.
The array can be byte [], int [], float [] …etc[/quote]

It doesn’t really explain to me why I should use this instead of simply Invoke() for example.

From Mike’s comment I have now deducted that I can actually use
either SendDataAction.Invoke(red,white) or SendDataAction.InvokeEx(red,white)

and depending on which one I used I’d have to write either:

int SendData(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{	
	unsigned char *redData = (unsigned char*)args[0]; 
	unsigned char *whiteData = (unsigned char*)args[1]; 
	
	int i;
	for(i = 0; i < 128; i++)
	{
		ShiftOutData(redData[i], whiteData[i]);
		i++;
	}

	RLPext->GPIO.WritePin(STB_PIN, 1);		
	RLPext->GPIO.WritePin(STB_PIN, 0);

	return 0;	
}

for the .Invoke() version, and:

int SendData(unsigned char *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{	
	unsigned char *redData = generalArray;
	unsigned char *whiteData = (unsigned char*)args[0]; 
	
	int i;
	for(i = 0; i < 128; i++)
	{
		ShiftOutData(redData[i], whiteData[i]);
		i++;
	}

	RLPext->GPIO.WritePin(STB_PIN, 1);		
	RLPext->GPIO.WritePin(STB_PIN, 0);

	return 0;	
}

for the .InvokeEx() version.

That does not explain to me why I should use on or the other.

(I keep asking because I’m still not getting the results I’m expecting)

I think it is clear if you read the documentation from the beginning.
Invoke() does NOT take a general argument.
InvokeEX() takes a general argument first.

For your issue, we don’t know why you are not getting the correct results. Is it because of a bug? wrong pin definition? RLP?

Test RLP first, pass the arrays and make sure they have correct data. For example sum the first 3 numbers and make sure they are correct in each array…
In C#, do something like this:
redData{[21, 87, 95}
int sum = Invoke(reddata);
if(sum != 21+87+95) ERROR

in C++: add them
return (redData[0] + redData[1] + redData[2]);

Mike
Thnx for the feedback.
Will try those things.

It’s most likely a bug on our side.
I’m not that familiar with C code so at firs tI asumed I simply
made a mistake in the C code, now I’m thinking maybe it’s a timing issue
because that’s the only thing that remains different in the C and C# version.

Oh my god… feel stupid now

I missed the following (and nobody noticed)

int i;
for(i = 0; i < SINGLE_COLOR_DISPLAY_DATA_LENGTH; i++)
{
	ShiftOutData(redData[i], whiteData[i]);
        i++;
}

Such a beginner’s mistake… I should go and hide somewhere…
Notice the i++ inside the loop

/me slaps himself in the face facepalm

Glad it worked for you and hope you like RLP