Created Mon, 02 Sep 2013 05:49:43 +0000 by unexpectedly
Mon, 02 Sep 2013 05:49:43 +0000
I added it to the shields compatibility list, but thought I'd start a thread to talk about it, too, because I absolutely love this shield!
I made the Adafruit 1.8" TFT with the ST7735 using SPI work on my uC32. While I wasn't able to make it work on my own Uno32, it works out-of-box on majenko's Uno32. Much credit to majenko for picking up the pace on chipKIT-specific improvements as well as amazing advances in graphics performance on this TFT. He's done more for it than I think anyone thought to reach for!
I will maintain major changes to ada_dspi_tft_chipKIT_v1xx.zip attached to this first post. The best way to keep current is follow the Github repository for this project. (direct link to zip) I will be adding small UI improvements to TFTDemo.ino in ST7735/examples; small tricks I learn and develop for display of info, etc.
[attachment=1]ada_tft_dspi_v104.jpg[/attachment] This shield is screwed to the face of my TPS test bench. Plastic was 3D printed from Airwolf 3D XL printer. CAD files of it are on GrabCAD.
With the chipKIT, I was able to convert the "PORT" commands from AVR-land into simple digitalWrite()s! On the AVR, the ST7735 library won't even run with all digitalWrite()s! This TFT shield uses the standard SPI pins ( 10, 12, 13 ) and additionally 8 as a DC / RS selector. The shield draws power from the +5V pin, but you can bypass its onboard logic shifter and go all 3.3V. People programming Netduinos for these shields figured this out and were able to get faster performance. Note: soldering and more work required if you're wanting to ditch the 5V altogether.
Analog 3 has a resistor network for the joystick, tested and working in the sample code as of 1.02 or 1.03.
Digital 4 is the chip select for the SD Card (which shares the SPI pins 11,12,13). SD Card functionality not yet implemented in the Hello World code. If I get a spare moment, I'll add the de rigueur put bitmap from SD Card onto screen. (note to self, I also need to add majenko's png-to-bmp php script to the project)
Anecdotal observations coming from AVR-land:
Product link: adafruit.com/products/802
Wed, 04 Sep 2013 06:33:24 +0000
Updated tft.ino to make the joystick work via a trivial menu handler. This should run and display the program name and version number in the "footer".
Lots of comments added in tft.ino.
Libraries: _GFX and _ST7735 removed. No changes to Libraries.
Wed, 04 Sep 2013 10:58:38 +0000
Well, I don't know about your Uno32 problems - I just plugged my TFT into my Uno32, installed the libs, and tft.ino, and it worked straight away.
Maybe your Uno32 is faulty?
Wed, 04 Sep 2013 12:31:59 +0000
Here is a tweaked version of the main control library which has been ported to DSPI and optimised for more efficient SPI usage.
It's set to 20Mbps SPI, and colour data is transferred as single 16-bit word transactions instead of 2 8-bit transactions.
Wed, 04 Sep 2013 12:44:10 +0000
And here's a tweaked GFX library which has support for displaying 16-bit bitmaps, with or without crude transparency.
// 8x8 image
const uint16_t pic[8*8] = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF,
0xFFFF, 0x0000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000, 0xFFFF,
0xFFFF, 0x0000, 0x8000, 0xF000, 0xF000, 0x8000, 0x0000, 0xFFFF,
0xFFFF, 0x0000, 0x8000, 0xF000, 0xF000, 0x8000, 0x0000, 0xFFFF,
0xFFFF, 0x0000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000, 0xFFFF,
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
};
tft.drawRGB(10, 10, pic, 8, 8); // Drawn at 10,10 as is
tft.drawRGBA(10, 10, pic, 8, 8, 0x0000); // Drawn at 10,10 with colour 0x0000 transparent
Now to write a graphical menu system with icons ;)
Wed, 04 Sep 2013 13:52:32 +0000
And here's a little PHP script for converting PNGs to raw data:
#!/usr/bin/php
<?php
$file = $argv[1];
$img = ImageCreateFromPNG($file);
$width = ImageSX($img);
$height = ImageSY($img);
print "// Image width: $width pixels\n";
print "// Image height: $height pixels\n";
print "const uint16_t image[$width*$height] = {\n";
$count = 0;
for ($y = 0; $y < $height; $y++) {
for ($x = 0; $x < $width; $x++) {
$color = ImageColorAt($img, $x, $y);
$a = 127 - (($color >> 24) & 0x7F);
$r = (($color >> 16) & 0xFF) * $a / 127;
$g = (($color >> 8) & 0xFF) * $a / 127;
$b = ($color & 0xFF) * $a / 127;
$r = $r >> 3;
$g = $g >> 2;
$b = $b >> 3;
$newcol = $r << 11 | $g << 5 | $b;
printf("0x%04X, ", $newcol);
$count++;
if ($count == 8) {
print "\n";
$count = 0;
}
}
}
print "};\n";
And what you can do with it: [attachment=0]IMG_20130904_144757.jpg[/attachment]
Wed, 04 Sep 2013 20:09:44 +0000
Damn, I have been trying to make a buffered version of the library, but my Uno32 just can't cut it. I don't want to limit it to the bigger boards really.
I was looking at a 7-bit framebuffer (with 128 entry palette) limiting you to 128 colours on screen at once, but with a much more efficient display routine. Bit 7 of the buffer (making it an 8-bit buffer) was to be a "modified" flag, and when a pixel was drawn on the screen, if it differed to what was there already, it was changed and the "modified" flag was updated. Then an update function would scan through the whole framebuffer finding pixels that had changed and just update the display with those pixels (looking up the colour in the palette), resetting the modified flags as it went.
It would have been fast, and made it look even more flash :)
But... 160x128 at 8 bits is 20480 bytes, and the Uno32 only has 16K :(
So bang goes that idea, unless I limit it to the bigger boards. I think a uC32 would do it though...
Wed, 04 Sep 2013 20:49:53 +0000
Wow! So the tweaked LIBs might take up lots of SRAM? Part of what I'm doing is so I can build a bitmap to output to the thermal printer (using 19k of RAM), so I might need to pass on the uber-nifty display for this...
DANGIT
Wed, 04 Sep 2013 20:56:58 +0000
Only the tweaked tweaked lib. The one up there with the bitmap support uses no more (maybe slightly less) than your version.
Thu, 05 Sep 2013 00:02:36 +0000
Hmmmm, I ran the DSPI code in my test bench and it worked... so I took apart the "doghouse" that holds the TFT, Proto shield, and uC32 and tried the Uno32. Didn't work.
Double checked I had the Uno32 board selected and chose the other COM port, too. :?
So then while I had it apart, I moved the power input connector for the uC32 from +12V to +9V. For this, I just moved the wire on the proto shield that feeds the power plug from the 12V for the easy driver stepper board to the printer's 9V. Put it all together, doesn't work. :oops: Tried SPI. Nope. Grrrr... The only thing I can imagine is that my front panel piece bends the TFT shield if over tightened. I removed the shield from the faceplate and it's now ok. :roll:
... redesigning the face plate so it can be printing tonight...
Picture is of my uC32 (rev A) and Uno32 (rev C): [attachment=0]uC32revA_Uno32revC.jpg[/attachment]
Thu, 05 Sep 2013 03:45:44 +0000
"Daily build" <--- if only you could hear the sarcasm dripping from my words as I mock myself saying that... :lol:
V1.03 has majenko's DSPI and GFX changes rolled in. I made a couple small edits to tft.ino (to include DSPI.h) and then also removed the blankPrint() functions from GFX since they appear to not be required on the chipKIT. That or when I ported the code, I did introduce the concept of background color, which was mostly commented out in the class previously.
Clearing the screen re-sets the text bg color to the color you're clearing the screen to. (Sounds like if the fgcolor matches the about-to-be-set background color, it shouldn't get set...)
Filename updated to denote the DSPI class, too.
:D
Thu, 05 Sep 2013 05:26:50 +0000
In case the kind reader is interested only in the most simple icons for menus, here is the ST7735_charmap code I wrote for the Arduino. Not even sure if it compiles on the chipKIT, though no reason it shouldn't...
As the font is originally derived from Adafruit's gld_font.c and then I moved it into GFX.h during the port to chipKIT, all of these symbols should come from the uint8_t font [] static definition. The font repeats every 0x100 chars, so char() input is %256.
There IS a picture of the output of that code, so you can get arrow keys' char() values without having to download and compile it:
Thu, 05 Sep 2013 06:36:24 +0000
Hi guys - I recently bought this shield and would love to get it working with my chipkit max32 board. I bought the board not knowing about the issues around AVR specific libraries and porting - safe to say I've been learning a lot in the last few days!
I've tried your libraries on the max32 without any success. After a bit of searching I realized this is probably because the max32 copies the layout of the Arduino Mega where the SPI is on pins 50-53. The Adafruit ST7735 library handles the Mega by using a software SPI rather than hardware SPI - I noticed you've simplified your library by removing this support.
I started trying to re-adapt your ST7735 library to do software SPI by comparing it with Adafruit's version... but I just don't know enough about this stuff to follow your changes i.e. when there's a change I don't know if it's to get rid of AVR code (and I can leave it) or if it's specific to the hardware SPI (and I need to add something).
Is there any chance of you updating your library to support the max32 board?
Thu, 05 Sep 2013 19:42:03 +0000
Before getting into bitbanging a software SPI... When I ran this on a Mega and wanted hardware SPI, there's a couple options...
Bend the 4 SPI pins outward and run jumpers to the Mega pins 50-53 (note the different order!) Optionally do this for pins 13,12,11 - pin 10 is actually optional, as the "SS" isn't tied ONLY to that pin and works everywhere.
Cut or de-solder those pins off the board and solder in jumper wires to the holes that are inset on the TFT shield. Yes, the chipKIT has double row there, so either squish them in underneath or run the wires on top of the shield. ... and route these to Mega's HW SPI.
Check out the picture. The "Mega" is a Seeedstudio AVR Mega, so the layout is a little different, but same concept. I believe normal Mega pins are back and forth, so form a square, and not in-line like these guys did. [attachment=0]tps_v2_icsp_800.jpg[/attachment]
Now, about that software SPI... yeah... I wasn't even able to get the DSPI working (majenko had to do that) ... is there any chance you can try the hardware solution? Hopefully, majenko can either implement software SPI in the DSPI-based library, or can give us/me hints how to go about it. The original Adafruit code wasn't too miserable to figure out what was HW vs SW spi. What really tipped me was the concept that the TFT uses SPI and also requires pin 8. Pin 8 is crucial to controlling the TFT and I was too focused on SPI thinking SPI was all there was to this shield.
Thu, 05 Sep 2013 20:09:49 +0000
For v1.04, I removed #define swap() from GFX, it was conflicting with the STL std::swap() function. So anyone needing STL libraries would need to make this change. BTW, it really made the linker mad and all the errors pointed deep in gcc 4.5.1. Thankfully, I saw swap() and remembered the macro so I could remove it.
As usual, I compiled this code and immediately zipped it up.
andrew_b : since I've got the libs open, I'll give a look-see about software SPI.
Thu, 05 Sep 2013 20:11:32 +0000
As long as you don't need anything fancy like setting of SPI modes, etc, there is a very handy command which basically gives you software SPI: shiftOut().
The TFT only receives SPI data, it never sends anything out, so the unidirectional software SPI of shiftOut() should be ideal.
Personally though I'd love to expand the DSPI library so you can create software SPI objects with it. That would rule.
Thu, 05 Sep 2013 20:37:03 +0000
Holy crap, it's slow using shiftOut() ;)
Thu, 05 Sep 2013 20:41:59 +0000
Oh, and this is my fancy framebuffered version (which I will release when I'm happy with it) doing fancy things: [url]http://www.youtube.com/watch?v=PLqe7xFhjWE[/url]
With shiftOut() some of those frames take a second or so to draw. The initial screen takes about 6 seconds to draw.
Thu, 05 Sep 2013 20:46:36 +0000
Well, I don't know about your Uno32 problems - I just plugged my TFT into my Uno32, installed the libs, and tft.ino, and it worked straight away. Maybe your Uno32 is faulty?
Oh!!! I missed this! OK, I'll update the text in the code, thanks!
I have been trying to make a buffered version of the library, but my Uno32 just can't cut it. I don't want to limit it to the bigger boards really.
Hmmm, this guy did a framebuffer for the Netduino, but it looks even bigger; maybe you could give that a read to see if there are any new ideas for you?
Thu, 05 Sep 2013 21:10:07 +0000
Hmmm... Cheers for that link. It's helped with the germ of an idea that was growing at the back of my mind. That is, "is it possible to blast out an entire frame in one transaction", and the answer, I am happy to say, is "Yes". I am now blasting out the entire frame each time you call update(), and it seems to be running at about the same speed regardless of the amount of data that needs refreshing (I was seeing slowdown before with increased data). Looks good.
Now... is there a way I can get it to do the transfer using DMA...?
Edit: No, I cannot. For the simple reason that it isn't a straight transfer, but a palette lookup. However, I can make it more efficient by pairing the pixels up into a single 32 bit SPI transfer. Only slightly more efficient, but still, every little helps.
Thu, 05 Sep 2013 21:16:50 +0000
As long as you don't need anything fancy like setting of SPI modes, etc, there is a very handy command which basically gives you software SPI: shiftOut(). The TFT only receives SPI data, it never sends anything out, so the unidirectional software SPI of shiftOut() should be ideal. Holy crap, it's slow using shiftOut() ;)
Yes, the bitbanging software SPI in Adafruit_ST7735 was pretty painful, especially if there are interrupts or anything else happening. I wasn't joking in the 1st post when I said you can watch pixels draw.
Personally though I'd love to expand the DSPI library so you can create software SPI objects with it. That would rule.
... well, for now, would you able to (please please) add shiftOut cheater software SPI to v1.04 for andrew_b? I opened up the files again and it's just too hot in my shop to start thinking that hard.
Personally, besides being lazy, I eliminated software SPI in the port to chipKIT because it was painfully slow on Arduino. In my head, I'll never, ever use this shield with soft SPI and instead do whatever it takes to run the wires (as shown in above post).
Muah!!! Just saw your reply, I'm very glad that helped!! :D I got his library working on my Netduino ... what really killed me was I couldn't figure out how to print()!! Then "using libraries" in MSVC is pretty painful, but not as bad as the issue which sank .NET for me: forever updating the target!
Thu, 05 Sep 2013 23:47:40 +0000
I shall add software SPI in the morning (I'm about to hit the sack).
How does alternate parameters to the constructor sound?
ST7735B tft = ST7735B(11, 13, ADA_CS, ADA_DC, 11, 13);
... for the SPI data and SPI clock respectively? Also, how does passing a DSPI object to the constructor for hardware SPI sound? It's something I do a lot of to allow you to select the SPI channel from the sketch:
DSPI1 spi;
ST7735B tft = ST7735B(&spi, ADA_CS, ADA_DC, 160, 128);
I'll add that too if you like as an option.
Fri, 06 Sep 2013 03:09:55 +0000
Also, how does passing a DSPI object to the constructor for hardware SPI sound? It's something I do a lot of to allow you to select the SPI channel from the sketch:
DSPI1 spi;
ST7735B tft = ST7735B(&spi, ADA_CS, ADA_DC, 160, 128);
I'll add that too if you like as an option.
That's a change I agree with completely, as it removes some of the magic from the background. Requiring the user to instantiate their own global DSPIx object seems more proper to me. :)
I shall add software SPI in the morning (I'm about to hit the sack). How does alternate parameters to the constructor sound?
ST7735B tft = ST7735B(11, 13, ADA_CS, ADA_DC, 11, 13);
... for the SPI data and SPI clock respectively?
So, it would be something like the Adafruit initialiSer?
ST7735(uint8_t CS, uint8_t DC, uint8_t SDO, uint8_t SCLK);
It can be just another constructor; no need for ST7735B (or is B how you keep the development work separate?).
BTW, I am still ignoring the RST in the constructor from Adafruit, as they only use it during initialization - and if it isn't needed to start the display (obviously) then why bother? Even if someone buys the breakout (and not the shield), there is no compelling reason to hook it up.
// for people using Ada TFT, joystick shield and hardware SPI
ST7735(&spi dspi, uint8_t CS, uint8_t DC);
// for people using Ada TFT, joystick shield and software SPI
ST7735(uint8_t CS, uint8_t DC, uint8_t SDO, uint8_t SCLK);
Also, the x and y are inherent to the hardware, so not really needed to declare... (and the setRotation() can move it around...?) I use (3) because it is good for my right handed employees and most peoples' usage seems to prefer landscape to allow more data display.
Thanks so much for helping! :mrgreen:
Fri, 06 Sep 2013 08:12:22 +0000
Interesting conversation ... and resulting link.
All of my mucking about with trying to get menus working led me to trying to research more about Open GL ES, for embedded systems, which led me to the above learning about Microchip's open Graphics Library. The diagram they have for it shows a "user interface" layer with buttons and touch screen?!?
And now it's time for me to go to sleep! :|
Fri, 06 Sep 2013 09:29:05 +0000
The "B" is my "buffered" version of the library.
I'll keep the constructor order the same as the Adafruit library I think - including moving the &spi pointer to the end of the constructor where the SDO and CLK settings are.
There are a number of routines I'd like to add to the GFX library at some point, including the ability to do anti-aliased line drawing (probably using Xiaolin Wu's algorithm).
Also, if I can squeeze enough memory out of the system I would like BOBs as well. (For those that don't have a history with the Amiga, a BOB is a Blitter OBject - a rapidly re-drawn sprite blasted into the framebuffer by the DMA controller), and even sprites (separate framebuffer objects overlaid on the main display framebuffer at refresh time).
Oh, and doing a rapid black/white switch test (while(1) { fill back, refresh, fill white, refresh}) appears to refresh the screen faster than the screen is physically capable of displaying it, and stripes / tearing occurs. It would be nice if they'd exposed the "tear effect line" (AKA vsync) from the ST chip...
Fri, 06 Sep 2013 10:25:12 +0000
Ok, here's the new version, v105, which has hardware SPI, selectable hardware SPI, and software SPI. There's also a new version of tft.ino that demonstrates them.
Can I suggest that you get this code into github so we can collaborate better?
Edit: I have made the internal methods of this ST7735 library protected, not private, so they can be referenced from a super class. My buffered version has been changed now to just inherit the ST7735 class and override some functions.
Fri, 06 Sep 2013 11:59:33 +0000
And here is my 256 colour buffered high speed library. Including kittens.
Fri, 06 Sep 2013 12:09:41 +0000
BTW, the indexed images were created in Gimp by exporting as "C Header Data" then tweaking the resultant files slightly (deleting the crap from the top, and changing the data types to be "const uint8_t ..." and renaming the variables.
Fri, 06 Sep 2013 18:05:06 +0000
And now I have sprites! And not just any sprites, but animated sprites!!!
There's a Sprites example there now with some classic sprites in it. Too many sprites does slow it down somewhat though.
Sat, 07 Sep 2013 01:51:52 +0000
Was busy actually working today, so almost no chipKIT fun at all! Attached majenko's update. I edited headers to reflect versions, etc, and I enum'd the joystick defines and also added the special character enum I created last night for arrows, etc.
majenko, will do, regarding github. I'll copy files to one of our servers that I've got github set up on, set up the repo and actually update and commit each version properly. My windows box absolutely refuses to play nice with github.
I didn't open up the buffered libs at all. Are they something we can add to ST7735 as more options to bungle in?
Thanks! Chris
Sat, 07 Sep 2013 06:35:18 +0000
Started and updating Git repo at: https://github.com/cacycleworks/chipKIT_ST7735
majenko: I think I added you...? :D I'm a little bit of a git newb.
OK, so I won't do any more casual off-Git development. So allegedly, a person should follow github for the most up-to-date and current working version. Note: I plan on sharing subtle improvements to tft.ino to help expand usability, so don't totally ignore the repo.
I'll announce on the 1st post that its ZIP will be updated with significant changes but probably not all of my various small tft.ino improvements.
Sat, 07 Sep 2013 10:22:50 +0000
Yup, you added me fine.
The way the buffered library works is that it inherits the non-buffered one as a base class. Any changes to the ST7735 class (other than functions overridden by the buffered class) will therefore be automatically available in the buffered class. It would be possible to combine the two, but it would be a bit of a mess, with the buffer and palette having to be dynamically allocated, which as it's such a large chunk of data may fail on chips with lower memory (32KiB minimum). By having it separate the memory can be allocated statically as part of the class object, and thus if it's going to fail it should refuse to link it in the first place. Much nicer :)
I'm just documenting the buffered library, then I'll add it to the repo.
Sat, 07 Sep 2013 13:59:35 +0000
I have a couple of 2.2" displays on their way to me - both the Adafruit one, and an unknown chinese one from eBay (Samsung S6D0164X01 chip).
As these both work very similar to the 1.8" display I am wondering if it would be sensible to convert this library into a generic TFT library that can handle lots of different displays.
The Chinese TFT screen I am getting has the full pinout available on a header, so you can use the nice fast 16-bit parallel interface, which I intend to wire up to the PMP of the PIC32 for blistering speed.
Sat, 07 Sep 2013 18:11:15 +0000
Adafruit has grappled with the lib / libs concept a bit. There is TFT, which perhaps GFX replaces? Then there are the sub libs for each "driver".
I can't help but feel like a LOT of the ST code could be moved to GFX. The thing with your 2.2" Hmmmm, looking at their ILI9340 driver they did do something of a rush job, as where they would call commandList(), it is commented out then there are the 150 writecommand()s & writedata()s.
I can imagine having GFX with SPI built in, which then gets used by ST7735 and ILI9340. They haven't gone through to see how to implement this... but even I can see there is too much cut-n-paste and not enough taking advantage of oop.
Major difference between ST7735 and ILI9340 are the tasks to init. But then I recall in ST7735.cpp that BLACKTAB being sprinkled around various functions...
void ST7735::drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height))
return;
setAddrWindow(x,y,x+1,y+1);
digitalWrite(_rs, HIGH);
digitalWrite(_cs, LOW);
if (tabcolor == INITR_BLACKTAB) color = swapcolor(color);
spiwrite16(color);
digitalWrite(_cs, HIGH);
}
And in ILI9340.cpp:
void Adafruit_ILI9340::drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;
setAddrWindow(x,y,x+1,y+1);
//digitalWrite(_dc, HIGH);
*dcport |= dcpinmask;
//digitalWrite(_cs, LOW);
*csport &= ~cspinmask;
spiwrite(color >> 8);
spiwrite(color);
*csport |= cspinmask;
//digitalWrite(_cs, HIGH);
}
(same code, only without the if(tab_color) and not yet de-AVRd)
Still thinking (please ignore the smoke...) you're getting the parallel 2.2" TFT... so you'll have a pair of SPI TFTs and also this other one. Is this it, too, then? They're counting on henningkarlsen's UTFT, which has never worked well for me. At least not as reliably as Adafruit's stuff.
So the GFX class can be inherited by ST / INI classes... the GFX can absorb the meat of the drawing functions pushColor(), drawPixel(), etc. And then the overloading ST/INI class can possibly just declare the communications type (as GFX already knows how to SPI). So perhaps we get rid of GFX::constructor() the literal function, moving that into GFX::GFX()?
Instantiating ST7735, the class itself tells GFX(128,160) as it is reasonable to assume the class would know. And ST7735's constructor(s) would call a common init() and also due to the sprinkling of if (tabcolor == INITR_BLACKTAB), have to overload where needed.
Does this make sense? I am repeatedly amazed by what you accomplish, so I hope I'm being of help. I feel like I am this guy telling you to come in and work Saturday (today). :roll: Oh, and I'm at work in the lab, so I guess it isn't total hypocrisy.
Sat, 07 Sep 2013 18:25:47 +0000
The way I see it there are at least three layers that are needed:
1 is ok as it is I think. I can't see communication being in GFX working well, as the communication is dependent on the device. How about a generic TFT device that goes between the actual device and the GFX library? That TFT device provides the communication facilities, such as writeCommand, pushColor, etc, and that layer can work in different ways (SPI, PMP Parallel, etc) depending on how it's initialized. Then the device specific library just has the device specific initialization routines, etc?
It's all a bit confusing as it's all interrelated.
And then you have the different ways of working, such as direct communication or framebuffered.
This is going to take some careful thought...
Edit: I think Polymorphism will be our friend here. Create different classes for different styles of the same thing and base them on the same base class to group them, then that base class can define an interface. That resultant class instance can then be passed to the constructor of another object for it to act on that interface.
Sat, 07 Sep 2013 19:10:05 +0000
I am toying with the idea of 3 base objects.
The TFT object which inherits GFX. This is the "master" object which controls everything else, and the one on which the user performs all operations.
The TFTCommunicator object class. This is inherited by all communications classes, so you have the TFTDSPI class, the TFTShiftOut class, the TFTPMP class, etc, which all must implement a standard interface.
The TFTDevice class, which defines the actual device and how to operate with it. Again, this defines a standard interface, so the ST7735 class would inherit that.
Then you pass 2 and 3, along with the dimensions of the display, to the constructor of the TFT class. Things like display rotation etc you could pass as parameters to the constructor of the TFTDevice class maybe? Those parameters aren't defined by the interface, so it can be down to each device's class how the constructor works.
Edit:
Thinking about it, instead of TFT inheriting GFX it would be better if TFT was GFX.
Also, now I come to think if it, the TFTCommunicator will have to be passed to the constructor of the TFTDevice, not to the TFT object.
Sat, 07 Sep 2013 20:03:15 +0000
There's so much "TFT".
What about LCD, VFT, and OLED? They'd use all the same functions, too?
As with my menuing paradox, part of the beauty of ST7735 as it exists and started was very easy user implementation. Plug in shield, make simple declaration and it just works. Of course, the comments in the demo code are about the extent of the user manual.
Yes, the comms, are definitely another object, class, etc, as at some level, the simple code to draw a pixel is going to need to choose to SPI, shiftout(), or do the parallel out. And I haven't even looked through how the buffered library works or what your demos do.
I'm currently tidying up TFTDemo.ino and trying to think about what I would have liked the most when I first got this shield and when I for started in chipKIT.
Sat, 07 Sep 2013 20:35:07 +0000
Funny you should mention LCD, etc. I have a GLCD class for 128x64 monochrome LCDs that would fit nicely into the model I am experimenting with.
I am undecided whether to have a separate TFT class, or just have the TFTDevice inherit it. That might make more sense, and be simpler to implement.
So it'd be something like:
DSPI0 spi;
TFTDSPI mycom(&spi, 10, 8);
ST7735 tft(&mycom, INITR_BLACKTAB);
tft.initializeDevice();
tft.fillScreen(0);
In fact, you might be able to pack it into one line:
ST7735 tft(new TFTDSPI(new DSPI0(), 10, 8), INITR_BLACKTAB);
Sat, 07 Sep 2013 23:00:42 +0000
Oh, and this is my fancy framebuffered version (which I will release when I'm happy with it) doing fancy things: [url]http://www.youtube.com/watch?v=PLqe7xFhjWE[/url]
Oh WOW!!! I never ever open youtube, that was worth it! Right out of 'Aliens'.
And now I have sprites! And not just any sprites, but animated sprites!!! There's a Sprites example there now with some classic sprites in it. Too many sprites does slow it down somewhat though.
LOVE IT!!! I didn't know sprites was a game!! That is fantastic. Really amazing this little screen and a chipKIT can do that!
I need to start reading through your code!
Oh, btw, I updated your sprites.ino as my down button wasn't reading with initial values. And about kittens.ino, it didn't compile because
#define ADA_CS IO11
#define ADA_DC IO12
... in my code, I changed it to
#define ADA_CS 10
#define ADA_DC 8
and it would compile ... but then the kittens got declared on DSP1 and not DSP0 ...? These changes I didn't edit on Github; wanted to make sure if you had specific reasons.
However, I think it is always best to make sure kittens work right away, as that is a powerful weapon for charming girlfriends with geek gadgetry!
Sat, 07 Sep 2013 23:14:48 +0000
Ah yes, those settings are for the specific board I am using - one of my home-grown ones that is still in beta at the moment. Change them to what works for a "normal" board ;)
Oh, and I have managed to get your TFTDemo.ino working with the first draft of my abstracted code. It needs some more improvements yet, and I need to write more comms drivers, but it's getting there. I'll add it to an Experimental folder on git shortly.
Sat, 07 Sep 2013 23:20:08 +0000
Funny you should mention LCD, etc. I have a GLCD class for 128x64 monochrome LCDs that would fit nicely into the model I am experimenting with. So it'd be something like:
DSPI0 spi;
TFTDSPI mycom(&spi, 10, 8);
ST7735 tft(&mycom, INITR_BLACKTAB);
tft.initializeDevice();
tft.fillScreen(0);
In fact, you might be able to pack it into one line:
ST7735 tft(new TFTDSPI(new DSPI0(), 10, 8), INITR_BLACKTAB);
... and then comes along the lazy `Murican who bundles the details in another constructor which just inits to the defaults... that's almost mean; allowing the shield to "just work" like that without having to even know what pins there are...
ST7735 tft( INITR_BLACKTAB );
tft.initializeDevice();
tft.fillScreen(0);
So I presume yours is also BLACKTAB? I have a red tab and a black tab. The red tab isn't that old (maybe 6 months?)
Sat, 07 Sep 2013 23:36:38 +0000
However, I think it is always best to make sure kittens work right away, as that is a powerful weapon for charming girlfriends with geek gadgetry!
Hell yeah! That's the whole point of it ;)
Sat, 07 Sep 2013 23:37:45 +0000
Changes made to kittens in Github! :mrgreen: Edit: heading home now... probably back on in a few hours.
Sat, 07 Sep 2013 23:39:41 +0000
Yes, mine is black tab. Though I would have thought it would be more sensible to have some other marker other than the colour of a tab that's on the protective cover you peel of and throw away.
On the LED boards I manufacture I have a little block of white silk screen where I place a blob of colour to indicate the LED combination that's on the board. They could have so easily done something similar.
Oh, and I have now created a TFTSoftSPI TFTCommunicator class for the experimental version that seems to work. I'll check in the experimental stuff for you now.
Sun, 08 Sep 2013 04:18:45 +0000
I've been going over the sprites code. How come one or two (usually two) end up with perfectly vertical or horizontal paths?
How does the movement work? I've been going over the code and moveBy() is a big part of it, but isn't the answer. Pulling out your dx and dy contributors to variables and then ensuring neither are 0 is not the solution. While I think that something going up and down would have dx of 0, detecting dx == 0 and changing it simply rails the sprite to one edge or the other.
My goal would be to make it so that a sprite never would stay horiz or vertical...
Girlfriend loved watching it and how eventually, you kill most of the sprites without actually doing anything. But then I notice the ones that are never on an angle...
Why?
Sun, 08 Sep 2013 09:08:35 +0000
When the sprite is created it decides on the DX / DY values using rand():
tft.setSprite(temp, 0, rand() % 7 - 3);
tft.setSprite(temp, 1, rand() % 7 - 3);
This DX / DY pair is stored in slots 0 and 1 of the sprite's internal memory area. The formula "rand() % 7 - 3" yields a number between -3 and +3, so -3, -2, -1, 0, 1, 2, 3.
That value pair is then used to move the sprite to a new location:
tft.moveBy(spr, tft.getSprite(spr, 0), tft.getSprite(spr, 1));
All that does is add that DX and DY value from the sprite's internal store to its current location. Obviously if DX is 0 then the X coordinate will never change, and it'll just go up and down.
You could modify the addSprite function so instead of it just using a simple random number it selected numbers from a list, or it repeated getting the random number if the number was 0.
You could abstract that into a little function:
int8_t randNoZero() {
int8_t v = 0;
while (v == 0) {
v = (rand() % 7) - 3;
}
return v;
}
then use the results of that function to set the DX and DY values:
tft.setSprite(temp, 0, randNoZero());
tft.setSprite(temp, 1, randNoZero());
Or that's the theory anyway ;)
Sun, 08 Sep 2013 10:46:27 +0000
Now... back to the "experimental" version...
I have just written a KS0108 driver and 8-bit parallel interface, and here's the results:
TFTPar8 chip1(GLCD_CS1, GLCD_DI, GLCD_CLK, GLCD_D0, GLCD_D1, GLCD_D2, GLCD_D3, GLCD_D4, GLCD_D5, GLCD_D6, GLCD_D7);
TFTPar8 chip2(GLCD_CS2, GLCD_DI, GLCD_CLK, GLCD_D0, GLCD_D1, GLCD_D2, GLCD_D3, GLCD_D4, GLCD_D5, GLCD_D6, GLCD_D7);
KS0108 tft(&chip1, &chip2);
The KS0108 displays have a number of individual chips, each one with its own CS line, and covers a 64x64 area. I have only seen 128x64 and 192x64 displays, but others may exist. Because of this I have kept the individual chips as separate entities, which helps keep the TFTPar8 interface as generic as possible. So the KS0108 is constructed with between 1 and 3 interfaces, which can quite happily overlap on pins as in this instance.
And here's how it looks: [attachment=0]ks0108.jpg[/attachment] :D
This is starting to look good... I am thinking I should rename TFT.h to Display.h maybe...
Edit: That's the last you'll see of that LCD... Somehow I just managed to toast it. Plugged the cables back in to my Uno32 exactly as they were before (they're soldered to 1x8 or 1x6 headers), and it didn't work. Played around a bit in the code trying to find out why, when I started to smell something. Reached over to the LCD and felt the heat coming off it. Looked closer, and the middle of the screen had turned purple. No idea what happened, but the Magic Smokeâ„¢ managed to break free from one of the CoB's on the back. Biatch! That's the second one of those I've killed now. Still, they were cheap.
Edit 2: I just bought a bigger one (192x64) on eBay from China to replace it for a tenner - cheaper even than the 128x64 I had originally.
Sun, 08 Sep 2013 14:50:25 +0000
You know, you really should get up earlier, you're missing all the good stuff.
Like fonts in the "Experimental" system: [attachment=0]fonts.jpg[/attachment]
tft.setFont(Fonts::Ubuntu20);
... etc - check Fonts.h for the list.
Tue, 10 Sep 2013 00:05:46 +0000
I've been having great fun with this TFT stuff. The "experimental" version is rapidly becoming a bit of a monster.
It now has:
Communication devices: TFTDSPI (Hardware SPI) TFTSoftSPI (Software SPI) TFTPar8 (8-bit parallel TFT interface) RawPar (1-bit to 32-bit raw parallel interface)
Physical devices: ST7735 (1.8" TFT) KS0108 (Monochrome graphical LCD) Matrix (up to 32x32 LED matrix)
Virtual devices: Framebuffer (All the sprites and framebuffer facilities now in a virtual device) Aggregator (Merge multiple displays into one big display)
Fonts: Default TFT font (6x5) Topaz (8x8 font from the Amiga) Sinclair (8x8 font from the good ol' Spectrum) Arial (8pt to 40pt in steps of 2) Arial Bold (8pt to 40pt in steps of 2) OldStandard (8pt to 40pt in steps of 2) Ubuntu (8pt to 44pt in steps of 2)
Extras: Colour library with 237 colour definitions in it.
But it does take a while to compile with it as there's so much in there.
Wed, 11 Sep 2013 07:16:58 +0000
I've been crushed with business stuff. BTW, I've got a great start on a struct menu framework! I'm so excited about it. A little too excited. BTW, great work on TFT Experimental!!
Wed, 11 Sep 2013 22:33:52 +0000
I updated ST7735/examples on github with simpleMenu. I finally got something working! It is C, translated to work in C++, and based on global typedef struct's in a way that supports submenus and callbacks.
I haven't yet figured out how I'm going to do action handlers where up/down can change things (meaning you stay trapped in this up/down until select or quit).
:D Chris
Wed, 11 Sep 2013 23:13:03 +0000
There must be something I'm missing:
simpleMenu.ino: In function 'void setup()':
Error at line 257 in file simpleMenu.ino:
'Wire' was not declared in this scope
simpleMenu.ino: In function 'void guiFooter()':
Error at line 301 in file simpleMenu.ino:
'date' was not declared in this scope
Error at line 301 in file simpleMenu.ino:
'getDate' was not declared in this scope
257 is easily fixed - but 301...?
Edit: deleted them and it compiles fine :P
Nice menu. Now to convert it into a class in the experimental system...
Menu myMainMenu(myMenuStruct);
myMainMenu.display(&tft);
...?
Now... how about menu navigation...? Maybe a MenuNavigator base class and a number of abstracted classes from it for things like analogue connected joystick, and individual buttons...
Thu, 12 Sep 2013 04:19:06 +0000
Whoops, sorry. The Wire stuff is for my RTC. Kinda like your DSPI1 ;)
Yeah. The array of SELECTIONs handles the navigation really well, so that's not my immediate classist problem.
The issues as I see with classes and OOP:
A big picture idea is:
mySketch class inherits from menuController class which either creates and instantiates tft or can otherwise access it.
More thoughts in no particular order:
I've got something which can accomplish this using the supplied menu code, but it's an ugly menu_edit() with a switch and a case for each different global variable. Eh, it gets the job done so far...
I'm still plugging away at the struct version of trying to implement my menu_edit() idea in a quasi-generic manner. When I come up with an example of the up/down, I'll update Github. :D
Thu, 12 Sep 2013 07:51:36 +0000
I updated the simpleMenu example in Github with two menu items that alter global variables in simpleMenu.ino. (and then I downloaded it and uploaded to my uC32) The menus which alter global variables are the top two, "Bank", where you can choose from 3 banks and Brightness, which is a phony float variable that increments from 0 to 1 via 0.1 steps to represent PWM or something.
These two cases resemble two program control variables I need to implement.
"Bank" is an int that represents enum'd option values to control the program's mode. The int itself is worthless to the user, so there's a corresponding global static char * label[] array to tell the user what each mode means. The best example for this sketch is to chose which bank you want to deal with.
"Brightness" is a float that is a totally fake float that varies from 0 to 1. In my exact case, it is a speed factor (probably a PWM) for the stepper motor. 0 means slow but lots of power. 1 means fast but low power. For me these variables match the stepper motor power against the item being tested; and the items tested have varying resistance force.
It is definitely NOT "oop" nor elegant, but it compiles and gets the job done, which is more than can be said of ANYthing else I found on the internet for chipKIT. ;) And hopefully, is enough to help majenko un-fu**, er, convert it to classes. :mrgreen:
Thu, 12 Sep 2013 08:03:04 +0000
BTW, I typically use branches for major mods. You can see them in the network graph on Github. And I have an answer about it on Stackoverflow.
Thu, 12 Sep 2013 09:23:11 +0000
I often use a fork then generate a pull request if it's not my repo, but as I have write access, yes branches are good.
What you need is polymorphism. That is what the whole of the experimental system is based around.
Basically, in OOP, when you create a new class by inheriting a base class the new class instances are type-compatible with the base class.
So you would have a MenuEntry class which describes the basic generic interface to a simple menu entry. You then create separate classes for each of the types of menu entry - integer, float, scale, toggle, sub-menu, etc, which all inherit the MenuEntry sub-class. You then build your menu structure up using those, and a wrapper Menu class to group them into a single menu unit.
You then also pass a pointer to the TFT object to that Menu class, which the Menu class can then use for displaying its results.
The MenuEntry class might have, for instance, functions for:
char * getText();
int getValue();
void leftAction();
void rightAction();
void selectAction();
etc.
The constructor might take different values depending on the child class's needs, such as an array of char * as a list of string entries to select from, or a lower and upper value, and a step value.
Incidentally, I'd steer away from dealing with float values - it just complicates matters somewhat - just stick to integers, but use bigger numbers, so instead of going from 0.0 to 1.0 in 0.1 steps, go from 0 to 10 in 1 steps, then divide it down in your program afterwards. That way there is just one getValue() function instead of different functions for different types.
So a menu, or submenu, could be constructed from an array of MenuItem objects, and the standard, but overloaded, functions would provide the facilities the Menu needs to render the display and operate on the entries within.
Then controlling it, you'd need a controller base class which defines maybe just one function - getDirection() or similar, which returns which direction you are moving the joystick, or which button you are pressing, etc, then you inherit that as the interface to a JoystickLadder object, or DirectionPad object, etc, which you can also pass to the Menu object for it to control everything.
Poymorphism really is the single most powerful thing in OOP. Take a look in the Experimental/TFT code and you'll see it all over the place.
Thu, 12 Sep 2013 18:28:39 +0000
PAL.h is making the TFT examples made compiler mad, so I commented that out and now it says:
TFTDemo.cpp: In function 'void loop()':
TFTDemo.cpp:138:15: error: 'UbuntuR' is not a member of 'Fonts'
majenko, your OOP guru-ness makes me feel like a dumb hick. :lol: I don't even know where to begin with all that stuff. When I worked up the current menuing stuff, I literally write a line of code, CTRL+R to see what breaks, add stuff, repeat. And I build up slowly from literally nothing.
Getting the void pointers sorted out required me going back through the OMLibraries on Github to sift through what he did in OMMenuMgr.cpp. Just above the magic, is this awesome comment:
// rationalize a way to display any sort of number as a char*, rationalize it buddy, rationalize it good...
Thu, 12 Sep 2013 19:00:41 +0000
LOL
PAL was me trying to get it to display things on a telly, but it didn't work. I since deleted it. It should already be gone from TFT.h.
UbuntuR has been renamed Ubuntu8 thru Ubuntu40 in steps of 2 - I've just updated the TFTDemo and pushed it to git.
I tend to sped a few hours just churning out code, and then I try and compile it and spend another half hour going through fixing all the errors ;)
My first forays into object oriented programming was writing MUDs in LPC back in the early 90's. Doing that, objects were literally just that - objects. Swords, helmets, orcs... Gave a great introduction to how object oriented programming works, with inheritance, etc.
The trick with polymorphism is to properly separate the interface from the implementation. The only functions that will be able to be called are those defined in the base class. Any that are specific to a child class can only be used within the child class, or by something that specifically references the child class, which you don't really want to do as the whole point of polymorphism is that you reference the base class.
Thu, 12 Sep 2013 20:22:12 +0000
I have just made the Experimental TFT library work on my new Arduino DUE board. Mostly it was just wrapping chipKIT specific stuff (like the DSPI code) in #ifdefs. I had to rename Matrix to LEDMatrix, as that was conflicting, and it won't work anyway at the moment as it relies on a timer, but the TFT itself works (with the delay(ms) put back in). [attachment=0]KittehOnDue.jpg[/attachment] Of course, due to the Arduino's somewhat inferior SPI facilities, you can only use software SPI - maybe if I add SPI device support to the library you could hack wires around the place to get hardware SPI, but as even the DUE only has one SPI channel that would be kind of annoying...
Fri, 13 Sep 2013 07:03:44 +0000
Whoops! ST7735 changed? Oh wait, I need to not have TFT in Libraries, as mpide went and found that ST7735.cpp. OOP is sneaky!
Fri, 13 Sep 2013 07:53:25 +0000
That's not OOP, that's MPIDE. I only really use MPIDE when programming MPIDE these days. I usually use UECIDE which is based on MPIDE but much better ;) It contains all the things I wanted to put in MPIDE but would have changed it beyond all recognition ;)
Sat, 14 Sep 2013 20:58:13 +0000
Hi, This works Great!!!
Thanks for all of the hard work.
Philip [attachment=0]P1000902.JPG[/attachment]
Sat, 14 Sep 2013 21:06:40 +0000
I'm still waiting for my 2.2" TFTs to arrive. Once they do I'll be adding support for them both, plus working on a PMP driver for ultra-high speed display updates.
But for now, here is the LEDMatrix driver from the experimental library... [attachment=0]matrix.jpg[/attachment]
Sun, 15 Sep 2013 07:17:12 +0000
Hi, This works Great!!! Thanks for all of the hard work. Philip
That's awesome!
Did you try out the TFT experimental library, too? You'll need to have either GFX+ST7735 OR TFT in mpide/Libraries -- not both at the same time.
:D Chris
Sun, 15 Sep 2013 21:48:59 +0000
I now have my 8x32 LED matrix strapped vertically to the side of my monitor, and it's showing 8 nice bar graphs of the read and write activity of the four hard drives in my computer. FLR TO THE MAX!!!
Mon, 16 Sep 2013 00:40:18 +0000
That's awesome! Did you try out the TFT experimental library, too? You'll need to have either GFX+ST7735 OR TFT in mpide/Libraries -- not both at the same time. :D Chris
No Chris, I haven't tried them yet, all four of the other demos work fine, kittens, space invaders, and the 2 menu programs. I haven't hook a pot up to run the menu or play the game either (i'll put a random number to similate me at the controls). I've tried 2 different 1.8 TFT screens from Ebay, the one with the 4 holes on the PCB and the other side edge connector.
I think what amazed me the most was the speed of the writing to the screen, awesome.
I've been usuing the Nokia LCD 5110 for my projects, thought about looking into spi direct writes for it, but it's a little out of my league to rewrite it.
right now my main project is a metal detector shield for the chipkit, hummm, maybe a color screen would be a nice upgrade..........
Philip
Mon, 16 Sep 2013 15:18:48 +0000
You thought the 1.8" screen was good - you should see the 2.2" screen!
I have just added support for it into the Experimental system - it's the ILI9340.
Not only is it that little bit bigger, but it's higher resolution too! They have 320x240 packed into this bad boy!!! [attachment=0]22inchtft.jpg[/attachment] It's so shiny, trying to get a photo of it without including myself was a bit tricky...
Tue, 17 Sep 2013 03:10:22 +0000
you sold me on it,
http://www.ebay.com/itm/200952295233#ht_3957wt_1004
that would be a go start for a home made peice of test equiptment
Philip
Tue, 17 Sep 2013 08:50:26 +0000
Nice one.
I see that has the "C" version of the chip. My Adafruit one has the "B" version - I don't know what differences, if any, there are. If it doesn't work straight off we may have to look into the data sheets and do some tweaking.
I have other 2.2" displays on order too, both low res and 320x240. These have the full parallel interface available though, so they will be able to do ultra-fast display updates (the 320x240 SPI interface is a bit sluggish - only a few frames per second).
I'll be writing a Parallel Master Port driver to run those with the ultimate in speed. 16 bits, hardware managed IO. Not sure how that would map to a chipKIT board's IO ports though, I'll have to go through all the required pins and find where they map to.
Also, a framebuffer for these high res ones uses a lot of memory. Even restricted to 256 colours like the Framebuffer pseudo-device is they use 76800 bytes of RAM, so you've no chance of doing the fancy stuff (sprites, fast refresh, etc) on anything less than a '695 or '795 chip.
I guess, once I have written the PMP driver, I could investigate attaching an off-chip block of SRAM as framebuffer backing store... That would be quite nice, though not quite as fast as the internal RAM. It might mean the ability to have a 16-bit framebuffer though. SPI SRAM could also be possible, but that would be even slower.
Tue, 17 Sep 2013 10:27:21 +0000
Pi$$ed off at not having a proper dev board (arduino layout) with a decent chip on it, i have just created myself a "Fubarduino". Through the magic of one of my protoshields, I have created an adapter for the Fubarino to give it a rudimentary Arduino Uno pinout. Lets me plug my nice TFT shields into the Fubarino instead of having to run wires around the place. [attachment=0]fubarduino.jpg[/attachment] I'm not 100% happy with the layout at the moment, I may change a few pins around (so the LED and button are completely separate from the Arduino footprint, and I have no UART at the moment), but Kitteh works on it, and that's the important thing ;)
Tue, 17 Sep 2013 16:08:39 +0000
ST7735 via SPI can be very fast, start video at 2:00 minute mark. Take a look here: http://www.youtube.com/watch?feature=player_detailpage&v=elHCGuDMlkg#t=127
I believe the SPI was done by DMA for max speed to transmit frame buffer.
Here's a chart showing how much impact DMA has on SPI transfers on maple:
1.125MHz, 0.85mbs (75%), 1.125mbs (100%) 2.259MHz, 1.35mbs (60%), 2.250mbs (100%) 4.500MHz, 1.80mbs (40%), 4.500mbs (100%) 9.000MHz, 2.20mbs (24%), 8.900mbs (99%) 18.00MHz, 2.20mbs (12%), 17.80mbs (99%) http://forums.leaflabs.com/topic.php?id=1075&page=2
I believe we will see the same kind of impact on our PIC32 microcontrollers if we enable DMA to do the work of transferring SPI data to ST7735 display. On the very bottom of the chart, DMA was 8x faster than non-DMA.
Tue, 17 Sep 2013 16:47:54 +0000
Quite possibly, and I did briefly think about it when initially working with the framebuffer system. However I almost immediately ruled it out as it can't do the palette lookups inside the DMA, and a 16 bit framebuffer would rule out the smaller chips. I might create a 16 bit framebuffer device for the 695 and 795 that is DMA driven. I have also been thinking about DMA block transfers in the palette version - have a pair of scanlines in 16 bit depth and fill one while transferring the other.
Edit: I have just been having a play, and it is going to be next to impossible to shoehorn DMA transfers into the system as it stands. The problem is the DSPI library. There is no clean way of the TFT system knowing which underlying SPI port is in use, so it can't set up the DMA transfer to the right stream. The logical place to put it would be in the DSPI library itself. In fact, the DSPI library could do with a little bit of work to make it more efficient and tidier. I could embed my own SPI library into the TFT system, but it really doesn't belong there, and I don't want to conflict with other SPI systems.
Tue, 17 Sep 2013 23:38:57 +0000
The ST7735 also accepts 12 bit palette (R-4, G-4, B-4), instead of the usual 16 bit palette. This means it takes 25% less memory, and likewise 25% less data needs to be transferred via SPI, should translate into 25% speed bonus. This works out to be 30KB of graphics frame memory, which is doable on my PIC32MX250f128 (32KB ram).
Wed, 18 Sep 2013 14:40:49 +0000
Except... You can't do 12-bit SPI, so you'd have to either do separate 8-bit transfers, which is slower than single 32-bit transfers, or pack the 12-bit values into a number of 32-bit clusters. That would be 8 pixels at once (96 bits) which would equate to 3 32-bit transfers.
Yes, it means you have a greater colour depth available, but is the extra overhead of compressing the data into 96 bits going to gain you much in the way of speed?
Thu, 19 Sep 2013 14:33:17 +0000
Create a 30KB array of int8 and then transmitting SPI as 8bit or 32bit would be straight forward. The only challenge is making a function to insert the 12bit data into the correct array index.
array[0]=[color=#00BF00]Red:4 Green:4[/color] array[1]=[color=#00BF00]Blue:4 [/color] [color=#0040BF]Red:4[/color] array[2]=[color=#0040BF]Green:4 Blue:4[/color] ... and then it repeats the same again array[3]=[color=#BF00FF]Red:4 Green:4[/color] array[4]=[color=#BF00FF]Blue:4[/color] ...
Sat, 28 Sep 2013 09:10:14 +0000
I have added support to the experimental TFT library for more TFT chips, fixed a couple of framebuffer bugs, expanded the comms class to make it bidirectional, created a PMP comms class, and added basic touchscreen support. I've been busy :)
Wed, 02 Oct 2013 19:32:26 +0000
Wow, it seems I've touched off a powderkeg! We went to a trade show and just got back... I'm completely worthless at the moment. :shock:
Wed, 02 Oct 2013 20:06:34 +0000
You're far from worthless!
The TFT library is rapidly becoming a bit of a beast. On MPIDE it takes an age to compile, mainly due to the quantity of fonts. I have combined all the fonts together on my test system, and it compiles a little faster, but it's bigger than the core...!
I want to create a replacement core at some point, and the TFT library will be the model for it: 100% object oriented, and namespaces to provide API compatibility with chipkit and Arduino.
Tue, 28 Jan 2014 06:59:21 +0000
majenko,
Ive been trying the tft library on github, I've been getting a few errors, framebuffer and math.h i think it was, i just can't figure it out, any ideas?
Philip
Tue, 28 Jan 2014 10:32:05 +0000
It's complex - very complex.
And made even more so by your forgetting to mention what the errors are, and provide code that causes the errors.
Wed, 29 Jan 2014 02:05:44 +0000
majenko,
here's the program i'm trying to run.
#include <DSPI.h>
#include <TFT.h>
// UNO hardware SPI pins
#define ADA_SCLK 13
#define ADA_MOSI 11
#define ADA_CS 10
#define ADA_DC 9
DSPI0 spi;
TFTDSPI mySpi(&spi, ADA_CS, ADA_DC);
ST7735 tft = ST7735(&mySpi, ST7735::BlackTab);
//enum COLORS { BLACK = 0x0000, BLUE = 0x001F, RED = 0xF800, ORANGE = 0xFA60, GREEN = 0x07E0,
// CYAN = 0x07FF, MAGENTA = 0xF81F, YELLOW = 0xFFE0, GRAY = 0xCCCC, WHITE = 0xFFFF };
void setup()
{
// tft.initializeDevice();
}
void loop()
{
}
the complier has an error in libraries\TFT\math.h:12: error 'uint32_7' does not name a type
it's a fresh mpide 20130715
Philip
Wed, 29 Jan 2014 07:33:46 +0000
Shouldn't that be uint32_t and not a 7?
Wed, 29 Jan 2014 09:40:12 +0000
It all works fine for me, in both UECIDE and MPIDE.
Make sure you have the absolute latest version of the library from github - you may have just coincided with a bad push, although there has never been a uint32_7 in Math.h
Wed, 29 Jan 2014 13:41:25 +0000
opps, it was a uint32_t and not a 7?
I'll down load the latest and give it a go, which mpide version are you usuing?
Philip
Wed, 29 Jan 2014 16:16:07 +0000
The github version.
Thu, 30 Jan 2014 01:13:00 +0000
i loaded the newest mpide 20140121 and the github
https://github.com/majenkotech/TFT
at work today, with the samples, still no luck with the examples. here's the errors when compiling "sprites"
[attachment=1]Capture.JPG[/attachment]
here's the error output from the code i posted a few posts back, I tried to keep it simple. [attachment=0]Capture2.JPG[/attachment]
Right now, I'm using the chipKIT_ST7735_master and all the same examples work fine. I've also have had good luck with the UTFT libraries too.
i was hoping to use the TFT library, from just looking at the code, it seems like the way to go
philip
Thu, 30 Jan 2014 01:51:15 +0000
Ah, that Sprites error is me not updating the examples when I have changed the API. Framebuffer data is now stored in a separate object (SRAM is the simplest one), which allows you to use other things besides the internal RAM of the chip for framebuffer storage )like SPI ram chips, for example - the SPIRAM class).
uint8_t buffer[ST7735::Height * ST7735::Width];
SRAM sram(buffer, ST7735::Height * ST7735::Width);
Framebuffer fb(ST7735::Height, ST7735::Width, &sram);
You create a buffer, link it to the SRAM class, then pass that as a parameter to the framebuffer. It adds an extra layer of complexity, but also a whole load more flexibility.
As for the other errors - I changed the update() function from taking a reference to taking a pointer, so they should be tft.update(&fb) now.
I have just pushed an updated version of Sprites to github.
Fri, 31 Jan 2014 00:46:46 +0000
hi Matt,
i downloaded the latest on github, still a math.h error
[attachment=0]Capture3.JPG[/attachment]
maybe someones else on the forum can run the software. that way i won't feel like i'm losing my marbles.
Thanks for the help
Philip
Fri, 31 Jan 2014 11:21:45 +0000
I'm guessing something on your version (which is getting a little long in the tooth - consider upgrading) isn't including stdint.h.
I've added it to TFT.h on github
... cut ...
#define RGB(r,g,b) ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
#include <algorithm>
#include <stdint.h>
// Base classes
#include <TFTCommunicator.h>
#include <DataStore.h>
#include <Color.h>
... cut ...
Try adding it to your copy (or downloading again) and see if it works.
Sun, 02 Feb 2014 17:07:46 +0000
i just downloaded the latest git hub version, i seen where you added stdint.h in the TFT.h file, with a clean install of mpide. still getting the math.h error.
is the math.h missing anything? is it the same one that's working for you, the same on github?
philip
Sun, 02 Feb 2014 17:57:55 +0000
The version on github is the version I use locally. Are you sure the version your MPIDE installation is using is the version you think it is? You don't have other copies anywhere?
Sun, 02 Feb 2014 18:39:11 +0000
i do have a few copies, on my "test copy"mpide, i have to change the sketchbook directory to avoid conflicts with my "working" sketch book directory.
as an experiment, i also loaded all of the "tft files" into the TFT/examples/TFTDEMO folder, opened the TFTdemo, had all of the tabs listed, same results.
tomarrow, at work i'll try it again, i run it off of a flash drive.
could the math.h in your tft directory be conflicting with the math.h located in hardware/pic32/complier/pic32-tools/pic32mx/include/c++/4.5.1/tr1 ?
philip
Sun, 02 Feb 2014 18:55:36 +0000
Hmmm... possible actually, if you are on an inferior operating system, I guess. The file is Math.h, not math.h, and a sensible operating system distinguishes between the two.
I'll try changing it and see if that helps.
Sun, 02 Feb 2014 19:16:34 +0000
that's kinda what i was thinking, what about removing the math.h and just put it in the calling program?
Sun, 02 Feb 2014 20:20:11 +0000
Could do. I have renamed it MathFuncs.h / .cpp though in GitHub. It's used in multiple places, and soon I'll get round to updating it with some new functions I have developed for another project that are about 90% more efficient.
Sun, 02 Feb 2014 20:48:43 +0000
Thanks, I'm looking forward to using your library, from what i see, it'll be very useful. I'll check it out
Philip
Tue, 04 Feb 2014 02:10:41 +0000
Matt, good news! it's working now, although the kittens still has a frame buffer error,
sprites, TFTdemo, simplemenu work,
[attachment=0]Thanks.JPG[/attachment]
Wed, 06 May 2015 10:35:15 +0000
Hello, I just picked up one of these shields w/ joystick, but I am having issues using it in either the most current MPIDE or the most recent UECIDE alpha. I just want to test if my shield works on a Chipkit Uno32 (unexpectedly's old one) but I keep getting strange errors like the below:
ST7735\ST7735.cpp.o:(.gnu.linkonce.r._ZTI6ST7735._ZTI6ST7735+0x8): undefined reference to `typeinfo for GFX'
collect2: ld returned 1 exit status
(this was taken from unexpectedly's initial thread of supposedly "working" examples on Adafruit) http://forums.adafruit.com/viewtopic.php?f=47&p=213748
Furthermore, all the ZIP archives downloaded from the thread here are corrupt: http://chipkit.net/forum/viewtopic.php?f=18&t=2530
I have tried using the following repositories in both UECIDE and MPIDE to no avail: errors present for each sketch are stated below:
https://github.com/cacycleworks/chipKIT_ST7735 IN MPIDE (current 20150318)
ST7735 - Simplemenu:
In file included from simpleMenu.cpp:13:0:
\mpide\libraries\GFX/GFX.h:15:21: fatal error: algorithm: No such file or directory
compilation terminated.
after editing GFX and comenting <algorithms> for the #define swap, and changing all std::swap to swap:
ST7735\ST7735.cpp.o:(.gnu.linkonce.r._ZTI6ST7735._ZTI6ST7735+0x8): undefined reference to `typeinfo for GFX'
collect2: ld returned 1 exit status
ST7735 - TFTDemo
ST7735\ST7735.cpp.o:(.gnu.linkonce.r._ZTI6ST7735._ZTI6ST7735+0x8): undefined reference to `typeinfo for GFX'
collect2: ld returned 1 exit status
ST7735B "buffered" - Kittens (before/after removing all "Kittens" but 1 to save RAM
Kittens.cpp.o: Link Error: Could not allocate section .bss.tft.tft, size = 21040 bytes, attributes = bss
Link Error: Could not allocate data memory
collect2: ld returned 1 exit status
ST7735B "buffered" - Sprites
Sprites.cpp.o: Link Error: Could not allocate section .bss.tft.tft, size = 21040 bytes, attributes = bss
Link Error: Could not allocate data memory
collect2: ld returned 1 exit status
IN UECIDE (current alpha Version 0.8.8alpha11)
ST7735 - TFTDemo (UECIDE complains a LOT about \ character in //commented lines \n like this)
Compiling...
• Compiling sketch...
Error at line 103 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:103:10: error: 'menu_edit' was not declared in this scope
Error at line 104 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:104:16: error: 'menu_edit' was not declared in this scope
Error at line 105 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:105:25: error: 'goto_menu' was not declared in this scope
Error at line 106 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:106:17: error: 'goto_menu' was not declared in this scope
Error at line 107 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:107:15: error: 'menu_hide' was not declared in this scope
Error at line 110 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:110:25: error: 'do_deposit' was not declared in this scope
Error at line 111 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:111:24: error: 'do_deposit' was not declared in this scope
Error at line 112 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:112:25: error: 'do_withdrawl' was not declared in this scope
Error at line 113 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:113:24: error: 'do_withdrawl' was not declared in this scope
Error at line 114 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:114:13: error: 'goto_menu' was not declared in this scope
Error at line 117 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:117:22: error: 'show_balance' was not declared in this scope
Error at line 118 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:118:21: error: 'show_balance' was not declared in this scope
Error at line 119 in file simpleMenu.ino
\UECIDE\libraries\ST7735\examples\simpleMenu\simpleMenu.ino:119:13: error: 'goto_menu' was not declared in this scope
Failed compiling sketch
ST7735B "buffered" - Kittens (before/after removing all "Kittens" but 1 to save RAM
Compiling...
• Compiling sketch...
• Compiling core...
‣ api
• Compiling libraries...
‣ ST7735B
‣ DSPI
‣ GFX
‣ ST7735
• Linking sketch...
\Temp\build-ed0a4c02-96c2-4f92-830b-b45d5c6d2508\Kittens.cpp.o: Link Error: Could not allocate section .bss.tft.tft, size = 21040 bytes, attributes = bss
Link Error: Could not allocate data memory
collect2: ld returned 1 exit status
Failed linking sketch
ST7735B "buffered" - Sprites
Compiling...
• Compiling sketch...
• Compiling core...
‣ api
• Compiling libraries...
‣ ST7735B
‣ DSPI
‣ GFX
‣ ST7735
• Linking sketch...
\Temp\build-d34f7ac2-e5f7-4a0f-bfa5-60a3799f4064\Sprites.cpp.o: Link Error: Could not allocate section .bss.tft.tft, size = 21040 bytes, attributes = bss
Link Error: Could not allocate data memory
collect2: ld returned 1 exit status
Failed linking sketch
I've also tried the repositories here and here: https://github.com/TFTLibraries/TFT <- No examples compile (in either MPIDE or UECIDE) IN MPIDE (current)
Simplemenu example
\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::windowData(uint16_t*, uint32_t)':
\mpide\libraries\TFT\PICadillo35t.cpp:349:16: error: 'DCH3CONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:359:9: error: 'DCH3INTbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:360:9: error: 'DCH3SSA' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:361:9: error: 'DCH3DSA' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:362:9: error: 'DCH3SSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:363:9: error: 'DCH3DSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:364:9: error: 'DCH3CSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:365:9: error: 'DCH3ECONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:369:9: error: 'DCH3CONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:372:9: error: 'DMACONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::closeWindow()':
\mpide\libraries\TFT\PICadillo35t.cpp:386:9: error: 'DCH3CONbits' was not declared in this scope
ST7735_Adafruit_1.8inch_TFT - Kittens (after including SPI.h manually)
Kittens.cpp:69:27: error: cannot declare variable 'fb' to be of abstract type 'Framebuffer332'
\mpide\libraries\TFT/Framebuffer332.h:6:43: note: because the following virtual functions are pure within 'Framebuffer332':
\mpide\libraries\TFT/Framebuffer.h:91:29: note: virtual void Framebuffer::bufferWrite(uint32_t, uint8_t)
Kittens.cpp: In function 'void setup()':
Kittens.cpp:78:5: error: 'class Framebuffer332' has no member named 'setAntiAlias'
Kittens (after removing the =0 after the virtual void inline bufferWrite)
Kittens.cpp: In function 'void setup()':
Kittens.cpp:78:5: error: 'class Framebuffer332' has no member named 'setAntiAlias'
Primitives (after adding SPI.h manually)
\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::windowData(uint16_t*, uint32_t)':
\mpide\libraries\TFT\PICadillo35t.cpp:349:16: error: 'DCH3CONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:359:9: error: 'DCH3INTbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:360:9: error: 'DCH3SSA' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:361:9: error: 'DCH3DSA' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:362:9: error: 'DCH3SSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:363:9: error: 'DCH3DSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:364:9: error: 'DCH3CSIZ' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:365:9: error: 'DCH3ECONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:369:9: error: 'DCH3CONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp:372:9: error: 'DMACONbits' was not declared in this scope
\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::closeWindow()':
\mpide\libraries\TFT\PICadillo35t.cpp:386:9: error: 'DCH3CONbits' was not declared in this scope
Sprites (after adding SPI.h manually)
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::windowData(uint16_t*, uint32_t)':
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:349:16: error: 'DCH3CONbits' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:359:9: error: 'DCH3INTbits' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:360:9: error: 'DCH3SSA' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:361:9: error: 'DCH3DSA' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:362:9: error: 'DCH3SSIZ' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:363:9: error: 'DCH3DSIZ' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:364:9: error: 'DCH3CSIZ' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:365:9: error: 'DCH3ECONbits' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:369:9: error: 'DCH3CONbits' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:372:9: error: 'DMACONbits' was not declared in this scope
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp: In member function 'virtual void PICadillo35t::closeWindow()':
C:\Users\HMkX2\Documents\mpide\libraries\TFT\PICadillo35t.cpp:386:9: error: 'DCH3CONbits' was not declared in this scope
https://github.com/MajenkoLibraries/DisplayCore/ <- HAS NO EXAMPLES TO TEST!
All in all, I am completely lost. No examples compile or work. Can anyone help dig me out of this mire?
Wed, 06 May 2015 12:39:05 +0000
I just tried DisplayCore and found I'd cocked that driver up a bit while converting from #define declarations to "static const" ones. I've just fixed it and pushed a new version to the repo, along with a small example.
Also, if you're in the alpha of UECIDE, you should just install it direct from the Plugin Manager. In fact, if you #include <ST7735.h> it should try and install it for you automatically.
Wed, 06 May 2015 14:11:04 +0000
Thank you a bunch, Majenko. :)
I can confirm the "Millis" sample compiles and runs on a Uno32 w/ "Adafruit 1.8" Color TFT Shield w/microSD and Joystick" (802) on UECIDE Version 0.8.8alpha11.
However, I noticed the "Slideshow" example for the BMPFile library had some issues compiling -- not merely "wrong TFT display #include used", but missing font #includes etc. As a bit of gratitude for your help, I've attached a working "Slideshow_ST7735" example you might possibly include for the Adafruit shield. The BMPs are resized and the proper #includes are provided. I tested it and it seems to work fine. I hope it helps you a little!
I initially thought it was because I was using the "0150 test" core version of MPIDE, so I had re-downloaded the "023 stable" core MPIDE and "stable" UECIDE and attempted using the "deprecated" version of TFT.h from Github. Unfortunately, those attempts only succeeded in wasting Digilent's and your bandwidth. :(
I should clarify I am using a Seedunio SPI 6-pin header shield with the "Mega hack" performed by unexpectedly in order to access/reroute the Hardware SPI pins from 6-pin header to the Adafruit shield pin 10-13. (unless ChipKit Uno32 Pins 10-13 are redundant copies of the 6-pin header -- unlike a AVR Mega, where they are independent)
Wed, 06 May 2015 14:42:26 +0000
Pins 11-13 are redundant copies, but 10 isn't.
Bandwidth is cheap (or free if you're using Github) so no worries there.
I have included your slideshow into the repo (thanks for that). The original one was written way-back when I started with the TFT library and obviously never got converted to the new framework when I ported the BMPFile library over. I've made some tweaks to it now.
Tue, 12 May 2015 09:08:40 +0000
Hi,
I'm trying to compile the chipKIT_ST7735 example downloaded from Github and I have this error: In file included from simpleMenu.cpp:13:0: C:\Users\NAME\Desktop\MPIDE.\hardware\pic32\libraries\GFX/GFX.h:15:21: fatal error: algorithm: No such file or directory compilation terminated.
This appear in this point: ... #endif
#include <algorithm> // #define swap(a, b) { int16_t t = a; a = b; b = t; } ...
Files I have copied to Libraries folder are: GFX -GFX.cpp -GFX.h -license.txt ST7735 -examples -simplemeu -TFTdemo -ST7735.cpp -ST7735.h
I have deleted all spaces and wrong characters from path.
I have not found any <algorithm> file or folder. Is there some mistake?
Thanks!
Tue, 11 Apr 2017 14:19:33 +0000
Two years later and I have run across the exact same issue with "algorithm.h' not found.
I'm guessing this project has been abandoned?
Thank you,
Ed
Tue, 11 Apr 2017 16:39:23 +0000
Yep. You want DisplayCore (http://displaycore.org).