Anything unique about Pin D53 (PB14) on the Due?

As per the title specifically I'm wondering if there is anything unique about digital pin 53 on the Due in the Arduino software, or something special about PIOB 14 on the ATSAM3x8E that would mess up with its timing or signalling when used as a GPIO pin? I can't for the life of me use this pin for parallel data comms.

Background:
I've got an 8bit parallel ILI9486 LCD on a shield. The software worked fine, but as part of a larger project I needed access to the Timer pins this shield was blocking so I set about re-writing the code to move all the pins to the bottom connector on the Due.

Didn't work. Okay. Surely a bug in the software. Couldn't find the bug in the software. So I started printing walking bit patterns. No problem there all pins lined up. A few ripped out hairs later with a bottle of Whiskey I set about moving each individual signal one pin at a time and testing as I went. After moving 13 control and data pins successfully (bouncing back and forth between setting new bit masks, and moving between PIOC and B and back again in the process I finally moved the last pin from Digital Pin 9 (PC21) to Digital Pin 53 (PB14) and it failed.
I swapped D1 and D0 on those pins. It still failed.
I checked the timing, it seems fine (significantly slower than the max speeds listed in the ILI9486 datasheets), I checked the write pulses come after the pin gets set, I checked the code, the voltage, I checked everything I could think of.

Finally I gave up and re-wrote the entire code to move everything over one pin (actually 3 now due to PCB layout issues) and everything works fine. Just for giggles I moved one of my data pins back to pin 53 and it failed again.

The only reference I could find anywhere in any schematic or otherwise is that pin 53 (PB14) shares a connection with CANTX1, but it appears Arduino doesn't yet support CAN Bus.

Has anyone heard or seen anything that would indicate that this pin is unique compared to the others as bit banging GPIO?

As you can see upon Graynomad pinout diagram, pin 53 (PB14) can be either CANTX1, or PWMH2 or a simple GPIO depending on your code (BTW you didn't post it ....). You can always test any pin with a simple blink code and see whether it works or not (in that case it works with pin 53). If this basic code doesn't work, maybe your board has been damaged.

FYI, there is a 3rd party CAN library for the DUE that works for CAN0 and CAN1 or both at the same time provided by Collin80 on Github.

I could have been more clear, I didn't post the code because the code appeared to work. With the oscilloscope I could see the pin change state, seemingly correctly. I checked the timing, and if I write PB14 and PB21 in unison they switch in unison, when I write to the display I can see the data move over that pin (though I wish I had a logic analyser to dig into it a bit more). PIOC gets written first, PIOB a few 100ns later and I verified the holdup time of that data before the strobe bit gets triggered.

Here's the code but only for completeness sake. As I said moving the pins over a bit so I don't use PB14 worked just fine.

Hence I was wondering if there was another angle related to it being a CANbus pin or something. I checked the electrical characteristics and PB14 doesn't have anything unique about it I could see. If anything PB21 does, but that pint works just fine.

I did a search on the rest of the code to see if there's any other reference to this pin in either Aduino's abstraction or PIOB directly but found nothing, hence I was wondering if the Arduino library did anything strange like require you to manually disable the CANbus or something since I write PIOB directly and not using digitalWrite() or pinMode().

Maybe I'll just blame the electron ghosts.

#define setWriteDir() { \
   PIOC->PIO_MDDR |=  0x0003F000; PIOC->PIO_OER |=  0x0003F000; PIOC->PIO_PER |=  0x0003F000; \
   PIOB->PIO_MDDR |=  0x00204000; PIOB->PIO_OER |=  0x00204000; PIOB->PIO_PER |=  0x00204000; }

#define write8(d) { \
        PIO_Set(PIOC,     (((d) & 0x04)<<(17-2)) \
                        | (((d) & 0x08)<<(16-3)) \
                        | (((d) & 0x10)<<(15-4)) \
                        | (((d) & 0x20)<<(14-5)) \
                        | (((d) & 0x40)<<(13-6)) \
                        | (((d) & 0x80)<<(12-7))); \
        PIO_Clear(PIOC,   (((~d) & 0x04)<<(17-2)) \
                        | (((~d) & 0x08)<<(16-3)) \
                        | (((~d) & 0x10)<<(15-4)) \
                        | (((~d) & 0x20)<<(14-5)) \
                        | (((~d) & 0x40)<<(13-6)) \
                        | (((~d) & 0x80)<<(12-7))); \
        PIO_Set(PIOB,     (((d) & 0x01)<<(21-0)) \
                        | (((d) & 0x02)<<(14-1))); \
        PIO_Clear(PIOB,   (((~d) & 0x01)<<(21-0)) \
                        | (((~d) & 0x02)<<(14-1))); \
        WR_STROBE; }

