SPI's BusyPin and BusyPin_ActiveState

A while back the SPI.Configuration constructor got a few new parameters: Cpu.Pin BusyPin and bool BusyPin_ActiveState.



public Configuration(Cpu.Pin ChipSelect_Port, bool ChipSelect_ActiveState, 
uint ChipSelect_SetupTime, uint ChipSelect_HoldTime, bool Clock_IdleState, 
bool Clock_Edge, uint Clock_RateKHz, SPI.SPI_module SPI_mod, 
Cpu.Pin BusyPin, bool BusyPin_ActiveState);


I found this diagram, but I’m still unsure how these should be used.
http://www.eewiki.net/display/LOGIC/Serial+Peripheral+Interface+(SPI)+Master+(VHDL)

I’m trying to read data from an 18bit ADC (LTC2364-18) via SPI. The data is going to be coming at 20Mhz. The chip’s spec mentions a pin called BUSY…

“Figure 10 shows a single LTC2364-18 operated in normal mode with CHAIN and RDL/SDI tied to ground. With RDL/SDI grounded, SDO is enabled and the MSB(D17) of the new conversion data is available at the falling edge of BUSY. This is the simplest way to operate the LTC2364-18.”

My ADC is getting fed by a spectrometer. I should get 256 pixels (18bits each) in a single scan. I’m thinking this is what the new BusyPin is for. I should be able to say, read the next 768 bytes (18bits * 256pixels) and netmf should handle waiting per the spec until the busy pin indicates data is available.

Thoughts? Has anyone used these BusyPin parameters?

Turns out the feature isn’t implemented (at least not for the STMF4). A few lines in the right place was all it took. This was the second time I had to modify native code, the first was to enable the watchdog. I’m happy to say it was pretty painless on both occasions. I’m loving netmf. I will upload once it’s polished and properly tested.

How do I mark my own reply as the answer? :wink:

Maybe post the code here and we can add to firmware.

Changes to CPU_SPI_decl.h …



struct SPI_CONFIGURATION
{
    GPIO_PIN       DeviceCS;
    BOOL           CS_Active;             // False = LOW active,      TRUE = HIGH active
    BOOL           MSK_IDLE;              // False = LOW during idle, TRUE = HIGH during idle
    BOOL           MSK_SampleEdge;        // False = sample falling edge,  TRUE = samples on rising
    BOOL           MD_16bits;
    UINT32         Clock_RateKHz;
    UINT32         CS_Setup_uSecs;
    UINT32         CS_Hold_uSecs;
    UINT32         SPI_mod;
    GPIO_FLAG      BusyPin;
	//----------------------------------------------------------------------------
	// I added this...
	//----------------------------------------------------------------------------
    UINT32         BusyPin_BytesPerFrame;		// Sensor arrays, etc. require large amounts of data to be read sequentially
    											// from ADCs, etc. that toggle the busy pin between frames.  Manage code can't
    											// execute fast enough to do this.  For example, reading 256 pixels from an
    											// analog array.  Something like an 18bit ADC might be used to read data.
    											// The ADC would commonly need to send 256 pixels, 18bits each, at 20Mhz.
	//----------------------------------------------------------------------------
};

struct SPI_XACTION_16
{
    UINT16* 	Write16;
    INT32   	WriteCount;
    UINT16* 	Read16;
    INT32   	ReadCount;
    INT32   	ReadStartOffset;
    UINT32  	SPI_mod;
    GPIO_FLAG 	BusyPin;
	//----------------------------------------------------------------------------
	// I added this...
	//----------------------------------------------------------------------------
    UINT32      BusyPin_BytesPerFrame;
	//----------------------------------------------------------------------------
};

struct SPI_XACTION_8
{
    UINT8* 		Write8;
    INT32  		WriteCount;
    UINT8* 		Read8;
    INT32  		ReadCount;
    INT32  		ReadStartOffset;
    UINT32 		SPI_mod;
    GPIO_FLAG 	BusyPin;
	//----------------------------------------------------------------------------
	// I added this...
	//----------------------------------------------------------------------------
    UINT32      BusyPin_BytesPerFrame;
	//----------------------------------------------------------------------------
};


Changes to STM32F4_SPI_functions.cpp …



