TFTLCD-library for Linux (Raspberry Pi)

EL Wire/Tape/Panels, LEDs, pixels and strips, LCDs and TFTs, etc products from Adafruit

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

Greetings,

I would like to use your 2.8" 240x320 LCD panel (https://www.adafruit.com/product/1770) in 8-bit mode (for speed), with a Raspberry Pi. I notice your library (https://github.com/adafruit/TFTLCD-Library) is for Arduino. Is there an equivalent for Raspberry Pi?

Ihab

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

I should add some details of what I've actually done so far. I have tried using a framebuffer driver (https://github.com/sammyizimmy/ili9341). However, so far, it doesn't seem to work "out of the box" (see https://github.com/sammyizimmy/ili9341/issues/8). I'm not sure if this code is highly supported by its author. I would be hard pressed to debug it myself since kernel module hacking is not a skill I have.

If I could get the framebuffer driver to work, that would be a great start. That said, it is actually overkill for my use case. For one thing, it does not seem to give me control over frame sync, which is actually a bug not a feature. :) I don't need generic apps to be able to paint the display. If I could link in some code that pushes a frame as part of the paint loop of my application, that would be the most ideal.

I have considered learning from the framebuffer driver, or from Adafruit's TFTLCD library, and doing what they do in my own userspace code. The most complex and finicky parts seem to involve the sequences of commands to set up the ILI9341. I would have to learn to use `/dev/gpiomem` to memory map the GPIOs to avoid executing a kernel trap for each bit transmitted. It would all be a bunch of work, so I'm wondering if someone has done something like this before.

Ihab

User avatar
sj_remington
 
Posts: 998
Joined: Mon Jul 27, 2020 4:51 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by sj_remington »

Which RPi?

Have you seen these instructions, and if so, do they not work?
https://tejaswid.github.io/wikitm/rpi/pi-tft-display

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

Thank you *sj_remington*. I had not seen these instructions. They use the ILI9341 in SPI mode, though, sending one bit at a time. The mode I'm looking for is 8-bit parallel mode, to get a sufficiently high frame rate.

Ihab

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

Greetings again,

I studied the code for the following:

https://github.com/adafruit/TFTLCD-Library
https://github.com/sammyizimmy/ili9341

I am using a Raspberry Pi not an Arduino device, so based on my experience so far I decided to create a simple demo program using the sysfs interface to GPIOs to make sure I got the pinouts correct. This is very slow, but that's okay for the time being; I know how to make it faster eventually.

My code is pasted below for reference. The connections are made according to:

https://github.com/sammyizimmy/ili9341/ ... ter/README

This is the behavior of the code:

https://photos.app.goo.gl/2JJkgfhMoUQVksvU6

In particular, note two things:
  • I should be in RGB mode, but it looks like the ILI9341 is swapping Green and Blue channels. If I had accidentally put it into BGR mode, then the first "stripe" would be Blue, but instead it is Red. If I had gotten the byte order of the 16-bit words wrong, then again the first "stripe" would not be Red. Instead, I see Red, then Blue, then Green, instead of Red, then Green, then Blue.
  • There seem to be strange points between "stripes" when the pixels alternate, almost like anti-aliasing. I have no idea what these are about. These are not consistent with single-bit errors, errors in wiring, or errors in the protocol.
I assume someone at Adafruit has dealt with the ILI9341 in some detail. Do these look like a common error? I understand I am not using the Adafruit branded Arduino library. I could, as a next step, wire up an Arduino and see if the issues are repro'ed. But at the moment, with my existing wiring, does anyone have ideas?

Ihab

Code: Select all

#include <iostream>
#include <sstream>
#include <fstream>
#include <cstring>
#include <time.h>
#include <linux/fb.h>
#include <unistd.h>
#include <math.h>

////////////////////////////////////////////////////////////////////////
// Generic (inefficient) functions for manipulating GPIOs
////////////////////////////////////////////////////////////////////////

void delay(uint16_t ms) {
  usleep(1000 * ms);
}

std::string str(const int k) {
  std::ostringstream s;
  s << k;
  return s.str();
}

