[SOLVED] Communicating with ILI9341 using new DUE SPI functions

Dear All,

Many apologies if this is an awfully n00b question – I’ve only just started working with Arduinos and have come a cropper on my first project.

I am trying to communicate with a cheap ILI9431 board using SPI. I can communicate with it fine using the old SPI functions, but whenever I use the ones for the DUE board, I break it.

Below is an example of the problem. You can comment/uncomment the #defines for “FastSPI” and “FastDigital” to switch between the old and new functions.

It works fine with FastDigital defined or undefined, but will never work with the FastSPI (DUE functions) defined.

I’m guessing it’s a n00b mistake with pin set-ups or I’ve failed to see a delay requirement in the ILI9431 datasheet (though I’ve tried liberally scattering delays throughout my code trying dumb luck and ignorance as my guide :)).

If anyone has experience with DUE SPI and can offer some help, I’d be very grateful.

Many thanks.

/*  
    Arduino DUE attached to ILI9341 board (3.3v)                                                                                                                                                                                                                                  
*/

// *******************************************************
// Comment/Uncomment to test DUE SPI and Fast digitalWrite
// *******************************************************
#define FastDigital
#define FastSPI
// *******************************************************

// Connection to TFT
#define TFT_CS  10
#define TFT_LED 9
#define TFT_DC  8
#define TFT_RST 7

#include <SPI.h>

// Try to get more speed than from original digitalWrite function
inline void digitalWrite2(int pin, boolean dat)
{
	if(dat) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
	else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;		
	delayMicroseconds(2); // found this minimum delay was needed for code to work
}

// Send command to TFT
void sendCMD(uint8_t index)
{
	// Set DC for command
	#ifdef FastDigital
		digitalWrite2(TFT_DC,LOW);
	#else	
		digitalWrite(TFT_DC,LOW);
	#endif
	
	// Send index
	#ifdef FastSPI
		SPI.transfer(TFT_CS, index);
	#else
		digitalWrite(TFT_CS, LOW);
		SPI.transfer(index);
	    digitalWrite(TFT_CS, HIGH);
	#endif
	
	// Set DC back to data
	#ifdef FastDigital
	    digitalWrite2(TFT_DC, HIGH);
	#else
	    digitalWrite(TFT_DC, HIGH);
	#endif
}

// Read TFT register & parameter
uint8_t readREG(uint8_t addr, uint8_t param)
{
	uint8_t data=0;

	sendCMD(0xD9);                                                   
	
	#ifdef FastSPI
	    SPI.transfer(TFT_CS, 0x10+param); 
	#else
	    digitalWrite(TFT_CS, LOW);
	    SPI.transfer(0x10+param);                                 
	    digitalWrite(TFT_CS, HIGH);
	#endif
	
	// Set DC for command
	#ifdef FastDigital
	    digitalWrite2(TFT_DC,LOW);
	#else
	    digitalWrite(TFT_DC,LOW);
	#endif
	
	// Transfer address and get data
	#ifdef FastSPI
	    SPI.transfer(TFT_CS, addr, SPI_CONTINUE);
	    //delay(10);  // tried adding delay here but doesn't help
	    data = SPI.transfer(TFT_CS, 0x00);	
	#else
	    digitalWrite(TFT_CS, LOW);
	    SPI.transfer(addr);
	    data = SPI.transfer(0x00);
		digitalWrite(TFT_CS, HIGH);
	#endif
	
	// Set DC back to data
	#ifdef FastDigital
		digitalWrite2(TFT_DC, HIGH);
	#else
		digitalWrite(TFT_DC, HIGH);
	#endif
	
	return data;
}

// Attempt reading the TFT's version - tries 5 times in case
// communications were not stable to start
uint32_t readID(uint32_t expected)
{
	uint32_t lastID;
	
	for (uint32_t tries=0; tries<5; tries++){
		// Get three byte value from register D3 which holds the ID
		lastID =          readREG(0xD3,1) << 16;
		lastID = lastID | readREG(0xD3,2) << 8;
		lastID = lastID | readREG(0xD3,3);
		
		if (lastID==expected) return lastID;	
	}
	return lastID;	
}


