Skip to content

Instantly share code, notes, and snippets.

@benpye
Last active December 11, 2021 19:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save benpye/2e31478aa05ac0714a12140d7d00114c to your computer and use it in GitHub Desktop.
Save benpye/2e31478aa05ac0714a12140d7d00114c to your computer and use it in GitHub Desktop.

Config Files

config.txt

# For more options and information see
# http://www.raspberrypi.org/documentation/configuration/config-txt.md
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
#disable_overscan=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on

# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
#dtparam=audio=on

# Disable spi and i2c, we need these pins.
dtparam=spi=off
dtparam=i2c_arm=off

# This is the bit for the display
dtoverlay=dpi18
overscan_left=0
overscan_right=0
overscan_top=0
overscan_bottom=0
framebuffer_width=320
framebuffer_height=240
enable_dpi_lcd=1
display_default_lcd=1
dpi_group=2
dpi_mode=87
dpi_output_format=3605013
hdmi_timings=240 0 20 10 10 320 0 6 2 0 0 0 0 60 0 6400000 6
display_rotate=0x10003

dpi18-overlay.dts

/plugin/;

/{
	compatible = "brcm,bcm2708";

	// There is no DPI driver module, but we need a platform device
	// node (that doesn't already use pinctrl) to hang the pinctrl
	// reference on - leds will do

	fragment@0 {
		target = <&leds>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&dpi18_pins>;
		};
	};

	fragment@1 {
		target = <&gpio>;
		__overlay__ {
			dpi18_pins: dpi18_pins {
				brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11
					     12 13 14 15 16 17 18 19 20
					     21>;
				brcm,function = <6>; /* alt2 */
				brcm,pull = <0>; /* no pull */
			};
		};
	};
};

msp_config.c

#include <msp430.h>

#include <stdint.h>

#define CSDIR P1DIR
#define CSOUT P1OUT
#define CSIN  P1IN
#define CSPIN BIT7

#define SCLDIR P1DIR
#define SCLOUT P1OUT
#define SCLIN  P1IN
#define SCLPIN BIT6

#define SDADIR P1DIR
#define SDAOUT P1OUT
#define SDAIN  P1IN
#define SDAPIN BIT5

void il9341_init()
{
	CSDIR  |= CSPIN;
	SCLDIR |= SCLPIN;
	SDADIR |= SDAPIN;

	CSOUT  |= CSPIN;
	SCLOUT &= ~SCLPIN;
	SDAOUT &= ~SDAPIN;
}

#define DELAY __delay_cycles(5)

// Write a word + DC bit
void il9341_write8_internal(int command, uint8_t b)
{
	if(command != 0)
		SDAOUT &= ~SDAPIN;
	else
		SDAOUT |= SDAPIN;

	DELAY;
	SCLOUT |= SCLPIN;
	DELAY;
	SCLOUT &= ~SCLPIN;

	int i;
	for(i = 7; i >= 0; i--)
	{
		if(b & (1 << i))
		{
			SDAOUT |= SDAPIN;
		}
		else
		{
			SDAOUT &= ~SDAPIN;
		}
		DELAY;
		SCLOUT |= SCLPIN;
		DELAY;
		SCLOUT &= ~SCLPIN;
	}
}

uint8_t il9341_read8_internal()
{
	uint8_t value = 0;

	int i;
	for(i = 7; i >= 0; i--)
	{
		DELAY;
		if(SDAIN & SDAPIN)
			value |= (1 << i);
		SCLOUT |= SCLPIN;
		DELAY;
		SCLOUT &= ~SCLPIN;
	}

	return value;
}

uint8_t il9341_read8(uint8_t command)
{
	// Reset clock before enabling LCD interface
	SCLOUT &= ~SCLPIN;
	CSOUT  &= ~CSPIN;

	// Switch to write
	SDADIR |= SDAPIN;
	il9341_write8_internal(1, command);

	// Switch to read
	SDADIR &= ~SDAPIN;
	uint8_t value = il9341_read8_internal();

	// Disable LCD interface
	CSOUT  |= CSPIN;

	return value;
}

uint32_t il9341_read24(uint8_t command)
{
	// Reset clock before enabling LCD interface
	SCLOUT &= ~SCLPIN;
	CSOUT  &= ~CSPIN;

	// Switch to write
	SDADIR |= SDAPIN;
	il9341_write8_internal(1, command);

	// Dummy clock cycle
	DELAY;
	SCLOUT |= SCLPIN;
	DELAY;
	SCLOUT &= ~SCLPIN;

	// Switch to read
	SDADIR &= ~SDAPIN;
	uint32_t value = il9341_read8_internal() << 16;
	value |= il9341_read8_internal() << 8;
	value |= il9341_read8_internal();

	// Disable LCD interface
	CSOUT  |= CSPIN;

	return value;
}