std::string gpio_dir(const int k) {
  return "/sys/class/gpio/gpio" + str(k);
}

std::string gpio_value(const int k) {
  return gpio_dir(k) + "/value";
}

std::string gpio_direction(const int k) {
  return gpio_dir(k) + "/direction";
}

void echo(std::string name, std::string value) {
  while (true) {
    std::fstream fs;
    fs.open(name, std::fstream::out);
    if (fs.is_open()) {
      fs << value << std::endl;
      return;
    }
  }
}

void export_gpio(const int k) {
  echo("/sys/class/gpio/export", str(k));
}

void wait_for_export(const int k) {
  while (true) {
    std::fstream fs;
    fs.open(gpio_value(k), std::fstream::in);
    if (fs.is_open()) {
      return;
    }
  }
}

void gpio_setoutput(const int k) {
  export_gpio(k);
  wait_for_export(k);
  echo(gpio_direction(k), "out");
}

void gpio_setstate(const int k, const int value) {
  echo(gpio_value(k), (value != 0) ? "1" : "0");
}

////////////////////////////////////////////////////////////////////////
// Definitions specific to the LCD panel pinouts
////////////////////////////////////////////////////////////////////////

#define DATA0 9
#define DATA1 11
#define DATA2 18
#define DATA3 23
#define DATA4 24
#define DATA5 25
#define DATA6 8
#define DATA7 7

#define DC 4
#define CS 27
#define RD 14
#define RW 15
#define IM0 10
#define RESET 17

#define DATA0 9
#define DATA1 11
#define DATA2 18
#define DATA3 23
#define DATA4 24
#define DATA5 25
#define DATA6 8
#define DATA7 7

#define DC 4
#define CS 27
#define RD 14
#define RW 15
#define IM0 10
#define RESET 17

void tft_init_gpios() {
  gpio_setoutput(DATA0);
  gpio_setoutput(DATA1);
  gpio_setoutput(DATA2);
  gpio_setoutput(DATA3);
  gpio_setoutput(DATA4);
  gpio_setoutput(DATA5);
  gpio_setoutput(DATA6);
  gpio_setoutput(DATA7);
  
  gpio_setoutput(DC);
  gpio_setoutput(CS);
  gpio_setoutput(RD);
  gpio_setoutput(RW);
  gpio_setoutput(IM0);
  gpio_setoutput(RESET);
  
  gpio_setstate(DATA0,0);
  gpio_setstate(DATA1,0);
  gpio_setstate(DATA2,0);
  gpio_setstate(DATA3,0);
  gpio_setstate(DATA4,0);
  gpio_setstate(DATA5,0);
  gpio_setstate(DATA6,0);
  gpio_setstate(DATA7,0);
    
  gpio_setstate(DC,1);
  gpio_setstate(CS,0);
  gpio_setstate(RD,1);
  gpio_setstate(RW,1);
  gpio_setstate(IM0,1);
  gpio_setstate(RESET,1);
}

void tft_hard_reset(void) {
  gpio_setstate(RESET,0);
  delay(120);
  gpio_setstate(RESET,1);
  delay(120);
}

void tft_set_parallel_data(char data) {
  gpio_setstate(DATA0,((data >> 0)  & 0x01));
  gpio_setstate(DATA1,((data >> 1)  & 0x01));
  gpio_setstate(DATA2,((data >> 2)  & 0x01));
  gpio_setstate(DATA3,((data >> 3)  & 0x01));
  gpio_setstate(DATA4,((data >> 4)  & 0x01));
  gpio_setstate(DATA5,((data >> 5)  & 0x01));
  gpio_setstate(DATA6,((data >> 6)  & 0x01));
  gpio_setstate(DATA7,((data >> 7)  & 0x01));
}

void tft_command_write(char command) {
  gpio_setstate(DC,0);
  tft_set_parallel_data(command);
  gpio_setstate(RW,0);
  gpio_setstate(RW,1);
}

void tft_data_write(char data) {
  gpio_setstate(DC,1);
  tft_set_parallel_data(data);
  gpio_setstate(RW,0);
  gpio_setstate(RW,1);
}

