RA8876 Parallel Display Library Testing

mjs513

Senior Member+
@wwatson - @KurtE
Started playing with your new library for the RA8876 using 8080 8bit mode on a T4.1. Running into a couple of issues
Anyways started running through the example sketches and mixed results using a T4.1 by the way.

Running the examples to start found that
GraphicsTest, Rotation, Gauges all seem to be working.

However FontTest4 and pictureEmbed seem to be have issues with rotations 1 and 3. Also seems to take quite a while to display an image in rotation 0. With pictureEmbed getting 2d fail messages maybe its just the example.

Round 2 testing next

UPDATE (2024/06/09): While things are still evolving the lib changes have gennerally been working. The current working branch is maintained by @wwatson at https://github.com/wwatson4506/Ra8876LiteTeensy/tree/combined_t4x_wip. Just remember its a WIP.
 
Last edited:
Wrote a quick sketch for testing rotation and getPixel function as thought it might be interesting to try out. Again seems issues with rotation and may pixel cursor?

1st heres the sketch
C++:
#include "Arduino.h"
#include "RA8876_t3.h"
#include "font_Arial.h"

// T4.1
uint8_t dc = 13;
uint8_t cs = 11;
uint8_t rst = 12;

RA8876_t3 tft = RA8876_t3(dc,cs,rst); //(dc, cs, rst)

// Array of Simple RA8876 Basic Colors
// Array of Simple RA8876 Basic Colors
PROGMEM uint16_t myColors[] = {
    0x0000, 0xffff,    0xf800,    0xfc10,    0x8000,    0x07e0,    0x87f0,    0x0400,
    0x001f,    0x051f,    0x841f,    0x0010,    0xffe0,    0xfff0,    0x8400,    0x07ff,
    0x87ff,    0x0410,    0xf81f,    0xfc1f,    0x8010,    0xA145
};

int interations = 0;
int w, h;
int i = 0;

#define BAND_WIDTH 16
#define BAND_HEIGHT 40
#define BAND_START_X 200
#define BAND_START_Y 200
uint16_t pixel_data[5500];

void setup() {
  while (!Serial && millis() < 5000) {} //wait for Serial Monitor

  //I'm guessing most copies of this display are using external PWM
  //backlight control instead of the internal RA8876 PWM.
  //Connect a Teensy pin to pin 14 on the display.
  //Can use analogWrite() but I suggest you increase the PWM frequency first so it doesn't sing.
//  pinMode(BACKLITE, OUTPUT);
//  digitalWrite(BACKLITE, HIGH);
    
  tft.begin(20); // 20 is working in 8bit and 16bit mode on T41

  tft.graphicMode(true);
  tft.setTextCursor(0,0);
  tft.setFont(Arial_14);


}

void testGetPixel(uint8_t rotation) {
  tft.setRotation(rotation);
  Serial.printf("ROTATION: %d\n", rotation);
  tft.fillScreen(0xf800);
  w = tft.width()-1; h = tft.height()-STATUS_LINE_HEIGHT-1;
  tft.printStatusLine(0,myColors[1],myColors[11], "Status Text");
  tft.fillRect(0, 0, 400, 200, myColors[8]);
  Serial.printf("Rect Color: 0x%x, Pixel Color: 0x%x\n", myColors[8], tft.getPixel(200, 100));

  tft.fillRect(tft.width()-400, tft.height()-200, 400, 200, myColors[8]);
  Serial.printf("Rect Color: 0x%x, Pixel Color: 0x%x\n", myColors[8], tft.getPixel(200, 100));

  //tft.fillRect(tft.width()-400, tft.height()-200, 400, 200, myColors[8]);
  //tft.fillRect(tft.width()-400, 0, 400, 200, myColors[8]);
  //tft.fillRect(0, tft.height()-200, 400, 200, myColors[8]);

}

