Drawing BMPs on multiple TFT's simultaneously

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
Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

Hi,

Have had a look around on the forum and not seen anything about this so thought I'd ask. This is more of a programming question but it's related to using the TFT displays

I want to draw BMPs on a series of TFT's all at the same time, triggered by a condition. If I use the following If statements it works fine, but it renders each display one after the other. Given that it takes approx 4 seconds to draw each BMP, when I'm using 4 displays I have to wait 16secs for all of the displays to update. Is there a simple way to trigger the bmpDraw commands at the same time without waiting for one to finish before executing the next?

I should point out that bmpDraw is directly from the spitftbitmap example.

Thanks!

Code: Select all

  
 if (t == 1) {

  bmpDraw1("1.bmp", 0, 0);
  bmpDraw2("2.bmp", 0, 0);
  bmpDraw3("3bmp", 0, 0);
  bmpDraw4("4bmp", 0, 0);

 }
 
 if (t == 2) {
    bmpDraw1("5.bmp", 0, 0);
    bmpDraw2("6.bmp", 0, 0);
    bmpDraw3("7.bmp", 0, 0);
    bmpDraw4("8.bmp", 0, 0);

 }
 

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

Lasereyesurgery wrote:Is there a simple way
Simple? No.
Complicated? Mmmmayyybe... But probably not on a Uno - you wouldn't have enough SRAM.

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

OK. Any tips for a Mega?

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

You'd have to re-write the bmpDraw routine to multiplex the data from the four different files. Essentially draw a buffer-load on the first display, then draw a buffer-load on the second display, etc.

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

OK, I get it. Thanks, I'll give it a go.

One other thing, I'm triggering the screens over UDP using the CC3000. However I can only seem to control them whilst the Arduino is connected to my laptop. When I run it standalone (with the external power supply) any control data I send has no effect. Here's a simplified sketch where changes in control values change the screen colour. Works perfectly when connected, nothing without the USB. Is there anything I need to add to the code to make this work? I've removed/commented out all instances of Serial commands which I thought would be the issue.

Code: Select all

// this sketch is adapted from the UDP sketch by mortonkopf on the pjrc forum.
#include <Adafruit_CC3000.h>
#include "Adafruit_ILI9340.h" // Hardware-specific library
#include <ccspi.h>
#include <SPI.h>
#include <SD.h>
#include "utility/socket.h"
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#ifndef PSTR
#define PSTR // Make Arduino Due happy
#endif


#define _cs 22 //10
#define _dc 24 //9
#define _rst 26 //8
#define SD_CS1 4 //4
#define SD_CS2 28 // 6

Adafruit_ILI9340 tft1 = Adafruit_ILI9340(_cs, _dc, _rst);

// http://forum.pjrc.com/threads/24897-understanding-how-CC3000-udp-client-works

//-----------------CC3000 and network info plus interrupt and control pins--------//
#define ADAFRUIT_CC3000_IRQ 3//  3  // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT 9 //5 previously pin 5 in all examples - 5 clashes with octows2811 library pins
#define ADAFRUIT_CC3000_CS    10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIV2);
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2
Adafruit_CC3000_Client client;


#define WLAN_SSID   "XXXX"        
#define WLAN_PASS   "xxxx"


//network location
byte mac[] = {0x08, 0x00, 0x28, 0x57, 0xAC, 0x96};//set you mac address (you can find it using buildtest example)
//unsigned long ip = 0xc0a8010a;//comment out if using static IP method

//-------for wifi connection timeout error------//
const unsigned long
  dhcpTimeout     = 60L * 1000L, // Max time to wait for address from DHCP
  connectTimeout  = 15L * 1000L; // Max time to wait for server connection
uint32_t t;

//int socket ( long domain , long type , long protocol );
/* domain selects the protocol family which will be used for communication. AF_INET is supported. */
/* type specifies the communication semantics. SOCK_STREAM and SOCK_DGRAM, SOCK_RAW are supported. */
/* protocol specifies a particular protocol to be used with the socket. IPPROTO_TCP, IPPROTO_UDP or IPPROTO_RAW are supported. */
/* On success the return value is the socket handle that is used for consequent socket operations. On error, -1 is returned. */