void tft_init() {
  tft_command_write(0x11); //exit SLEEP mode
  tft_data_write(0x00);
  
  tft_command_write(0xB6); //display function control
  tft_data_write(0x0A);
  tft_data_write(0xA2);
  
  tft_command_write(0x3A); //pixel format = 16 bit per pixel
  tft_data_write(0x05);

  tft_command_write(0x36); //memory access control
  tft_data_write(0x00);

  tft_command_write(0x29); //display ON
}

void tft_write_image(const struct fb_image *image) {
  tft_command_write(0x2C); //Memory Write
  for(int y=0; y < image->width ;y++){
    for(int x=0; x < image->height ;x++){
      int base = (y * image->width * 2) + (x * 2);
      tft_data_write(image->data[base]);
      tft_data_write(image->data[base + 1]);
    }
  }
  tft_command_write(0x29); //display ON	
}

////////////////////////////////////////////////////////////////////////
// Main function
////////////////////////////////////////////////////////////////////////

const uint16_t kRed    = 0xf800;
const uint16_t kGreen  = 0x07e0;
const uint16_t kBlue   = 0x001f;
const uint16_t kWhite  = 0xffff;

void fill_image(uint16_t* d, int width, int height) {
  int i = 0;
  int s = width * height;
  while (i < s) {
    for (int j = 0; j < 1024 && i < s; j++) {
      d[i++] = kRed;
    }
    for (int j = 0; j < 1024 & i < s; j++) {
      d[i++] = kGreen;
    }
    for (int j = 0; j < 1024 & i < s; j++) {
      d[i++] = kBlue;
    }
    for (int j = 0; j < 1024 & i < s; j++) {
      d[i++] = kWhite;
    }
  }
}

int main(int argc, char** argv) {
  const uint16_t width = 240;
  const uint16_t height = 320;
  
  const size_t data_bytes = width * height * sizeof(uint16_t);
  char* data = (char*) malloc(data_bytes);
  memset(data, 0, data_bytes);
  
  fill_image((uint16_t*) data, width, height);
  
  struct fb_image image;
  memset(&image, 0, sizeof(image));

  image.dx = 0;
  image.dy = 0;
  image.width = width;
  image.height = height;
  image.data = data;

  tft_init_gpios();
  tft_hard_reset();
  tft_init();
  tft_write_image(&image);
}

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

I had a bug where I used & instead of && but this does not affect the output. The updated section of code is below. Again, the problem remains! :)

Code: Select all

void fill_image(uint16_t* d, int width, int height) {
  int i = 0;
  int s = width * height;
  while (i < s) {
    for (int j = 0; j < kStripe && i < s; j++) {
      d[i++] = kRed;
    }
    for (int j = 0; j < kStripe && i < s; j++) {
      d[i++] = kGreen;
    }
    for (int j = 0; j < kStripe && i < s; j++) {
      d[i++] = kBlue;
    }
    for (int j = 0; j < kStripe && i < s; j++) {
      d[i++] = kWhite;
    }
  }
}

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

I rewired using an Arduino Uno and the result is that the "DEADBEEF Groop" text is shown in the correct colors (yellow, red and green). Therefore, the color swap must be due to my own code.

https://photos.app.goo.gl/NKseJCDzCCdYCM5A7

If anyone has hints as to why this color swap may have occurred, I'd be interested to hear them. Otherwise, I'm basically going to reverse engineer the Arduino library further, to figure out what's wrong with my Raspberry Pi code.

Ihab

User avatar
ihab
 
Posts: 62
Joined: Tue May 17, 2016 5:20 pm

Re: TFTLCD-library for Linux (Raspberry Pi)

Post by ihab »

I may not be able to do much more hacking this weekend, but the following program "works" on an Arduino Uno with the ILI9341 display. It demonstrates that, really, the most important setup commands are simple and should work just fine.

I hope to make the read / write functions portable, ensure they work on the Arduino Uno, then create a Raspberry Pi version of the program and hope it works there too.