void colorBar(uint8_t rotation){
  tft.setRotation(rotation);
  tft.fillScreen(BLACK);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 0, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, RED);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 1, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, GREEN);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 2, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, BLUE);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 3, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, BLACK);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 4, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, WHITE);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 5, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, YELLOW);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 6, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, CYAN);
  tft.fillRect(BAND_START_X + BAND_WIDTH * 7, BAND_START_Y, BAND_WIDTH, BAND_HEIGHT, 0xF81F);
  tft.printStatusLine(0,myColors[1],myColors[11], "Status Text");
  memset(pixel_data, 0, sizeof(pixel_data));
  readRect(BAND_START_X, BAND_START_Y, BAND_WIDTH * 8, BAND_HEIGHT, pixel_data);
  tft.writeRect(BAND_START_X, BAND_START_Y + BAND_HEIGHT, BAND_WIDTH * 8, BAND_HEIGHT, pixel_data);
}

void readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors) {
  for(uint16_t j = y; j < (h + y); j++) {
    for(uint16_t i = x; i < (w + x); i++) {
      *pcolors++ = tft.getPixel(i, j);
    }
  }
}

//=============================================================================
// Wait for user input
//=============================================================================
void WaitForUserInput() {
    Serial.println("Hit Enter to continue");
    Serial.flush();
    while (Serial.read() == -1)
        ;
    while (Serial.read() != -1)
        ;
}

void test() {
  tft.setTextColor(BLACK);
  
  testGetPixel(0);
  WaitForUserInput();
  testGetPixel(1);
  WaitForUserInput();
  testGetPixel(2);
  WaitForUserInput();
  testGetPixel(3);
  WaitForUserInput();

  colorBar(0);
  WaitForUserInput();
  colorBar(1);
  WaitForUserInput();
  colorBar(2);
  WaitForUserInput();
  colorBar(3);
  WaitForUserInput();

}
void loop() {
  test();
}

Now for the fun. The first part of the test draws 2 rectangles in opposite corners of the display and then displays the results of getPixel at the mid point of the first rect (there is a reason).
Code:
ROTATION: 0
Rect Color: 0x1f, Pixel Color: 0xf800
After 1st rectangle is drawn(0, 0, 400, 100) I read the pixel at (200, 100) and its returning the value of fillScreen, in case RED. Now when I draw the second rectange at (tft.width()-400, tft.height()-200, 400, 200) (TFT Width: 1024, Height: 600), and read the pixel at (200, 100) I get
[CODE} Rect Color: 0x1f, Pixel Color: 0x1f[/CODE] I get the correct color for the rectangle

Another test is drawing a colorbar (from one of Kurt's tests, getting the pixels and then displaying them below. So
Rotation 0 (works for rotation 0 and 1)
IMG_1292.jpg

But for Rotation 2 fails but gets the right colors - position is off)
IMG_1293.jpg


Not sure we ever tested with the SPI version or not. Yes I have a custom readRect function in the sketch. Not pretty but it works.
Cheers
mike
 
@mjs513 - Tested the sketch on my end and got the same result as you did. Tried "ILI_ADA_FontTest4.ino" and it also messes up on rotation 2 and then returning to rotation 0 messes up:
Rotate_issues.jpg


and rotation 2:
Rotation 2_issue.jpg


I noticed that there is no issue when running "RA8876Rotation.ino":
RA8876Rotate.jpg


Not sure what the difference is:unsure:

Edit: Ignore the little red cursor in the lower right hand corner. It is residual from a previous sketch. I need to update the RA8876_T3 library to turn off the text cursor during initialization...
 
Not sure what the difference is:unsure:
Guess is that with the rotation sketch we are only using graphic primitives which in the RA8876 uses the graphic engine. As for the font - could be which font.

So the question is why does it happen with one and not the other - are we calling something first or forgeting to go back and forth between text and graphic mode? Have to dig deeper.
 
I have mine setup and working in same state as yours.

I thought I would try out the MTP picture viewer, but so far it does not appear to want to display an image...

Need to debug...
 
Ok think I found one issue with the FontTest4 sketch.

If you comment out this line (156 or so)
Code:
else tft.setFontDef();
and replace with
Code:
//else tft.setFontDef();
    else {}
no issue with rotation - draws cleanly.

fontDef is defined in the ,h as
Code:
inline void setFontDef() {
        _use_default = 1;
        //if(_portrait && (!_use_gfx_font || !_use_ili_font)) {
        //    _cursorX += _cursorY;
        //_cursorY -= _cursorX;
        //}
        _use_ili_font=0;
        _use_gfx_font=0;
        _use_int_font=1;
        _use_tfont=0;
        setActiveWindow();
        _textPosition(_cursorX, _cursorY, false);
        };
so something with cursorX and cursorY not correct my guess

Still not sure whats wrong with the sketch I posted but its diner time so maybe more later
 
Ok think I found one issue with the FontTest4 sketch.

If you comment out this line (156 or so)
Code:
else tft.setFontDef();
and replace with
Code:
//else tft.setFontDef();
    else {}
no issue with rotation - draws cleanly.

fontDef is defined in the ,h as
Code:
inline void setFontDef() {
        _use_default = 1;
        //if(_portrait && (!_use_gfx_font || !_use_ili_font)) {
        //    _cursorX += _cursorY;
        //_cursorY -= _cursorX;
        //}
        _use_ili_font=0;
        _use_gfx_font=0;
        _use_int_font=1;
        _use_tfont=0;
        setActiveWindow();
        _textPosition(_cursorX, _cursorY, false);
        };
so something with cursorX and cursorY not correct my guess

Still not sure whats wrong with the sketch I posted but its diner time so maybe more later
Dinner time here also. Will test after...
 
I narrowed it down to:
Code:
    inline void setFontDef() {
        _use_default = 1;
//        if(_portrait && (!_use_gfx_font || !_use_ili_font)) {
//            _cursorX += _cursorY;
//        _cursorY -= _cursorX;
//        }
        _use_ili_font=0;
        _use_gfx_font=0;
        _use_int_font=1;
        _use_tfont=0;
//        setActiveWindow();
        _textPosition(_cursorX, _cursorY, false);
        };
Commenting out the line "setActiveWindow();" fixes the problem. Not sure if commenting it out raises other issues.
Also noticed this when starting rotations 1-3:
Miss_aligned_text.jpg

Sorry about the picture quality. The upper two text lines should be centered in the graphic boxes if I am not mistaken...
 
I am getting a little farther with the Picture viewer. I get images displayed but goes away quick....

It has to do with we were using a sort of frame buffer in the output code:

The SPI code:
Code:
inline void writeRect(int x, int y, int cx, int cy, uint16_t *pixels) {
    Serial.printf("WriteRect(%d, %d, %d, %d, %p)\n", x, y, cx, cy, pixels);
#if 0 // defined(_RA8876_T3)
    tft.useCanvas();
    tft.putPicture_16bpp(x, y, cx, cy);
    //tft.startSend();
    SPI.transfer(RA8876_SPI_DATAWRITE);
    SPI.transfer(pixels, NULL, cx * cy * 2);
    //tft.endSend(true);
#else
    tft.useCanvas();
    tft.writeRect(x, y, cx, cy, pixels);
#endif
}

And later when we want the new image to be displayed we called Update Screen... Note the 0 was not there...

Needless to say the SPI.transfer will not work in this case... I have it now just calling the internal writeRect, which does show the data, but it does not work like before... Need to figure out the proper way to replace the SPI.transfer in this case...

Current hacked up version of the sketch
 

Attachments

  • mtp_tft_picture_view_RA8876_p-240624a.zip
    135.5 KB · Views: 5
I am getting a little farther with the Picture viewer. I get images displayed but goes away quick....

It has to do with we were using a sort of frame buffer in the output code:

The SPI code:
Code:
inline void writeRect(int x, int y, int cx, int cy, uint16_t *pixels) {
    Serial.printf("WriteRect(%d, %d, %d, %d, %p)\n", x, y, cx, cy, pixels);
#if 0 // defined(_RA8876_T3)
    tft.useCanvas();
    tft.putPicture_16bpp(x, y, cx, cy);
    //tft.startSend();
    SPI.transfer(RA8876_SPI_DATAWRITE);
    SPI.transfer(pixels, NULL, cx * cy * 2);
    //tft.endSend(true);
#else
    tft.useCanvas();
    tft.writeRect(x, y, cx, cy, pixels);
#endif
}

