Getting the TFT display (ILI9340) running on Raspberry Pi

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

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
dylanryan
 
Posts: 4
Joined: Sun Jan 12, 2014 2:53 pm

Getting the TFT display (ILI9340) running on Raspberry Pi

Post by dylanryan »

I recently purchased the 2.2" TFT LCD display (http://www.adafruit.com/products/1480), and am trying to get it working on my Raspberry Pi.

I've gone over the Adafruit arduino code and datasheets, and have done my best to port it, but I am not having any luck. Specifically, I can apparently communicate with it just fine over SPI, and one of the commands in the Begin function toggles the backlight brighter which is promising (though I didn't see the command documented in the datasheet), but it doesn't appear as if any other commands actually work: I get responses of 0 in every place I expect to get data back, and other than being able to make the backlight brighter, I can't get the display to respond at all.

Curious if anyone has successfully gotten this working on a Pi (ideally in C or C++, but if it is in python or something, I can probably adapt it). I've seen other topics where the suggested course of action is to simply google it. I've tried, I can't find any specific posts of this working on the Pi.

EDIT: I was able to port the bit-banged software-spi version of the Arduino library to the Pi successfully, and it works. It's actually a hair faster than the hardware-SPI code running on my arduino for most operations, too, but it really monopolizes the CPU to do all that bit-banging. Next step: try to get hardware SPI (with just a small amount of manual fiddling on the D/C line) working with this code on the Pi. If I can manage that, I'll post my code for future readers. The important thing I needed to do to get the bit-banged version work was to delay between changing the Pi's GPIO pins. I wound up delaying by using for(volatile int i=0;i<5;++i);. Going any lower than 5 and it was unreliable, but burning that many cycles seems to be just sufficient. nanosleep is way too slow (likely due to the minimum sleep time). Everything else was fairly straightforward.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by adafruit_support_mike »

You may be running into timing problems.

Linux is a time-sliced operating system, so any process you have will get suspended after about 10ms of runtime and other processes will get swapped in. To avoid that, you need a kernel driver (which isn't subject to process switching).

I don't know if that is the problem, but it's something to check.

User avatar
dylanryan
 
Posts: 4
Joined: Sun Jan 12, 2014 2:53 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by dylanryan »

Well, it actually is working surprisingly well with the bit-banged version (CPU usage aside), I just needed to delay because it seems the Pi's GPIO pins don't update instantly (the method to change them requires memory-mapping a part of kernel memory and setting bits in it, so maybe kernel needs to see that to notice changes and send them out to the actual hardware, I don't know). I'm getting a full screen update in about half a second, which tells me that I'm running at around 2.5 million bits (=GPIO toggles) per second.

My main problem at this point is that I haven't been able to get any "real" hardware SPI implementation working (I am working with SPIdev at the moment), because the LCD driver seems to have some strange/nonstandard requirements. The arduino code, for example, seems to manually toggle the clock line once between bytes even when using hardware-SPI. Also, as far as I can tell, SPIdev is at least somewhat asynchronous: It writes to a buffer, returns, and then the kernel sends the buffer over the wire in the background. But this LCD requires toggling the D/C line at specific points (between command and data bytes), etc. Maybe I need a different SPI library that is fully synchronous.

EDIT: Ok, well, I got hardware SPI working (more or less) on the Pi. I actually prefer to write my own code anyway, so I'm not going to port the libraries you provided (I'll write my own for my project. It's actually easier for me). But, for other people having trouble making this work on their RPi. I used SPIdev, but instead of using the full duplex ioctl method of sending data (which everyone seems to suggest), I found that the half-duplex read and write calls actually worked. So, I basically made a function to talk to this that does the following:
* sets the D/C pin low
* for(volatile int i=0;i<5;++i) ; // super tiny delay
* writes the command byte to the SPI bus
* sets the DC pin high
* for(volatile int i=0;i<5;++i) ; // super tiny delay
* Writes any parameters you want
* Reads any return values you want

Then I can just call it with the command, how many bytes of arguments I want to write, and how many bytes of responses I want.

I'm not 100% sure if the reading part actually works (I get garbage data when I get device ID, for example), but *writing* appears to work, and I can change pixels and such which is all I really want.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by adafruit_support_mike »

Glad to hear you have a version that does what you want. Nice work!

User avatar
dylanryan
 
Posts: 4
Joined: Sun Jan 12, 2014 2:53 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by dylanryan »

Thanks. Mostly just took really examining the arduino code to figure out exactly what it was doing. I'll try to excise enough of my code from my project to make a standalone demo and post it here in the next couple days (though it won't be anywhere near as full-featured as the Adafruit library: I only needed lines, rectangles, and text, so I didn't bother with circles or anything like that) for anyone else who wants to get this working on a Pi. At least it'd be a start for others to work from.

I don't know if you actually have any control over the descriptions on the main site, but it might be nice to update the description of this module to note that although to communicates over 4-wire SPI, there is a required 5th wire that must be toggled between sending commands and data (plus a reset wire which is probably optional, but I am using it, driving the total to 6 wires), so that you cannot directly use any SPI libraries without manually toggling some pins in the middle of what would otherwise be a single transmission. It didn't take me long to figure it out, but since the description said it just needed SPI, I assumed that it would be much easier to adapt.

EDIT: Something else to help future Ras-Pi users: spidev on the pi has a 4kb buffer. If you try to send a single SPI burst larger than that, it'll error out on you. There are various ways to increase that buffer (google is your friend), but I don't really want it to have a huge buffer, so in my case, I just sent data in 4KB chunks if I needed to send more. Seems to work (I didn't re-send the command bytes). Since filling the whole screen takes a hair over 150KB of data transfer (specifically, 150KB for the pixels, plus 11 bytes to set up the rectangles and tell it to start a fill), that means 38 "chunks", but that seems somewhat preferable to having spidev hold onto a 151 kb buffer all the time since I mostly don't need to fill the whole screen at once anyway.
Last edited by dylanryan on Wed Jan 22, 2014 10:16 pm, edited 1 time in total.

edfield
 
Posts: 3
Joined: Wed Nov 07, 2012 3:00 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by edfield »

Hi, I just came across your post, and you might be able to save me some time! I'm trying to get this display running directly from hardware (an FPGA), and I was starting to decipher a minimum set of commands that would be necessary to get it running. All I want to be able to do is set/clear indivdual pixels, using their X-Y address. I wondered if you might be able to tell me what would be necessary from power-up to achieve this.

Thanks!

User avatar
dylanryan
 
Posts: 4
Joined: Sun Jan 12, 2014 2:53 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by dylanryan »

Sure.

First, to send a byte, you set the MOSI pin to the most significant bit in your byte, then toggle CLK high and then low, then set MOSI to the next bit in your byte, toggle CLK high and then low, and so on for all 8 bits (bit-banging it out this way is kinda painful, but if you don't have dedicated SPI code to do it for you, that's all you can do).

To send a command, you set the DC pin high, send the command byte, set the DC pin low, and then send any data (arguments) for that command.

When sending multi-byte values, send the bytes most-significant-byte first, as well.

Colors are 16 bits, 5-6-5 format (so rrrrrggg gggbbbbb)

Pixels are addressed with 0,0 at the "top-left corner", with x growing to the right and y growing down. Top-let depends on orientation


Now, one thing to note: Setting individual pixels, although workable, is going to be somewhat slow. This is because it requires sending 10 bytes to define a rectangle to fill (regardless of the size of the rectangle), and then 1 byte plus 2 bytes per pixel (the color) to fill it. So, to fill a pixel, you make a 1x1 rectangle and fill it, which takes a total of 13 bytes per pixel, which is somewhat absurd. But if you filled a 20 pixel region instead, it'd take the same constant 10 bytes to define the larger rectangle, and then 41 bytes to set the data, for a total of 51 bytes, or 2.55 bytes per pixel. So, the more pixels you fill at a time, the fewer bytes per pixel it takes (though since colors are 2 bytes, the asymptote is 2 bytes per pixel, obviously).


So, the specific commands to fill a rectangle are as follows:
let x0 be the x coordinate of the left edge of the rectangle, and x1 be the x coordinate of the right edge. Let y0 and y1 be the corresponding top and bottom edges of the rectangle. All these are 16-bit quantities.
send the command 0x2A with arguments (x0,x1) // defines the x limits of the rectangle
send the command 0x2B with arguments (y0,y1) // defines the y limits of the rectangle
send the command 0x2C with arguments (all your pixels, in left-to-right, top-to-bottom order).

So, to set the single pixel at x=20,y=30 to red, you would do the following (where the first byte is the command, and the other bytes are data/arguments)

Code: Select all

sendCommand(0x2A, 0x00,0x14,0x00,0x14)
sendCommand(0x2B, 0x00,0x1E,0x00,0x1E)
sendCommand(0x2C, 0xF8,0x00)

Now, the adafruit arduino library has the following initialization code. I haven't tried to figure out which parts of it are and are not needed yet, so I am just doing this all at power up:

Code: Select all

sendCommand(0x3F, 0x03,0x80,0x02) // I have no idea what this does
sendCommand(0xCF, 0x00,0xC1,0x30) // I have no idea what this does
sendCommand(0xED, 0x64,0x03,0x12,0x81) // I have no idea what this does
sendCommand(0xE8, 0x85,0x00,0x78) // I have no idea what this does
sendCommand(0xCB, 0x39,0x2C,0x00,0x34,0x02) // I have no idea what this does
sendCommand(0xF7, 0x20) // I have no idea what this does
sendCommand(0xEA, 0x00,0x00) // I have no idea what this does

sendCommand(0xC0, 0x23) // sets the first power control register
sendCommand(0xC1, 0x10) // sets the second power control register
sendCommand(0xC5, 0x3E,0x28) // VCM control 1
sendCommand(0xC7, 0x86) // VCM control 2
sendCommand(0x36, 0x48) // rotation
sendCommand(0x3A, 0x55) // unknown
sendCommand(0xB1, 0x00,0x18) // unknown
sendCommand(0xB6, 0x08,0x82,0x27) // display function control
sendCommand(0xF2, 0x00) // 3Gamma disable
sendCommand(0x26, 0x01) // gamma curve select
sendCommand(0xE0, 0x0F,0x31,0x2B,0x0C,0x0E,0x08,0x4E,0xF1,0x37,0x07,0x10,0x03,0x0E,0x09,0x00) // set gamma 1
sendCommand(0xE1, 0x00,0x0E,0x14,0x03,0x11,0x07,0x31,0xC1,0x48,0x08,0x0F,0x0C,0x31,0x36,0x0F) // set gamma 2
sendCommand(0x11) // sleep off
sendCommand(0x29) // display on
You might be able to get away without doing some or all of that (though sleep off and display on at the end are likely necessary).


Another set of commands that might be helpful are for rotating the display (so that you can set which pixel is at 0,0). The 4 orientations are (where which version is upside down is somewhat arbitrary, this is just what I am calling them based on how I mounted the LCD):

Code: Select all

portrait:                sendCommand(0x36,0x48)
portrait (upside down):  sendCommand(0x36,0x88)
landscape:               sendCommand(0x36,0xE8)
landscape (upside down): sendCommand(0x36,0x28)

You can get more info from either Adafruit's arduino code, or the datasheets themselves (the datasheet is quite readable, at least the part about what the commands are). Let me know if you need any more information.

edfield
 
Posts: 3
Joined: Wed Nov 07, 2012 3:00 pm

Re: Getting the TFT display (ILI9340) running on Raspberry P

Post by edfield »

What a fantastic amount of information - thanks, I really appreciate it!

My problem was that I got as far as the first few commands in the arduino code and, like you, couldn't understand what they were for as they dont seem to relate to any command in the datasheet, so I thought I must have misunderstood something. (incidentally, I think the first one is "EF" not "3F", just in case it makes a difference!)

I take your point about the ineffeciency of setting individual pixels, and I could change this a bit, but it probably wont matter for my application. My "real" display is a binary (i.e. on or off, no colour) matrix, 256x128 pixels, about eight feet wide by four feet high, which doesn't fit on my desk! I'm planning to use this little TFT to emulate the display while I test and debug the software, and it wont block out all the light.....

Thanks again for your help.
Ed

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

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