Some pins have the Schmitt trigger feature, whereas others don't have. PB14 doesn't have this feature (see Sam3x datasheet page 12)

Hmmm wonder if that could have an impact. That said I was able to read okay from any register which wasn't addressed with bit 0, and I would have thought the Schmitt trigger would only affect inputs.

Something to keep in mind though, thanks, I'm completely new to this chip.

Looking at another write8() macro

#define WRITE_DELAY { WR_ACTIVE; }
...
#define BMASK         (1<<25)
#define CMASK         (0xBF << 21)
#define write_8(x)   {  PIOB->PIO_CODR = BMASK; PIOC->PIO_CODR = CMASK; \
                        PIOB->PIO_SODR = (((x) & (1<<2)) << 23); \
                        PIOC->PIO_SODR = (((x) & (1<<0)) << 22) \
                                       | (((x) & (1<<1)) << 20) \
                                       | (((x) & (1<<3)) << 25) \
                                       | (((x) & (1<<4)) << 22) \
                                       | (((x) & (1<<5)) << 20) \
                                       | (((x) & (1<<6)) << 18) \
                                       | (((x) & (1<<7)) << 16); \
					 }
...
#define write8(x)     { write_8(x); WRITE_DELAY; WR_STROBE; IDLE_DELAY; }

I am guessing that you are using a Due to write to the 8080-8 data bus of a TFT.
It is much more efficient to clear all the data bits with PIO_CODR and then set appropriate bits with PIO_SODR.
However there is a penalty. You must abide by the TFT controller timing constraints e.g.

SSD1289  tWC =100ns  tWRH = 50ns  tRCFM =1000ns  tRC =1000ns (tWCFM= 238ns)
SSD1963  tWC = 26ns  tWRH = 13ns  tRCFM = 110ns  tRC =  72ns

i.e. put a small delay between the valid data bus and the strobe signal.

No, I have not tested PB14 behaviour. But I doubt that it will be different to any other PBxx pin. Have you tried pinMode(53, OUTPUT); digitalWrite(53, ...); ?

David.

Hey thanks. I'll give your code idea a whirl. You guessed correctly, which doesn't surprise me at all. I read a few of your posts on this and saw you're already familiar with the code code on LCDwiki. This was allegedly supposed to make their (possibly copied from you based on your past posts) library suitable for the shield. I say allegedly because the pins weren't actually correct which is why I was messing with this function in the first place. I hadn't gotten as far as optimising yet.

I don't think I need to worry about timing yet. The ILI9486 is quite a fast chip and I think I'm still quite far away from having to worry about timing considerations.

Digital read and write work. As does my code in terms of actually toggling the pin which is why I'm so damn confused in the first place. But to be honest I'm at a complete loss. I checked the bits toggle, I checked the voltage hits 3.3V. I checked the timing and all PIOB pins toggle at the same time, and other PIOB pins work, and I checked I wasn't in breach of the timing requirements.

Anyway I'm guessing from no obvious answer that there's nothing special about PB14, other than the CANBus which shouldn't have an impact.