uint32_t il9341_read32(uint8_t command)
{
	// Reset clock before enabling LCD interface
	SCLOUT &= ~SCLPIN;
	CSOUT  &= ~CSPIN;

	// Switch to write
	SDADIR |= SDAPIN;
	il9341_write8_internal(1, command);

	// Dummy clock cycle
	DELAY;
	SCLOUT |= SCLPIN;
	DELAY;
	SCLOUT &= ~SCLPIN;

	// Switch to read
	SDADIR &= ~SDAPIN;
	uint32_t v[4];
	v[3] = il9341_read8_internal();
	v[2] = il9341_read8_internal();
	v[1] = il9341_read8_internal();
	v[0] = il9341_read8_internal();

	// Disable LCD interface
	CSOUT  |= CSPIN;

	return *(uint32_t *)v;
}

void il9341_write(uint8_t command, uint8_t *data, int num)
{
	// Reset clock before enabling LCD interface
	SCLOUT &= ~SCLPIN;
	CSOUT  &= ~CSPIN;

	// Switch to write
	SDADIR |= SDAPIN;
	il9341_write8_internal(1, command);

	int i;
	for(i = 0; i < num; i++)
		il9341_write8_internal(0, data[i]);

	// Disable LCD interface
	CSOUT  |= CSPIN;
}

int main(void)
{
	WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
	P1DIR |= 0x01;                            // Set P1.0 to output direction

	il9341_init();

	il9341_write(0x01, (void*)0, 0);
	__delay_cycles(120000);
	il9341_write(0x28, (void*)0, 0);
	il9341_write(0xB0, (uint8_t[]){0b11100000}, 1);
	il9341_write(0x3A, (uint8_t[]){0b01100110}, 1);
	il9341_write(0xF6, (uint8_t[]){0b00001001, 0b00100001, 0b00000110}, 3);
	il9341_write(0xB5, (uint8_t[]){0b00000100, 0b00000010, 0b00001010, 0b00010100}, 4);
	il9341_write(0xB6, (uint8_t[]){0x0A, 0x82, 0x27, 0x05}, 4);
	il9341_write(0x51, (uint8_t[]){0}, 1);
	il9341_write(0x11, (void*)0, 0);
	il9341_write(0x29, (void*)0, 0);

	for(;;);
}

Instructions

  1. Modify the config.txt to have the display settings above
  2. Hook up the display to the Raspberry Pi, ignore the display enable pin. You will need the full RGB18 interface + vsync, hsync and pclk.
  3. Compile the overlay using the dtc (should have packages for most Linux distros), make sure you compile as an overlay. Put it in overlays on the SD card.
  4. Use something to configure the display, you should be able to use the Raspberry Pi, but I used an MSP430 Launchpad with the code above. The config interface depends upon the config pins on the ILI9341 chip.

Important

The display module used MUST be configured for the RGB interface, not SPI or the 8080 interface. IM pins in the ILI9341 datasheet.

Different controllers/resolutions will mean you need to tweak the configuration code and the DPI settings in the config.txt , this should work with any ILI9341 display though.

Useful links

Let’s add a dirt cheap screen to the Raspberry Pi B+ - Same interface, different screen

DPI pinout - Useful to find where the pins are on the GPIO header (less useful for the compute module)

Compatible display - Has a controller and module datasheet easily avaliable, gives all the pins needed, handles the LCD backlight voltage. Bit more expensive than the eBay panels but most of those will not work. NOTE: Ask for the display with the RGB interface (send an email) and you probably want pin headers, not the flat flex connector. Make sure you get the 3.3V option.

ILI9341 datasheet

@woozer
Copy link

woozer commented Jan 17, 2017

Question, once the display is set to rgb mode using SPI, can the setting be saved to eeprom of the display? Or do I need to set the mode after each power up.

When I ask for an rgb version, will the mode RGB by default?

Thanks,
W

@robertely
Copy link

Thanks for posting this Ben, I'm going to try using these displays.

Also i'm glad you got something out of my lone blog post

@withgallantry
Copy link

Thanks for this post, it's been really useful. I'm curious about the IM pins. Every configuration I can find is either 8080 parallel mode or SPI. Do you know what configuration allows for RGB interface? I've looked in the data sheet extensively but as I said, can only find SPI or 8080.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment