Using native code

RLP has always been the best for optimizing things but, with TinyCLR, our team managed to make major improvements in this area. With the way you add native interops now, you can easily access the entire API. Everything is exposed in the TinyCLR.h file.

As a quick example, We are trying to refresh a SPI display. This display is 16bpp only but to save on RAM, we will draw in 8bpp. Our flush method will convert 16bit color space to 8bit.

Here is the flush code in C#. This takes about 10 seconds to update the display!!

public void Flush(byte[] data) {
    SetClip(0, 0, Width, Height);
    WriteCommand(0x2C);
    controlPin.Write(GpioPinValue.High);
    for (int i = 0; i < Width * Height; i++) {
        //blue
        int blue = (data[i] & 3) << 3;
        int red = (data[i] & (7 << (2 + 3))) ;
        int green = (data[i] & (7 << 2))>>2;

        if (data[i] != 0) {
            buffer2[0] = (byte)(red | green);
            buffer2[1] = (byte)blue;
        }
        else {
            buffer2[0] = 0x00;
            buffer2[1] = 0x00;
        }
        spi.Write(buffer2);
    }
}

I then took that deep loop and converted it to interop (native call)

Here is the first part in C#

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void NativeFlushHelper(byte[] data);
public void NativeFlush(byte[] data) {
    SetClip(0, 0, Width, Height);
    WriteCommand(0x2C);
    controlPin.Write(GpioPinValue.High);
    NativeFlushHelper(data);
}

and here is the C++ part

#include "AdafruitDisplayShield.h"

TinyCLR_Result Interop_AdafruitDisplayShield_GHIElectronics_TinyCLR_ST7735_ST7735::NativeFlushHelper___VOID__SZARRAY_U1(const TinyCLR_Interop_MethodData md) {

	
	auto ip = reinterpret_cast<const TinyCLR_Interop_Provider*>(md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider));
	
	TinyCLR_Interop_ClrValue arg1;
	
	ip->GetArgument(ip, md.Stack, 1, arg1);
	uint8_t* data = reinterpret_cast<uint8_t*>(arg1.Data.SzArray.Data);
	uint8_t buffer2[2];

	
	auto spiProvider = (const TinyCLR_Spi_Provider*)md.ApiProvider.FindByIndex(&md.ApiProvider, "GHIElectronics.TinyCLR.NativeApis.STM32F4.SpiProvider",0,TinyCLR_Api_Type::SpiProvider);

    if (spiProvider == nullptr) 
		return TinyCLR_Result::ArgumentNull;

	for (int i = 0; i < 160 * 128; i++) {
		int blue = (data[i] & 3) << 3;
		int red = (data[i] & (7 << (2 + 3)));
		int green = (data[i] & (7 << 2)) >> 2;

		if (data[i] != 0) {
			buffer2[0] = (uint8_t)(red | green);
			buffer2[1] = (uint8_t)blue;
		}
		else {
			buffer2[0] = 0x00;
			buffer2[1] = 0x00;
		}
		size_t sz = 2;
		if(spiProvider->Write(spiProvider, 0, buffer2, sz) != TinyCLR_Result::Success)
			return TinyCLR_Result::InvalidOperation;
	}
	return TinyCLR_Result::Success;
}

The display now updates in half a second! Yes from almost 10 seconds to half a second!

There are still major improvements happening in this coming release, to make this even easier, so stay tuned.

And YES this works on any open source device running TinyCLR. This test was done with FEZ holding the Adafruit 1.8" color display shield. Everything is open source!

7 Likes

(I wish there was a way to compile c# into assembly)

Very interesting article shared on my google +.:sunglasses:
https://plus.google.com/collection/YHFDSE

1 Like

thanks Issa for sharing that, i really appreciate. MNO, found your article really interesting. can i ask you some questions based on it? thanks!

Go ahead ask