Note: I am hoping this approach can give me 30 fps, assuming I use the Secondary Memory Interface (SMI) to push the image bytes via fast DMA (see https://forums.raspberrypi.com/viewtopi ... 1#p2035371). If anyone knows a reason why this is unlikely to succeed, please let me know!

Ihab

(All material in this discussion thread is provided per the MIT license, as is the case for https://github.com/adafruit/TFTLCD-Library.)

Code: Select all

#include "Arduino.h"
#include "pin_magic.h"
#include "registers.h"

#define TFTWIDTH 240
#define TFTHEIGHT 320

class Modified_TFTLCD {
 public:
  Modified_TFTLCD(uint8_t cs, uint8_t cd, uint8_t wr, uint8_t rd, uint8_t rst);
  void begin(uint16_t id = 0x9325);
  void fillScreen(uint16_t color);
  void reset(void);
  void setAddrWindow(int x1, int y1, int x2, int y2);
  void writeRegister24(uint8_t a, uint32_t d);
  void writeRegister32(uint8_t a, uint32_t d);
  void flood(uint16_t color, uint32_t len);
 private:  
  volatile uint8_t *csPort, *cdPort, *wrPort, *rdPort;
  uint8_t
    csPinSet, cdPinSet, wrPinSet, rdPinSet,
    csPinUnset, cdPinUnset, wrPinUnset, rdPinUnset,
    _reset;
};

Modified_TFTLCD::Modified_TFTLCD(uint8_t cs,
				 uint8_t cd,
				 uint8_t wr,
				 uint8_t rd,
                                 uint8_t reset) {
  _reset = reset;
  csPort = portOutputRegister(digitalPinToPort(cs));
  cdPort = portOutputRegister(digitalPinToPort(cd));
  wrPort = portOutputRegister(digitalPinToPort(wr));
  rdPort = portOutputRegister(digitalPinToPort(rd));

  csPinSet = digitalPinToBitMask(cs);
  cdPinSet = digitalPinToBitMask(cd);
  wrPinSet = digitalPinToBitMask(wr);
  rdPinSet = digitalPinToBitMask(rd);

  csPinUnset = ~csPinSet;
  cdPinUnset = ~cdPinSet;
  wrPinUnset = ~wrPinSet;
  rdPinUnset = ~rdPinSet;
  
  *csPort |= csPinSet; // Set all control bits to HIGH (idle)
  *cdPort |= cdPinSet; // Signals are ACTIVE LOW
  *wrPort |= wrPinSet;
  *rdPort |= rdPinSet;
  
  pinMode(cs, OUTPUT); // Enable outputs
  pinMode(cd, OUTPUT);
  pinMode(wr, OUTPUT);
  pinMode(rd, OUTPUT);

  digitalWrite(reset, HIGH);
  pinMode(reset, OUTPUT);

  setWriteDirInline(); // Set up LCD data port(s) for WRITE operations
}

void Modified_TFTLCD::begin(uint16_t id) {
  uint8_t i = 0;

  reset();

  delay(200);

  CS_ACTIVE;
  writeRegister8inline(ILI9341_SOFTRESET, 0);
  delay(50);
  writeRegister8inline(ILI9341_DISPLAYOFF, 0);
  
  writeRegister8inline(ILI9341_POWERCONTROL1, 0x23);
  writeRegister8inline(ILI9341_POWERCONTROL2, 0x10);
  writeRegister16inline(ILI9341_VCOMCONTROL1, BANNED);
  writeRegister8inline(ILI9341_VCOMCONTROL2, 0xC0);
  writeRegister8inline(ILI9341_MEMCONTROL, ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR);
  writeRegister8inline(ILI9341_PIXELFORMAT, 0x55);
  writeRegister16inline(ILI9341_FRAMECONTROL, 0x001B);
  
  writeRegister8inline(ILI9341_ENTRYMODE, 0x07);
  /* writeRegister32(ILI9341_DISPLAYFUNC, 0x0A822700);*/
  
  writeRegister8inline(ILI9341_SLEEPOUT, 0);
  delay(150);
  writeRegister8inline(ILI9341_DISPLAYON, 0);
  delay(500);
  setAddrWindow(0, 0, TFTWIDTH - 1, TFTHEIGHT - 1);
}

void Modified_TFTLCD::reset(void) {
  CS_IDLE;
  // CD_DATA;
  WR_IDLE;
  RD_IDLE;

  if (_reset) {
    digitalWrite(_reset, LOW);
    delay(2);
    digitalWrite(_reset, HIGH);
  }

  // Data transfer sync
  CS_ACTIVE;
  CD_COMMAND;
  write8inline(0x00);
  for (uint8_t i = 0; i < 3; i++)
    WR_STROBE; // Three extra 0x00s
  CS_IDLE;
}

void Modified_TFTLCD::setAddrWindow(int x1, int y1, int x2, int y2) {
  CS_ACTIVE;

  uint32_t t;

  t = x1;
  t <<= 16;
  t |= x2;
  writeRegister32(ILI9341_COLADDRSET, t); // HX8357D uses same registers!
  t = y1;
  t <<= 16;
  t |= y2;
  writeRegister32(ILI9341_PAGEADDRSET, t); // HX8357D uses same registers!
  
  CS_IDLE;
}

void Modified_TFTLCD::flood(uint16_t color, uint32_t len) {
  uint16_t blocks;
  uint8_t i, hi = color >> 8, lo = color;

  CS_ACTIVE;
  CD_COMMAND;
  write8inline(0x2C);

  // Write first pixel normally, decrement counter by 1
  CD_DATA;
  write8inline(hi);
  write8inline(lo);
  len--;

  blocks = (uint16_t)(len / 64); // 64 pixels/block
  if (hi == lo) {
    // High and low bytes are identical.  Leave prior data
    // on the port(s) and just toggle the write strobe.
    while (blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        WR_STROBE;
        WR_STROBE;
        WR_STROBE;
        WR_STROBE; // 2 bytes/pixel
        WR_STROBE;
        WR_STROBE;
        WR_STROBE;
        WR_STROBE; // x 4 pixels
      } while (--i);
    }
    // Fill any remaining pixels (1 to 64)
    for (i = (uint8_t)len & 63; i--;) {
      WR_STROBE;
      WR_STROBE;
    }
  } else {
    while (blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        write8inline(hi);
        write8inline(lo);
        write8inline(hi);
        write8inline(lo);
        write8inline(hi);
        write8inline(lo);
        write8inline(hi);
        write8inline(lo);
      } while (--i);
    }
    for (i = (uint8_t)len & 63; i--;) {
      write8inline(hi);
      write8inline(lo);
    }
  }
  CS_IDLE;
}

