Voting resources, early voting, and poll worker information - VOTE. ... Adafruit is open and shipping.
0

Low level port access, help porting 8bit TFT library?
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Low level port access, help porting 8bit TFT library?

by moonie223 on Fri Aug 07, 2020 4:05 pm

Hello everyone!

I am trying to get one of your 3.5" TFT breakouts running as fast as possible. It currently works on a 32u4, but I don't know enough to get pin_magic doing it's thing. For some reason I can't get pin directions right, asking for the ID wipes out the entire port so I must be doing something wrong. Otherwise, I think I have it moving data pins pretty reliably...

I started trying to use a itsybitsy m4, but I also have a few other boards around.

I would like to use PA16-PA23 for data pins, or D0, D1, D7, D9, D10, D11, D13, D12 in that order if I am not mistaken. Nice and neat!

I would also like to use PA7-PA4 for control lines, or D2, A5, A1, A4. For reset, I am using PA14, D4. This should leave SPI/I2C open, plus the neopixel or another uart. So, should be able to do more than just run a screen, poll data too!

Because I'm a super novice I wiped out all the ifdefs in the library, made a backup of the old one. I find it hard to keep track of them when bouncing around so much, figure I can add what I come up with back later if anyone seemed interested.

Here is what I've come up with so far, and it appears to put out valid data on the scope but the screen won't display anything. I have to hard code the ID, because if I do a data read my poor coding messes up the control pins.

Code: Select all | TOGGLE FULL SIZE
#define write8inline(d)                                             \
{                                                            \
   PORT->Group[0].OUT.reg |= (d * 0x00010000);                           \   // Apply data offset, put on output register
   delayMicroseconds(10);                                          \   // Slow things down for my scope...
   PORT->Group[0].OUTCLR.reg |= (~d * 0x00010000);                        \   // Clear the output data
   delayMicroseconds(10);                                          \   // Slow things down for my scope...
   WR_STROBE;                                                   \
   delayMicroseconds(10);                                          \   // Slow things down for   my scope...
}

#define read8inline(result)                                          \
{                                                            \
   RD_ACTIVE;                                                   \
   delayMicroseconds(10);                                          \   // Slow things down for my scope...
   result = ((PORT->Group[0].IN.reg & 0x00FF0000)>>16);                  \   // Read data from port, apply data offset
   delayMicroseconds(10);                                          \   // Slow things down for my scope...
   RD_IDLE;                                                   \
}

#define setWriteDirInline()                                          \
{                                                            \
   PORT->Group[0].DIRTGL.reg |= 0x00FF0000;                           \   // Toggle the data pins from OUTPUT to INPUT, setReadDirInline always called soon after...
}

#define setReadDirInline()                                          \
{                                                            \
   PORT->Group[0].DIRTGL.reg |= 0x00FF0000;                           \   // Toggle the data pins back from INPUT to OUTPUT, mostly always data output...
}

// When using the TFT breakout board, control pins are configurable.
#define RD_ACTIVE   PORT->Group[0].OUTCLR.reg |= rdPinSet      // PIO_Clear(rdPort, rdPinSet)  bitmasks are gathered earlier, pinunset bitmask removed, using OUTSET and OUTCLR instead
#define RD_IDLE      PORT->Group[0].OUTSET.reg |= rdPinSet      // PIO_Set(rdPort, rdPinSet)
#define WR_ACTIVE   PORT->Group[0].OUTCLR.reg |= wrPinSet      // PIO_Clear(wrPort, wrPinSet)
#define WR_IDLE      PORT->Group[0].OUTSET.reg |= wrPinSet      // PIO_Set(wrPort, wrPinSet)
#define CD_COMMAND   PORT->Group[0].OUTCLR.reg |= cdPinSet      // PIO_Clear(cdPort, cdPinSet)
#define CD_DATA      PORT->Group[0].OUTSET.reg |= cdPinSet      // PIO_Set(cdPort, cdPinSet)
#define CS_ACTIVE   PORT->Group[0].OUTCLR.reg |= csPinSet      // PIO_Clear(csPort, csPinSet)
#define CS_IDLE      PORT->Group[0].OUTSET.reg |= csPinSet      // PIO_Set(csPort, csPinSet)


Code: Select all | TOGGLE FULL SIZE
  csPinSet = digitalPinToBitMask(cs); // Resolves to 0x80
  cdPinSet = digitalPinToBitMask(cd);          // 0x40
  wrPinSet = digitalPinToBitMask(wr);          // 0x20
  rdPinSet = digitalPinToBitMask(rd);           // 0x10
  rstPinSet = digitalPinToBitMask(reset);     // 0x04

  PORT->Group[0].DIR.reg |= csPinSet; // Set all control bits to OUTPUT
  PORT->Group[0].DIR.reg |= cdPinSet; // Signals are ACTIVE LOW   
  PORT->Group[0].DIR.reg |= wrPinSet;
  PORT->Group[0].DIR.reg |= rdPinSet;
  PORT->Group[0].DIR.reg |= rstPinSet;

  PORT->Group[0].OUT.reg |= csPinSet; // Set all control bits to HIGH
  PORT->Group[0].OUT.reg |= cdPinSet; // Signals are ACTIVE LOW
  PORT->Group[0].OUT.reg |= wrPinSet;
  PORT->Group[0].OUT.reg |= rdPinSet;
  PORT->Group[0].OUT.reg |= rstPinSet;   



Code: Select all | TOGGLE FULL SIZE
void Adafruit_TFTLCD::reset(void) {

  CS_IDLE;
  //  CD_DATA;
  WR_IDLE;
  RD_IDLE;
  PORT->Group[0].OUTCLR.reg |= rstPinSet;   // Set Reset pin LOW
  delay(2);
  PORT->Group[0].OUT.reg |= rstPinSet;   // Set Reset pin HIGH
  pinMode(3, OUTPUT);                                 // PortB wiped too?
   digitalWrite(3, HIGH);                            // Just a test...
  // Data transfer sync
  CS_ACTIVE;
  CD_COMMAND;
  write8(0x00);
  for (uint8_t i = 0; i < 3; i++)
  WR_STROBE; // Three extra 0x00s
  CS_IDLE;
}


Am I at all on the right track with how I am doing things here?

Thank you in advance!

moonie223
 
Posts: 16
Joined: Sun Feb 05, 2017 2:44 pm

Re: Low level port access, help porting 8bit TFT library?

by westfw on Sun Aug 09, 2020 6:01 pm

I would like to use PA16-PA23 for data pins

In theory:
23.8 Register Description
Registers can be 8, 16, or 32 bits wide. Atomic 8-, 16- and 32-bit accesses are supported. In addition, the 8-bit quarters and 16-bit halves of a 32-bit register, and the 8-bit halves of a 16-bit register can be accessed directly.

So you can access PA16-PA23 as byte load/stores, and do away with all of the fancy and confusing and/or logic.
The standard register definitions do not make this easy :-(
Perhaps something like:
Code: Select all | TOGGLE FULL SIZE
// Third byte of PORT B
#define PBB3_OUT ( ((__IO uint8_t *) (&PORT->Group[0].OUT.reg))+2)
//   :
*PBB3_OUT = d;

westfw
 
Posts: 1720
Joined: Fri Apr 27, 2007 1:01 pm
Location: SF Bay area

Please be positive and constructive with your questions and comments.