//int recvfrom ( long sd , void * buf , long len , long flags , sockaddr * from , socklen_t * fromlen );
//recvfrom() will write the senders IP and port number into the from structure.//
/* sd specifies the socket handle. */
/* buf points to the buffer where the message should be stored. */
/* len specifies the length in bytes of the buffer pointed to by the buffer argument. Range: 1-1460 bytes. */
/* flag specifies the type of message reception. On this version, this parameter is not supported. */
/* len specifies the length in bytes of the buffer pointed to by the buffer argument. Range: 1-1460 bytes. */
/* from is a pointer to an address structure indicating the source address: sockaddr. */
/* On success the return return the number of bytes received. On error, -1 is returned. 0 if timeout occurred. */



//-------------------for UDP socket, bind and receive--------//
unsigned long port = 8888;
long sockfd;
unsigned long localPort;
sockaddr_in  socketAddr, from;
socklen_t sockLen;
unsigned char buff[3]; //default was 512, but this the amount of values that the UDP client reads in in each message.
int freeRam () 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}
void setup() {  
  
  
  //Serial.begin(115200);
//------ Initialise the CC3000 module----//
  tft1.begin();
  
  //Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS2)) {
   // Serial.println("failed!");
   return;
  }
 // Serial.println("OK!");
 // Serial.println(F("\nInitializing..."));
  if (!cc3000.begin())
  {
  //  Serial.println(F("Couldn't begin()! Check your wiring?"));
    while(1);
  }

  cc3000.begin();
  memset(&socketAddr, 0, sizeof(sockaddr_in));

 /*-------------setting a static IP Address--------------------  */

//unsigned long IPAdd[1] = {0x1800A8C0};//;0xC0A80009  these are in reverse hex
//unsigned long SubNetMask[1] = {0x00FFFFFF};
//unsigned long dfGW[1] = {0x0100A8C0};
//unsigned long DNSServer[1] = {0x0100A8C0};

 //Use this for a DHCP connection
unsigned long IPAdd[4] = {0x00};
unsigned long SubNetMask[4] = {0x00};
unsigned long dfGW[4] = {0x00};
unsigned long DNSServer[4] = {0x00};


//   if (!cc3000.setStaticIPAddress(IPAdd, SubNetMask, dfGW, DNSServer))
//    {
//      Serial.println(F("Failed to set static IP"));
//     while(true);
//    }
    
//requires changes to cc3000.cpp and .h files for static IP to work!!!!!!//
//for info on setting up the cc3000 - http://learn.adafruit.com/downloads/pdf/adafruit-cc3000-wifi.pdf
//for changes to files for setting a static address se http://forums.adafruit.com/viewtopic.php?f=31&t=47004
/*-------------------- End setting static IP Address---------------------*/

//------ Connect to  WiFi network
cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY);
  //Serial.println("Connected to WiFi network!"); 
  
  
  /* Wait for DHCP to complete */
//  Serial.println(F("Request DHCP"));
//  while (!cc3000.checkDHCP())
//  {
//    delay(100); // ToDo: Insert a DHCP timeout!
//  }  
//
//  /* Display the IP address DNS, Gateway, etc. */  
//  while (! displayConnectionDetails()) {
//    delay(1000);
//  }

//--------connect the UDP port-----//
  int  startTime = millis();
    do {
      client = cc3000.connectUDP(IPAdd[1], port);
      
    } while((!client.connected()) &&
            ((millis() - startTime) < connectTimeout));

//    if(client.connected()) {
//      Serial.print(F("connected to UDP port  ")); 
//      Serial.println(port);
//    }

//----------set up the socket------//
  socketInit();
}
//------------end of setup---------------------//

void loop() {
//------get the received data and print to serial------//  
  grabInput();
    }


/**************************************************************************/
/*!
    @brief  Displays the driver mode (tiny of normal), and the buffer
            size if tiny mode is not being used

    @note   The buffer size and driver mode are defined in cc3000_common.h
*/
/**************************************************************************/
void displayDriverMode(void)
{
  #ifdef CC3000_TINY_DRIVER
    //Serial.println(F("CC3000 is configure in 'Tiny' mode"));
  #else
//    Serial.print(F("RX Buffer : "));
//    Serial.print(CC3000_RX_BUFFER_SIZE);
//    Serial.println(F(" bytes"));
//    Serial.print(F("TX Buffer : "));
//    Serial.print(CC3000_TX_BUFFER_SIZE);
//    Serial.println(F(" bytes"));
  #endif
}

/**************************************************************************/
/*!
    @brief  Tries to read the CC3000's internal firmware patch ID
*/
/**************************************************************************/
uint16_t checkFirmwareVersion(void)
{
  uint8_t major, minor;
  uint16_t version;
  
#ifndef CC3000_TINY_DRIVER  
  if(!cc3000.getFirmwareVersion(&major, &minor))
  {
  //  Serial.println(F("Unable to retrieve the firmware version!\r\n"));
    version = 0;
  }
  else
  {
 //   Serial.print(F("Firmware V. : "));
  //  Serial.print(major); Serial.print(F(".")); Serial.println(minor);
    version = major; version <<= 8; version |= minor;
  }
#endif
  return version;
}

/**************************************************************************/
/*!
    @brief  Tries to read the 6-byte MAC address of the CC3000 module
*/
/**************************************************************************/
void displayMACAddress(void)
{
  uint8_t macAddress[6];
  
  if(!cc3000.getMacAddress(macAddress))
  {
  //  Serial.println(F("Unable to retrieve MAC Address!\r\n"));
  }
  else
  {
  //  Serial.print(F("MAC Address : "));
    cc3000.printHex((byte*)&macAddress, 6);
  }
}


/**************************************************************************/
/*!
    @brief  Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
  
  if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
  //  Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
  //  Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
  //  Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
  //  Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
  //  Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
  //  Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
  //  Serial.println();
    return true;
  }
}

/**************************************************************************/
/*!
    @brief  Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/

void listSSIDResults(void)
{
  uint8_t valid, rssi, sec, index;
  char ssidname[33]; 

  index = cc3000.startSSIDscan();

 // Serial.print(F("Networks found: ")); Serial.println(index);
 // Serial.println(F("================================================"));

  while (index) {
    index--;

    valid = cc3000.getNextSSID(&rssi, &sec, ssidname);
    

  cc3000.stopSSIDscan();
}

//==========test to get input from cc3000 and print to serial=================//
void grabInput()
{
 // Serial.println("I'm in grabInput");
  
//------check client socket open----//
    if(client.connected()) {
  //    Serial.print(F("connected to UDP port  ")); 
  //    Serial.println(port);
    } 
//-------end of socketcheck------//

  int inData;
 // Serial.print("inData start value = ");Serial.println(inData);
  sockLen = sizeof(sockaddr_in);
// Serial.println("receive test ...");
 inData = recvfrom(sockfd, buff, sizeof(buff), 0, (sockaddr*)&from, &sockLen);
  
int FreeRamUse;

  int i; // declare i for handling buffer content (i.e. UDP bytes)
  int t = buff[i]; // assign the buffer to variable t
  
 //-----print buffer contents------//
  for(i=0;i<sizeof(buff);i++){
  
  }

   if (t == 1) {
 tft1.fillScreen(ILI9340_RED);
 
 }
    if (t == 2) {
 tft1.fillScreen(ILI9340_BLUE);

 
 }
    if (t == 3) {  
 tft1.fillScreen(ILI9340_GREEN);

 
 }

}


void socketInit(){
   //-----set a socket and check for it, then bind it-----//
    sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//    if (sockfd < 0){
//      //  Serial.println("socket failure");}
//      else {
     //   Serial.print ("Sockfd = ");
      //  Serial.println (sockfd);      }
        
    socketAddr.sin_family = AF_INET;
    socketAddr.sin_addr.s_addr = 0;
    socketAddr.sin_port = htons(port/*localPort*/);
    bind(sockfd, (sockaddr*)&socketAddr, sizeof(sockaddr_in));
  //  Serial.print("check for sockfd number = ");Serial.println(sockfd);
  //  Serial.print("check for port number = ");Serial.println(localPort);
  //  Serial.println(", end of socketInit();");
  }
  
  
  
  