And later when we want the new image to be displayed we called Update Screen... Note the 0 was not there...

Needless to say the SPI.transfer will not work in this case... I have it now just calling the internal writeRect, which does show the data, but it does not work like before... Need to figure out the proper way to replace the SPI.transfer in this case...

Current hacked up version of the sketch
I never have played with this. Definitely will check it out:D
 
I vaguely remember that the built in fonts only work in one or two rotations.
Don't remember - thought we had them working in all directions - unless it was only the internal fonts that had issues.
Commenting out the line "setActiveWindow();" fixes the problem. Not sure if commenting it out raises other issues.
Also noticed this when starting rotations 1-3:
May raise other issues - have to hook up the SPI version as a double check.
 
Don't remember - thought we had them working in all directions - unless it was only the internal fonts that had issues.
I am pretty sure the built in fonts probably including ones you can have with extra chips only worked in a few directions.
May raise other issues - have to hook up the SPI version as a double check.
I keep mine hooked up, will try this later. Is there an easy way to have both versions of the library on your machine?
 
@KurtE - @wwatson
To keep both libs I renamed the parallel class to RA8876_t41_8p (here it is if you are interested). With the SPI version was seeing the same craziness with the FontTest4 as we saw with parallel version. Did notice that the GFX Fonts are not working:
Code:
//  {nullptr, &FreeMonoBoldOblique12pt7b,  "GFX FreeMonoBoldOblique12pt7b", WHITE, WHITE},
//  {nullptr, &FreeMonoBoldOblique12pt7b,  "GFX FreeMonoBoldOblique12pt7b", RED, YELLOW},
//  {nullptr, &FreeSerif12pt7b,  "GFX FreeSerif12pt7b", WHITE, WHITE},
//  {nullptr, &FreeSerif12pt7b,  "GFX FreeSerif12pt7b", RED, YELLOW},
not sure why.

After commenting them out saw the same issue. So since using Internal fonts may be an issue I commented out using the internal fonts
Code:
//  {nullptr, nullptr,  "Internal Font", RED, YELLOW},

and voila rotations is working again and now my desk is looking like Kurt;s

Tried the earlier sketch with reading pixels and the same thing is happening so same issue with SPI and parallel with reading/writing rects

Guess thats next
 

Attachments

  • RA8876_t41_8p.zip
    1.2 MB · Views: 9
I just pushed up a new branch:

Working on getting GFX Fonts (and probably buttons to work) So far it compiles.

One major issue, was this library was including ILI9341_t3.h file, so it was always bringing in all of the code from that library.
So from the ILI9488_t3 library, I copied over a couple of files like ILI9341_fonts.h which defines the font format.
Changed all of the files in this project to use it. Removed _fonts.h

Also updated the buttons stuff, to do like the other libraries, which required that the code be up in header file...

Things build now. I can include Adafruit_GFX.h in sketch and the font files. But they are not drawing... Need to debug that.

Note: These changes are not specific to just the 8080-T41 branch

Edit: One very big clue was even simple sketch like:

Code:
#include "RA8876_t3.h"


// T4.1
uint8_t dc = 13;
uint8_t cs = 11;
uint8_t rst = 12;
RA8876_t3 tft = RA8876_t3(dc, cs, rst);  //(dc, cs, rst)
void setup() {


    Serial.begin(38400);
    long unsigned debug_start = millis();
    while (!Serial && ((millis() - debug_start) <= 5000))
        ;
    Serial.println("Setup");
    tft.begin(20);
}
void loop() {
    tft.fillScreen(RED);
    delay(2000);
    tft.fillScreen(GREEN);
    delay(2000);
    tft.fillScreen(BLUE);
    delay(2000);
}

Showed at the end of the build which version of the ILI9341_t3 library was included...
 
Last edited:
I just pushed up a new branch:

Working on getting GFX Fonts (and probably buttons to work) So far it compiles.

One major issue, was this library was including ILI9341_t3.h file, so it was always bringing in all of the code from that library.
So from the ILI9488_t3 library, I copied over a couple of files like ILI9341_fonts.h which defines the font format.
Changed all of the files in this project to use it. Removed _fonts.h