BOOL CPU_SPI_nWrite16_nRead16( const SPI_CONFIGURATION& Configuration, UINT16* Write16, INT32 WriteCount, UINT16* Read16, INT32 ReadCount, INT32 ReadStartOffset )
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    if(!CPU_SPI_Xaction_Start( Configuration )) return FALSE;
    SPI_XACTION_16 Transaction;
    Transaction.Read16          = Read16;
    Transaction.ReadCount       = ReadCount;
    Transaction.ReadStartOffset = ReadStartOffset;
    Transaction.Write16         = Write16;
    Transaction.WriteCount      = WriteCount;
    Transaction.SPI_mod         = Configuration.SPI_mod;
	//----------------------------------------------------------------------------
	// This stuff wasn't being passed before...
	//----------------------------------------------------------------------------
    Transaction.BusyPin			= Configuration.BusyPin;
    Transaction.BusyPin_BytesPerFrame = Configuration.BusyPin_BytesPerFrame;
	//----------------------------------------------------------------------------
    if(!CPU_SPI_Xaction_nWrite16_nRead16( Transaction )) return FALSE;
    return CPU_SPI_Xaction_Stop( Configuration );
}

BOOL CPU_SPI_nWrite8_nRead8( const SPI_CONFIGURATION& Configuration, UINT8* Write8, INT32 WriteCount, UINT8* Read8, INT32 ReadCount, INT32 ReadStartOffset )
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    if(!CPU_SPI_Xaction_Start( Configuration )) return FALSE;
    SPI_XACTION_8 Transaction;
    Transaction.Read8           = Read8;
    Transaction.ReadCount       = ReadCount;
    Transaction.ReadStartOffset = ReadStartOffset;
    Transaction.Write8          = Write8;
    Transaction.WriteCount      = WriteCount;
    Transaction.SPI_mod         = Configuration.SPI_mod;
	//----------------------------------------------------------------------------
	// This stuff wasn't being passed before...
	//----------------------------------------------------------------------------
    Transaction.BusyPin			= Configuration.BusyPin;
    Transaction.BusyPin_BytesPerFrame = Configuration.BusyPin_BytesPerFrame;
	//----------------------------------------------------------------------------
    if(!CPU_SPI_Xaction_nWrite8_nRead8( Transaction )) return FALSE;
    return CPU_SPI_Xaction_Stop( Configuration );
}


And…



BOOL CPU_SPI_Xaction_nWrite16_nRead16( SPI_XACTION_16& Transaction )
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();

    ptr_SPI_TypeDef spi = g_STM32_Spi_Port[Transaction.SPI_mod];

    UINT16* outBuf = Transaction.Write16;
    UINT16* inBuf  = Transaction.Read16;
    INT32 outLen = Transaction.WriteCount;
    INT32 num, ii, i = 0;

    if (Transaction.ReadCount) { // write & read
        num = Transaction.ReadCount + Transaction.ReadStartOffset;
        ii = -Transaction.ReadStartOffset;
    } else { // write only
        num = outLen;
        ii = 0x80000000; // disable write to inBuf
    }

    UINT16 out = outBuf[0];
    UINT16 in;
    spi->DR = out; // write first word
    while (++i < num) {

	//----------------------------------------------------------------------------
	//Wait until the busy pin toggles
	//----------------------------------------------------------------------------
	if (Transaction.BusyPin.Pin != GPIO_PIN_NONE)
	{
		while (CPU_GPIO_GetPinState( Transaction.BusyPin.Pin ) != Transaction.BusyPin.ActiveState);
	}
	//----------------------------------------------------------------------------

        if (i < outLen) out = outBuf[i]; // get new output data
        while (!(spi->SR & SPI_SR_RXNE)); // wait for Rx buffer full
        in = spi->DR; // read input
        spi->DR = out; // start output
        if (ii >= 0) inBuf[ii] = in; // save input data
        ii++;

	//----------------------------------------------------------------------------
	//When all the bytes in a frame at read, wait until the busy pin toggles back.
	//----------------------------------------------------------------------------
	if (Transaction.BusyPin.Pin != GPIO_PIN_NONE)
	{
		if (Transaction.BusyPin_BytesPerFrame > 1)
		{
			if ((i + 1) % (Transaction.BusyPin_BytesPerFrame / 2) == 0)
			{
				while (CPU_GPIO_GetPinState( Transaction.BusyPin.Pin ) == Transaction.BusyPin.ActiveState);
			}
		}
	}
	//----------------------------------------------------------------------------

    }
    while (!(spi->SR & SPI_SR_RXNE)); // wait for Rx buffer full
    in = spi->DR; // read last input
    if (ii >= 0) inBuf[ii] = in; // save last input

    return TRUE;
}

BOOL CPU_SPI_Xaction_nWrite8_nRead8( SPI_XACTION_8& Transaction )
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();

    ptr_SPI_TypeDef spi = g_STM32_Spi_Port[Transaction.SPI_mod];

    UINT8* outBuf = Transaction.Write8;
    UINT8* inBuf  = Transaction.Read8;
    INT32 outLen = Transaction.WriteCount;
    INT32 num, ii, i = 0;

    if (Transaction.ReadCount) { // write & read
        num = Transaction.ReadCount + Transaction.ReadStartOffset;
        ii = -Transaction.ReadStartOffset;
    } else { // write only
        num = outLen;
        ii = 0x80000000; // disable write to inBuf
    }

    UINT8 out = outBuf[0];
    UINT16 in;
    spi->DR = out; // write first word
    while (++i < num) {

	//----------------------------------------------------------------------------
	//Wait until the busy pin toggles
	//----------------------------------------------------------------------------
	if (Transaction.BusyPin.Pin != GPIO_PIN_NONE)
	{
		while (CPU_GPIO_GetPinState( Transaction.BusyPin.Pin ) != Transaction.BusyPin.ActiveState);
	}
	//----------------------------------------------------------------------------

        if (i < outLen) out = outBuf[i]; // get new output data
        while (!(spi->SR & SPI_SR_RXNE)); // wait for Rx buffer full
        in = spi->DR; // read input
        spi->DR = out; // start output
        if (ii >= 0) inBuf[ii] = (UINT8)in; // save input data


	//----------------------------------------------------------------------------
	//When all the bytes in a frame at read, wait until the busy pin toggles back.
	//----------------------------------------------------------------------------
	if (Transaction.BusyPin.Pin != GPIO_PIN_NONE)
	{
		if (Transaction.BusyPin_BytesPerFrame != 0)
		{
			if ((i + 1) % Transaction.BusyPin_BytesPerFrame == 0)
			{
				while (CPU_GPIO_GetPinState( Transaction.BusyPin.Pin ) == Transaction.BusyPin.ActiveState);
			}
		}
	}
	//----------------------------------------------------------------------------

        ii++;
    }
    while (!(spi->SR & SPI_SR_RXNE)); // wait for Rx buffer full
    in = spi->DR; // read last input
    if (ii >= 0) inBuf[ii] = (UINT8)in; // save last input

    return TRUE;
}


Changes to SPI.cs …



        public class Configuration
        {
            public readonly Cpu.Pin ChipSelect_Port;
            public readonly bool ChipSelect_ActiveState;
            public readonly uint ChipSelect_SetupTime;
            public readonly uint ChipSelect_HoldTime;
            public readonly bool Clock_IdleState;
            public readonly bool Clock_Edge;
            public readonly uint Clock_RateKHz;
            public readonly SPI_module SPI_mod;
            public readonly Cpu.Pin BusyPin;
            public readonly bool    BusyPin_ActiveState;            
	    //----------------------------------------------------------------------------
	    // I added this...
	    //----------------------------------------------------------------------------
	    public readonly uint BusyPin_BytesPerFrame;
	    //----------------------------------------------------------------------------

            public Configuration(
                                  Cpu.Pin ChipSelect_Port,
                                  bool ChipSelect_ActiveState,
                                  uint ChipSelect_SetupTime,
                                  uint ChipSelect_HoldTime,
                                  bool Clock_IdleState,
                                  bool Clock_Edge,
                                  uint Clock_RateKHz,
                                  SPI_module SPI_mod
                                ) : this( ChipSelect_Port,
                                          ChipSelect_ActiveState,
                                          ChipSelect_SetupTime,
                                          ChipSelect_HoldTime,
                                          Clock_IdleState,
                                          Clock_Edge,
                                          Clock_RateKHz,
                                          SPI_mod,
                                          Cpu.Pin.GPIO_NONE,
                                          false)
                                          
                                
            {
            }
            
            public Configuration(
                                  Cpu.Pin ChipSelect_Port,
                                  bool ChipSelect_ActiveState,
                                  uint ChipSelect_SetupTime,
                                  uint ChipSelect_HoldTime,
                                  bool Clock_IdleState,
                                  bool Clock_Edge,
                                  uint Clock_RateKHz,
                                  SPI_module SPI_mod,
                                  Cpu.Pin BusyPin,
                                  bool BusyPin_ActiveState,
				  //----------------------------------------------------------------------------
				  // I added this... (and the comma above)
				  //----------------------------------------------------------------------------
				  uint BusyPin_BytesPerFrame
				  //----------------------------------------------------------------------------
                                  
                                )
            {
                this.ChipSelect_Port = ChipSelect_Port;
                this.ChipSelect_ActiveState = ChipSelect_ActiveState;
                this.ChipSelect_SetupTime = ChipSelect_SetupTime;
                this.ChipSelect_HoldTime = ChipSelect_HoldTime;
                this.Clock_IdleState = Clock_IdleState;
                this.Clock_Edge = Clock_Edge;
                this.Clock_RateKHz = Clock_RateKHz;
                this.SPI_mod = SPI_mod;
                this.BusyPin = BusyPin;
                this.BusyPin_ActiveState = BusyPin_ActiveState;
		//----------------------------------------------------------------------------
		// I added this... 
		//----------------------------------------------------------------------------
                this.BusyPin_BytesPerFrame = BusyPin_BytesPerFrame;
		//----------------------------------------------------------------------------
            }
        }