User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

What kind of external power supply are you using? Sounds like that's not providing enough juice.

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

It's the Adafruit 5v 10A supply, which should give more than enough juice for one TFT screen and a CC3000.

(this one)
https://www.adafruit.com/product/658

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

There's nothing in the code that cares about whether or not USB is connected. I can only think that there's something wrong with your power supply, or something wrong with your wiring. You're connecting the power supply to the barrel-jack on the Arduino, right?

What kind of Arduino are you using?

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

Hi , it's a Mega (tested on two now - one old, one new).

Yes, I'm connecting the supply to the power socket by the USB port. I can run other sketches just fine, e.g. one that powers 4 neo pixel 4x8.

With this sketch the power lights appear on the Arduino, the CC3000 and the TFT displays the white background.

I'm not changing any wiring from disconnecting the USB and plugging in the adaptor.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

Do you have a USB-TTL Serial adapter, like one of these?
https://www.adafruit.com/product/284
https://www.adafruit.com/product/70
https://www.adafruit.com/product/954

If so, connect it to Digital 0 and Digital 1 (and GND) so that you can see the output in Serial Monitor without having the USB power connected. Perhaps we can get some clues from that.

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

I don't have one of those I'm afraid.

I figured out a way to make it work though with regards to the order in which the Arduino and the device that sends the control data connect to the network. Although I've no idea why this only makes a difference without the USB connection.

Lasereyesurgery
 
Posts: 32
Joined: Thu Feb 13, 2014 7:47 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by Lasereyesurgery »

adafruit_support_rick wrote:You'd have to re-write the bmpDraw routine to multiplex the data from the four different files. Essentially draw a buffer-load on the first display, then draw a buffer-load on the second display, etc.
OK, so I had a bit of time this week and tried to give this a go. I've re-written bmpDraw in an attempt to draw both images simultaneously, but I've gotten a bit stuck.

In the code I'm writing two different images to two TFT screens, in an attempt to cut the time it takes to draw them one at a time. The result of the code below is that both screens are populated with a rather fuzzy version of bmpFile1. They are drawn simultaneously but it's the same image. I've had to guess values for row and column. Perhaps someone with a better grasp of coding who can see where I may have gone wrong? Also, given the time it takes to draw a file is this method actually possible to save overall time in the long run? i.e with a whole load of screens. Much appreciated.

Code: Select all