Hey thanks again for the great work, one of your posts pointed me towards getting the display working in the first place :smiley:

From a practical point of view:

  1. A Uno Shield plugs into Due
  2. A Mega Shield plugs into Due
  3. 40-pin Adapter Shields are made for Due
  4. 40-pin Mega Adapter Shield plugs into Due.

(1), (2), (4) all involve the data bus on random PORT pins for a Due. But since the Due is quite fast you still get acceptable performance.

(3) maps the data bus nicely to the Due PORT pins. Gives perfect performance. In fact you have to slow things down !!

If you are designing a custom SAM3X pcb, you can route TFT pins sensibly like (3)

However it seems a little crazy to use an elderly SAM3X chip when there are much better ARM chips on the market. Yes, it gives Due compatibility. But NXP chips (Teensy) or STM32 have got good Arduino support.

David.

@david_prentice Curiously I tried your optimisation this morning and played around a bit more with this. Interestingly it is not universally faster with this library. I'll have to dig a bit and find out why.

Firstly moving from PIOB and PIOC to a single PIO nearly halved the time for the LCD test routine from 26.9 seconds to 17.1, stands to reason since we only need to write the port once.

But your optimisation... well overall throughout the entire test routine it shaved 0.5 seconds off to 16.6 total. But it was inconsistent.
For instance clearing the screen, filling the screen red, green, then blue, actually took 18% longer with the optimised routine.

Actually every routine where a Command is sent followed by repeated data actually took longer. By that I mean commands such as Fill_Rect which set a starting x,y, and then just spam colour values to the LCD are slower, but routines such as drawing a line which calculates then manually sets individual pixels sending out command data command data etc, are faster.

Sounds like a fun job for the end of my project to figure out why :slight_smile:

Go on. Please quote which library you are using. And a link to your actual screen.

I would not expect anything from the LCDWIKI badly spelled library to work efficiently.

Say exactly what your hardware is. There are plenty of libraries that will give an excellent performance with a SAM3X Due. But not many expect 8080-8 interface.
I can dig out my USE_DUE_8BIT_PROTOSHIELD and check the 320x480 Adafruit Tests time. It is probably about 1.5 seconds. Obviously longer for a Uno Shield on a Due.

Note that ILI9486 with 8080-16 is supported by Bodmer's TFT_HX8357 library. It has impressive speed and wide range of examples.

David.

Indeed. I noticed that after I bought the display. I was quick to jump on an 8080-8 interface as I didn't want to deal with multiple bus speeds on SPI, and the "slower" SPI transfers, though since then I realise the fastest interface may actually be DMA anyway.

Anyway, it took a bit of digging to identify the display, which is how I ended up on LCDWiki.
The display is this one: 3.5inch Arduino Display-UNO - LCD wiki except the SKU without the touch screen. ILI9486 driver. 8bit parallel interface.
Library is the same one linked to from the page. http://www.lcdwiki.com/res/Program/Arduino/3.5inch/UNO_8BIT_ILI9486_MAR3501_V1.1/3.5inch_Arduino_8BIT_Module_ILI9486_MAR3501_V1.1.zip I know you're familiar with it because I saw you reference this one day.

Changes to the library were just the pinout (currently all data pins are on PIOC) Link to my mcu_8bit_magic.h:

The library includes some default codes which runs a bunch of tests drawing lines and circles etc. It looks similar to the ones used by other LCD libraries, but I haven't checked if its identical. My benchmarking code looks like:

  startTime = millis();
  fill_screen_test();
  endTime = millis() - startTime;
  SerialUSB.print("Fill test: ");
  SerialUSB.println(endTime);
  startTime = millis();
  text_test();
  endTime = millis() - startTime;
  SerialUSB.print("Text test: ");
  SerialUSB.println(endTime);
  startTime = millis(); 
....

With that I get the following table of results:

Test		    PIOB/C	 PIOC	  PIOC With Mask Clear
-----------------------------------------
Fill 		    1473	 (741)	  879
Text 		    657	     419	  (402)
Lines 		    10118	 7389	  (6232)
hlLines 	    1096	 (949)	  976
Rectangle 	    531	     (360)	  392
FillRect 	    3645	 (2018)	  2321
Fillcircles 	964	     (590)	  591
Cirlces 	    803	     609	  (495)
Triangles 	    817	     546	  (499)
FillTriangles 	2106	 (1251)	  1276
RoundedRect 	593	     (408)	  432
FillRndRect 	3329	 (1776)	  2062
----------------------------------------
		        26132	 17056	  (16557)

I'll do a bit of investigation to see how easy it is to convert Bodmer's Library to 8080-8. If its as easy as a "magic.h" file then I should be golden :slight_smile:

I have added the equivalent times for MCUFRIEND_kbv v2.9.9Release and for the special
USE_DUE_8BIT_PROTOSHIELD

I guess that the badly spelled library uses similar "Adafruit Tests". The important numbers are Fill and Lines. They show the fillRect() and drawPixel() performance.

No, I have not studied the badly spelled library functions. Nor do I want to.
I have not looked at Bodmer's TFT_HX8357 code recently. From memory, he has multiple inline calls to write the 16-bit data bus. You would need to replace all of these with 8-bit versions.

If you want to see Bodmer's excellent examples look at TFT_eSPI. Which incidentally does support your 8-bit Shield.

If you possess a regular Due or clone, plug in your Display Shield and install MCUFRIEND_kbv.

David.

Woo-Hoo. I have found something good about the new Forum Software!
Adding stuff to a formatted table shows the output immediately.

Yeah I can imagine you don't want to work your way though this code. I though I saw a post where you said you had a look at it (and even noticed they ... ahem borrowed some of your code).

Holy crap your library is fast, and more feature rich to boot!

Thanks. I got it working on the shield, my job for later this week is to adapt it to my adjusted pinout. I gave it a quick whirl this afternoon but so far it failed. Need to see what I got wrong.

I do have a quick question. In your code you have written:

// If you are not using a shield, use a full Adafruit constructor()
// e.g. Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

Do you mean the Adafruit_TFTLCD constructor or the MCUFRIEND_kbv constructor in this case? I was confused because the definition of the constructor says:

MCUFRIEND_kbv::MCUFRIEND_kbv(int CS, int RS, int WR, int RD, int _RST):Adafruit_GFX(240, 320) {
// we can not access GPIO pins until AHB has been enabled.
}

What is AHB and which constructor should I be using for a custom pinout?

Cheers, thanks so much for your help with this by the way.

My library does not use any of the constructor arguments. The arguments are just dummies.

It is designed for Shields. i.e. known wiring.

If you want to have special custom wiring, you write an entry in mcufriend_special.h
(Or I write it for you).
Control pins are easy.
It is fairly simple for a data bus using contiguous port pins e.g PC1-PC8
If you have random port pins it gets a bit fiddly.

Regarding AHB. A global constructor() is called before main() i.e. before any startup code has enabled timers, ports, peripherals, ...
This means that the constructor can only assign a few variables. Nothing is done to the hardware until you call readID(), ..., begin(), ...

The ARM manual will explain AHB.

Oh, Bodmer's library(s) are faster than mine.

David.

Rightio I understand now. I took the afternoon to dig into your code a bit more and figured out how you laid out the custom shield. Your example code for the graphics test has defines in it that remain unused and that threw me off so I was changing CS, RD, WD, etc in the wrong place.

Got it all working with my custom pinout. That's fantastic as now the ATSAM3X8E's hardware Quadrature decoder pins are freed up again.

Also AHB, I thought you were referencing something else in your code, didn't realise you were talking about the ARM bus :slight_smile: good to know that there's issues using it before main() is called.

Thanks a lot. Learnt a lot. Time to actually code up this controller. I can't sit here optimising LCD speed forever :slight_smile:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.