EDIT: I wasn’t checking to see if the BusyPin was being used. This version I’ve tested and verified.

2 Likes

I hard coded my “bytes per frame” for the BusyPin. I did test the code, and it worked. I’m reading 256pixels, 18bits each at 25Mhz. Finally. Communicating with my SPI device is this simple…

             byte[] w = new byte[0];
             byte[] t = new byte[768];
             Program.SPI.WriteRead(w, t);

That gives me an array with 18bits every 3 bytes.

…The thing is, I haven’t actually used this exact version of code (EDIT: I have since posting, one small change was required, it’s working now).

I’m trying to compile the client DLLs now so I can use my improved SPI Configuration class. How do I do that?

I ran this command in my firmware folder’s root. It’s been executing for a while (20mins) now…

msbuild dotNetMF.proj /t:build /p:flavor=release

There’s no watchdog in the Cerb firmware?? I’m definitely going to be interested in that function for my current project…

My firmware uses different pins for SPI, etc. because I have a custom board. You’ll need to fix those assignments (all in the platform_selector file). Are you compiling your own firmware now? All you need is GCC (4.6-2012-q4-update : GNU Arm Embedded Toolchain) the source code.

Also, my watchdog implementation requires a separate thread that constantly “kicks” the watchdog to prevent reboot. And you can’t disable the feature without rebooting. If you’re fine with those limitations, my code should work fine for you.

But back to compiling the client libraries… I’m generating my own Microsoft.SPOT.Hardware.dll file with my changes in it. But every time I add the DLL to my project Visual Studio switches back to Microsoft’s original file.

Somebody here has to know what I’m doing wrong. It’s probably something stupid. Help me help you!

Compiling the client libraries is important. Right now I put the code to kick the watchdog in the setter method for the watchdog timeout (Microsoft.SPOT.Hardware.Watchdog.Timeout). This is my app code…


private static void watchdog()
        {
            TimeSpan watchdogTimespan = new TimeSpan(4095 * TimeSpan.TicksPerMillisecond);

            while (true)
            {
                Microsoft.SPOT.Hardware.Watchdog.Timeout = watchdogTimespan;
                Thread.Sleep(100);
            }
        }

        public static void Init()
        {
            watchdogThread = new Thread(new ThreadStart(watchdog));
            watchdogThread.Priority = ThreadPriority.Highest;
            watchdogThread.Start();

            Microsoft.SPOT.Hardware.Watchdog.Behavior = WatchdogBehavior.HardReboot;
            Microsoft.SPOT.Hardware.Watchdog.Enabled = true;
        }

If I can get these client libraries compiled I can stop re-purposing the Timeout setter method. And also add the new constructor I need for my SPI fix.

So, I never figured out how to do this. My workaround instead was to backup the current .NET runtime, then copy over it. :\ This seems to do what I wanted. Now I can access my version of Microsoft.SPOT.Hardware with the SPI fix from Visual Studio…

[UpdateRuntime.cmd]


@ echo off
IF (%1) == () GOTO ARG_ERROR 
IF (%2) == () GOTO ARG_ERROR 


copy %1\BuildOutput\public\Release\Client\dll %2\Assemblies\le
copy %1\BuildOutput\public\Release\Client\pe\le %2\Assemblies\le

copy %1\BuildOutput\public\Release\Client\dll %2\Assemblies\be
copy %1\BuildOutput\public\Release\Client\pe\be %2\Assemblies\be

GOTO END

:ARG_ERROR
@ echo off
@ echo.
@ echo Invalid syntax.
@ echo.
@ echo USAGE: UpdateRuntime [pk path] [runtime path]
@ echo EXAMPLE:
@ echo UpdateRuntime "E:\netmfpk\v4.3" "C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3"
@ echo.

:END