/***************************************************
  This is an example sketch for the Adafruit 2.2" SPI display.
  This library works with the Adafruit 2.2" TFT Breakout w/SD card
  ----> http://www.adafruit.com/products/1480
 
  Check out the links above for our tutorials and wiring diagrams
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional)
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/

#include <Adafruit_GFX.h>    // Core graphics library
#include "Adafruit_ILI9340.h" // Hardware-specific library
#include <SPI.h>
#include <SD.h>

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

// TFT display and SD card will share the hardware SPI interface.
// Hardware SPI pins are specific to the Arduino board type and
// cannot be remapped to alternate pins.  For Arduino Uno,
// Duemilanove, etc., pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.
//#define TFT_RST 8
//#define TFT_DC 9
//#define TFT_CS 10
//#define SD_CS 4
//
//Adafruit_ILI9340 tft1 = Adafruit_ILI9340(TFT_CS, TFT_dDC, TFT_RST);

// These are the pins used for the UNO
// for Due/Mega/Leonardo use the hardware SPI pins (which are different)
//#define _sclk 13//13
//#define _miso 12//12
//#define _mosi 11//11
#define _cs 10 //10
#define _dc 9
#define _rst 8
#define SD_CS1 4
#define SD_CS2 6
#define _cs1 2
#define _dc1 7
#define _rst1 5

// Using software SPI is really not suggested, its incredibly slow
//Adafruit_ILI9340 tft = Adafruit_ILI9340(_cs, _dc, _mosi, _sclk, _rst, _miso);
// Use hardware SPI
Adafruit_ILI9340 tft1 = Adafruit_ILI9340(_cs, _dc, _rst);
Adafruit_ILI9340 tft = Adafruit_ILI9340(_cs1, _dc1, _rst1);
void setup(void) {
  Serial.begin(9600);

  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS2)) {
    Serial.println("failed!");
   return;
  }
  Serial.println("OK!");
    


  tft.begin();

  tft1.begin();

    
  
//  tft1.fillScreen(ILI9340_BLUE);
  bmpDraw("rose.bmp", "woof1.bmp", 0, 0, 0, 0);

}

void loop() {
}

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster.  20 pixels seems a
// good balance.

#define BUFFPIXEL 80


void bmpDraw(char *filename, char *filename2, uint8_t x, uint8_t y, uint8_t x2, uint8_t y2)

{

  File     bmpfile1;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;

  uint8_t  r1, g1, b1;
  uint32_t pos = 0, startTime = millis();
  
  File     bmpfile2;
  int      bmpWidth2, bmpHeight2;   // W+H in pixels
  uint8_t  bmpDepth2;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset2;        // Start of image data in file
  uint32_t rowSize2;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer2[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx2 = sizeof(sdbuffer2); // Current position in sdbuffer
  boolean  goodBmp2 = false;       // Set to true on valid header parse
  boolean  flip2    = true;        // BMP is stored bottom-to-top
  int      w2, h2, row2, col2;
  uint8_t  r2, g2, b2;
  uint32_t pos2 = 0, startTime2 = millis();
  

  if((x >= tft1.width()) || (y >= tft1.height())) return;
  if((x2 >= tft.width()) || (y2 >= tft.height())) return;

  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');
  Serial.println("made it this far");

//  // Open requested file on SD card
  if ((bmpfile1 = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }
////  // Open requested file on SD card
  if ((bmpfile2 = SD.open(filename2)) == NULL) {
    Serial.print("File not found");
    return;
  }
Serial.println("made it this far");
  // Parse BMP header
  
  if((read16(bmpfile1) == 0x4D42) && (read16(bmpfile2) == 0x4D42)) { // BMP signature
  
   Serial.print("File size2: "); Serial.println(read32(bmpfile2));
    (void)read32(bmpfile2); // Read & ignore creator bytes
    bmpImageoffset2 = read32(bmpfile2); // Start of image data
    Serial.print("Image Offset2: "); Serial.println(bmpImageoffset2, DEC);
    // Read DIB header
    Serial.print("Header size2: "); Serial.println(read32(bmpfile2));
    bmpWidth2  = read32(bmpfile2);
    bmpHeight2 = read32(bmpfile2);
 
  
    Serial.print("File size: "); Serial.println(read32(bmpfile1));
    (void)read32(bmpfile1); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpfile1); // Start of image data
    Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print("Header size: "); Serial.println(read32(bmpfile1));
    bmpWidth  = read32(bmpfile1);
    bmpHeight = read32(bmpfile1);
    
    if((read16(bmpfile1) == 1) && (read16(bmpfile2) == 1)) { // # planes -- must be '1'. Need condition here for more images
      bmpDepth = read16(bmpfile1); // bits per pixel

       bmpDepth2 = read16(bmpfile2); // bits per pixel
      Serial.print("Bit Depth2: "); Serial.println(bmpDepth2);
      
      Serial.print("Bit Depth: "); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpfile1) == 0) && (bmpDepth2 == 24) && (read32(bmpfile2) == 0)) { // 0 = uncompressed. Need conditions here for more images

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print("Image size: ");
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);
        
        goodBmp2 = true; // Supported BMP format -- proceed!
        Serial.print("Image size: ");
        Serial.print(bmpWidth2);
        Serial.print('x');
        Serial.println(bmpHeight2);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;
        rowSize2 = (bmpWidth2 * 3 + 3) & ~3;
        
 
        

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        
         if(bmpHeight2 < 0) {
          bmpHeight2 = -bmpHeight2;
          flip2      = false;
        }


        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft1.width())  w = tft1.width()  - x;
        if((y+h-1) >= tft1.height()) h = tft1.height() - y;
        
        w2 = bmpWidth2;
        h2 = bmpHeight2;
        if((x2+w2-1) >= tft.width())  w2 = tft.width()  - x2;

        // Set TFT address window to clipped image bounds
        tft1.setAddrWindow(x, y, x+w-1, y+h-1);
        tft.setAddrWindow(x2, y2, x2+w2-1, y2+h2-1);
        
    int h1;
    int h2;
    int w1;
    int w2;
    int row;
    int col;
    int idx;
    int minRows;
    int minCols;
    int maxRows;
    int maxCols;
    
    // -----------------------------------
    // These varaibles are kind of unknown
   
    h1 = 300; // image rows 1
    h2 = 300; // image rows 2
    w1 = 300; // image cols 1
    w2 = 300; // image cols 2
    // -----------------------------------
    minRows = min(h1,h2); //
    maxRows = max(h1,h2); //
    minCols = min(w1,w2); //
    maxCols = max(w1,w2); //
    bmpImageoffset=54; // this is the value reported in serial monitor


    for (row=0; row<maxRows; row++)
    { // For each scanline...
        // Seek to start of scan line.  It might seem labor-
        // intensive to be doing this on every line, but this
        // method covers a lot of gritty details like cropping
        // and scanline padding.  Also, the seek only takes
        // place if the file position actually needs to change
        // (avoids a lot of cluster math in SD library).
        
        
        
        if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
        else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
//--------------------------

        
        if(row<w1 && bmpfile1.position() != pos) { // Need seek?
            bmpfile1.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
        }
        if(row<w2 && bmpfile2.position() != pos) { // Need seek?
            bmpfile2.seek(pos);
            buffidx2 = sizeof(sdbuffer2); // Force buffer reload
        }
        
        for (col=0; col<maxCols; col++)
        { // For each pixel...
            
            if (col<w1 && row<w1)
            {
                // Time to read more pixel data?
                if (buffidx >= sizeof(sdbuffer)) { // Indeed
                    bmpfile1.read(sdbuffer, sizeof(sdbuffer));
                    buffidx = 0; // Set index to beginning
                }
                
                // IMAGE 1
                // Convert pixel from BMP to TFT format, push to display
                b = sdbuffer[buffidx++];
                g = sdbuffer[buffidx++];
                r = sdbuffer[buffidx++];
                tft1.pushColor(tft.Color565(r,g,b));
                // Time to read more pixel data?
            }
     
            if (col<w2 && row2<w2)
            {
                if (buffidx2 >= sizeof(sdbuffer2)) { // Indeed
                    bmpfile2.read(sdbuffer2, sizeof(sdbuffer2));
                    buffidx2 = 0; // Set index to beginning
                }
                // IMAGE 2
                // Convert pixel from BMP to TFT format, push to display
                b = sdbuffer[buffidx2++];
                g = sdbuffer[buffidx2++];
                r = sdbuffer[buffidx2++];
                tft.pushColor(tft.Color565(r,g,b));
                // Time to read more pixel data?
            }
        }
        }
        }             
      }
         
        Serial.print("Loaded in ");
        Serial.print(millis() - startTime2);
        Serial.println(" ms");
  }

  bmpfile1.close();
  if(!goodBmp) Serial.println("BMP1 format not recognized.");
   bmpfile2.close();
  if(!goodBmp2) Serial.println("BMP2 format not recognized.");
} 
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File & f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File & f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}



User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Drawing BMPs on multiple TFT's simultaneously

Post by adafruit_support_rick »

I'l have a look.
Lasereyesurgery wrote:Also, given the time it takes to draw a file is this method actually possible to save overall time in the long run?
No. It will take the same amount of time - maybe longer, since you will have more bookkeeping to take care of.

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

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