Also updated the buttons stuff, to do like the other libraries, which required that the code be up in header file...

Things build now. I can include Adafruit_GFX.h in sketch and the font files. But they are not drawing... Need to debug that.

Note: These changes are not specific to just the 8080-T41 branch

Edit: One very big clue was even simple sketch like:

Code:
#include "RA8876_t3.h"


// T4.1
uint8_t dc = 13;
uint8_t cs = 11;
uint8_t rst = 12;
RA8876_t3 tft = RA8876_t3(dc, cs, rst);  //(dc, cs, rst)
void setup() {


    Serial.begin(38400);
    long unsigned debug_start = millis();
    while (!Serial && ((millis() - debug_start) <= 5000))
        ;
    Serial.println("Setup");
    tft.begin(20);
}
void loop() {
    tft.fillScreen(RED);
    delay(2000);
    tft.fillScreen(GREEN);
    delay(2000);
    tft.fillScreen(BLUE);
    delay(2000);
}

Showed at the end of the build which version of the ILI9341_t3 library was included...
Should we use your branch as a base for development?
 
Working on getting GFX Fonts (and probably buttons to work) So far it compiles.

One major issue, was this library was including ILI9341_t3.h file, so it was always bringing in all of the code from that library.
So from the ILI9488_t3 library, I copied over a couple of files like ILI9341_fonts.h which defines the font format.
Changed all of the files in this project to use it. Removed _fonts.h
@KurtE - glad you found the problem. Maybe we should change the lib name like I posted so we can have SPI and Parallel libs on the same machine

Going to play after I settle in from getting back from Drs.
 
While @KurtE was resolving the issue GFX fonts was trying to isolate and resolve the issue with getPixel. After adding a 3 graphic object, so this is what I have now:
Code:
  tft.fillScreen(0xf800);   <---- obj 1
  tft.fillRect(0, 0, 400, 200, myColors[8]); <----- obj 2
  uint16_t pixel =  tft.getPixel(200, 100);
  Serial.printf("Rect Color: 0x%x, Pixel Color: 0x%x\n", myColors[8], pixel);

  tft.fillCircle(200, 100, 50, GREEN);   <---- obj 3
  Serial.printf("Circle Color: 0x%x, Pixel Color: 0x%x\n", GREEN, tft.getPixel(200, 100));

I noticed getPixel was returning the previous objects color. That got me scratching my head. Using a Canvas got it to return the correct color and after looking how we did that I added a call to select current screen
Code:
selectScreen(currentPage);

so the function now looks like this
Code:
ru16 RA8876_t41_p::getPixel(ru16 x,ru16 y) {
    ru16 rdata = 0;
  ru16 dummy = 0;
 
  selectScreen(currentPage);
    graphicMode(true);
    setPixelCursor(x, y);                  // set memory address
    ramAccessPrepare();                      // Setup SDRAM Access
  dummy = lcdDataRead();
  rdata = (lcdDataRead() & 0xff);        // read low byte
  rdata |= lcdDataRead() << 8;        // add high byte
     return rdata;
}

so now that piece is working in all rotations:
Code:
TFT Width: 1024, Height: 600
ROTATION: 0
Rect Color: 0x1f, Pixel Color: 0x1f
Circle Color: 0x7e0, Pixel Color: 0x7e0
Hit Enter to continue
TFT Width: 600, Height: 1024
ROTATION: 1
Rect Color: 0x1f, Pixel Color: 0x1f
Circle Color: 0x7e0, Pixel Color: 0x7e0
Hit Enter to continue
TFT Width: 1024, Height: 600
ROTATION: 2
Rect Color: 0x1f, Pixel Color: 0x1f
Circle Color: 0x7e0, Pixel Color: 0x7e0
Hit Enter to continue
TFT Width: 600, Height: 1024
ROTATION: 3
Rect Color: 0x1f, Pixel Color: 0x1f
Circle Color: 0x7e0, Pixel Color: 0x7e0

Now to figure why color bar positions are off for rotation 2 and 3
 
