Driving an AdaFruit ST7735 TFT display with a netduino
AdaFruit recently released a sweet little TFT display that I was dying to hook up to a netduino: the display features a resolution of 128*160 pixels, is capable of showing 18-bit colors and has a microSD card reader on the back of the breakout board. As usual, Limor wrote a nicely detailed Arduino tutorial showing how to connect the display and how to write sketches to drive it.
The Arduino driver relies on the ability of the Atmega168/368 to toggle digital lines extremely fast, which does not work well on the netduino due to the latency introduced by the .Net Micro Framework: even when configured to use hardware SPI, the Arduino driver constantly toggles a data/command output line, rspin below, which would be unbearably slow on the netduino if the same method were applied.
You can see an example of this in the drawPixel function:
void ST7735::drawPixel(uint8_t x, uint8_t y,uint16_t color) {
if ((x >= width) || (y >= height)) return;
setAddrWindow(x,y,x+1,y+1);
// setup for data
*portOutputRegister(rsport) |= rspin;
*portOutputRegister(csport) &= ~ cspin;
spiwrite(color >> 8);
spiwrite(color);
*portOutputRegister(csport) |= cspin;
}
Driving the ST7735 on the netduino
The netduino has one advantage over the Arduino: it has plenty of RAM. So, instead of toggling I/O lines slowly all the time and using next to zero RAM, the netduino driver allocates a 40K buffer corresponding to the resolution of the display in 12-bit depth colors (16 bits per pixel) and leaves the ST7735 in ‘data’ mode upon initialization.
Drawing always happens on the internal buffer first. Then, whenever the actual display needs refreshing, the display I/O operations are performed using hardware SPI, blasting the entire 40K buffer. It may sound crazy but using this method on the netduino is faster than refreshing a single pixel while toggling an I/O line!
Take a look at the following netduino code to see how this works, compared to the Arduino driver drawPixel funtion above:
private void Initialize() {
<...>
DataCommand.Write(Command);
Write((byte)LcdCommand.DISPON);
Thread.Sleep(50);
DataCommand.Write(Command);
Write((byte)LcdCommand.NORON); // normal display on
Thread.Sleep(10);
SetAddressWindow(0, 0, Width - 1, Height - 1);
DataCommand.Write(Data); } private void SetPixel(int x, int y, ushort color) { if ((x < 0) || (x >= Width) || (y < 0) || (y >= Height)) return; var index = ((y * Width) + x) * sizeof(ushort); SpiBuffer[index] = (byte) (color >> 8); SpiBuffer[++index] = (byte)(color); }
public void Refresh() {
Spi.Write(SpiBuffer);
}
The caveat is that allocating a 40K buffer on the netduino does not leave room for much object allocation afterwards. In fact, memory is so tight that OutOfMemory exceptions will be common if not careful and planning your application accordingly.
Before going any further, let’s look at the display in action on the netduino. Please forgive the quality of the video as the camera on my phone doesn’t do justice to the display.
Pretty zippy, huh?
Some hacking required…
As I was trying to get the ST7735 display to work with the netduino, I discovered that the display would stop communicating when configuring SPI to run @ 10 MHz or above.
This is a huge issue for a couple reasons: the speed of the SPI bus determines the display frame rate on the netduino and secondly, mounting a microSD card on the netduino configures SPI to run @ 10 MHz minimum. No matter what. This meant that I could either use the display or the microSD card but never together, preventing scenarios such as loading pictures from the microSD card for display for instance.
Based on previous experience, I began suspecting that the CD4050B logic level-shifter on the ST7735 breakout board was the root cause of the issue and decided to take it out of the equation altogether by shorting out its pins so that it would no longer convert signals. Since the netduino works at a 3.3v logic level, there was no risk of damaging the display driver.
Sure enough, eliminating the logic-level shifter allowed me to initialize SPI @ 40MHz on the netduino as well as using the the microSD card reader without any issues.
Connecting the AdaFruit ST7735 display to the netduino
- netduino GND -> ST7735 GND
- netduino Vcc (3.3v or 5.0v) -> ST7735 Vcc
- netduino D8 (or other Dx pin) -> ST7735 RESET
- netduino D7 (or other Dx pin) -> ST7735 D/C
- netduino D10 -> ST7735 CARD_CS
- netduino D9 (or other Dx pin) ->ST7735 TFT_CS
- netduino D11 -> ST7735 MOSI
- netduino D13 -> ST7735 SCK
- netduino D12 -> ST7735 MISO
Note that the ST7735 LITE pin can be connected to netduino Vcc (3.3v or 5.0v) or any of the netduino’s PWM pins if you’d like to control the intensity of the display’s back light.
As always, you can find the code as part of the netduino.helpers library.
Conclusion
The AdaFruit ST7735 TFT is a fast, bright and convenient all-in-one display and microSD reader:
Using it on the netduino is sub-optimal for the time being due to the memory requirements involved in making the display work at a decent frame rate but, if used carefully, it is possible to achieve very cool results with it right now. Keep in mind that the memory requirement will eventually go away once the netduino starts supporting native ARM code with .Net Micro Framework 4.2, letting applications toggle I/O lines super fast.
Finally, my only wish is for AdaFruit Industries to consider producing versions of their products without built-in logic level-shifters as they can negatively impact some micro controllers such as the netduino.
Happy hacking!
Color Flow image courtesy of Bartelme Design
Update on 08/23/2011: here’s the same hack applied to the stand-alone version on the adafruit microSD card reader, this time with a 10K resistor between Vcc and Gnd to ensure that the electrical behavior of the chip remains stable.