void setup()
{
    uint32_t myID;
	
	// Print the mode selected from un/commening the defines
	#ifdef FastSPI
		Serial.println("Running with fast SPI");
	#else
		Serial.println("Running with slow SPI");
	#endif
	
	#ifdef FastDigital
	    Serial.println("Running with fast digitialWrite");
	#else
	    Serial.println("Running with slow digitalWrite");
	#endif	

	// Setup pin directions
	pinMode(TFT_CS,OUTPUT);
	pinMode(TFT_DC,OUTPUT);
	pinMode(TFT_LED,OUTPUT);
	pinMode(TFT_RST,OUTPUT);
	pinMode(13,OUTPUT);
	
	// Output default values
	digitalWrite(TFT_DC,LOW);
	digitalWrite(TFT_LED,HIGH);
	digitalWrite(TFT_CS, HIGH);
	
	// Reset TFT
	digitalWrite(TFT_RST,LOW);
	delay(100);
	digitalWrite(TFT_RST,HIGH);
	
	// Initialise SPI
	#ifdef FastSPI
		SPI.begin(TFT_CS);
		SPI.setClockDivider(21); // 4Mhz, same as default "slow" SPI
	#else
		SPI.begin();
    #endif
		
	myID = readID(0x9341);  // the ID we're expecting from ILI9341
	if (myID != 0x9341) {
		// Print error
		Serial.println("************");
		Serial.print("Wrong TFT ID: expected 0x9341 but found 0x");
		Serial.println(myID,HEX);	
		Serial.println("************");
	} else {
		// Print success
		Serial.print("Success! TFT ID = 0x");
		Serial.println(myID,HEX);
	}
	
}

// Just blink the LED to prove it is alive
void loop()
{
	digitalWrite2(13, HIGH);
	delay(500);
	digitalWrite2(13, LOW);
	delay(500);
}

The big difference between the Uno (etc) and the Due wrt SPI is that the Due controls the CS for you. Look at the difference in the two SPI lib's.

Mark

Thanks Mark.

I think I've accounted for that, though, I've left the CS unaffected when using the Due functions but have driven it low and high when testing it with the old code (only when #define FastSPI has been commented out)

For example, in my code I have sections like this:

#ifdef FastSPI
SPI.transfer(TFT_CS, index); // Using the Due SPI function without affecting CS
#else
digitalWrite(TFT_CS, LOW); // Drive CS low
SPI.transfer(index); // Old school SPI function
digitalWrite(TFT_CS, HIGH); // Drive CS high
#endif

Cheers,

Dax

You have a problem here:

	// Setup pin directions
	pinMode(TFT_CS,OUTPUT);   // <------ here
	pinMode(TFT_DC,OUTPUT);
	pinMode(TFT_LED,OUTPUT);
	pinMode(TFT_RST,OUTPUT);
	pinMode(13,OUTPUT);

The TFT_CS pin is managed by the Due hardware exclusively - if you set it
also as an output GPIO pin as with the line pinMode(TFT_CS,OUTPUT)
then you have the SPI hardware actually fighting the GPIO hardware and
risk damaging the Due. (put a 'scope on the CS line and you'll see it go from
3.3V down to 1.6V or so....)

This ought to be better documented as its not obvious, and its a real case where
you might be able to fry a Due without atttaching anything to the pins, since it
seems there are duplicate output drivers on some pins (rather than doing it properly
with a gate or selector).

With the Due you avoid setting pinMode for any SPI pin, just use the SPI library.

I believe the examples with the Due SPI library are broken (but happen to work
by accident).

[edit: No, its more complicated than that - if you use just SPI.begin(), then
you seem to be OK with the pinMode declaration, but if you use SPI.begin (10), and
then SPI.transfer (10, ...) you get the behaviour whereby using pinMode is dangerous....

When pinMode clashes with the SPI hardware the current consumption jumps about 30mA,
so its a definitely two drivers fighting. So far I haven't fried my Due, but its a worry
]

Very many thanks for your help with this, MarkT - you've solved it!

After removing the pinMode line that you've kindly identified, the code now works with either library. I guess this means I was lucky and it didn't fry the board.

I'd agree that, given it's potential consequence, this potential error could do with being made a little more obvious in the library reference. Surely I can't have been the only numpty to do this on their first project :blush:

It might be nice if the Arduino gurus could rewrite the SPI.begin(CS) function to ensure the CS pin is in the necessary HiZ from the GPIO driver.

Thanks again,

Dax

Aha, a little more perusing things has discovered the true cause of the SPI CS
failing.

Arduino pin 4 is connected to both PA29 and PC26 (package pins 112, 137)
Arduino pin 10 is connected to both PA28 and PC29 (package pins 111, 102)
Arduino pin 52 is not connected to PA30 (the SPI CS2) since that pin is not
present on the package at all.

Thus these outputs can indeed fight if pinMode is selected as output
(pin 4 maps to PC26 for non-SPI uses, pin 10 to PC29)

Arduino pin 52 cannot be used for SPI as far as I can tell.