I have the mtp_tft_picture_view_RA8876_p.ino working now in 8bit, 16bit and 8bit async modes. Not setup yet to do 16bit async mode. One change was needed in RA8876_t3.cpp:
Code:
//**************************************************************//
// For 16-bit byte-reversed data.
// Note this is 4-5 milliseconds slower than the 8-bit version above
// as the bulk byte-reversing SPI transfer operation is not available
// on all Teensys.
//**************************************************************//
void RA8876_t3::bteMpuWriteWithROPData16(ru32 s1_addr,ru16 s1_image_width,ru16 s1_x,ru16 s1_y,ru32 des_addr,ru16 des_image_width,
ru16 des_x,ru16 des_y,ru16 width,ru16 height,ru8 rop_code,const unsigned short *data) {
  ru16 i,j;
  bteMpuWriteWithROP(s1_addr, s1_image_width, s1_x, s1_y, des_addr, des_image_width, des_x, des_y, width, height, rop_code);

  while(WR_IRQTransferDone == false) {} //Wait for any IRQ transfers to complete

  FlexIO_Config_SnglBeat();
  CSLow();
  DCHigh();
  for(j=0;j<height;j++) {
    for(i=0;i<width;i++) {
delayNanoseconds(10);   // Initially setup for the T4.1 board <---- Uncommented this line.
      if(_rotation & 1) delayNanoseconds(70);
      p->SHIFTBUF[0] = *data++;
      /*Wait for transfer to be completed */
      while(0 == (p->SHIFTSTAT & (1 << 0))) {}
      while(0 == (p->TIMSTAT & (1 << 0))) {}
    }
  }
  /* De-assert /CS pin */
  CSHigh();
}
The delay is needed in 16bit non-async mode. 16bit mode and 8bit async mode display times are almost identical.
A 533x380 image:
PNG533x380.jpg

Display time 70ms in 8bit mode and 60ms in 16bit mode. 8bit async mode time is about same as 16bit mode in non-async mode.

A 1024x768 image:
JPG1024x768.jpg

Display time 183ms in 8bit mode and 157ms in 16bit mode. 8bit async mode time is about same as 16bit mode in non-async mode.

At line 70 I added:
Code:
// Uncomment to use 8bit async mode. Must be in 8bit mode. 16bit mode
// does not work in async mode.
#if BUS_WIDTH == 8
#define USE_ASYNC
#endif
BUS_WIDTH is defined in RA8876_t3.h:
Code:
89 #define BUS_WIDTH 8  /*Available options are 8 or 16 */
Other changes made to the sketch are:
Code:
        if (imageFile) {
#if defined(_RA8876_T3)
          tft.useCanvas();
          tft.selectScreen(SCREEN_2);
#endif
Uncommented two lines. And:
Code:
// helper to helper...
inline void writeRect(int x, int y, int cx, int cy, uint16_t *pixels) {
//    Serial.printf("WriteRect(%d, %d, %d, %d, %p)\n", x, y, cx, cy, pixels);
#if 0 // defined(_RA8876_T3)
    tft.useCanvas();
    tft.putPicture_16bpp(x, y, cx, cy);
    //tft.startSend();
    SPI.transfer(RA8876_SPI_DATAWRITE);
    SPI.transfer(pixels, NULL, cx * cy * 2);
    //tft.endSend(true);
#else
    tft.useCanvas();
#if defined(USE_ASYNC)
//    Serial.printf("Async Mode.\n");
    tft.pushPixels16bitAsync(pixels, x, y, cx, cy);
#else
    tft.writeRect(x, y, cx, cy, pixels);
#endif
#endif
}
The zipped sketch is attached below...
 

Attachments

  • mtp_tft_picture_view_RA8876_p.ino.zip
    12.7 KB · Views: 7
1719347072666.png

Top one is running the SPI version. Checked in version up at:

Bottom is hacked up one on Parallel, note: I redid it just now. Tried to neuter Some of the paging stuff, to get it up and running.

What the code was trying to do is: Display an image.
Then start uploading the next image, and when the upload has completed and the configured display time for a picture has expired
then tell the display the new image.

This is just a fun thing that @mjs513 and myself did a couple of years ago, It also is setup to for MTP, so you can download new pictures to your SDCard and the like. Again just a fun one to have around.
 
Back
Top