.
.
.
thanks for noting that the cd4050 is poor at high frequencies, we’ll look for a better 4050 that can handle higher speeds. the poor arduino maxes out at 4 mhz
in the meantime, shorting the pins is a pretty good solution.
Fabien: the demo is amazing. I will try to do it with another TFT module as well.
1- I wonder why they chosen an “ancient” CMOS chip, when it’s clear that there’s need for MHz frequencies.
2- The shorts across the chip are not a good choice electrically speaking, because they places the chip in a unstable state, with high current consumption. However, I agree that it would be the easiest workaround.
3- Funny (or sad) when you say “Netduino has plenty of RAM”…whaaa!…I own a Netduino Plus and the few RAM available is the major problem!
Thank you very much indeed.
Cheers
> 2- The shorts across the chip are not a good choice electrically speaking, because they places the chip in a unstable state, with high current consumption.
While you’ve got the iron out, why not just remove that chip completely?
Removing the chip was not needed to get me unblocked at the time
Thanks Mario
Shorting out the pins is just a hack to get me unblocked while I was figuring things out.
I should have soldered a large resistor (10M ohm) between Vcc and GND on the 4050 to deal with the electrical issue you pointed out.
Cheers,
-Fabien.
rather than the 10M resistor between Vcc and GND, what about just lifting these 2 pins off the board so they are not connected. the chip then should draw no power at all with signals simply traveling accross the solder bridges. this does seem to work with no issues. (tested before and after the mod with graphicstest-highspeed.pde on the arduino, will hook it to beagle bone soon as a display.
Eric
There’s always more than one way of doing things
in our defense…now that we examined the datasheet, it says max clk freq is 15MHz and thats assuming there’s no slewing issues (which you almost certainly will have with a breadboard and long wires)
Hello.
What do you think about using the “2.8″ TFT Touch Shield for Arduino” ( http://www.adafruit.com/products/376 http://www.ladyada.net/products/tfttouchshield/ ) with Netduino? Looks like it has built-in RAM buffer, so the memory shouldn’t be an issue.
I only started using my Netduino+ and I can’t asses the possibility/complexity of using this shield with Netduino.
What are your thoughts?
Hi there,
I have started working on a driver and a hardware interface for 8 bit parallel addressing this touch screen. So, stay tuned
Cheers,
-Fabien.
Hello,
I’m looking around for things I can add to my “SpyCar”. I want to make a car with a camera, microphone and bleeper. Since this will be a remote control car, I have two Netduinos. Would you recommend this screen and the camera described in your most recent post communicating to each other using XBees?
Hey Niels,
Are you looking for a ‘live’ video feed from the spy car or are you interested in taking still snapshots on command? Knowing your requirements would help me better answer your questions.
Cheers,
-Fabien.
Hi Fabien,
Sorry for my late answer. I would like to have a live video feed. The situation I would like most would be being able to store the video as well, but I’ll skip that for now, displaying a live feed is enough.
Hi Niels,
Given your live video requirement, I believe that the best approach is for you to use the NTSC output of the VC0706 camera, connected to a small video transmitter which would allow you to receive the live feed on a standard TV tuner. Taking snapshots, even at a small resolution will be too slow for live video. In addition, you’re have to find a way to decompress the JPEG images before displaying them on the ST7735 TFT screen, which will be very slow.
I hope this helps.
Cheers,
-Fabien.
Hi Fabien,
Thank you for your reply, but I have one question still unanswered: can I use XBees for sending the data back and forth (instead of a video transmitter, as you mentioned)?
Forget my last post, would you recommend me this transmitter? http://www.rangevideo.com/index.php?main_page=product_info&products_id=67 combined with this receiver: http://www.rangevideo.com/index.php?main_page=product_info&cPath=35_36&products_id=232
Hello, here I’m again. Do I need the drivers from the netduino.helpers library for the camera and the screen? Or can I just connect the camera somehow to the transmitter and the screen in a way to the receiver?
The netduino helpers library will make it easy for you to initialize the camera and turn the NTSC signal ON / OFF. However, there are other ways to achieve the same results. You may even be able to configure the camera to always output its NTSC signal through a one-time configuration using the VC0706 CommTool software. However, I have never tried doing it this way.
Yes, it looks like this transmitter / receiver pair will do the trick. Just make sure that the transmitter will accept an NTSC video composite signal before you buy.
Also, both of these require a 12v power supply so you’ll need to do a conversion to get regulated 5v DC to power the camera and the micro controller.
Yes, you could you XBee transceivers as a replacement for a serial TTL cable to communicate with the camera and receive snapshot data.
Hello Fabien,
I want to thank you for your great help! Now I can buy the parts that are needed.
Hi Fabian,
thanks for your post. Would you know when the .net MF 4.2 arrives and how you would utilize with the TFT without the hack?
Hi Michiel, The .NET MF should ship early this Fall from what I hear. About the ST7735 display: it can be used w/o the hack if you don’t need to access the microSD card on the display and you configure SPI to communicate below 10Mhz. I definitely plan on experimenting with .NET MF 4.2 and this display and will report my findings here. Stay tuned
Hello Fabien,
With the release of the .Net Micro Framework 4.2 are you going to be re-visiting this code to take account of the fast IO switching. I have already ported your lib to VB and created a DLL. Some examples drawing text would be very usefull too. Great work so far though. I have been coding in .net for about 15 years but am just moving into the Micro framwork.
Cheers Pete.
Hi Pete,
Unfortunately, native ARM code support is not (yet) part of the .Net MF 4.2, so re-visiting this driver is moot at this point. About the text drawing: you should dig into the netduino helpers samples to see how the marquee drawing sample is implemented in the AdaFruit LPD8806 LED strip driver.
Cheers,
-Fabien.
Hi Fabien-
Thanks very much for publishing your findings about the slow level shifter on the Adafruit 1.8″ TFT module. Not surprisingly, the issue also affects the Adafruit module when used with the Raspberry Pi. Readers may be interested to know that the (almost) identical module available from SainSmart doesn’t have this problem. I can drive the SainSmart 1.8″ TFT module just fine with 32 MHz SPI (8x faster than I can reliably drive the Adafruit module): http://www.whence.com/rpi/
You’re welcome. Great work on that fb driver, btw
Cheers,
-Fabien.
Hi Fabien,
Nice Article! I tried converting your driver code to .net micro 4.2. I had to change the ExtendedSpiConfiguration to SPI.Configuration because the BitsPerTransfer field was throwing an exception. Now, I get some communication with the display, but not correctly. There is a row of random colored pixels along two edges and all the drawing functions are distorted. Just curious if you have any ideas offhand on where to look for the problem?
Thanks!
John
Hi John,
Thanks
Yes, changing ExtendedSpiConfiguration to SPI.Configuration is fine.
‘ExtendedSpiConfiguration’ used to be needed to configure variable bit SPI communication back in .Net MF 4.1.x and it may have been deprecated since then. It’s been a while since I touched the .Net MF and I have not kept up with the latest changes, so I don’t know for sure. About the “random colored pixels along two edges and all the drawing functions are distorted”: the two issues are likely caused by the same root cause which is the initialization of the IC driver which specifies the number of rows and columns. There may have been hardware changes since I wrote this driver, and it’s possible that the initialization sequence needs tweaking.
I hope this is helpful to you.
Best regards,
-Fabien.
Thanks Fabien,
Well, I got it working, but I am not sure I figured out why yet!! The following edits made it work perfectly (each commented out line is replaced by the one following it). I just removed the offsets and everything works perfectly now! I haven’t figured out what these offsets were for yet (do you recall?).
Also, one other question if you don’t mind. What do you use to create the *.BIN binary images?
Thanks again!
John
private void SetAddressWindow(byte x0, byte y0, byte x1, byte y1)
{
DataCommand.Write(Command);
Write((byte)LcdCommand.CASET); // column addr set
DataCommand.Write(Data);
Write(0×00);
// Write((byte) (x0 + 2)); // XSTART
Write((byte) (x0 )); // XSTART
Write(0×00);
// Write((byte) (x1 + 2)); // XEND
Write((byte) (x1)); // XEND
DataCommand.Write(Command);
Write((byte)LcdCommand.RASET); // row addr set
DataCommand.Write(Data);
Write(0×00);
// Write((byte) (y0 + 1)); // YSTART
Write((byte) (y0)); // YSTART
Write(0×00);
// Write((byte) (y1 + 1)); // YEND
Write((byte) (y1)); // YEND
DataCommand.Write(Command);
Write((byte)LcdCommand.RAMWR); // write to RAM
}
Hi John,
On CodePlex, check out http://netduinohelpers.codeplex.com/SourceControl/changeset/view/e0ddb8758a93#Tools/VideoStripBmp/VideoStripBmp/Program.cs and http://netduinohelpers.codeplex.com/SourceControl/changeset/view/e0ddb8758a93#Tools/24BitBmpToBigEndianBinary/24BitBmpToBigEndianBinary/Program.cs
Both projects will produce bitmaps in the proper format.
I hope this helps.
Cheers,
-Fabien.
Oh, Great! That’s just what I needed. I notice that GIMP can write RGB565 but I think it makes a header that I would have to strip out. This is more convenient! Out of curiousity do you do this stuff (Netduino/.NETMF/etc.) as a hobby, job or both?
Thanks once again,
John
Hi John,
It started out as a hobby in 2007 and as a way to teach myself electronics. It turned into a full time job in December of 2011. If you wander to http://nwazet.com you can see what I’ve been up to since then
Cheers,
-Fabien.
That’s impressive Fabien! I am an IC designer (analog) by day and learning netduino/etc out of curiosity and to do a few side projects. I was interested to see how well managed code could work in an embedded app (my previous experience had been with TI DSPs/PIC/MSP430 so this was a new concept to me). So far my experience has been mixed. For slow, complicated things it is great and a big time saver. However, I often find it is hard to get the timing I need (even in human interface stuff) and of course if I need to toggle digital outputs directly the performance is dismal (a task so easy to accomplish on even the cheapest traditional micro). I assume as processor power continues to increase (and cost reduce) the managed route will eventually find a way to take over much of the embedded space. I am curious of your opinion of this given your expertise and insight in this area.
Do you think the majority of embedded apps will eventually run managed code?
What areas of commercial applications currently use managed code?
How is .NETMF perceived in the field? Has it gained serious traction in commercial applications or is it more of a hobbyist thing at this time?
What does Microsoft hope to get out of providing the .NETMF (i.e. what is in it for them — how do they plan to make money from it? I assume it is license free even in commercial apps now?)
Thanks if you have the time to comment at all!
John
Thanks
I agree with your assessment: managed code is great when latency doesn’t matter. It’s great to use for ‘command and control’ applications and rapid prototyping. It does save a huge amount of development time in these spaces.
Outside of that, you need something else for building real time applications or the ability to mix C and managed code. Unfortunately, that’s not always possible with all .Net MF ports, Netduino being one of them, sadly. You have to go to GHI for that and even then, it comes with caveats.
About the perception of the .Net MF in the field of embedded apps: much like ‘Rodney Dangerfield’, it doesn’t get any respect
Being born at MSFT has had a great deal to do with it in the first place: it’s a powerful stigma to overcome in a world where OSS has dominated tool chains and platforms for a long time. Whether this is deserved is an entirely different story.
I don’t believe that MSFT has any intentions of investing in .Net in the future, let alone the .Net MF. MSFT has very little to gain from either technologies anymore.
The .Net MF is indeed license free, whether for fun or for profit
Cheers,
-Fabien.
PS: I only speak for myself, based on my personal experiences.