void Modified_TFTLCD::fillScreen(uint16_t color) {
  setAddrWindow(0, 0, TFTWIDTH - 1, TFTHEIGHT - 1);
  flood(color, (long)TFTWIDTH * (long)TFTHEIGHT);
}

void Modified_TFTLCD::writeRegister24(uint8_t r, uint32_t d) {
  CS_ACTIVE;
  CD_COMMAND;
  write8inline(r);
  CD_DATA;
  delayMicroseconds(10);
  write8inline(d >> 16);
  delayMicroseconds(10);
  write8inline(d >> 8);
  delayMicroseconds(10);
  write8inline(d);
  CS_IDLE;
}

void Modified_TFTLCD::writeRegister32(uint8_t r, uint32_t d) {
  CS_ACTIVE;
  CD_COMMAND;
  write8inline(r);
  CD_DATA;
  delayMicroseconds(10);
  write8inline(d >> 24);
  delayMicroseconds(10);
  write8inline(d >> 16);
  delayMicroseconds(10);
  write8inline(d >> 8);
  delayMicroseconds(10);
  write8inline(d);
  CS_IDLE;
}

#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

Modified_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

void setup(void) {
  tft.reset();
  tft.begin(0x9341);
} 

void loop(void) {
 testFillScreen();
}

#define BLACK   0x0000
#define RED     0xF800
#define GREEN   0x07E0
#define BLUE    0x001F
#define WHITE   0xFFFF

void testFillScreen() {
  tft.fillScreen(RED);
  tft.fillScreen(GREEN);
  tft.fillScreen(BLUE);
  tft.fillScreen(BLACK);
  tft.fillScreen(WHITE);  
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Glowy things (LCD, LED, TFT, EL) purchased at Adafruit”